1

I have an "ImageManipulator" class that performs some cropping, resizing and rotating of camera images on the iPhone.

At the moment, everything works as expected but I keep getting a few huge spikes in memory consumption which occasionally cause the app to crash.

I have managed to isolate the problem to a part of the code where I check for the current image orientation property and rotate it accordingly to UIImageOrientationUp. I then get the image from the bitmap context and save it to disk.

This is currently what I am doing:

CGAffineTransform transform = CGAffineTransformIdentity;
// Check for orientation and set transform accordingly...
transform = CGAffineTransformTranslate(transform, self.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);

// Create a bitmap context with the image that was passed so we can perform the rotation
CGContextRef ctx = CGBitmapContextCreate(NULL, self.size.width, self.size.height,
                                         CGImageGetBitsPerComponent(self.CGImage), 0,
                                         CGImageGetColorSpace(self.CGImage),
                                         CGImageGetBitmapInfo(self.CGImage));

// Rotate the context
CGContextConcatCTM(ctx, transform);

// Draw the image into the context
CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);

// Grab the bitmap context and save it to the disk...

Even after trying to scale the image down to half or even 1/4 of the size, I am still seeing the spikes to I am wondering if there is a different / more efficient way to get the rotation done as above?

Thanks in advance for the replies. Rog

Rog
  • 18,602
  • 6
  • 76
  • 97
  • How big is the image? We had similar problem, so we first scaled image down to acceptable dimension and then we applied rotation. – Akash Kava Jul 03 '11 at 07:47
  • Are you creating big autoreleased objects in a lengthy operation? – Eiko Jul 03 '11 at 08:13
  • 2
    The memory spike is because `CGBitmapContextCreate` must allocate a buffer to hold the image data, which would be about `width * height * CGImageGetBitsPerComponent(self.CGImage) / 8` bytes. It's also possible that the `CGContextDrawImage` allocates another similarly-sized buffer if it needs to load or decompress the image data for the original image before drawing it. I don't know of any more efficient way to do it, though. – Anomie Jul 03 '11 at 16:58

4 Answers4

1

If you are saving to JPEG, I guess an alternative approach is to save the image as-is and then set the rotation to whatever you'd like by manipulating the EXIF metadata? See for example this post. Simple but probably effective, even if you have to hold the image payload bytes in memory ;)

Community
  • 1
  • 1
ThomasRS
  • 8,215
  • 5
  • 33
  • 48
1

Things you can do:

  1. Scale down the image even more (which you probably don't want)

  2. Remember to release everything as soon as you finish with it

  3. Live with it

I would choose option 2 and 3.

Image editing is very resource intensive, as it loads the entire raw uncompressed image data into the memory for processing. This is inevitable as there is absolutely no other way to modify an image other than to load the complete raw data into the memory. Having memory consumption spikes doesn't really matter unless the app receives a memory warning, in that case quickly get rid of everything before it crashes. It is very rare that you would get a memory warning, though, because my app regularly loads a single > 10 mb file into the memory and I don't get a warning, even on older devices. So you'll be fine with the spikes.

Greg
  • 9,068
  • 6
  • 49
  • 91
0
  • Have you tried checking for memory leaks and analyzing allocations?
  • If the image is still too big, try rotating the image in pieces instead of as a whole.
James Lim
  • 12,915
  • 4
  • 40
  • 65
-1

As Anomie mentioned, CGBitmapContextCreate creates a context. We should release that by using

CGContextRelease(ctx);

If you have any other objects created using create or copy, that should also be released. If it is CFData, then

CFRelease(cfdata);
Ilanchezhian
  • 17,426
  • 1
  • 53
  • 55