Magick++ C++ Input 16-bit Greyscale PNG - c++

I've got a series of PNG files that I know is in a 16-bit greyscale format. I haven't had a problem loading this up with Magick++ and getting access to the data in 8-bit format (this was done, primarily, because all the code was there and no change was necessary).
magick_image->write(0, 0, grey->GetWidth(), grey->GetHeight(), "R", Magick::CharPixel, grey->GetBeginData());
Please note that grey is in our own container for images, but the memory layout is just one block of pre-allocated memory.
I've now been told that we need to get access to the full 16-bit range, and I'm not too sure how to do this. I presume I wouldn't use Magick::CharPixel, but the others described in the documentation don't specify what bit size they actually are.
So, I need to be able to do the following things:
Determine whether it is a 16-bit image in the first place
Read out of the Magick::Image class into a block of memory that would map to place into a block of memory for a u_int16_t
Can anyone help with this?

There's a few ways to do this, but without seeing the definitions of magick_image & grey, this answer is based on a few assumptions.
To work with bare-bone grey, I would argue it could be defined as...
struct MaybeGray {
std::vector<u_int16_t> blob;
size_t width;
size_t height;
MaybeGray(size_t w, size_t h) : blob(w * h) { width = w; height = h; };
size_t getWidth() { return width; };
size_t getHeight() { return height; };
void * getBeginData() { return blob.data(); } // C11? Is this allowed?
};
Next, I'll create a 2x2 canvas image to satisfy magick_image.
Magick::Image magick_image;
Magick::Geometry size(2, 2);
magick_image.size(size);
magick_image.read("XC:blue");
// Apply gray scale (Magick::Image.quantize may be better.)
magick_image.type(Magick::GrayscaleType);
Determine whether it is a 16-bit image in the first place
Magick::Image.depth can be used to identify, and set depth value.
const size_t DEPTH = 16;
if ( magick_image.depth() != DEPTH ) {
magick_image.depth(DEPTH);
}
Read out of the Magick::Image class into a block of memory that would map to place into a block of memory for a u_int16_t
What you're doing with Magick::Image.write is correct. However Magick::CharPixel would be for 8-bit, 256 colors, depth. For 16-bit, use Magick::ShortPixel.
struct MaybeGray gray(2, 2);
magick_image.write(0, 0,
gray.getWidth(),
gray.getHeight(),
"R",
Magick::ShortPixel,
gray.getBeginData());
How to test
A image canvas of XC:red should fill the blob with 0xFFFF, and XC:black with 0x0000. Use ImageMagick's convert & identify utilities to create the expected results.
# Create identical canvas
convert -size 2x2 xc:blue -colorspace gray -depth 16 blue.tiff
# Dump info
identify -verbose blue.tiff
Image: blue.tiff
Format: TIFF (Tagged Image File Format)
Class: DirectClass
Geometry: 2x2+0+0
Units: PixelsPerInch
Type: Grayscale
Base type: Grayscale
Endianess: LSB
Colorspace: Gray
Depth: 16/14-bit
Channel depth:
gray: 14-bit
Channel statistics:
Gray:
min: 4732 (0.0722057)
max: 4732 (0.0722057)
mean: 4732 (0.0722057)
standard deviation: 0 (0)
kurtosis: 0
skewness: 0
Colors: 1
Histogram:
4: ( 4732, 4732, 4732) #127C127C127C gray(7.22057%)
# ... rest omitted
And the verbose info confirms that I have 16-bit gray image, and the histogram informs me that MaybeGrey.blob would be filled with 4 0x127C.
Which it is.

Related

uint8_t buffer to cv::Mat conversion results in distorted image

I have a Mipi camera that captures frames and stores them into the struct buffer that you can see below. Once the frame is stored I want to convert it into a cv::Mat, the thing is that the Mat ends up looking like the first pic.
The var buf.index is just part of the V4L2 API, useful to understand which buffer I'm using.
//The structure where the data is stored
struct buffer{
void *start;
size_t length;
};
struct buffer *buffers;
//buffer->mat
cv::Mat im = cv::Mat(cv::Size(width, height), CV_8UC3, ((uint8_t*)buffers[buf.index].start));
At first I thought that the data might be corrupted but storing the image with lodepng results in a nice image without any distortion.
unsigned char* out_buf = (unsigned char*)malloc( width * height * 3);
for(int pix = 0; pix < width*height; ++pix) {
memcpy(out_buf + pix*3, ((uint8_t*)buffers[buf.index].start)+4*pix+1, 3);
}
lodepng_encode24_file(filename, out_buf, width, height);
I bet it's something really silly.
the picture you post has oddly colored pixels and the patterns look like there's more information than simply 24 bits per pixel.
after inspecting the data, it appears that V4L gives you four bytes per pixel, and the first byte is always 0xFF (let's call that X). further, the channel order seems to be XRGB.
create a cv::Mat using 8UC4 to contain the data.
to use the picture in OpenCV, you need BGR order. cv::split the received data into its four color planes which are X,R,G,B. use cv::merge to reassemble the B,G,R planes into a picture that OpenCV can handle, or reassemble into R,G,B to create a Mat for other purposes (that other library you seem to use).

DevIL/OpenIL isn't loading alpha channel

I am having an issue where the .png image that I want to load as a byte array using DevIL is not having an alpha channel.
A complete black image is also appearing as having alpha channel values as 0.
This is my image loading function:
DevILCall(ilGenImages(1, &m_ImageID));
DevILCall(ilBindImage(m_ImageID));
ASSERT("Loading image: " + path);
DevILCall(ilLoadImage(path.c_str()));
GraphicComponents::Image image(
ilGetData(),
ilGetInteger(IL_IMAGE_HEIGHT),
ilGetInteger(IL_IMAGE_WIDTH),
ilGetInteger(IL_IMAGE_BITS_PER_PIXEL)
);
return image;
The Image object I am using is as follows:
struct Image
{
ILubyte * m_Image;
const unsigned int m_Height;
const unsigned int m_Width;
const unsigned int m_BPP;
Image(ILubyte imageData[ ], unsigned int height, unsigned int width, unsigned int bpp);
~Image();
};
And this is how I am printing out the image data for now:
for(unsigned int i = 0; i < image->m_Height*image->m_Width*4; i+=4)
{
LOG("Red:");
LOG((int) image->m_Image[i]);
LOG("Green:");
LOG((int) image->m_Image[i+1]);
LOG("Blue:");
LOG((int) image->m_Image[i+2]);
LOG("Alpha:");
LOG((int) image->m_Image[i+3]);
}
I also tried using the ilTexImage() to format the loaded image to RGBA format but that also doesn't seem to work. The printing loop starts reading garbage values when I change the maximum value of the loop variable to 4 times the number of pixels in the image.
The image is also confirmed to have an alpha channel.
What might be going wrong here?
EDIT: ilGetInteger(IL_IMAGE_BPP) is returning 3, which should mean RGB for now. When I use the ilTexImage() to force 4 channels, then ilGetInteger(IL_IMAGE_BPP) returns 4 but I still see garbage values popping up at the std output
The problem was fixed by a simple ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE) call after loading the image.
I suppose DevIL loads the image in RGB mode with unsigned byte values by default and to use otherwise, you need to convert the loaded image using ilConvertImage().

Loading RAW grayscale image with FreeImage

How can I load RAW 16-bit grayscale image with FreeImage?
I have unsigned char* buffer with raw data. I know its dimensions in pixels and I know it is 16bit grayscale.
I'm trying to load it with
FIBITMAP* bmp = FreeImage_ConvertFromRawBits(buffer, 1000, 1506, 2000, 16, 0, 0, 0);
and get broken RGB888 image. It is unclear what color masks I should use for grayscale as it has only one channel.
After many experiments I found partially working solution with FreeImage_ConvertFromRawBitsEx:
FIBITMAP* bmp = FreeImage_ConvertFromRawBitsEx(true, buffer, FIT_UINT16, 1000, 1506, 2000, 16, 0xFFFF, 0xFFFF, 0xFFFF);
(thanks #1201ProgramAlarm for hint with masks).
In this way, FreeImage loads the data, but in some semi-custom format. Most of conversion and saving functions (tried: JPG, PNG, BMP, TIF) fail.
As I can't load data in native 16bit format, I preferred to convert it into 8bit grayscale
unsigned short* buffer = new unsigned short[1000 * 1506];
// load data
unsigned char* buffer2 = new unsigned char[1000 * 1506];
for (int i = 0; i < 1000 * 1506; i++)
buffer2[i] = (unsigned char)(buffer[i] / 256.f);
FIBITMAP* bmp = FreeImage_ConvertFromRawBits(buffer2, 1000, 1506, 1000, 8, 0xFF, 0xFF, 0xFF, true);
This is really not the best solution, I even don't want to mark it as right answer (will wait for something better). But after this the format will be convenient for FreeImage and it could save/convert data to whatever.
Concerning your issue: I have read this from their PDF documentation FreeImage1370.pdf:
FreeImage_ConvertFromRawBits
1 4 8 16 24 32
DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertFromRawBits(BYTE *bits, int width, int
height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned
blue_mask, BOOL topdown FI_DEFAULT(FALSE));
Converts a raw bitmap somewhere in memory to a FIBITMAP. The parameters in this
function are used to describe the raw bitmap. The first parameter is a pointer to the start of
the raw bits. The width and height parameter describe the size of the bitmap. The pitch
defines the total width of a scanline in the source bitmap, including padding bytes that may be
applied. The bpp parameter tells FreeImage what the bit depth of the bitmap is. The
red_mask, green_mask and blue_mask parameters tell FreeImage the bit-layout of the color
components in the bitmap. The last parameter, topdown, will store the bitmap top-left pixel
first when it is TRUE or bottom-left pixel first when it is FALSE.
When the source bitmap uses a 32-bit padding, you can calculate the pitch using the
following formula:
int pitch = ((((bpp * width) + 31) / 32) * 4);
In the code you are showing:
FIBITMAP* bmp = FreeImage_ConvertFromRawBits(buffer, 1000, 1506, 2000, 16, 0, 0, 0);
You have the appropriate FIBTMAP* return type, you pass in your buffer of raw bits. From there the 2nd & 3rd parameters which are the width & height: width = 1000, height = 1506 and the 4th parameter which is the pitch: pitch = 2000 (if the bitmap is using 32bit padding refer to the last note above), the 5th parameter will be the bit depth measured in bpp you have as bpp = 16, the next 3 parameters are for your RGB color masks. Here you label them all as being 0. The last parameter is a bool flag for the orientation of the image :
if (topdown == true ) {
stores top-left pixel first )
else {
bottom left pixel is stored first
}
in which you omit the value.
Without more code of how you are reading in the file, parsing the header information etc. to prepare your buffer it is hard to tell where else there may be an error or an issue, but from what you provided; I think you need to check the color channel masks for grayscale images.
EDIT - I found another PDF for FreeImage from standford.edu here that refers to an older version 3.13.1 however the function declaration - definition doesn't look like it has changed any and they provide examples for b FreeImage_ConvertToRawBits & Free_Image_ConvertFromRawBits:
// this code assumes there is a bitmap loaded and
// present in a variable called ‘dib’
// convert a bitmap to a 32-bit raw buffer (top-left pixel first)
// --------------------------------------------------------------
FIBITMAP *src = FreeImage_ConvertTo32Bits(dib);
FreeImage_Unload(dib);
// Allocate a raw buffer
int width = FreeImage_GetWidth(src);
int height = FreeImage_GetHeight(src);
int scan_width = FreeImage_GetPitch(src);
BYTE *bits = (BYTE*)malloc(height * scan_width);
// convert the bitmap to raw bits (top-left pixel first)
FreeImage_ConvertToRawBits(bits, src, scan_width, 32,
FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK,
TRUE);
FreeImage_Unload(src);
// convert a 32-bit raw buffer (top-left pixel first) to a FIBITMAP
// ----------------------------------------------------------------
FIBITMAP *dst = FreeImage_ConvertFromRawBits(bits, width, height, scan_width,
32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, FALSE);
I think this should help you with your question about the bit masks for the color channels in a grayscale image.
You already mentioned the FreeImage_ConvertFromRawBitsEx() function, which was added at some point between FreeImage v3.8 and v3.17, but are you calling it correctly? I was able to use this function with 16-bit grayscale data:
int nBytesPerRow = nWidth * 2;
int nBitsPerPixel = 16;
FIBITMAP* pFIB = FreeImage_ConvertFromRawBitsEx(TRUE, pImageData, FIT_UINT16, nWidth, nHeight, nBytesPerRow, nBitsPerPixel, 0, 0, 0, TRUE);
Note that nBytesPerRow and nBitsPerPixel have to be specified correctly for the 16-bit data. Also, I believe the color mask parameters are irrelevant for this data, since it is monochrome.
EDIT: I noticed that you said that saving the 16-bit data did not work correctly. That may be due to the file formats themselves. The only file format that I have found to be compatible with 16-bit grayscale data is TIFF. So, if you have 16-bit grayscale data, you can save a TIFF with FreeImage_Save() but you cannot save a BMP.

Going from PNG data to tesseract

I am trying to move image data from Magick++ to tesseract.
I have the PNG data and some info about it.
And the signature for the tesseract method is:
void SetImage(const unsigned char* imagedata, int width, int height, int bytes_per_pixel, int bytes_per_line);
The first three arguments I can supply just fine. But bytes_per_pixel and bytes_per_line I'm not so sure about. The image itself has 11564 pixels but the length of the data is only 356 bytes... It's mostly a white image with some text. 11564/356 = 32.48 which obviously is not the correct bytes per pixel. How can I get the right bytes / pixel information? It's ok to just get that for one image on my desktop or something and set it as a constant, all the images I'm processing will have the same format.
Then as far as bytes per line, would that just be image width in pixels * bytes per pixel?
bytes_per_pixel can be obtained from PNG data. They are usually 8, 24 or 32.
bytes_per_line too, but you can compute it doing: bytes_per_pixel * width / 8

How to read an 8bit bitmap's pixel colour and postion from byte array

I currently have my 8bit bitmap's pixel data stored in an array of bytes: BYTE* pixelData.
How would I now go about coding my own function to return a pixel's colour by it's position? I've done a fair amount of searching and haven't found anything that covers this using C++.
BYTE GetPixelColor(BYTE* src, int x, int y, int srcWidth)
{
return src[y * srcWidth + x];
}
...
//suppose you have a 800 x 600 bmp, to get the color for pixel at x 30, y 200
BYTE color = GetPixelColor(src, 30, 200, 800);
You can't, not enough data. Almost all 8bpp pixel formats are indexed formats. They use a color table that stores the actual RGB color, the byte at the pixel location is an index into that table. The table contains 256 entries.
You'll also need a pointer to the color table.