Is it possible to make a bit map image as array? - objective-c

I would like to make a bit map image as follow array.
I think that will need to use quartz...
How to can make a bit map image?
int bit_map[10][10] = {{0,0,1,0,0,0,0,1,0,0},
{0,1,1,1,1,0,1,1,1,0},
{1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,0,1,1,1,1,1,1,0,0},
{0,0,0,1,1,1,1,0,0,0},
{0,0,0,0,1,1,0,0,0,0}};

Here is code to create a CGImageRef (cir) from the C array. Note that things can be simpler if the C array is one dimensional with values of 0 and 255.
size_t width = 10;
size_t height = 10;
int bit_map[10][10] = {
{0,0,1,0,0,0,0,1,0,0},
{0,1,1,1,1,0,1,1,1,0},
{1,1,1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1,1,1},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,0,1,1,1,1,1,1,0,0},
{0,0,0,1,1,1,1,0,0,0},
{0,0,0,0,1,1,0,0,0,0}
};
UIImage *barCodeImage = nil;
size_t bitsPerComponent = 8;
size_t bitsPerPixel = 8;
size_t bytesPerRow = (width * bitsPerPixel + 7) / 8;
void *imageBytes;
size_t imageBytesSize = height * bytesPerRow;
imageBytes = calloc(1, imageBytesSize);
for (int i=0; i<10; i++) {
for (int j=0; j<10; j++) {
int pixel = bit_map[j][i];
if (pixel == 1)
pixel = 255;
((unsigned char*)imageBytes)[((i+1) * (j+1)) - 1] = pixel;
}
}
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, imageBytes, imageBytesSize, releasePixels);
CGColorSpaceRef colorSpaceGrey = CGColorSpaceCreateDeviceGray();
CGImageRef cir = CGImageCreate (width,
height,
bitsPerComponent,
bitsPerPixel,
imageBytesSize,
colorSpaceGrey,
kCGImageAlphaNone,
provider,
NULL,
NO,
kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpaceGrey);

Related

CGDataProviderRefReleaseCallback not being called

I'm creating a CGBitmap using the code below, but the done_callback function is never called. What am I doing wrong? The bitmap is created correctly but I have no way to free the pixel buffer afterwards. Is it because the CGBitmap is never released?
void done_callback(void *info, const void *data, size_t size)
{
free((void *)data);
NSLog(#"Done!");
}
static CGImageRef SVG_to_CGImage(char const *svg, int width, int height)
{
using namespace lunasvg;
auto doc = Document::loadFromData(svg);
if(doc.get() == nullptr) {
return nullptr;
}
auto bmp = doc->renderToBitmap(width, height);
if(!bmp.valid()) {
return nullptr;
}
UInt32 *pixels = (UInt32 *)malloc(width * height * 4);
// rearrange the BGRA to RGBA
for(int y=0; y<height; ++y) {
UInt32 *s = (UInt32 *)(bmp.data() + y * width * 4);
UInt32 *d = pixels + y * width;
for(int x=0; x<width; ++x) {
UInt32 argb = *s++;
UInt32 a = argb & 0xff000000;
UInt32 r = (argb & 0xff0000) >> 16;
UInt32 g = argb & 0xff00;
UInt32 b = (argb & 0xff) << 16;
*d++ = a | r | g | b;
}
}
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, width * height * 4, done_callback);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
return CGImageCreate(width, height, 8, 32, 4 * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
}

Using bitmapData to modify an image

So I've got a method that successfully gets the color of a pixel.
//arrColT is an NSMutableArray<NSColor *>
NSInteger width = [bitIn pixelsWide];
NSInteger height = [bitIn pixelsHigh];
NSInteger rowBytes = [bitIn bytesPerRow];
unsigned char* pixels = [bitIn bitmapData];
int row, col;
//For every row,
for (row = 0; row < height; row++)
{
unsigned char* rowStart = (unsigned char*)(pixels + (row * rowBytes));
unsigned char* nextChannel = rowStart;
//For every pixel in a row,
for (col = 0; col < width; col++)
{
//Get its color.
unsigned char red, green, blue, alpha;
red = *nextChannel;
int intRed = (int)red;
nextChannel++;
green = *nextChannel;
int intGreen = (int)green;
nextChannel++;
blue = *nextChannel;
int intBlue = (int)blue;
nextChannel++;
alpha = *nextChannel;
int intAlpha = (int)alpha;
nextChannel++;
NSColor *colHas = [NSColor colorWithCalibratedRed:(float)intRed/255 green: (float)intGreen/255 blue: (float)intBlue/255 alpha:(float)intAlpha/255];
for (int i = 0; i<[arrColT count]; i++) {
//If the target color is equal to the current color, replace it with the parallel replace color...somehow.
if([colHas isEqualTo:arrColT[i]]){
}
}
}
}
The question is, how do I get color data back into the bitmapData?
With hope,
radzo73
See Technical Q&A QA1509, which basically advises
create pixel buffer (by creating CGContextRef of predefined format and drawing your image to that);
manipulate the bytes within that pixel buffer as you want; and
create resulting image with CGBitmapContextCreateImage.
E.g.
- (UIImage *)convertImage:(UIImage *)image
{
// get image
CGImageRef imageRef = image.CGImage;
// prepare context
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big; // bytes in RGBA order
NSInteger width = CGImageGetWidth(imageRef);
NSInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 4 * width, colorspace, bitmapInfo);
// draw image to that context
CGRect rect = CGRectMake(0, 0, width, height);
CGContextDrawImage(context, rect, imageRef);
uint8_t *buffer = CGBitmapContextGetData(context);
// ... manipulate pixel buffer bytes here ...
// get image from buffer
CGImageRef outputImage = CGBitmapContextCreateImage(context);
UIImage *result = [UIImage imageWithCGImage:outputImage scale:image.scale orientation:image.imageOrientation];
// clean up
CGImageRelease(outputImage);
CGContextRelease(context);
CGColorSpaceRelease(colorspace);
return result;
}
For example, here is a B&W conversion routine:
/** Convert the image to B&W as a single (non-parallel) task.
*
* This assumes the pixel buffer is in RGBA, 8 bits per pixel format.
*
* #param buffer The pixel buffer.
* #param width The image width in pixels.
* #param height The image height in pixels.
*/
- (void)convertToBWSimpleInBuffer:(uint8_t *)buffer width:(NSInteger)width height:(NSInteger)height
{
for (NSInteger row = 0; row < height; row++) {
for (NSInteger col = 0; col < width; col++) {
NSUInteger offset = (col + row * width) * 4;
uint8_t *pixel = buffer + offset;
// get the channels
uint8_t red = pixel[0];
uint8_t green = pixel[1];
uint8_t blue = pixel[2];
uint8_t alpha = pixel[3];
// update the channels with result
uint8_t gray = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
pixel[0] = gray;
pixel[1] = gray;
pixel[2] = gray;
pixel[3] = alpha;
}
}
}
If you want to improve performance, you can use dispatch_apply and stride through your image buffer in parallel:
/** Convert the image to B&W, using GCD to split the conversion into several concurrent GCD tasks.
*
* This assumes the pixel buffer is in RGBA, 8 bits per pixel format.
*
* #param buffer The pixel buffer.
* #param width The image width in pixels.
* #param height The image height in pixels.
* #param count How many GCD tasks should the conversion be split into.
*/
- (void)convertToBWConcurrentInBuffer:(uint8_t *)buffer width:(NSInteger)width height:(NSInteger)height count:(NSInteger)count
{
dispatch_queue_t queue = dispatch_queue_create("com.domain.app", DISPATCH_QUEUE_CONCURRENT);
NSInteger stride = height / count;
dispatch_apply(height / stride, queue, ^(size_t idx) {
size_t j = idx * stride;
size_t j_stop = MIN(j + stride, height);
for (NSInteger row = j; row < j_stop; row++) {
for (NSInteger col = 0; col < width; col++) {
NSUInteger offset = (col + row * width) * 4;
uint8_t *pixel = buffer + offset;
// get the channels
uint8_t red = pixel[0];
uint8_t green = pixel[1];
uint8_t blue = pixel[2];
uint8_t alpha = pixel[3];
// update the channels with result
uint8_t gray = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
pixel[0] = gray;
pixel[1] = gray;
pixel[2] = gray;
pixel[3] = alpha;
}
}
});
}
Well it depends on what bitIn is, but something like this should do the trick.
First, create a context. You can do this once and keep the context if you are going to make a lot of changes to the data and need a lot of pictures.
self.ctx = CGBitmapContextCreate (
[bitIn bitmapData],
[bitIn pixelsWide],
[bitIn pixelsHigh],
[bitIn bitsPerPixel],
[bitIn bytesPerRow],
[bitIn colorSpace],
[bitIn bitmapInfo] );
Here I freely used bitIn hoping it will provide all the missing pieces. Then you can assemble it all later, even after further changes to the data, using something like
CGImageRef cgImg = CGBitmapContextCreateImage ( self.ctx );
UIImage * img = [UIImage imageWithCGImage:cgImg];
CGImageRelease ( cgImg );
Now you can make more changes to the data and do the same to get a new image.

Objective C - save UIImage as a BMP file

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

Converting CVImageBufferRef YUV 420 to cv::Mat RGB and displaying it in a CALayer?

Once that I can't get successfully anything different of YCbCr 420 from the camera (https://stackoverflow.com/questions/19673770/objective-c-avcapturevideodataoutput-videosettings-how-to-identify-which-pix)
So, my goal is to display this CMSampleBufferRef (YCbCr 420), after process with opencv, as a colored frame (CGImageRef, using RGB color model) in the CALayer.
In the camera capture file I put this:
#define clamp(a) (a>255?255:(a<0?0:a));
cv::Mat* YUV2RGB(cv::Mat *src){
cv::Mat *output = new cv::Mat(src->rows, src->cols, CV_8UC4);
for(int i=0;i<output->rows;i++)
for(int j=0;j<output->cols;j++){
// from Wikipedia
int c = src->data[i*src->cols*src->channels() + j*src->channels() + 0] - 16;
int d = src->data[i*src->cols*src->channels() + j*src->channels() + 1] - 128;
int e = src->data[i*src->cols*src->channels() + j*src->channels() + 2] - 128;
output->data[i*src->cols*src->channels() + j*src->channels() + 0] = clamp((298*c+409*e+128)>>8);
output->data[i*src->cols*src->channels() + j*src->channels() + 1] = clamp((298*c-100*d-208*e+128)>>8);
output->data[i*src->cols*src->channels() + j*src->channels() + 2] = clamp((298*c+516*d+128)>>8);
}
return output;
}
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(imageBuffer, 0);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
uint8_t *baseAddress = (uint8_t*)CVPixelBufferGetBaseAddress(imageBuffer);
CVPlanarPixelBufferInfo_YCbCrBiPlanar *bufferInfo = (CVPlanarPixelBufferInfo_YCbCrBiPlanar *)baseAddress;
NSUInteger yOffset = EndianU32_BtoN(bufferInfo->componentInfoY.offset);
NSUInteger yPitch = EndianU32_BtoN(bufferInfo->componentInfoY.rowBytes);
NSUInteger cbCrOffset = EndianU32_BtoN(bufferInfo->componentInfoCbCr.offset);
NSUInteger cbCrPitch = EndianU32_BtoN(bufferInfo->componentInfoCbCr.rowBytes);
uint8_t *yBuffer = baseAddress + yOffset;
uint8_t *cbCrBuffer = baseAddress + cbCrOffset;
cv::Mat *src = new cv::Mat((int)(height), (int)(width), CV_8UC4);
//YUV -> cv::Mat
for(int i = 0; i< height; i++)
{
uint8_t *yBufferLine = &yBuffer[i * yPitch];
uint8_t *cbCrBufferLine = &cbCrBuffer[(i >> 1) * cbCrPitch];
for(int j = 0; j < width; j++)
{
uint8_t y = yBufferLine[j];
uint8_t cb = cbCrBufferLine[j & ~1];
uint8_t cr = cbCrBufferLine[j | 1];
src->data[i*width*src->channels() + j*src->channels() + 0] = y;
src->data[i*width*src->channels() + j*src->channels() + 1] = cb;
src->data[i*width*src->channels() + j*src->channels() + 2] = cr;
}
}
cv::Mat *output = YUV2RGB(src);
CGColorSpaceRef grayColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(output->data, output->cols, output->rows, 8, output->step, grayColorSpace, kCGImageAlphaNoneSkipLast);
CGImageRef dstImage = CGBitmapContextCreateImage(context);
dispatch_sync(dispatch_get_main_queue(), ^{
customPreviewLayer.contents = (__bridge id)dstImage;
});
CGImageRelease(dstImage);
CGContextRelease(context);
CGColorSpaceRelease(grayColorSpace);
output->release();
src->release();
}
I got some issues (I was really stuck) trying to get to this point, I will describe here. It might be someone else's problem as well:
That clamp function is essential to ensure the correct conversion YUV->RGB
For some reason I couldn't just keep 3 channels on the image data, it somehow was causing a problem when it was about to display the image. So I changed kCGImageAlphaNone to kCGImageAlphaNoneSkipLast in the CGBitmapContextCreate. Also, I used 4 channels in the cv::Mat constructor.
Part of this code I adapted from kCVPixelFormatType_420YpCbCr8BiPlanarFullRange frame to UIImage conversion

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.