Since CGContextDrawImage can be quite expensive, I'm trying to minimize the amount of data I'm giving it while I examine the pixel data. If I have two images and the CGRect of their intersection, can I get CGContextDrawImage to only draw the intersection of each independently (resulting in two CGContextRefs containing the intersection of one of the images)?
Here's some code that doesn't work, but should be close to what I need for one image...
CGImageRef imageRef = [image CGImage];
NSUInteger width = rectIntersect.size.width;
NSUInteger height = rectIntersect.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
// rectIntersect down here contains the intersection of the two images...
CGContextDrawImage(context, rectIntersect, imageRef);
Well, you don't need to draw them unless you really want a CGBitmapContext with them. Use CGImageCreateWithImageInRect() to create sub-images. This doesn't necessarily require the frameworks to copy any image data. It may just reference the image data from the original. So, it can be quite efficient.
If you really do need the images drawn into contexts, you can of course just draw the sub-images.
Related
I want to extract data from UIImage and do something with that data.
I found an memory issue while extracting the data from the UIImage.
To isolate the issue i created a new project with just a method that extract data from UIImage.
-(void)runMemoryValidation:(NSArray*)images {
for ( int i=0; i<images.count; i++) {
#autoreleasepool {
NSString* imageName = [images objectAtIndex:i];
UIImage* image = [UIImage imageNamed:imageName];
NSUInteger width = 500;//CGImageGetWidth(image.CGImage);
NSUInteger height = 500;//CGImageGetHeight(image.CGImage);
//Ref<IntMatrix> matrix(new IntMatrix(width,height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *data = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(data, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.CGImage);
CGContextRelease(context);
free(data);
}
}
}
I'm sending this method 100 file names and for each loop i load the image and extract the data.
Attached a screenshot, you can see the memory getting higher very quickly and doesn't get released after the for loop finish
What am i doing wrong ?
Thanks!
You are not doing anything wrong related to memory management in this code. As #picciano said in his comment +imageNamed: method caches the images it loads, therefore use +imageWithContentsOfFile: method which doesn't. Also do your measures on an actual device, since there's a differece in memory usage, pressure, etc. when testing on the simulator.
Below is some code that converts a UIImage to a CGImage, makes some changes to the CGImage, then converts it back to a UIImage.
This works if my architectures include arm6 and arm7 only. If I add arm64, the UIImage returned at the end is null.
There are some hard coded numbers in the code, which makes me think that is the problem. But I am not sure how to programmatically determine these values.
Here's the code, minus some details in the middle:
CGImageRef imageRef = [anImage CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// Raw data malloc'd
unsigned char *rawData = malloc(height * width * 4);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context =
CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
...
// Change the alpha or color of pixels based on certain criteria
...
CGImageRef ref = CGBitmapContextCreateImage(context);
free(rawData);
CGContextRelease(context);
image = [UIImage imageWithCGImage:ref];
CFRelease(ref);
Any thoughts on what's happening here?
ios-converting-uiimage-to-rgba8-bitmaps-and-back is a very good article that I found which describes how we can deal with bitmap buffer and UIImages. This article deals with RGBA32/ RGBA8 bitmap images. The bitmap images are created with char * buffer with size width * height * 4. ie, each pixel information will have 4 bytes of data, 1 byte each for storing red, green, blue and alpha respectively. On creating the bitmap image the bitmap info 'kCGImageAlphaPremultipliedLast' is given. CGColorSpaceCreateDeviceRGB() is used for converting back the bitmapbuffer to UIImage. By changing the bitmap info we can also deal with RGBA 24 images.
I need to deal with RGBA 5551 bitmap images. red, green and blue colors are given 5 bits to represent the respetive colors and 1 bit for storing alpha value. If we are creating such a bitmap, how we can allocate buffer for a char * bitmap. Is it possible to convert into a UIImage data type?. Any help will be appreciated.
Here the BITS_PER_COMPONENT is 5 and BITS_PER_COMPONENT is 16. With this code I have successfully. kCGBitmapByteOrder16Little indicates the byte order of the char buffer.
size_t bufferLength = width * height * 2;
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, bufferLength, NULL);
size_t bitsPerComponent = BITS_PER_COMPONENT;
size_t bitsPerPixel = BITS_PER_PIXEL;
size_t bytesPerRow = 2 * width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
if(colorSpaceRef == NULL) {
NSLog(#"Error allocating color space");
CGDataProviderRelease(provider);
return nil;
}
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder16Little | kCGImageAlphaNoneSkipFirst;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGImageRef iref = CGImageCreate(width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorSpaceRef,
bitmapInfo,
provider, // data provider
NULL, // decode
YES, // should interpolate
renderingIntent);
uint32_t* pixels = (uint32_t*)malloc(bufferLength);
if(pixels == NULL) {
NSLog(#"Error: Memory not allocated for bitmap");
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpaceRef);
CGImageRelease(iref);
return nil;
}
CGContextRef context = CGBitmapContextCreate(pixels,
width,
height,
bitsPerComponent,
bytesPerRow,
colorSpaceRef,
bitmapInfo);
you need to create 16 bit image, so provide bpp as 16, bpc as 5, below will be the code:
size_t width = CGImageGetWidth(screenShotImageRef);
size_t height = CGImageGetHeight(screenShotImageRef);
size_t bytesPerRow = width * (bpc == 5 ? 2 : 4);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bitmapContext = CGBitmapContextCreate(buffer(provide buffer where you want to write image into), width, height, bpc, bytesPerRow, colorSpace, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrderDefault);
CGContextDrawImage(bitmapContext, CGRectMake(0, 0, width, height), screenShotImageRef);
CGContextRelease(bitmapContext);
CGColorSpaceRelease(colorSpace);
I am creating a bitmap in one of the iOS apps in which I draw something on screen and capture the context using the following code :
CGImageRef imageRef = image.CGImage;
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
unsigned char *rawData = (unsigned char*) calloc(height * width * bytesPerPixel, sizeof(unsigned char));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
This data I later use to check if the pixels are black or not. The problem I am facing is when I draw something and its black in color (RGB value 0,0,0) , the RGB value for that pixel is correctly detected in retina displays but in non retina its not giving the correct value.It does not give me 0,0,0 in non retina. Does anyone know about it?
Check the size of your UIImage. It may be different in non-retina display and hence you may check the color of another pixel if it's non retina display on the device.
I'm trying to create specific custom filter effects on image for iOS. So far, I've been trying to get raw data using CGBitmapContextCreate. However, I don't really have an idea of how to modify my rawData. I hope to perform calculations on it. I hope to effect pixel by pixel with the rawData but I have no idea how to manipulate it.
I also don't know how I can draw my bitmap context to a UIImage, so I can render the finished product on an UIImageView.
Could somebody give me some pointers to how I might be able to achieve that?
Here's my code so far:
// First get the image into your data buffer
CGImageRef imageRef = imageView.image.CGImage;
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = calloc(height * width * 4, sizeof(unsigned char));
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
//perform calculations on rawData? or context? not sure!! i hope to effect pixel by pixel.
//am i doing this correctly?
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//set the imageview with the new image
[imageView setImage:newImage];
CGContextRelease(context);
You are almost there.
You can perform your transactions on the RGB and Alpha channels via:
for (NSUInteger i = 0 ; i < rawDataSpace ; i+=4) {
rawData[i+0] = ...
rawData[i+1] = ...
rawData[i+2] = ...
rawData[i+3] = ... // = Alpha channel