Advertise Books Events Forum News Social Networking Support Us

sdkIQ for iPhone
($4.99)

Shape Up
($0.99)

Your First iPhone App
($1.99)

iVidCam Free
(free)

Kid Art
($0.99)

iPUBQUIZ
(£1.19)

ArtStudio
($3.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 10-14-2008, 03:52 PM   #1 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 31
Default 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.
shawn is offline   Reply With Quote
Old 10-14-2008, 04:20 PM   #2 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 31
Default

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.
shawn is offline   Reply With Quote
Old 10-14-2008, 04:45 PM   #3 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
Default

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?
PhoneyDeveloper is offline   Reply With Quote
Old 10-14-2008, 04:52 PM   #4 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 31
Default

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?
shawn is offline   Reply With Quote
Old 10-14-2008, 05:33 PM   #5 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
Default

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;
}
PhoneyDeveloper is offline   Reply With Quote
Old 10-15-2008, 09:41 AM   #6 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 31
Default

Thanks Phoney! With a few tweaks that method worked great with no warnings!
shawn is offline   Reply With Quote
Old 10-22-2008, 08:49 PM   #7 (permalink)
New Member
 
Join Date: Oct 2008
Posts: 7
Default

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.
tgersic is offline   Reply With Quote
Old 10-22-2008, 10:42 PM   #8 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
Default

What are you doing with the UIImage that's returned by this method?
PhoneyDeveloper is offline   Reply With Quote
Old 10-23-2008, 08:26 AM   #9 (permalink)
New Member
 
Join Date: Oct 2008
Posts: 7
Default

Quote:
Originally Posted by PhoneyDeveloper View Post
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
tgersic is offline   Reply With Quote
Old 10-23-2008, 09:59 AM   #10 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
Default

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.
PhoneyDeveloper is offline   Reply With Quote
Old 10-23-2008, 10:25 AM   #11 (permalink)
New Member
 
Join Date: Oct 2008
Posts: 7
Default

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
tgersic is offline   Reply With Quote
Old 10-23-2008, 11:23 AM   #12 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
Default

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.
PhoneyDeveloper is offline   Reply With Quote
Old 10-23-2008, 12:46 PM   #13 (permalink)
Registered Member
 
Join Date: Aug 2008
Posts: 80
Default

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.
ecume is offline   Reply With Quote
Old 11-02-2008, 06:48 AM   #14 (permalink)
New Member
 
Join Date: Nov 2008
Posts: 2
Default I get unrecognized selector when I try to use this code.

Quote:
Originally Posted by PhoneyDeveloper View Post
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
chuckbrandt is offline   Reply With Quote
Old 11-02-2008, 08:19 AM   #15 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
Default

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);
PhoneyDeveloper is offline   Reply With Quote
Old 11-02-2008, 08:49 AM   #16 (permalink)
New Member
 
Join Date: Nov 2008
Posts: 2
Default Thank You!

That worked, thank you very much.

Chuck
chuckbrandt is offline   Reply With Quote
Old 11-02-2008, 09:41 AM   #17 (permalink)
Registered Member
 
Join Date: Aug 2008
Posts: 80
Default

where does CGImageRef imageRef get released? shouldn't that be released along with 'ref'?
ecume is offline   Reply With Quote
Old 11-02-2008, 05:02 PM   #18 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
Default

That doesn't get released. CGImage is an accessor. It doesn't have alloc or copy in its name.
PhoneyDeveloper is offline   Reply With Quote
Old 11-07-2008, 02:29 PM   #19 (permalink)
New Member
 
Join Date: Nov 2008
Posts: 1
Default 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.
caldwell_jason is offline   Reply With Quote
Old 11-15-2008, 03:48 PM   #20 (permalink)
hkk
Registered Member
 
Join Date: Oct 2008
Posts: 10
Default 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).
hkk is offline   Reply With Quote
Old 11-21-2008, 10:55 AM   #21 (permalink)
Registered Member
 
Stitch's Avatar
 
Join Date: Aug 2008
Posts: 398
Default

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 View Post
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)
Stitch is offline   Reply With Quote
Old 11-22-2008, 05:05 PM   #22 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
Default

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.
PhoneyDeveloper is offline   Reply With Quote
Old 08-12-2009, 01:37 PM   #23 (permalink)
Registered Member
 
Join Date: Oct 2008
Posts: 4
Default 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.
alones is offline   Reply With Quote
Old 08-18-2009, 01:21 PM   #24 (permalink)
Registered Member
 
Join Date: Aug 2009
Posts: 13
Default

Quote:
Originally Posted by alones View Post
(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.
vocaro is offline   Reply With Quote
Old 08-18-2009, 05:07 PM   #25 (permalink)
Registered Member
 
Join Date: Aug 2009
Posts: 13
Default

Quote:
Originally Posted by Stitch View Post
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.
vocaro is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


Enter the iPhone App Challenge!  Win $500!
» Advertisements
» Stats
Members: 24,314
Threads: 39,112
Posts: 171,494
Top Poster: smasher (2,576)
Welcome to our newest member, biggybillly
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 06:11 PM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.2.0