Copy OpenCV IplImage or Mat into Freeimage FIBITMAP - opencv

I need to exchange data between FreeImage (FIBITMAP) and OpenCV format (IplImage and/or Mat). I'm fine with getting data from a FIBITMAP into an IplImage or Mat since FreeImage gives you a function FreeImage_GetScanLine which you can set the OPenCV imageData ptr equal to.
However, I'm stuck on how to do the reverse, i.e. once I have an OpenCV image, how do I get its data into a FreeImage image?

Here is a more detailed code for the conversion. There are many image data types in either library, I tried supporting most common ones. This assumes you are passing a cv::Mat as the source. FreeImage has the view perspective of lower left!
/* These are openCV types
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
*/
/* these are FI types
FIT_UNKNOWN = 0, // unknown type
FIT_BITMAP = 1, // standard image : 1-, 4-, 8-, 16-, 24-, 32-bit
FIT_UINT16 = 2, // array of unsigned short : unsigned 16-bit
FIT_INT16 = 3, // array of short : signed 16-bit
FIT_UINT32 = 4, // array of unsigned long : unsigned 32-bit
FIT_INT32 = 5, // array of long : signed 32-bit
FIT_FLOAT = 6, // array of float : 32-bit IEEE floating point
FIT_DOUBLE = 7, // array of double : 64-bit IEEE floating point
FIT_COMPLEX = 8, // array of FICOMPLEX : 2 x 64-bit IEEE floating point
FIT_RGB16 = 9, // 48-bit RGB image : 3 x 16-bit
FIT_RGBA16 = 10, // 64-bit RGBA image : 4 x 16-bit
FIT_RGBF = 11, // 96-bit RGB float image : 3 x 32-bit IEEE floating point
FIT_RGBAF = 12 // 128-bit RGBA float image : 4 x 32-bit IEEE floating point
*/
if(_dib) // get rid of the current dib.
FreeImage_Unload(_dib);
int width = src.size().width;
int height = src.size().height;
switch(src.type())
{
case CV_8U :{_dib = FreeImage_AllocateT(FIT_BITMAP,width, height, 8) ;}break; // 8 bit grayscale
case CV_8UC3:{_dib = FreeImage_AllocateT(FIT_BITMAP,width, height, 24);}break; // 24 bit RGB
case CV_16U :{_dib = FreeImage_AllocateT(FIT_UINT16,width, height, 16);}break; // 16 bit grayscale
case CV_16S :{_dib = FreeImage_AllocateT(FIT_INT16 ,width, height, 16);}break;
case CV_32S :{_dib = FreeImage_AllocateT(FIT_INT32 ,width, height, 32);}break;
case CV_32F :{_dib = FreeImage_AllocateT(FIT_FLOAT ,width, height, 32);}break;
case CV_64F :{_dib = FreeImage_AllocateT(FIT_DOUBLE,width, height, 32);}break;
default:ASSERT(FALSE);
}
if(_dib==NULL)
return FALSE;
int srcRowBytes = width * src.elemSize();
for (int ih=0;ih<height;ih++)
{
BYTE* ptr2Line = FreeImage_GetScanLine(_dib,(height-1)-ih);
memcpy(ptr2Line,src.ptr(ih),srcRowBytes);
}
_bHasChanged = TRUE;
return TRUE;

You have to be a little careful just copying the data pointer, a lot of image formats have padding to start each new line on eg a 4byte boundary.
If your image has a GetScanLine() function then it's probably safer to make an empty plImage*/cv::Mat and memcopy each row with the pointer returned from GetScanLine() and the .ptr() member of cv::MAt
cv::Mat &src
int srcRowBytes = width * src.elemSize();
for (int ih=0;ih<height;ih++) {
memcpy(dest.pointer_to_row(ih),src.ptr(ih),srcRowBytes);
}

Well, if you don't mind the copy, you can just create an IplImage*/cv::Mat header for the FIBITMAP and then copy (using the opencv function), like this:
cv::Mat src; // your source image
FIBITMAP whatever; // allocate space for your FIBITMAP here
cv::Mat wrapper(height, width, CV_8UC3, ptr_from_FIBITMAP, step);
src.copyTo(wrapper);

Related

How to get raw ARGB data from bitmap (iOS)?

Android:
import android.graphics.Bitmap;
public void getPixels (int[] pixels, int offset, int stride, int x, int y, int width, int height);
Bitmap bmap = source.renderCroppedGreyscaleBitmap();
int w=bmap.getWidth(),h=bmap.getHeight();
int[] pix = new int[w * h];
bmap.getPixels(pix, 0, w, 0, 0, w, h);
Returns in pixels[] a copy of the data in the bitmap.
Each value is a packed int representing a Color.
The stride parameter allows the caller to allow for gaps in the returned pixels array between rows.
For normal packed results, just pass width for the stride value.
The returned colors are non-premultiplied ARGB values.
iOS:
#implementation UIImage (Pixels)
-(unsigned char*) rgbaPixels
{
// The amount of bits per pixel, in this case we are doing RGBA so 4 byte = 32 bits
#define BITS_PER_PIXEL 32
// The amount of bits per component, in this it is the same as the bitsPerPixel divided by 4 because each component (such as Red) is only 8 bits
#define BITS_PER_COMPONENT (BITS_PER_PIXEL/4)
// The amount of bytes per pixel, in this case a pixel is made up of Red, Green, Blue and Alpha so it will be 4
#define BYTES_PER_PIXEL (BITS_PER_PIXEL/BITS_PER_COMPONENT)
// Define the colour space (in this case it's gray)
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
// Find out the number of bytes per row (it's just the width times the number of bytes per pixel)
size_t bytesPerRow = self.size.width * BYTES_PER_PIXEL;
// Allocate the appropriate amount of memory to hold the bitmap context
unsigned char* bitmapData = (unsigned char*) malloc(bytesPerRow*self.size.height);
// Create the bitmap context, we set the alpha to none here to tell the bitmap we don't care about alpha values
CGContextRef context = CGBitmapContextCreate(bitmapData,self.size.width,self.size.height,BITS_PER_COMPONENT,bytesPerRow,colourSpace,kCGImageAlphaFirst);//It returns null
/* We are done with the colour space now so no point in keeping it around*/
CGColorSpaceRelease(colourSpace);
// Create a CGRect to define the amount of pixels we want
CGRect rect = CGRectMake(0.0,0.0,self.size.width,self.size.height);
// Draw the bitmap context using the rectangle we just created as a bounds and the Core Graphics Image as the image source
CGContextDrawImage(context,rect,self.CGImage);
// Obtain the pixel data from the bitmap context
unsigned char* pixelData = (unsigned char*)CGBitmapContextGetData(context);
// Release the bitmap context because we are done using it
CGContextRelease(context);
//CGColorSpaceRelease(colourSpace);
return pixelData;
#undef BITS_PER_PIXEL
#undef BITS_PER_COMPONENT
}
But it can't work.
CGBitmapContextCreate(bitmapData,self.size.width,self.size.height,BITS_PER_COMPONENT,bytesPerRow,colourSpace,kCGImageAlphaFirst);
It returns NULL.
I need the same array as pix[ ] above,how can I make it?

How to calculate image histogram of 32bit floating point image in OPenCV

I want to calculate histogram of an image hows pixels are of type 32F (32 bit floating point). What should be the parameter values of "calcHist" function for:
- dims
- bins
- range
Well I've done this many times. Something like so:
cv::Mat matSrc; // this is a CV_32FC1 normalised image
int nHistSize = 65536;
float fRange[] = { 0.0f, 1.0f };
const float* fHistRange = { fRange };
cv::Mat matHist;
cv::calcHist(&matSrc, 1, 0, cv::Mat(), matHist, 1, &nHistSize, &fHistRange);
As it says in the documentation describing the source arrays:
Source arrays. They all should have the same depth, CV_8U or CV_32F ,
and the same size. Each of them can have an arbitrary number of
channels.
So CV_32F is supported. In this situation, the range (in my example 0.0 to 1.0) is binned into the number of bins required (in my example 65536).

How to make OpenCV IplImage for 16 bit gray-data?

This code is for 8 bit data to make gray-scale IplImage.
IplImage* img_gray_resize = NULL;
img_gray_resize = cvCreateImage(cvSize(320, 256), IPL_DEPTH_8U, 1);
DWORD dwCount;
LVDS_SetDataMode(0); // o for 8 bit mode and 1 for 16 bit mode
dwCount = (LONG)320 * (LONG)256;
unsigned char* m_pImage = NULL;
m_pImage = new unsigned char[320 * 256];
for (int i=0; i<320 * 256; i++) m_pImage[i] = NULL;
LVDS_GetFrame(&dwCount, m_pImage);
int width = 320;
int height = 256;
int nn = 0;
int ii = 0;
for (int y=0; y<height; y++)
{
for (int x=0; x<width; x++)
{
ii = y * width + x;
if(nn < (height*width))
img_gray_resize->imageData[ii] = m_pImage[nn++];
}
}
delete [] m_pImage;
I need to display 16 bit gray-scale image. If I display 8 bit data, some information is missing from the image. However, LVDS_SetDataMode() can provide both types of data. I am using a library for frame grabber device. Please help me.
16 bit images should be stored in IPL_DEPTH_16U (or CV_16U) mode. This is the correct memory layout.
However, displaying them depends on your display hardware.
Most regular display APIs, e.g. OpenCV's highgui, can only display 8-bit images.
To actually display the image, you will have to convert your image to 8-bits for display.
You will need to decide how to do this. There are many ways to do this, depending on your application and complexity. Some options are:
Show MSB = right-shift the image by 8 pixels.
Show LSB = saturate anything above 255.
In fact, right-shift by any value between 0-8 bits, combined with a cv::saturate_cast to avoid value wrap-around.
HDR->LDR = Apply dynamic range compression algorithms.
as I know,only 8bit data can be displayed,you need to find the best way to convert the 16bit to 8bit to minimize the information you lose. Histogram equalization can be applyed to do this.
Finally, I have solved the problem by following way:
dwCount = (LONG)320 * (LONG)256 * 2;
LVDS_SetDataMode(1);
img_gray_resize->imageData[ii] = m_pImage[nn++] >> 6;
Just shift bits to right (2, 3, 4, 5, 6, ...), where you get good result, use that value.

How to access each channel of a pixel using cuda tex2D

I'm learning cuda texture memory. Now, I got a opencv Iplimage, and I get its imagedata. Then I bind a texture to this uchar array, like below:
Iplimage *image = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
unsigned char* imageDataArray = (unsigned char*)image->imagedata;
texture<unsigned char,2,cudaReadModeElementType> tex;
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(8, 8, 8, 0,
cudaChannelFormatKindUnsigned);
cudaArray *cuArray = NULL;
CudaSafeCall(cudaMallocArray(&cuArray,&channelDesc,width,height));
cudaMemcpy2DToArray(cuArray,0,0,imageDataArray,image->widthstep,
width * sizeof(unsigned char), height, cudaMemcpyHostToDevice);
cudaBindTextureToArray(texC1_cf,cuArray_currentFrame, channelDesc);
Now I lanch my kernel, and I want to access each pixel, every channel of that image. This is where I get confused.
I use this code to get the pixel coordinate (X,Y):
int X = (blockIdx.x*blockDim.x+threadIdx.x);
int Y = (blockIdx.y*blockDim.y+threadIdx.y);
And how can I access each channel of this (X,Y)? what's the code below return?
tex2D(tex, X, Y);
Besides this, Can you tell me how texture memory using texture to access an array, and how this transform looks like?
To bind a 3 channel OpenCV image to cudaArray texture, you have to create a cudaArray of width equal to image->width * image->nChannels, because the channels are stored interleaved by OpenCV.
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<unsigned char>();
cudaArray *cuArray = NULL;
CudaSafeCall(cudaMallocArray(&cuArray,&channelDesc,width * image->nChannels,height));
cudaMemcpy2DToArray(cuArray,0,0,imageDataArray,image->widthstep, width * image->nChannels * sizeof(unsigned char), height, cudaMemcpyHostToDevice);
cudaBindTextureToArray(texC1_cf,cuArray_currentFrame, channelDesc);
Now, to access each channel separately in the kernel, you just have to multiply the x index with number of channels and add the offset of desired channel like this:
unsigned char blue = tex2D(tex, (3 * X) , Y);
unsigned char green = tex2D(tex, (3 * X) + 1, Y);
unsigned char red = tex2D(tex, (3 * X) + 2, Y);
First one is blue because OpenCV stores images with channel sequence BGR.
As for the error you get when you try to access texture<uchar3,..> using tex2D; CUDA only supports creating 2D textures of 1,2 and 4 element vector types. Unfortunately, ONLY 3 is not supported which is very good for binding RGB images and is a really desirable feature.

How to convert an 8-bit OpenCV IplImage* to a 32-bit IplImage*?

I need to convert an 8-bit IplImage to a 32-bits IplImage. Using documentation from all over the web I've tried the following things:
// general code
img2 = cvCreateImage(cvSize(img->width, img->height), 32, 3);
int height = img->height;
int width = img->width;
int channels = img->nChannels;
int step1 = img->widthStep;
int step2 = img2->widthStep;
int depth1 = img->depth;
int depth2 = img2->depth;
uchar *data1 = (uchar *)img->imageData;
uchar *data2 = (uchar *)img2->imageData;
for(h=0;h<height;h++) for(w=0;w<width;w++) for(c=0;c<channels;c++) {
// attempt code...
}
// attempt one
// result: white image, two red spots which appear in the original image too.
// this is the closest result, what's going wrong?!
// see: http://files.dazjorz.com/cache/conversion.png
((float*)data2+h*step2+w*channels+c)[0] = data1[h*step1+w*channels+c];
// attempt two
// when I change float to unsigned long in both previous examples, I get a black screen.
// attempt three
// result: seemingly random data to the top of the screen.
data2[h*step2+w*channels*3+c] = data1[h*step1+w*channels+c];
data2[h*step2+w*channels*3+c+1] = 0x00;
data2[h*step2+w*channels*3+c+2] = 0x00;
// and then some other things. Nothing did what I wanted. I couldn't get an output
// image which looked the same as the input image.
As you see I don't really know what I'm doing. I'd love to find out, but I'd love it more if I could get this done correctly.
Thanks for any help I get!
The function you are looking for is cvConvertScale(). It automagically does any type conversion for you. You just have to specify that you want to scale by a factor of 1/255 (which maps the range [0...255] to [0...1]).
Example:
IplImage *im8 = cvLoadImage(argv[1]);
IplImage *im32 = cvCreateImage(cvSize(im8->width, im8->height), 32, 3);
cvConvertScale(im8, im32, 1/255.);
Note the dot in 1/255. - to force a double division. Without it you get a scale of 0.
Perhaps this link can help you?
Edit In response to the second edit of the OP and the comment
Have you tried
float value = 0.5
instead of
float value = 0x0000001;
I thought the range for a float color value goes from 0.0 to 1.0, where 1.0 is white.
Floating point colors go from 0.0 to 1.0, and uchars go from 0 to 255. The following code fixes it:
// h is height, w is width, c is current channel (0 to 2)
int b = ((uchar *)(img->imageData + h*img->widthStep))[w*img->nChannels + c];
((float *)(img2->imageData + h*img2->widthStep))[w*img2->nChannels + c] = ((float)b) / 255.0;
Many, many thanks to Stefan Schmidt for helping me fix this!
If you do not put the dot (.), some compilers will understand is as an int division, giving you a int result (zero in this case).
You can create an IplImage wrapper using boost::shared_ptr and template-metaprogramming. I have done that, and I get automatic garbage collection, together with automatic image conversions from one depth to another, or from one-channel to multi-channel images.
I have called the API blImageAPI and it can be found here:
http://www.barbato.us/2010/10/14/image-data-structure-based-shared_ptr-iplimage/
It is very fast, and make code very readable, (good for maintaining algorithms)
It is also can be used instead of IplImage in opencv algorithms without changing anything.
Good luck and have fun writing algorithms!!!
IplImage *img8,*img32;
img8 =cvLoadImage("a.jpg",1);
cvNamedWindow("Convert",1);
img32 = cvCreateImage(cvGetSize(img8),IPL_DEPTH_32F,3);
cvConvertScale(img8,img32,1.0/255.0,0.0);
//For Confirmation Check the pixel values (between 0 - 1)
for(int row = 0; row < img32->height; row++ ){
float* pt = (float*) (img32->imageData + row * img32->widthStep);
for ( int col = 0; col < width; col++ )
printf("\n %3.3f , %3.3f , %3.3f ",pt[3*col],pt[3*col+1],pt[3*col+2]);
}
cvShowImage("Convert",img32);
cvWaitKey(0);
cvReleaseImage(&img8);
cvReleaseImage(&img32);
cvDestroyWindow("Convert");

Resources