Please do not post the same thing multiple times. The board software automatically flags certain posts as needing moderator attention. This happens the most often for new users. I'm pretty sure this is made clear at the time you attempt to post. Posting the same thing over and over again just makes that many more posts the moderators have to weed through later. This makes us sad. Don't make us sad. If your post/thread doesn't appear, just wait a while. Don't post it again. If it hasn't shown up by the next day, then you can try again. I normally go through posts in the mornings, and try to check a few times throughout the day, but I'm not here 24/7. There will typically be a significant delay before posts are approved. Just be patient.
I have done lots of searching for a way to resize images via the iPhone SDK and I have come across a few methods which "work" but the resulting image does not look nearly as good as if you took the full resolution image and told it to draw inside a rectangle; which obviously if you could use the same interpolation routines as that drawing call does you should be able to get the same result. So what I am looking for is a way to resize an image with minimal visual anomalies. I have found that if I use a re-sized image when drawing it is much quicker than using the full resolution image (obviously).
Please make sure any links provided are current; I have been given countless links to methods which are void given the current version of the SDK as well as some links that provide "questionable" means to accomplish this task using functions which are not fully supported by the SDK and could easily change in the future.
Almost certainly the way that simply drawing an image onscreen works is by CGContextDrawImage(). To scale an image create a bitmap context whose axial ratio is the same as the original image's. CGContextDrawImage(), CGBitmapContextCreateImage(), UIImage imageWithCGImage.
I have not tried it using the CG library; I am not terribly familiar with how to create a bitmap and draw to it. Do you have any resources showing a similar action or anything that may help me down the right path?
// ============================================================== // resizedImage // ============================================================== // Return a scaled down copy of the image.
// There's a wierdness with kCGImageAlphaNone and CGBitmapContextCreate // see Supported Pixel Formats in the Quartz 2D Programming Guide // Creating a Bitmap Graphics Context section // only RGB 8 bit images with alpha of kCGImageAlphaNoneSkipFirst, kCGImageAlphaNoneSkipLast, kCGImageAlphaPremultipliedFirst, // and kCGImageAlphaPremultipliedLast, with a few other oddball image kinds are supported // The images on input here are likely to be png or jpeg files if (alphaInfo == kCGImageAlphaNone) alphaInfo = kCGImageAlphaNoneSkipLast;
// Build a bitmap context that's the size of the thumbRect CGContextRef bitmap = CGBitmapContextCreate( NULL, thumbRect.size.width, // width thumbRect.size.height, // height CGImageGetBitsPerComponent(imageRef), // really needs to always be 8 4 * thumbRect.size.width, // rowbytes CGImageGetColorSpace(imageRef), alphaInfo );
// Draw into the context, this scales the image CGContextDrawImage(bitmap, thumbRect, imageRef);
// Get an image from the context and a UIImage CGImageRef ref = CGBitmapContextCreateImage(bitmap); UIImage* result = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap); // ok if NULL CGImageRelease(ref);
I've tried using the function provided by Phoney, and I've found that the memory associated with "ref" never gets released. If I call this function enough times, I'll get a memory warning from the system, and then a crash. It's strange because ref is pretty clearly being released properly with CGImageRelease(ref), but when I watch the object allocations with Leaks, I can see that the memory associated with it is never actually freed. Even more bizarre is that it doesn't get detected as a leak by Leaks, it just never gets freed. I have the exact same problem with _imageScaleToSize. Every time I call it, my memory footprint balloons up by 7MB or so, never to go back down.
Has anybody experienced this? Any possible solutions?
What are you doing with the UIImage that's returned by this method?
Hi Phoney, thanks for writing. Ultimately, I'm saving it to an sqlite database. The image is taken from the camera, reduced in size, and then saved to sqlite. However, if I change resizedImage to something like this, I don't have the memory problem:
Does Leaks report this as a leak or just as memory that's in use? There's no memory leak, per se, in this code. It's what happens to the UIImage object after it's returned that determines whether a leak happens or not. Do you add the UIImage to an array or a dictionary or a UIImageView or anything like that? The db doesn't have anything directly to do with this. Can you tell if it's the CGImageRef itself or the buffer that it creates that's living on?
There's a possibility that the images are being cached by the framework intentionally.
You could try changing that line to
UIImage* result = [[[UIImage alloc] initWithCGImage:ref] autorelease];
...ref has a retain count of 1. But after the next line...
UIImage* result = [UIImage imageWithCGImage:ref];
...ref has a retain count of 2. I'm not really sure why. The obvious answer of calling CGImageRelease twice to bring the retain count down to 0 causes an EXC_BAD_ACCESS later when I try to access the returned image. I think that may be what you were trying to handle with the autorelease above, which I just tried, but I don't think CGImageRef is autoreleaseable, so no dice...
The CGImageRef is retained by the UIImage. Most likely all UIImages are just a wrapper for CGImageRefs. I would expect that the CGImageRef would be released when the UIImage is released. That's why I asked what you were doing with it when it was returned from the function. The lifespan of the image ref and the UIImage should be the same.
If you don't do anything at all with the UIImage, just let its lifespan expire when the autorelease pool is drained, then what happens? If the memory goes away then it's a bug in your code. If the memory still remains then it's probably a system issue.
managing memory with UIImages is very tricky because the iPhone does so much to try to optimize the use of memory, including caching things behind your back. The more you can stick within the CG domain, the more you will have predictable control of your memory.
I get the following uncaught exception when I call this code: 2008-11-02 06:35:15.265 GiftFinder[3208:20b] *** -[NSCFData CGImage]: unrecognized selector sent to instance 0x45d4000 2008-11-02 06:35:15.267 GiftFinder[3208:20b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSCFData CGImage]: unrecognized selector sent to instance 0x45d4000'
Is there a framework I need to add or something?
I am very new to iPhone development and Objective-C so it seems more likely I've done something wrong. Please advise
The code works great for me...almost. I am doing something wrong because my image is rotating counterclock-wise.
I am grabbing an image from the camera, running it through this code to resample it to screen resolution, then displaying it. At that point, it is rotated. That was not happening before I started resampling it.
I think it must have something to do with the origin of the CGContext being in the botton left corner. But I've tried several variations on the rect I pass in, all to no avail.
Any thoughts on getting my images back to right-side-up?
To get the highest possible interpolation quality you might want to add the following line to PhoneyDeveloper's code, right after the creation of the CGContext:
I'm trying to resize UIImages before saving them as images to the documents folder.
I've implemented the code below from PhoneyDeveloper which is working well for resizing my images.
The trouble is the images are rotated 90 degrees anti-clockwise. Is there an easy way to fix this? Or another solution to resize UIImages? It must be documented though.
Thanks
PhoneyDeveloper;23898 said:
// ============================================================== // resizedImage // ============================================================== // Return a scaled down copy of the image.
// There's a wierdness with kCGImageAlphaNone and CGBitmapContextCreate // see Supported Pixel Formats in the Quartz 2D Programming Guide // Creating a Bitmap Graphics Context section // only RGB 8 bit images with alpha of kCGImageAlphaNoneSkipFirst, kCGImageAlphaNoneSkipLast, kCGImageAlphaPremultipliedFirst, // and kCGImageAlphaPremultipliedLast, with a few other oddball image kinds are supported // The images on input here are likely to be png or jpeg files if (alphaInfo == kCGImageAlphaNone) alphaInfo = kCGImageAlphaNoneSkipLast;
// Build a bitmap context that's the size of the thumbRect CGContextRef bitmap = CGBitmapContextCreate( NULL, thumbRect.size.width, // width thumbRect.size.height, // height CGImageGetBitsPerComponent(imageRef), // really needs to always be 8 4 * thumbRect.size.width, // rowbytes CGImageGetColorSpace(imageRef), alphaInfo );
// Draw into the context, this scales the image CGContextDrawImage(bitmap, thumbRect, imageRef);
// Get an image from the context and a UIImage CGImageRef ref = CGBitmapContextCreateImage(bitmap); UIImage* result = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap); // ok if NULL CGImageRelease(ref);
return result; }
BUZZER! : iTunes Library Music Quiz (1 or 2 Player)
One is using Phoney's way (in fact, I've fixed Phoney's function considering width and height).
And the othere is using UIGraphicsBeginImageContext() and UIGraphicsGetImageFromCurrentImageContext().
F.Y.I, I've tested them.
Anyway resizing functions are as below,
#1 - using UIGraphicsBeginImageContext() and UIGraphicsGetImageFromCurrentImageContext()
-(UIImage*)resizedImage1:(UIImage*)inImage inRect:(CGRect)thumbRect { // Creates a bitmap-based graphics context and makes it the current context. UIGraphicsBeginImageContext(thumbRect.size); [inImage drawInRect:thumbRect];
// There's a wierdness with kCGImageAlphaNone and CGBitmapContextCreate // see Supported Pixel Formats in the Quartz 2D Programming Guide // Creating a Bitmap Graphics Context section // only RGB 8 bit images with alpha of kCGImageAlphaNoneSkipFirst, kCGImageAlphaNoneSkipLast, kCGImageAlphaPremultipliedFirst, // and kCGImageAlphaPremultipliedLast, with a few other oddball image kinds are supported // The images on input here are likely to be png or jpeg files if (alphaInfo == kCGImageAlphaNone) alphaInfo = kCGImageAlphaNoneSkipLast;
// Build a bitmap context that's the size of the thumbRect CGFloat bytesPerRow;
Alones, your fix is wrong. The bytesPerRow parameter is independent of aspect ratio. Even if the image is wider than it is tall, that doesn't affect the bytes per row. PhoneyDeveloper's original code is correct.
The trouble is the images are rotated 90 degrees anti-clockwise. Is there an easy way to fix this?
Yes, if you take a picture with the iPhone's camera, the UIImage.imageOrientation property might be set to UIImageOrientationDown, UIImageOrientationRight, or whatever, and the resize code posted here doesn't take that into account. For a fix, see this thread.
Phoney, there are some problems with your code. In particular, the last parameter takes a CGBitmapInfo type, but you're passing a CGImageAlphaInfo type. This can chop off the byte ordering information stored in CGBitmapInfo, resulting in odd problems such as pink-tinted images. Also, you're hard coding the bytesPerRow value when in fact it should be based on the original image. The following is a revised version of your code:
// Returns a rescaled copy of the image; its imageOrientation will be UIImageOrientationUp // If the new size is not integral, it will be rounded up - (UIImage *)makeResizedImage:(CGSize)newSize quality:(CGInterpolationQuality)interpolationQuality { CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); CGImageRef imageRef = self.CGImage;
// Compute the bytes per row of the new image size_t bytesPerRow = CGImageGetBitsPerPixel(imageRef) / CGImageGetBitsPerComponent(imageRef) * newRect.size.width; bytesPerRow = (bytesPerRow + 15) & ~15; // Make it 16-byte aligned
// Build a bitmap context that's the same dimensions as the new size CGContextRef bitmap = CGBitmapContextCreate(NULL, newRect.size.width, newRect.size.height, CGImageGetBitsPerComponent(imageRef), bytesPerRow, CGImageGetColorSpace(imageRef), CGImageGetBitmapInfo(imageRef));
// Draw into the context; this scales the image CGContextDrawImage(bitmap, newRect, imageRef);
// Get the resized image from the context and a UIImage CGImageRef resizedImageRef = CGBitmapContextCreateImage(bitmap); UIImage *resizedImage = [UIImage imageWithCGImage:resizedImageRef];
// Clean up CGContextRelease(bitmap); CGImageRelease(resizedImageRef);
return resizedImage; }
As with your original function, this code works as expected only when UIImage.imageOrientation == UIImageOrientationUp.
Replies
The problem with this code is that it is un-documented which means it could go by the wayside and the interpolation is poor.
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeHave you tried doing it that way?
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeI have not tried it using the CG library; I am not terribly familiar with how to create a bitmap and draw to it. Do you have any resources showing a similar action or anything that may help me down the right path?
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeI've tried using the function provided by Phoney, and I've found that the memory associated with "ref" never gets released. If I call this function enough times, I'll get a memory warning from the system, and then a crash. It's strange because ref is pretty clearly being released properly with CGImageRelease(ref), but when I watch the object allocations with Leaks, I can see that the memory associated with it is never actually freed. Even more bizarre is that it doesn't get detected as a leak by Leaks, it just never gets freed. I have the exact same problem with _imageScaleToSize. Every time I call it, my memory footprint balloons up by 7MB or so, never to go back down.
Has anybody experienced this? Any possible solutions?
Thanks,
Tom
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeUIImage* resizedImage(UIImage *inImage, CGRect thumbRect)
{
return inImage;
}
Could it be something to do with the database?
Thanks,
Tom
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeThere's a possibility that the images are being cached by the framework intentionally.
You could try changing that line to
and see if that makes any difference.
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeAfter this line...
...ref has a retain count of 1. But after the next line...
...ref has a retain count of 2. I'm not really sure why. The obvious answer of calling CGImageRelease twice to bring the retain count down to 0 causes an EXC_BAD_ACCESS later when I try to access the returned image. I think that may be what you were trying to handle with the autorelease above, which I just tried, but I don't think CGImageRef is autoreleaseable, so no dice...
Thanks,
Tom
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeIf you don't do anything at all with the UIImage, just let its lifespan expire when the autorelease pool is drained, then what happens? If the memory goes away then it's a bug in your code. If the memory still remains then it's probably a system issue.
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome2008-11-02 06:35:15.265 GiftFinder[3208:20b] *** -[NSCFData CGImage]: unrecognized selector sent to instance 0x45d4000
2008-11-02 06:35:15.267 GiftFinder[3208:20b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSCFData CGImage]: unrecognized selector sent to instance 0x45d4000'
Is there a framework I need to add or something?
I am very new to iPhone development and Objective-C so it seems more likely I've done something wrong. Please advise
in .h file:
UIImage* resizedImage(UIImage *inImage, CGRect thumbRect);
in .m file:
UIImage* resizedImage(UIImage *inImage, CGRect thumbRect)
{
... Exactly as above
}
UIImage *image = (UIImage *)ABPersonCopyImageData(person);
CGRect sz = CGRectMake(0.0f, 0.0f, 69.0f, 69.0f);
UIImage *smallImage = resizedImage(image, sz);
Thanks, Chuck Brandt
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeYou're typecasting an NSData* to a UIImage* and they're not the same.
Try this:
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeChuck
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeI am grabbing an image from the camera, running it through this code to resample it to screen resolution, then displaying it. At that point, it is rotated. That was not happening before I started resampling it.
I think it must have something to do with the origin of the CGContext being in the botton left corner. But I've tried several variations on the rect I pass in, all to no avail.
Any thoughts on getting my images back to right-side-up?
Thanks.
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeI have not done any real tests how much this matters (if at all). But I think it can't hurt (unless performance is a big issue).
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeI've implemented the code below from PhoneyDeveloper which is working well for resizing my images.
The trouble is the images are rotated 90 degrees anti-clockwise. Is there an easy way to fix this? Or another solution to resize UIImages? It must be documented though.
Thanks
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeI think the 90 degrees rotated issue is the way things work. I don't have an iPhone so I haven't run into it.
I think that the solution is to apply a transform to the context to rotate it 90 degrees before the CGContextDrawImage call.
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeAnd the othere is using UIGraphicsBeginImageContext() and UIGraphicsGetImageFromCurrentImageContext().
F.Y.I, I've tested them.
Anyway resizing functions are as below,
#1 - using UIGraphicsBeginImageContext() and UIGraphicsGetImageFromCurrentImageContext()
-(UIImage*)resizedImage1:(UIImage*)inImage inRect:(CGRect)thumbRect {
// Creates a bitmap-based graphics context and makes it the current context.
UIGraphicsBeginImageContext(thumbRect.size);
[inImage drawInRect:thumbRect];
return UIGraphicsGetImageFromCurrentImageContext();
}
#2 - updating Phoney's way
-(UIImage*)resizedImage2:(UIImage*)inImage inRect:(CGRect)thumbRect {
CGImageRef imageRef = [inImage CGImage];
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
// There's a wierdness with kCGImageAlphaNone and CGBitmapContextCreate
// see Supported Pixel Formats in the Quartz 2D Programming Guide
// Creating a Bitmap Graphics Context section
// only RGB 8 bit images with alpha of kCGImageAlphaNoneSkipFirst, kCGImageAlphaNoneSkipLast, kCGImageAlphaPremultipliedFirst,
// and kCGImageAlphaPremultipliedLast, with a few other oddball image kinds are supported
// The images on input here are likely to be png or jpeg files
if (alphaInfo == kCGImageAlphaNone)
alphaInfo = kCGImageAlphaNoneSkipLast;
// Build a bitmap context that's the size of the thumbRect
CGFloat bytesPerRow;
if( thumbRect.size.width > thumbRect.size.height ) {
bytesPerRow = 4 * thumbRect.size.width;
} else {
bytesPerRow = 4 * thumbRect.size.height;
}
CGContextRef bitmap = CGBitmapContextCreate(
NULL,
thumbRect.size.width, // width
thumbRect.size.height, // height
8, //CGImageGetBitsPerComponent(imageRef), // really needs to always be 8
bytesPerRow, //4 * thumbRect.size.width, // rowbytes
CGImageGetColorSpace(imageRef),
alphaInfo
);
// Draw into the context, this scales the image
CGContextDrawImage(bitmap, thumbRect, imageRef);
// Get an image from the context and a UIImage
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage* result = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap); // ok if NULL
CGImageRelease(ref);
return result;
}
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like AwesomeAs with your original function, this code works as expected only when UIImage.imageOrientation == UIImageOrientationUp.
- Spam
- Abuse
- Troll
0 • Off Topic Insightful Disagree Dislike Like Awesome