Objective C - save UIImage as a BMP file - objective-c

Similar questions have been asked but they always contain the answer "use UIImagePNGRepresentation or UIImageJPEGRepresentation" which will not work for me. I need to save the file as a bitmap.
From my understanding, the UIImage data is already a bitmap. In which case I shouldn't have to create a bitmap. Or do I? I found someone who posted this question but I'm unsure what exactly the answer is, as it is not clear to me. I've even gone so far as to create a char pointer array but I still don't know how to save it as a bitmap. Is there anybody out there who can help me understand how to save the UIImage as a bitmap? Or tell me the steps and I'll research the steps.
Thank you.
EDIT:
I found this tutorial (with code) for converting the image to a bitmap but the bitmap returned is an unsigned char *. Either I'm on the right path or I'm going down in the wrong direction. So now I'm going to see if I can write out the unsigned char * to a .bmp file.

Below is the category that I have pieced together for UIImage that lets me access the raw BGRA data as well as data ready to be written to disk with an appropriate bitmap header.
#interface UIImage (BitmapData)
- (NSData *)bitmapData;
- (NSData *)bitmapFileHeaderData;
- (NSData *)bitmapDataWithFileHeader;
#end
# pragma pack(push, 1)
typedef struct s_bitmap_header
{
// Bitmap file header
UInt16 fileType;
UInt32 fileSize;
UInt16 reserved1;
UInt16 reserved2;
UInt32 bitmapOffset;
// DIB Header
UInt32 headerSize;
UInt32 width;
UInt32 height;
UInt16 colorPlanes;
UInt16 bitsPerPixel;
UInt32 compression;
UInt32 bitmapSize;
UInt32 horizontalResolution;
UInt32 verticalResolution;
UInt32 colorsUsed;
UInt32 colorsImportant;
} t_bitmap_header;
#pragma pack(pop)
#implementation UIImage (BitmapData)
- (NSData *)bitmapData
{
NSData *bitmapData = nil;
CGImageRef image = self.CGImage;
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
UInt8 *rawData;
size_t bitsPerPixel = 32;
size_t bitsPerComponent = 8;
size_t bytesPerPixel = bitsPerPixel / bitsPerComponent;
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
size_t bytesPerRow = width * bytesPerPixel;
size_t bufferLength = bytesPerRow * height;
colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace)
{
// Allocate memory for raw image data
rawData = (UInt8 *)calloc(bufferLength, sizeof(UInt8));
if (rawData)
{
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst;
context = CGBitmapContextCreate(rawData,
width,
height,
bitsPerComponent,
bytesPerRow,
colorSpace,
bitmapInfo);
if (context)
{
CGRect rect = CGRectMake(0, 0, width, height);
CGContextTranslateCTM(context, 0, height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, rect, image);
bitmapData = [NSData dataWithBytes:rawData length:bufferLength];
CGContextRelease(context);
}
free(rawData);
}
CGColorSpaceRelease(colorSpace);
}
return bitmapData;
}
- (NSData *)bitmapFileHeaderData
{
CGImageRef image = self.CGImage;
UInt32 width = (UInt32)CGImageGetWidth(image);
UInt32 height = (UInt32)CGImageGetHeight(image);
t_bitmap_header header;
header.fileType = 0x4D42;
header.fileSize = (height * width * 4) + 54;
header.reserved1 = 0x0000;
header.reserved2 = 0x0000;
header.bitmapOffset = 0x00000036;
header.headerSize = 0x00000028;
header.width = width;
header.height = height;
header.colorPlanes = 0x0001;
header.bitsPerPixel = 0x0020;
header.compression = 0x00000000;
header.bitmapSize = height * width * 4;
header.horizontalResolution = 0x00000B13;
header.verticalResolution = 0x00000B13;
header.colorsUsed = 0x00000000;
header.colorsImportant = 0x00000000;
return [NSData dataWithBytes:&header length:sizeof(t_bitmap_header)];
}
- (NSData *)bitmapDataWithFileHeader
{
NSMutableData *data = [NSMutableData dataWithData:[self bitmapFileHeaderData]];
[data appendData:[self bitmapData]];
return [NSData dataWithData:data];
}
#end

Related

How to convert from CGImageRef to GraphicsMagick Blob type?

I have a fairly standard RGBA image as a CGImageRef.
I'm looking to convert this into a GraphicsMagick Blob (http://www.graphicsmagick.org/Magick++/Image.html#blobs)
What's the best way to go about transposing it?
I have this but it produces only a plain black image if I specify PNG8 in the pathString or it crashes:
- (void)saveImage:(CGImageRef)image path:(NSString *)pathString
{
CGDataProviderRef dataProvider = CGImageGetDataProvider(image);
NSData *data = CFBridgingRelease(CGDataProviderCopyData(dataProvider));
const void *bytes = [data bytes];
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
size_t length = CGImageGetBytesPerRow(image) * height;
NSString *sizeString = [NSString stringWithFormat:#"%ldx%ld", width, height];
Image pngImage;
Blob blob(bytes, length);
pngImage.read(blob);
pngImage.size([sizeString UTF8String]);
pngImage.magick("RGBA");
pngImage.write([pathString UTF8String]);
}
Needed to get the image in the right RGBA format first. The original CGImageRef had a huge number of bytes per row. Creating a context with only 4 bytes per pixel did the trick.
// Calculate the image width, height and bytes per row
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
size_t bytesPerRow = 4 * width;
size_t length = bytesPerRow * height;
// Set the frame
CGRect frame = CGRectMake(0, 0, width, height);
// Create context
CGContextRef context = CGBitmapContextCreate(NULL,
width,
height,
CGImageGetBitsPerComponent(image),
bytesPerRow,
CGImageGetColorSpace(image),
kCGImageAlphaPremultipliedLast);
if (!context) {
return;
}
// Draw the image inside the context
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextDrawImage(context, frame, image);
// Get the bitmap data from the context
void *bytes = CGBitmapContextGetData(context);

dsp on an UIImage [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to get pixel data from a UIImage (Cocoa Touch) or CGImage (Core Graphics)?
Let's say I have a UIImage that I would like to get the rgb matrix of, in order to do some processing on it, not to change it, just get the UIImage data so i can use my C algorithms on it.
As you probably know, all the math is done on the image rgb matrixes.
The basic procedure is to create a bitmap context with CGBitmapContextCreate, then draw your image into that context and get the internal data with CGBitmapContextGetData. Here's an example:
UIImage *image = [UIImage imageNamed:#"MyImage.png"];
//Create the bitmap context:
CGImageRef cgImage = [image CGImage];
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
size_t bitsPerComponent = 8;
size_t bytesPerRow = width * 4;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
//Draw your image into the context:
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
//Get the raw image data:
unsigned char *data = CGBitmapContextGetData(context);
//Example how to access pixel values:
size_t x = 0;
size_t y = 0;
size_t i = y * bytesPerRow + x * 4;
unsigned char redValue = data[i];
unsigned char greenValue = data[i + 1];
unsigned char blueValue = data[i + 2];
unsigned char alphaValue = data[i + 3];
NSLog(#"RGBA at (%i, %i): %i, %i, %i, %i", x, y, redValue, greenValue, blueValue, alphaValue);
//Clean up:
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
//At this point, your data pointer becomes invalid, you would have to allocate
//your own buffer instead of passing NULL to avoid this.

<Error>: CGImageCreate: invalid image bits/pixel: 8

i'm using opencv with Xcode , i get this method to convert from IplImage to UIImage:
-(UIImage *)UIImageFromIplImage:(IplImage *)image {
NSLog(#"IplImage (%d, %d) %d bits by %d channels, %d bytes/row %s", image->width, image->height, image->depth, image->nChannels, image->widthStep, image->channelSeq);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSData *data = [NSData dataWithBytes:image->imageData length:image->imageSize];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
CGImageRef imageRef = CGImageCreate(image->width, image->height,
image->depth, image->depth * image->nChannels, image->widthStep,
colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault,
provider, NULL, false, kCGRenderingIntentDefault);
UIImage *ret = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationUp];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return ret;
}
the problem is, when i pass any image to this method(png,jpg,tiff) this error appears
: CGImageCreate: invalid image bits/pixel: 8, please help me in resolving that error,thanks.
if your image is gray(not RGBA), use this:
colorSpace = CGColorSpaceCreateDeviceGray();
From what I've done in my apps, you actually need to provide an alpha value in your image data. What I do, is taking the data out of the opencv struct, add an alpha value, and create the CGImage. Here is the code I use with the C++ API (if you stick to C just replace the call to aMat.ptr<>(y) by a pointer to the first pixel of the y-th row):
// Colorspace
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char* data = new unsigned char[4*aMat.cols*aMat.rows];
for (int y = 0; y < aMat.rows; ++y)
{
cv::Vec3b *ptr = aMat.ptr<cv::Vec3b>(y);
unsigned char *pdata = data + 4*y*aMat.cols;
for (int x = 0; x < aMat.cols; ++x, ++ptr)
{
*pdata++ = (*ptr)[2];
*pdata++ = (*ptr)[1];
*pdata++ = (*ptr)[0];
*pdata++ = 0;
}
}
// Bitmap context
CGContextRef context = CGBitmapContextCreate(data, aMat.cols, aMat.rows, 8, 4*aMat.cols, colorSpace, kCGImageAlphaNoneSkipLast);
CGImageRef cgimage = CGBitmapContextCreateImage(context);
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
delete[] data;
The shuffling part is necessary because OpenCV handles BGR images, while Quartz expects RGB.

Alpha Mask Extraction process Objective C implementation

Do you have an Objective C implementation equivalent to ImageMagick's command :
convert -alpha Extract -type optimize -strip -quality 60 +dither Source.png Alpha.jpg
I was not able to find any solution right now.
I m looking for an AlphaExtractor snippet that would extract the alpha from a png and save it to JPG Grayscale
The mask is created using the code snippet :
CGImageRef createMaskWithImage(CGImageRef image)
{
int maskWidth = CGImageGetWidth(image);
int maskHeight = CGImageGetHeight(image);
// round bytesPerRow to the nearest 16 bytes, for performance's sake
int bytesPerRow = (maskWidth + 15) & 0xfffffff0;
int bufferSize = bytesPerRow * maskHeight;
// we use CFData instead of malloc(), because the memory has to stick around
// for the lifetime of the mask. if we used malloc(), we'd have to
// tell the CGDataProvider how to dispose of the memory when done. using
// CFData is just easier and cleaner.
CFMutableDataRef dataBuffer = CFDataCreateMutable(kCFAllocatorDefault, 0);
CFDataSetLength(dataBuffer, bufferSize);
// the data will be 8 bits per pixel, no alpha
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);//CGColorSpaceCreateDeviceGray();
CGContextRef ctx = CGBitmapContextCreate(CFDataGetMutableBytePtr(dataBuffer),
maskWidth, maskHeight,
8, bytesPerRow, colorSpace, kCGImageAlphaNone);
// drawing into this context will draw into the dataBuffer.
CGContextDrawImage(ctx, CGRectMake(0, 0, maskWidth, maskHeight), image);
CGContextRelease(ctx);
// now make a mask from the data.
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(dataBuffer);
CGImageRef mask = CGImageMaskCreate(maskWidth, maskHeight, 8, 8, bytesPerRow,
dataProvider, NULL, FALSE);
CGDataProviderRelease(dataProvider);
CGColorSpaceRelease(colorSpace);
CFRelease(dataBuffer);
return mask;
}
and saved :
-(void)_saveJPEGImage:(CGImageRef)imageRef path:(NSString *)path {
NSURL *fileURL = [NSURL fileURLWithPath:path];
CFURLRef fileUrlRef=(CFURLRef)fileURL;
CFMutableDictionaryRef mSaveMetaAndOpts = CFDictionaryCreateMutable(nil, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(mSaveMetaAndOpts, kCGImageDestinationLossyCompressionQuality, [NSNumber numberWithFloat:0.7]); // set the compression quality here
CFDictionarySetValue(mSaveMetaAndOpts, kCGImageDestinationBackgroundColor, kCGColorClear);
CGImageDestinationRef dr = CGImageDestinationCreateWithURL (fileUrlRef, kUTTypeJPEG , 1, NULL);
CGImageDestinationAddImage(dr, imageRef, mSaveMetaAndOpts);
CGImageDestinationFinalize(dr);
CFRelease(dr);
}
A really quick an dirty working solution :
Assuming we have a 32bytes raw data (if not the code need to be adapted)
1- we iterate through the bytes by +4 steps and alter the r,g,b components.
CGImageRef ref=CGImageCreateCopy([_imageView image]);
NSData *data = (NSData *)CGDataProviderCopyData(CGImageGetDataProvider(ref));
char *bytes = (char *)[data bytes];
int i;
for( i= 0; i < [data length]; i += 4)
{
int r = i;
int g = i+1;
int b = i+2;
int a = i+3;
bytes[r] = 0;
bytes[g] = 0;
bytes[b] = 0;
bytes[a] = bytes[a];
}
2- We create a new RGBA (32Bit) image reference with the "modified data" :
size_t width = CGImageGetWidth(ref);
size_t height = CGImageGetHeight(ref);
size_t bitsPerComponent = CGImageGetBitsPerComponent(ref);
size_t bitsPerPixel = CGImageGetBitsPerPixel(ref);
size_t bytesPerRow = CGImageGetBytesPerRow(ref);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(ref);
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,bytes, [data length], NULL);
CGImageRef newImageRef = CGImageCreate (
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
bitmapInfo,
provider,
NULL,
false,
kCGRenderingIntentDefault
);
3- We save this new 32Bytes image reference to a jpeg file.
The generated JPG will be usable as a mask.
We could do it a cleaner way by creating an 8Bit context and writing the "alpha component" only.
I see two problems:
CGContextDrawImage(ctx, CGRectMake(0, 0, maskWidth, maskHeight), image);
doesn't extract the alpha, it just alpha-composites the image onto a black background.
If the image is black with transparency then an all-black image would be the expected output.
and:
CGImageRef mask = CGImageMaskCreate(maskWidth, maskHeight, 8, 8, bytesPerRow,
dataProvider, NULL, FALSE);
You're treating this mask you create like a real image. If you replace this line with
CGImageRef mask = CGImageCreate(maskWidth, maskHeight, 8, 8, bytesPerRow, colorSpace, 0,
dataProvider, NULL, FALSE, kCGRenderingIntentDefault);
Then you will get a greyscale version of your image (see problem 1)

Converting RGB data into a bitmap in Objective-C++ Cocoa

I have a buffer of RGB unsigned char that I would like converted into a bitmap file, does anyone know how?
My RGB float is of the following format
R [(0,0)], G[(0,0)], B[(0,0)],R [(0,1)], G[(0,1)], B[(0,1)], R [(0,2)], G[(0,2)], B[(0,2)] .....
The values for each data unit ranges from 0 to 255. anyone has any ideas how I can go about making this conversion?
You can use CGBitmapContextCreate to make a bitmap context from your raw data. Then you can create a CGImageRef from the bitmap context and save it. Unfortunately CGBitmapContextCreate is a little picky about the format of the data. It does not support 24-bit RGB data. The loop at the beginning swizzles the rgb data to rgba with an alpha value of zero at the end. You have to include and link with ApplicationServices framework.
char* rgba = (char*)malloc(width*height*4);
for(int i=0; i < width*height; ++i) {
rgba[4*i] = myBuffer[3*i];
rgba[4*i+1] = myBuffer[3*i+1];
rgba[4*i+2] = myBuffer[3*i+2];
rgba[4*i+3] = 0;
}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bitmapContext = CGBitmapContextCreate(
rgba,
width,
height,
8, // bitsPerComponent
4*width, // bytesPerRow
colorSpace,
kCGImageAlphaNoneSkipLast);
CFRelease(colorSpace);
CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext);
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("image.png"), kCFURLPOSIXPathStyle, false);
CFStringRef type = kUTTypePNG; // or kUTTypeBMP if you like
CGImageDestinationRef dest = CGImageDestinationCreateWithURL(url, type, 1, 0);
CGImageDestinationAddImage(dest, cgImage, 0);
CFRelease(cgImage);
CFRelease(bitmapContext);
CGImageDestinationFinalize(dest);
free(rgba);
Borrowing from nschmidt's code to produce a familiar, if someone red-eyed image:
int width = 11;
int height = 8;
Byte r[8][11]={
{000,000,255,000,000,000,000,000,255,000,000},
{000,000,000,255,000,000,000,255,000,000,000},
{000,000,255,255,255,255,255,255,255,000,000},
{000,255,255,255,255,255,255,255,255,255,000},
{255,255,255,255,255,255,255,255,255,255,255},
{255,000,255,255,255,255,255,255,255,000,255},
{255,000,255,000,000,000,000,000,255,000,255},
{000,000,000,255,255,000,255,255,000,000,000}};
Byte g[8][11]={
{000,000,255,000,000,000,000,000,255,000,000},
{000,000,000,255,000,000,000,255,000,000,000},
{000,000,255,255,255,255,255,255,255,000,000},
{000,255,255,000,255,255,255,000,255,255,000},
{255,255,255,255,255,255,255,255,255,255,255},
{255,000,255,255,255,255,255,255,255,000,255},
{255,000,255,000,000,000,000,000,255,000,255},
{000,000,000,255,255,000,255,255,000,000,000}};
Byte b[8][11]={
{000,000,255,000,000,000,000,000,255,000,000},
{000,000,000,255,000,000,000,255,000,000,000},
{000,000,255,255,255,255,255,255,255,000,000},
{000,255,255,000,255,255,255,000,255,255,000},
{255,255,255,255,255,255,255,255,255,255,255},
{255,000,255,255,255,255,255,255,255,000,255},
{255,000,255,000,000,000,000,000,255,000,255},
{000,000,000,255,255,000,255,255,000,000,000}};
char* rgba = (char*)malloc(width*height*4);
int offset=0;
for(int i=0; i < height; ++i)
{
for (int j=0; j < width; j++)
{
rgba[4*offset] = r[i][j];
rgba[4*offset+1] = g[i][j];
rgba[4*offset+2] = b[i][j];
rgba[4*offset+3] = 0;
offset ++;
}
}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bitmapContext = CGBitmapContextCreate(
rgba,
width,
height,
8, // bitsPerComponent
4*width, // bytesPerRow
colorSpace,
kCGImageAlphaNoneSkipLast);
CFRelease(colorSpace);
CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext);
free(rgba);
UIImage *newUIImage = [UIImage imageWithCGImage:cgImage];
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 11,8)];
[iv setImage:newUIImage];
Then, addSubview:iv to get the image into your view and, of course, do the obligatory [releases] to keep a clean house.