How to get raw ARGB data from bitmap (iOS)? - 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?

Related

Why does CGBitmapContextCreate expect double the number of bytesPerRow?

I'm trying to generate a pixel buffer using a bitmap context on iOS (in ObjC). The abridged code (to remove null checks etc) is below.
CGFloat width = 1;
CGFloat height = 1;
CVPixelBufferRef buffer;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
width,
height,
kCVPixelFormatType_OneComponent8,
nil,
&buffer);
CVPixelBufferLockBaseAddress(buffer, 0);
void *data = CVPixelBufferGetBaseAddress(buffer);
CGColorSpaceRef space = CGColorSpaceCreateDeviceGray();
CGContextRef ctx = CGBitmapContextCreate(data,
width,
height,
8,
0,
space,
(CGBitmapInfo) kCGImageAlphaNoneSkipLast);
// ... draw into context
CVPixelBufferUnlockBaseAddress(buffer, 0);
This is trying to create a bitmap context for a single pixel, where both the input and the output pixels are 8-bit grayscale.
I get the following output (I added the bold):
CGBitmapContextCreate: invalid data bytes/row: should be at least 2 for 8 integer bits/component, 1 components, kCGImageAlphaNoneSkipLast.
Why does it double the expected bytes per row? This is consistent for the width / height combinations I've tried, and 'works' if I halve the width parameter in CGBitmapContextCreate. Note also that if I pass in a value for bytesPerRow then it still fails this check and gives the same output.
Am I missing something obvious?
Edit: formatting.
kCGImageAlphaNoneSkipLast was wrong. I needed to use kCGImageAlphaNone.
The bitmap create call now looks like:
CGContextRef ctx = CGBitmapContextCreate(data,
width,
height,
8,
CVPixelBufferGetBytesPerRow(buffer),
space,
kCGImageAlphaNone);

How can I manipulate the pixel values in a CGImageRef in Xcode

I have some
CGImageRef cgImage = "something"
Is there a way to manipulate the pixel values of this cgImage? For example if this image contains values between 0.0001 and 3000 thus when I try to view or release the image this way in an NSImageView (How can I show an image in a NSView using an CGImageRef image)
I get a black image, all pixels are black, I think it has to do with setting the pixel range values in a different color map (I don't know).
I want to be able to manipulate or change the pixel values or just be able to see the image by manipulating the color map range.
I have tried this but obviously it doesn't work:
CGContextDrawImage(ctx, CGRectMake(0,0, CGBitmapContextGetWidth(ctx),CGBitmapContextGetHeight(ctx)),cgImage);
UInt8 *data = CGBitmapContextGetData(ctx);
for (**all pixel values and i++ **) {
data[i] = **change to another value I want depending on the value in data[i]**;
}
Thank you,
In order to manipulate individual pixels in an image
allocate a buffer to hold the pixels
create a memory bitmap context using that buffer
draw the image into the context, which puts the pixels into the
buffer
change the pixels as desired
create a new image from the context
free up resources (note be sure to check for leaks using instruments)
Here's some sample code to get you started. This code will swap the blue and red components of each pixel.
- (CGImageRef)swapBlueAndRedInImage:(CGImageRef)image
{
int x, y;
uint8_t red, green, blue, alpha;
uint8_t *bufptr;
int width = CGImageGetWidth( image );
int height = CGImageGetHeight( image );
// allocate memory for pixels
uint32_t *pixels = calloc( width * height, sizeof(uint32_t) );
// create a context with RGBA pixels
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate( pixels, width, height, 8, width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast );
// draw the image into the context
CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image );
// manipulate the pixels
bufptr = (uint8_t *)pixels;
for ( y = 0; y < height; y++)
for ( x = 0; x < width; x++ )
{
red = bufptr[3];
green = bufptr[2];
blue = bufptr[1];
alpha = bufptr[0];
bufptr[1] = red; // swaps the red and blue
bufptr[3] = blue; // components of each pixel
bufptr += 4;
}
// create a new CGImage from the context with modified pixels
CGImageRef resultImage = CGBitmapContextCreateImage( context );
// release resources to free up memory
CGContextRelease( context );
CGColorSpaceRelease( colorSpace );
free( pixels );
return( resultImage );
}

Bad argument (image must have CV_8UC3 type) in grabCut

I am using grabCut algorithm using the following code:
cv::Mat img=[self cvMatFromUIImage:image];
cv::Rect rectangle(10,10,300,150);
cv::Mat result; // segmentation (4 possible values)
cv::Mat bgModel,fgModel; // the models (internally used)
// GrabCut segmentation
cv::grabCut(img, // input image
result, // segmentation result
rectangle, // rectangle containing foreground
bgModel,fgModel, // models
3, // number of iterations
cv::GC_INIT_WITH_RECT); // use rectangle
// Get the pixels marked as likely foreground
cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);
// Generate output image
cv::Mat foreground(img.size(),CV_8UC3,
cv::Scalar(255,255,255));
result=result&1;
img.copyTo(foreground, result);
result);
image=[self UIImageFromCVMat:foreground];
ImgView.image=image;
The code to convert UIImage to Mat image looks like this
- (cv::Mat)cvMatFromUIImage:(UIImage *)imge
{
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imge.CGImage);
CGFloat cols = imge.size.width;
CGFloat rows = imge.size.height;
cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
CGContextRef contextRef = CGBitmapContextCreate(
cvMat.data, // Pointer to data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault);
// Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), imge.CGImage);
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpace);
return cvMat;
}
But I got the error
OpenCV Error: Bad argument (image must have CV_8UC3 type) in grabCut.
If I change
cv::Mat cvMat(rows, cols, CV_8UC4); line to cv::Mat cvMat(rows, cols, CV_8UC3);
then I get <Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaNoneSkipLast; 342 bytes/row..
I am confused here for what to do.
Any help please
The Problem seems to be, that the image you get has an alpha channels, while grabcut expects a rgb image without an alpha channel. So you need to get rid of the additional channel.
You can do this for example with this function:
cv::cvtColor(img , img , CV_RGBA2RGB);

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.

Copy OpenCV IplImage or Mat into Freeimage FIBITMAP

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);

Resources