 |
|
 |
|
 |
10-14-2008, 03:52 PM
|
#1 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 31
|
Resize Image High Quality
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.
|
|
|
10-14-2008, 04:20 PM
|
#2 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 31
|
The best method I have found that even comes close to doing what I want is the following line of code:
Code:
UIImage *img = [[UIImage imageNamed:@"image.png"] _imageScaledToSize:CGSizeMake(32.0f, 32.0f) interpolationQuality:1];
The problem with this code is that it is un-documented which means it could go by the wayside and the interpolation is poor.
|
|
|
10-14-2008, 04:45 PM
|
#3 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 1,431
|
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.
Have you tried doing it that way?
|
|
|
10-14-2008, 04:52 PM
|
#4 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 31
|
Thanks for the reply Phoney!
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?
|
|
|
10-14-2008, 05:33 PM
|
#5 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 1,431
|
Code:
// ==============================================================
// resizedImage
// ==============================================================
// Return a scaled down copy of the image.
UIImage* resizedImage(UIImage *inImage, 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
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;
}
|
|
|
10-15-2008, 09:41 AM
|
#6 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 31
|
Thanks Phoney! With a few tweaks that method worked great with no warnings!
|
|
|
10-22-2008, 08:49 PM
|
#7 (permalink)
|
|
New Member
Join Date: Oct 2008
Posts: 7
|
Hi All,
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?
Thanks,
Tom
Last edited by tgersic; 10-22-2008 at 08:59 PM.
|
|
|
10-22-2008, 10:42 PM
|
#8 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 1,431
|
What are you doing with the UIImage that's returned by this method?
|
|
|
10-23-2008, 08:26 AM
|
#9 (permalink)
|
|
New Member
Join Date: Oct 2008
Posts: 7
|
Quote:
Originally Posted by PhoneyDeveloper
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:
UIImage* resizedImage(UIImage *inImage, CGRect thumbRect)
{
return inImage;
}
Could it be something to do with the database?
Thanks,
Tom
|
|
|
10-23-2008, 09:59 AM
|
#10 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 1,431
|
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
Code:
UIImage* result = [[[UIImage alloc] initWithCGImage:ref] autorelease];
and see if that makes any difference.
|
|
|
10-23-2008, 10:25 AM
|
#11 (permalink)
|
|
New Member
Join Date: Oct 2008
Posts: 7
|
You're correct. Leaks does not report it as a leak. It's just memory that is never freed. What I've found is this:
After this line...
Code:
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
...ref has a retain count of 1. But after the next line...
Code:
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...
Thanks,
Tom
|
|
|
10-23-2008, 11:23 AM
|
#12 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 1,431
|
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.
Last edited by PhoneyDeveloper; 10-23-2008 at 11:28 AM.
|
|
|
10-23-2008, 12:46 PM
|
#13 (permalink)
|
|
Registered Member
Join Date: Aug 2008
Posts: 80
|
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.
|
|
|
11-02-2008, 06:48 AM
|
#14 (permalink)
|
|
New Member
Join Date: Nov 2008
Posts: 2
|
I get unrecognized selector when I try to use this code.
Quote:
Originally Posted by PhoneyDeveloper
Code:
//
UIImage* resizedImage(UIImage *inImage, CGRect thumbRect)
{
...
}
|
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
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
|
|
|
11-02-2008, 08:19 AM
|
#15 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 1,431
|
On this line:
Code:
UIImage *image = (UIImage *)ABPersonCopyImageData(person);
You're typecasting an NSData* to a UIImage* and they're not the same.
Try this:
Code:
NSData* imageData = (NSData*)ABPersonCopyImageData(person);
UIImage* image = [UIImage imageWithData: imageData];
[imageData release];
CGRect sz = CGRectMake(0.0f, 0.0f, 69.0f, 69.0f);
UIImage *smallImage = resizedImage(image, sz);
|
|
|
11-02-2008, 08:49 AM
|
#16 (permalink)
|
|
New Member
Join Date: Nov 2008
Posts: 2
|
Thank You!
That worked, thank you very much.
Chuck
|
|
|
11-02-2008, 09:41 AM
|
#17 (permalink)
|
|
Registered Member
Join Date: Aug 2008
Posts: 80
|
where does CGImageRef imageRef get released? shouldn't that be released along with 'ref'?
|
|
|
11-02-2008, 05:02 PM
|
#18 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 1,431
|
That doesn't get released. CGImage is an accessor. It doesn't have alloc or copy in its name.
|
|
|
11-07-2008, 02:29 PM
|
#19 (permalink)
|
|
New Member
Join Date: Nov 2008
Posts: 1
|
Why is my image rotating
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?
Thanks.
|
|
|
11-15-2008, 03:48 PM
|
#20 (permalink)
|
|
Registered Member
Join Date: Oct 2008
Posts: 10
|
high quality interpolation
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:
Code:
CGContextSetInterpolationQuality(bitmap, kCGInterpolationHigh)
I 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).
|
|
|
11-21-2008, 10:55 AM
|
#21 (permalink)
|
|
Registered Member
Join Date: Aug 2008
Posts: 398
|
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
Quote:
Originally Posted by PhoneyDeveloper
Code:
// ==============================================================
// resizedImage
// ==============================================================
// Return a scaled down copy of the image.
UIImage* resizedImage(UIImage *inImage, 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
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)
|
|
|
11-22-2008, 05:05 PM
|
#22 (permalink)
|
|
New Member
Join Date: Sep 2008
Posts: 1,431
|
Howdy,
I 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.
|
|
|
08-12-2009, 01:37 PM
|
#23 (permalink)
|
|
Registered Member
Join Date: Oct 2008
Posts: 4
|
I made two method for reszing.
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()
Code:
-(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
Code:
-(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;
}
Last edited by alones; 08-12-2009 at 01:44 PM.
|
|
|
08-18-2009, 01:21 PM
|
#24 (permalink)
|
|
Registered Member
Join Date: Aug 2009
Posts: 13
|
Quote:
Originally Posted by alones
(in fact, I've fixed Phoney's function considering width and height).
...
Code:
CGFloat bytesPerRow;
if( thumbRect.size.width > thumbRect.size.height ) {
bytesPerRow = 4 * thumbRect.size.width;
} else {
bytesPerRow = 4 * thumbRect.size.height;
}
|
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.
|
|
|
08-18-2009, 05:07 PM
|
#25 (permalink)
|
|
Registered Member
Join Date: Aug 2009
Posts: 13
|
Quote:
Originally Posted by Stitch
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.
|
|
|
 |
|
| Thread Tools |
|
|
| Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
|
» Advertisements |
» Online Users: 367 |
| 30 members and 337 guests |
| amartinwest, brunovaz, chrish2os, cje, Cliff76, ColouredRobot, darkcrayon, dot, dre, ershaer, Eskema, Fuzzy Logic, gandohr, georgeburns, guyhundere, Hololont, jazztpt, jbro, manofham, moehac, MrMattMac, Nuncha, pcgeek, qwertyp, robin1508, Rumpy, RyanW, smaxim, tateyaku, TheKernal |
| Most users ever online was 779, 05-11-2009 at 09:55 AM. |
» Stats |
Members: 24,314
Threads: 39,112
Posts: 171,494
Top Poster: smasher (2,576)
|
| Welcome to our newest member, biggybillly |
|