iOS Pixel Access of UIImage results in crash/skewing - ios

I am trying to access the pixels of a certain image which has been resized using this block:
-(UIImage *)imageResize:(UIImage *)imageResizable scaledToSize:(CGSize)newSize
{
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[imageResizable drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
... where newSize is the size of an UIView, where I am trying to fit this image.
Now, I am supposed to access the pixels of this image, and do some filtering on it.
I use the following code block:
-(UIImage*) filter : (UIImage *) image
{
CGImageRef imageBuffTarget = [image CGImage];
CFMutableDataRef pixelDataTarget = CFDataCreateMutableCopy(0, 0, CGDataProviderCopyData(CGImageGetDataProvider(imageBuffTarget)));
NSUInteger width2 = CGImageGetWidth(imageBuffTarget);
NSUInteger height2 = CGImageGetHeight(imageBuffTarget);
UInt8 *target_image = (UInt8 *)CFDataGetMutableBytePtr(pixelDataTarget);
// Going forward, I want to do some processing here, on the *target_image data.
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo1 = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
CGContextRef context = CGBitmapContextCreate(target_image, CGImageGetWidth(imageBuffTarget), CGImageGetHeight(imageBuffTarget), CGImageGetBitsPerComponent(imageBuffTarget), CGImageGetBytesPerRow(imageBuffTarget), colorSpaceRef, bitmapInfo1);
CGImageRef imageRef = CGBitmapContextCreateImage (context);
UIImage *newimage = [UIImage imageWithCGImage:imageRef];
CGColorSpaceRelease(colorSpaceRef);
CGContextRelease(context);
CFRelease(imageRef);
return newimage;
}
I take the image from the UIView, pass it on to the 'filter' method and set it back to the view.
But, on doing this, the app crashes and I get the following error in the console:
<Error>: CGBitmapContextCreate: invalid data bytes/row: should be at least 1280 for 8 integer bits/component, 3 components, kCGImageAlphaPremultipliedLast.
<Error>: CGBitmapContextCreateImage: invalid context 0x0
When I change:
CGContextRef context = CGBitmapContextCreate(target_image, CGImageGetWidth(imageBuffTarget), CGImageGetHeight(imageBuffTarget), CGImageGetBitsPerComponent(imageBuffTarget), CGImageGetBytesPerRow(imageBuffTarget), colorSpaceRef, bitmapInfo1);
TO
CGContextRef context = CGBitmapContextCreate(target_image, CGImageGetWidth(imageBuffTarget), CGImageGetHeight(imageBuffTarget), CGImageGetBitsPerComponent(imageBuffTarget), 2*CGImageGetBytesPerRow(imageBuffTarget), colorSpaceRef, bitmapInfo1);
(2 multiplied to the 'bytes per row', which does exceed 1280)
the app doesn't crash, but the output on the view comes out to be a distorted and skewed version of the original image.
Please note that when I call CGImageGetHeight(imageBuffTarget) and CGImageGetWidth(imageBuffTarget), I get the exact height and width of the ImageView whose size I passed into the imageResize method.
Could you please help me figuring out the mistake in this code.
Thanks in advance.

Related

subimage from UIImage

I 've a PNG loaded into an UIImage. I want to get a portion of the image based on a path (i.e. it might not be a rectangular). Say, it might be some shape with arcs, etc. Like a drawing path.
What would be the easiest way to do that?
Thanks.
I haven't run this, so it may not be perfect but this should give you an idea.
UIImage *imageToClip = //get your image somehow
CGPathRef yourPath = //get your path somehow
CGImageRef imageRef = [imageToClip CGImage];
size_t width = CGImageGetWidth(imageRef);  
size_t height = CGImageGetHeight(imageRef);
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, CGImageGetColorSpace(imageRef), kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextAddPath(context, yourPath);
CGContextClip(context);
CGImageRef clippedImageRef = CGBitmapContextCreateImage(context);
UIImage *clippedImage = [UIImage imageWithCGImage:clippedImageRef];//your final, masked image
CGImageRelease(clippedImageRef);
CGContextRelease(context);
The easiest way to add a category to the UIImage with follow method:
-(UIImage *)scaleToRect:(CGRect)rect{
// Create a bitmap graphics context
// This will also set it as the current context
UIGraphicsBeginImageContext(size);
// Draw the scaled image in the current context
[self drawInRect:rect];
// Create a new image from current context
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
// Pop the current context from the stack
UIGraphicsEndImageContext();
// Return our new scaled image
return scaledImage;
}

UIImage create bitmap context with png-8

Hi I am resizing my image using code from
http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
- (UIImage *)resizedImage:(CGSize)newSize
transform:(CGAffineTransform)transform
drawTransposed:(BOOL)transpose
interpolationQuality:(CGInterpolationQuality)quality {
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width);
CGImageRef imageRef = self.CGImage;
CGBitmapInfo bitMapInfo = CGImageGetBitmapInfo(imageRef);
// Build a context that's the same dimensions as the new size
CGContextRef bitmap = CGBitmapContextCreate(NULL,
newRect.size.width,
newRect.size.height,
CGImageGetBitsPerComponent(imageRef),
0,
CGImageGetColorSpace(imageRef),
bitMapInfo);
// Rotate and/or flip the image if required by its orientation
CGContextConcatCTM(bitmap, transform);
// Set the quality level to use when rescaling
CGContextSetInterpolationQuality(bitmap, quality);
// Draw into the context; this scales the image
CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef);
// Get the resized image from the context and a UIImage
CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
// Clean up
CGContextRelease(bitmap);
CGImageRelease(newImageRef);
return newImage;
}
It is working as expected for normal images. However, it fails when I try to give it a png-8 image. I know it as a png-8 image from typing file image.png in the command line.
The output is
image.png: PNG image data, 800 x 264, 8-bit colormap, non-interlaced
The error message in the console is colorspace not supported.
After some googling, I realized that "indexed color spaces are not supported for bitmap graphics contexts."
Following some advice, instead of using the original colorspace, I changed it to
colorSpace = CGColorSpaceCreateDeviceRGB();
Now I am getting this new error:
CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 24 bits/pixel; 3-component color space; kCGImageAlphaNone; 2400 bytes/row.
FYI, my image is 800 px wide.
How can I resolve this issue? Thanks a lot!
I realized that the list of supported formats are here:
https://developer.apple.com/library/ios/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB
And there is none with 24bits/pixel.
So I ended using the accepted solution here:
iPhone: Changing CGImageAlphaInfo of CGImage

iOS retrieve different pixels in pixel by pixel comparison of UIImages

I am trying to do a pixel by pixel comparison of two UIImages and I need to retrieve the pixels that are different. Using this Generate hash from UIImage I found a way to generate a hash for a UIImage. Is there a way to compare the two hashes and retrieve the different pixels?
If you want to actually retrieve the difference, the hash cannot help you. You can use the hash to detect the likely presence of differences, but to get the actual differences, you have to use other techniques.
For example, to create a UIImage that consists of the difference between two images, see this accepted answer in which Cory Kilgor's illustrates the use of CGContextSetBlendMode with a blend mode of kCGBlendModeDifference:
+ (UIImage *) differenceOfImage:(UIImage *)top withImage:(UIImage *)bottom {
CGImageRef topRef = [top CGImage];
CGImageRef bottomRef = [bottom CGImage];
// Dimensions
CGRect bottomFrame = CGRectMake(0, 0, CGImageGetWidth(bottomRef), CGImageGetHeight(bottomRef));
CGRect topFrame = CGRectMake(0, 0, CGImageGetWidth(topRef), CGImageGetHeight(topRef));
CGRect renderFrame = CGRectIntegral(CGRectUnion(bottomFrame, topFrame));
// Create context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
if(colorSpace == NULL) {
printf("Error allocating color space.\n");
return NULL;
}
CGContextRef context = CGBitmapContextCreate(NULL,
renderFrame.size.width,
renderFrame.size.height,
8,
renderFrame.size.width * 4,
colorSpace,
kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
if(context == NULL) {
printf("Context not created!\n");
return NULL;
}
// Draw images
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, CGRectOffset(bottomFrame, -renderFrame.origin.x, -renderFrame.origin.y), bottomRef);
CGContextSetBlendMode(context, kCGBlendModeDifference);
CGContextDrawImage(context, CGRectOffset(topFrame, -renderFrame.origin.x, -renderFrame.origin.y), topRef);
// Create image from context
CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage * image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGContextRelease(context);
return image;
}

quartz 2D image alpha masking

found this little code snippet that seems to do what i want, but im getting yelled at by xcode saying self.CGimage isnt a property of my view controller. (which makes sense since thats a UIimage property). What changes would i need to make to this code for it to be functional? Thanks!
- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {
CGContextRef mainViewContentContext;
CGColorSpaceRef colorSpace;
UIImage* tempImage;
colorSpace = CGColorSpaceCreateDeviceRGB();
// create a bitmap graphics context the size of the image
mainViewContentContext = CGBitmapContextCreate (NULL, image.size.width, image.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);
// free the rgb colorspace
CGColorSpaceRelease(colorSpace);
CGImageRef maskingImage = [maskImage CGImage];
CGContextClipToMask(mainViewContentContext, CGRectMake(0, 0, maskImage.size.width, maskImage.size.height), maskingImage);
CGContextDrawImage(mainViewContentContext, CGRectMake(0, 0, image.size.width, image.size.height), self.CGImage);
// Create CGImageRef of the main view bitmap content, and then
// release that bitmap context
CGImageRef mainViewContentBitmapContext = CGBitmapContextCreateImage(mainViewContentContext);
// convert the finished resized image to a UIImage
UIImage *theImage = [UIImage imageWithCGImage:mainViewContentBitmapContext];
// image is retained by the property setting above, so we can
// release the original
CGContextRelease(mainViewContentContext);
CGImageRelease(mainViewContentBitmapContext);
maskingImage = nil;
CGImageRelease(maskingImage);
// return the image
return theImage;
}
Try replacing self.CGImage with image.CGImage.
Place this method in a UIImage category (or subclass).

Does the code below have potential memory leak?

The last two lines of code below it's returning gives me a potential memory leak warning. .....Is this a true positive warning or false positive warning? If true, how do i fix it? Thanks a lot for your help!
-(UIImage*)setMenuImage:(UIImage*)inImage isColor:(Boolean)bColor
{
int w = inImage.size.width + (_borderDeep * 2);
int h = inImage.size.height + (_borderDeep * 2);
CGColorSpaceRef colorSpace;
CGContextRef context;
if (YES == bColor)
{
colorSpace = CGColorSpaceCreateDeviceRGB();
context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
}
else
{
colorSpace = CGColorSpaceCreateDeviceGray();
context = CGBitmapContextCreate(NULL, w, h, 8, w, colorSpace, kCGImageAlphaNone);
}
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextDrawImage(context, CGRectMake(_borderDeep, _borderDeep, inImage.size.width, inImage.size.height), inImage.CGImage);
CGImageRef image = CGBitmapContextCreateImage(context);
CGContextRelease(context); //releasing context
CGColorSpaceRelease(colorSpace); //releasing colorSpace
//// The two lines of code above caused Analyzer gives me a warning of potential leak.....Is this a true positive warning or false positive warning? If true, how do i fix it?
return [UIImage imageWithCGImage:image];
}
You're leaking the CGImage object (that's stored in your image variable). You can fix this by releasing the image after creating the UIImage.
UIImage *uiImage = [UIImage imageWithCGImage:image];
CGImageRelease(image);
return uiImage;
The reason for this is that CoreGraphics follows the CoreFoundation ownership rules; in this case, the "Create" rule. Namely, functions with "Create" (or "Copy") return an object that you are required to release yourself. So in this case, CGBitmapContextCreateImage() is returning a CGImageRef that you are responsible for releasing.
Incidentally, why aren't you using the UIGraphics convenience functions to create your context? Those will handle putting the right scale on the resulting UIImage. If you want to match your input image, you can do that as well
CGSize size = inImage.size;
size.width += _borderDeep*2;
size.height += _borderDeep*2;
UIGraphicsBeginImageContextWithOptions(size, NO, inImage.scale); // could pass YES for opaque if you know it will be
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
[inImage drawInRect:(CGRect){{_borderDeep, _borderDeep}, inImage.size}];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
You have to free CGImageRef you made. CGBitmapContextCreateImage has "create" in the name, which means (Apple is strict with its naming conventions) that you are responsible for freeing this memory.
Replace the last line with
UIImage *uiimage = [UIImage imageWithCGImage:image];
CGImageRelease(image);
return uiimage;

Resources