FreeImage: Get pixel color - c++

I'm writing a little application that reads color of each pixel in image and writes it to file. First I did it in Python, buit it's too slow on big images. Then I discovered FreeImage library, which I could use, but I can't understand how to use GetPixelColor method.
Could you please provide an example on how to get color, for example, of pixel[50:50]?
Here is information about GetPixelColor: http://freeimage.sourceforge.net/fnet/html/13E6BB72.htm.
Thank you very much!

With FreeImagePlus using a 24 or 32 bit image, getting the pixel at coords 50, 50 would look like this:
fipImage input;
RGBQUAD pixel;
input.load("myimage.png");
height = in.getHeight();
in.getPixelColor(50, height-1-50, &pixel);
Be aware that in FreeImage the origin is bottom left, so y values will probably need to be inverted by subtracting y from the image height as above.

To get pixel color from an input image: img, from a function call let's say: void read_image(const char* img) follow the below code snippet.
Here is the code snippet for above read_image function:
FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(img);
FIBITMAP *bmp = FreeImage_Load(fif, img);
unsigned width = FreeImage_GetWidth(bmp);
unsigned height = FreeImage_GetHeight(bmp);
int bpp = FreeImage_GetBPP(bmp);
FIBITMAP* bitmap = FreeImage_Allocate(width, height, bpp);
RGBQUAD color; FreeImage_GetPixelColor(bitmap, x, y, &color);
variable color will contain the color of the image pixel. You can extract rgb values as follows:
float r,g,b;
r = color.rgbRed;
g = color.rgbGreen;
b = color.rgbBlue;
Hope it helps!

Related

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.

Why is the OpenCL Kernel not using normal x y cooordinates with Image2D?

TLDR;
For anyone arriving here whilst trying to figure out how to do gaussian blur or grayscale with OpenCL, the final working code is here. Note that in that repo I'm actually running the whole thing inside Docker with GPU access using Nvidia's Docker wrapper. You can look inside the 'Dockerfile' for the steps that need to be taken to get the code running, or just run it using Nvidia-Docker if you have that setup and are running on an Nvidia GPU.
Original Question:
Using the following kernel in an OpenCL image filter application I get the expected result, that is, a returned grayscale version of the input image:
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |
CLK_ADDRESS_CLAMP_TO_EDGE |
CLK_FILTER_NEAREST;
__kernel void process(__read_only image2d_t src,
__write_only image2d_t dst)
{
int x = get_global_id(0);
int y = get_global_id(1);
float4 color;
color = read_imagef(src, sampler, (int2)(x, y));
float gray = (color.x + color.y + color.z) / 3;
write_imagef(dst, (int2)(x,y), (float4)(gray, gray, gray, 0));
}
So far, so good. I then tried to create a kernel that would just copy across the top and left border of the image:
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |
CLK_ADDRESS_CLAMP_TO_EDGE |
CLK_FILTER_NEAREST;
__kernel void process(__read_only image2d_t src,
__write_only image2d_t dst)
{
int x = get_global_id(0);
int y = get_global_id(1);
float4 color;
if (x < 10 || y < 10)
{
color = read_imagef(src, sampler, (int2)(x, y));
write_imagef(dst, (int2)(x,y), (float4)(color.x, color.y, color.z, 0));
}
else
{
write_imagef(dst, (int2)(x,y), (float4)(0,0,0,0));
}
}
The returned image is not what I expected:
I'm loading the input image this way:
// Load an image using the OpenCV library and create an OpenCL
// image out of it
cl::Image2D LoadImage(cl::Context context, char *fileName, int &width, int &height)
{
cv::Mat image = cv::imread(fileName, CV_LOAD_IMAGE_COLOR);
cv::Mat imageRGBA;
width = image.rows;
height = image.cols;
cv::cvtColor(image, imageRGBA, CV_RGB2RGBA);
char *buffer = reinterpret_cast<char *>(imageRGBA.data);
cl::Image2D clImage(context,
CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
cl::ImageFormat(CL_RGBA, CL_UNORM_INT8),
width,
height,
0,
buffer);
return clImage;
}
The output image:
cl::Image2D imageOutput(context,
CL_MEM_WRITE_ONLY,
cl::ImageFormat(CL_RGBA, CL_UNORM_INT8),
width,
height,
0,
NULL);
The Kernel:
cl::Program program(context, util::loadProgram("border.cl"), true);
cl::make_kernel<cl::Image2D, cl::Image2D> filter(program, "process");
cl::NDRange global(width, height);
filter(cl::EnqueueArgs(queue, global), clImageInput, imageOutput);
Then reading the image back:
cl::size_t<3> origin;
origin[0] = 0; origin[1] = 0, origin[2] = 0;
cl::size_t<3> region;
region[0] = width; region[1] = height; region[2] = 1;
float* oup = new float[width * height];
queue.enqueueReadImage(imageOutput, CL_TRUE, origin, region, 0, 0, oup);
cv::imwrite(filename_out, cv::Mat(width, height, CV_8UC4, oup));
Why is the image being processed the way it is? Only selecting pixels with a y coordinate less than 10 seems to work, but selecting pixels with an x coordinate less than 10 seems to stagger across the image.
if I write a test image using the following line in the kernel:
write_imagef(dst, (int2)(x,y), (float4)((float)x / 512.0f, 0, 0, 0));
I get the following image:
The first strange thing is that the blue channel is being set, not the red. I have no idea why as I am alway loading and saving the image in RGBA order. Secondly, the banding is very unusual, I'm not sure how to interpret this.
If I use the following line in the kernel:
write_imagef(dst, (int2)(x,y), (float4)(0, (float)y / 512.0f, 0, 0));
I get the following image:
This looks the way I would expect.
I can provide more code if necessary but using the grayscale kernel in the exact same harness works perfectly. As does another kernel not listed here which simply copies all the pixels across.
I'm running the code on and Nvidia Geforce 980M with OpenCL 1.2
I'm not seeing anything obvious yet. One strange thing: your image is CL_RGBA, CL_UNORM_INT8 but you're reading it out into an array of floats? How are you displaying it from that? Second, I'm not famliar with your kernel launch technique; what is filter and is it launching with dimension of 2? Regarding the issue you're seeing, I'd suggest using process of elimination to figure out where the problem lies. For example, (1) if you remove the conditional and copy all pixels, do you get the whole image? (2) Instead of writing black where the conditional is false, what if you write a Red channel gradient based on X position and a Green channel gradient based on Y position. Do you get a double gradient? Based on results, continue to divide the problem until you find the cause. It looks a lot like a row pitch issue, perhaps in the display function?
Ok, so the issue was the way I was reading height and width was backwards, i.e.
width = image.rows;
height = image.cols;
Should have been
height = image.rows;
width = image.cols;
With this corrected, the rest of the code can stay the same, except the last line where I save the image to disk, here the values need to be swapped again, i.e.
cv::imwrite(filename_out, cv::Mat(width, height, CV_8UC4, oup));
Needs to change to:
cv::imwrite(filename_out, cv::Mat(height, width, CV_8UC4, oup));
I think this ultimately comes down to the matrix approach to an image where the first coordinate is actually the row number, which is the height and the second coordinate is the column number, which is the width.
The diagnostics #Dithermaster mentioned really helped, as did printing out the assumed width and height, which was ultimately incorrect.
It's interesting that by having both of those errors in the code a pixel for pixel copy worked fine, but once you start to perform actions based on the x,y coordinates you get some really funky results.

Count the black pixels using OpenCV

I'm working in opencv 2.4.0 and C++
I'm trying to do an exercise that says I should load an RGB image, convert it to gray scale and save the new image. The next step is to make the grayscale image into a binary image and store that image. This much I have working.
My problem is in counting the amount of black pixels in the binary image.
So far I've searched the web and looked in the book. The method that I've found that seems the most useful is.
int TotalNumberOfPixels = width * height;
int ZeroPixels = TotalNumberOfPixels - cvCountNonZero(cv_image);
But I don't know how to store these values and use them in cvCountNonZero(). When I pass the the image I want counted from to this function I get an error.
int main()
{
Mat rgbImage, grayImage, resizedImage, bwImage, result;
rgbImage = imread("C:/MeBGR.jpg");
cvtColor(rgbImage, grayImage, CV_RGB2GRAY);
resize(grayImage, resizedImage, Size(grayImage.cols/3,grayImage.rows/4),
0, 0, INTER_LINEAR);
imwrite("C:/Jakob/Gray_Image.jpg", resizedImage);
bwImage = imread("C:/Jakob/Gray_Image.jpg");
threshold(bwImage, bwImage, 120, 255, CV_THRESH_BINARY);
imwrite("C:/Jakob/Binary_Image.jpg", bwImage);
imshow("Original", rgbImage);
imshow("Resized", resizedImage);
imshow("Resized Binary", bwImage);
waitKey(0);
return 0;
}
So far this code is very basic but it does what it's supposed to for now. Some adjustments will be made later to clean it up :)
You can use countNonZero to count the number of pixels that are not black (>0) in an image. If you want to count the number of black (==0) pixels, you need to subtract the number of pixels that are not black from the number of pixels in the image (the image width * height).
This code should work:
int TotalNumberOfPixels = bwImage.rows * bwImage.cols;
int ZeroPixels = TotalNumberOfPixels - countNonZero(bwImage);
cout<<"The number of pixels that are zero is "<<ZeroPixels<<endl;

OpenGL - Getting The Display Color Bits

I'm looking at the LWJGL wiki for taking a screenshot (found here) and I noticed the comment Assuming a 32-bit display with a byte each for red, green, blue, and alpha. in this code:
GL11.glReadBuffer(GL11.GL_FRONT);
int width = Display.getDisplayMode().getWidth();
int height= Display.getDisplayMode().getHeight();
int bpp = 4; // Assuming a 32-bit display with a byte each for red, green,
//blue, and alpha.
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
GL11.glReadPixels(0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer );
How would I change the var bpp based off of the current system?
In case you're still looking for an answer:
You can get the bytes per pixel of the current display mode by getting it from LWJGL's Display class. It would look something like this:
int bpp = Display.getDisplayMode().getBitsPerPixel()/4

getting Y value[Ycbcr] of one Pixel in opencv

I'm trying to get the Y value of pixel from a frame that's in Ycbcr color mode.
here what I' wrote:
cv::Mat frame, Ycbcrframe, helpframe;
........
cvtColor(frame,yCbCrFrame,CV_RGB2YCrCb); // converting to Ycbcr
Vec3b intensity =yCbCrFrame.at<uchar>(YPoint);
uchar yv = intensity.val[0]; // I thought it's my Y value but its not, coz he gives me I think the Blue channel of RGB color space
any Idea how what the correct way to do that
what about the following code?
Vec3f Y_pix = YCbCrframe.at<Vec3f>(rows, cols);
int pixelval = Y_pix[0];
(P.S. I havent tried it yet)
You need to know both the depth (numerical format and precision of channel sample) as well as the channel count (typically 3, but can also be 1 (monochrome) or 4 (alpha-containing)), ahead of time.
For 3-channel, 8-bit unsigned integer (a.k.a. byte or uchar) pixel format, each pixel can be accessed with
mat8UC3.at<cv::Vec3b>(pt);