I'm using libtiff to read Image data into an array. I have the following code
std::vector <uint32>> image;
uint32 width;
uint32 height;
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
uint32 npixels = width * height;
uint32* raster;
raster = (uint32*)_TIFFmalloc(npixels * sizeof(uint32));
if (TIFFReadRGBAImageOriented(tif, width, height, raster, ORIENTATION_TOPLEFT, 0) == 1)
{
std::cout << "success" << std:endl;
}
This code works. However, what I actually want is to reduce my width and height so that only a cropped part of the image is read into the raster. Thus my actual code for npixels is:
uint32 npixels = (width -100) * (height -100);
When I try to run this, I get an:
Exception Error at 0x00007FFEC7A2FC4E (tiff.dll): Access violation when trying to write at position 0x00000251B12C7000
In the libtiff documentation it says:
The raster is assumed to be an array of width times height 32-bit entries, where width must be less than or equal to the width of the image (height may be any non-zero size). If the raster dimensions are smaller than the image, the image data is cropped to the raster bounds.
based on that I thought reducing npixels does the trick... How do I cut the right and lower part of the image I want to write into my raster?
You just changed the number of elements in allocated buffer, but still try to read the image of original size, thus you get access violation since the buffer is overflown. To get the cropping you should pass correct width and height to TIFFReadRGBAImageOriented as well:
uint32 nwidth = width - 100;
uint32 nheight = height - 100;
uint32 npixels = nwidth * nheight;
raster = (uint32*)_TIFFmalloc(npixels * sizeof(uint32));
if (TIFFReadRGBAImageOriented(tif, nwidth, nheight, raster, ORIENTATION_TOPLEFT, 0) == 1)
{
std::cout << "success" << std:endl;
}
Related
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.
I am going to use the grabcutNPP from cuda sample in order to speed up the image processing. The original sample code is implemented for FIBITMAP, but my input/output type will be Mat.
I had figured out most of the code but stuck in the cudaMemcpyDeviceToHost step...As a CUDA beginner, I have no reason why it always stops at this step
Here is part of my code :
void grabcutGPU(Mat& _src, Mat& _dst, Rect _srcRect){
GrabCut *grabcut;
const size_t width = _src.rows;
const size_t height = _src.cols;
size_t image_pitch;
size_t result_pitch;
size_t trimap_pitch;
uchar4 *gpu_src, *gpu_dst;
unsigned char *d_trimap;
NppiRect rect;
// rect to nppirect
rect.x = _srcRect.x;
rect.y = _srcRect.y;
rect.width = _srcRect.width;
rect.height = _srcRect.height;
//melloc for src_image
checkCudaErrors(cudaMallocPitch(&gpu_src, &image_pitch, width * sizeof(uchar4), height));
checkCudaErrors(cudaMemcpy2D(gpu_src, image_pitch, _src.ptr<uchar4>(), width * sizeof(uchar4), width * sizeof(uchar4), height, cudaMemcpyHostToDevice));
// melloc foe rect
checkCudaErrors(cudaMallocPitch(&d_trimap, &trimap_pitch, width, height));
// Setup GrabCut
grabcut = new GrabCut(gpu_src, (int)image_pitch, d_trimap, (int)trimap_pitch, width, height);
//rect to memory
checkCudaErrors(TrimapFromRect(d_trimap, (int)trimap_pitch, rect, width, height));
//grabcut segmentation
grabcut->computeSegmentationFromTrimap();
//melloc for dst_image
checkCudaErrors(cudaMallocPitch(&gpu_dst, &result_pitch, width * 4, height));
//GPU process
checkCudaErrors(ApplyMatte(2, gpu_dst, (int)result_pitch, gpu_src, (int)image_pitch, grabcut->getAlpha(), grabcut->getAlphaPitch(), width, height));
size_t output_pitch = result_pitch;
//send result to dst
checkCudaErrors(cudaMemcpy2D(_dst.ptr(), (int)output_pitch, gpu_dst, result_pitch, width * 4, height, cudaMemcpyDeviceToHost));
delete grabcut;
checkCudaErrors(cudaDeviceSynchronize(), "Kernel Launch Failed");
checkCudaErrors(cudaFree(gpu_src), "CUDA Free Failed");
checkCudaErrors(cudaFree(gpu_dst));
checkCudaErrors(cudaFree(d_trimap), "CUDA Free Failed");}
This question is solved.
First of all, the row and col are carelessly mistaken.
Then, the input mat were 3 channels but this function needs 4 channels mat for passing result. It could be solved by converting colour type.
Thanks to Micka, or I might never notice the channel problem.
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.
I'm confused about the way libjpeg jpeg_read_scanlines works. It's my understanding that it decompresses a JPEG, row by row, and creates a decompressed pixel buffer.
Typical usage is something like:
jpeg_decompress_struct cinfo;
...
unsigned char* image = new unsigned char[cinfo.image_width * cinfo.image_height];
unsigned char* ptr = image;
int row_stride = cinfo.image_width;
while (cinfo.output_scanline < cinfo.image_height)
{
jpeg_read_scanlines(&cinfo, &ptr, 1);
ptr += row_stride;
}
Question: I'm confused about the output buffer size. In all example code I see which uses jpeg_read_scanlines, the size of the output buffer is width X height, where width and height refer to the dimensions of the JPEG file. So for a 10x10 JPEG file we'd have a 100 byte output buffer.
But... isn't the size of each RGB pixel 3 bytes (24-bit)? So shouldn't the uncompressed data actually be width X height X 3 bytes?
Why isn't it?
I notice that with code which uses jpeg_write_scanlines, the buffer to be compressed IS width X height X 3. So why is the buffer used with jpeg_read_scanlines only width X height?
You are only reading 1 line at a time with the line
jpeg_read_scanlines(&cinfo, &ptr, 1);
so you only needed the line
unsigned char* image = new unsigned char[cinfo.image_width * cinfo.image_height];
to be
unsigned char* image = new unsigned char[cinfo.image_width * cinfo.image_components];
The start of the buffer is being re-used for every scanline. Most of your current buffer is actually unused.
For RGB data, output_components will be 3 (R,G,B).
Here's some related documentation from libjpeg.txt:
output_width image width and height, as scaled
output_height
out_color_components # of color components in out_color_space
output_components # of color components returned per pixel
colormap the selected colormap, if any
actual_number_of_colors number of entries in colormap
output_components is 1 (a colormap index) when quantizing colors; otherwise it
equals out_color_components. It is the number of JSAMPLE values that will be
emitted per pixel in the output arrays.
Typically you will need to allocate data buffers to hold the incoming image.
You will need output_width * output_components JSAMPLEs per scanline in your
output buffer, and a total of output_height scanlines will be returned.
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!