This article summarizes the general problem of programmatically selecting a region from an iPhone image that may be rotated (i.e. has an embedded EXIF orientation tag) and my solution.
Some Objective-C iPhone code I've been writing recently needed to extract a region of the original UIImage provided to UIImagePickerControllerDelegates. If you've used this controller you'll know that when using a UIImagePickerController to select an image source, the full original image and optionally a selected region is returned (assuming the user didn't cancel). The nice thing about the selected region is that it is already oriented vertically in relation to how it appears on-screen.
In contrast, the full image is not rotated but still contains EXIF rotation data, which is made available to your code via the UIImage.orientation property. This is where you have to manage your selection rects properly.
Sites I found useful learning about this were the official EXIF specification, Wikipedia's EXIF page and finally Ben Gotow's post with a solution. This post focus just on the selection problem.
The guts of it
Here's a composition of my four test images without EXIF data and how they appear in a viewer.
As soon as the respective EXIF codes 1, 3, 6 and 8 are added to these and we view them, voila! All appears to be good in the world. Code 1 is redundant, but it has was added to the image anyway for completeness.
Note that the original image for EXIF code 6 appears to be lying on it's left side, and it is associated with the Objective-C enum UIImageOrientationRight. From the class doc for UIImageOrientation, it's ambiguous what "Right" means, but with these graphic examples it's clear. I intuitively understand UIImageOrientationRight to mean "The image will be rotated 90 degrees to-the-right or clockwise when it is rendered". The same understanding can be applied to UIImageOrientationLeft (both 90 degrees CCW or 270 degrees CW are used in different places).
The goal here is to select the bordered yellow region. The region starts at (100,100) and has width=300, height=400. So with the non-rotated image, this area can be selected with this code:
UIImage *sourceImage = ... // Our image CGRect selectionRect = CGRectMake(100.0, 100.0, 300.0, 400.0); CGImageRef resultImageRef = CGImageCreateWithImageInRect(sourceImage.CGImage, selectionRect); UIImage *resultImage = [[UIImage alloc] initWithCGImage:resultImageRef];
So far so good. But of course when we apply this code to our images, the resultImage doesn't always contain what we want:
Ok, that's clearly a stuff-up (and that's pretty much what confronted me before writing this). A good solution here is to apply affine transformations to the selection CGRect so that it coincides with the original image's area that we are interested in. We continue to create the CGRect in a "vertically oriented" coordinate space, pass it to the function TransformCGRectForUIImageOrientation, and use the transformed CGRect to select the area we want. This is a natural fit to the problem domain as the difference between the source and rotated images can be represented as affine transformations. math.h is required for the PI constants.
CGRect TransformCGRectForUIImageOrientation(CGRect source, UIImageOrientation orientation, CGSize imageSize) { switch (orientation) { case UIImageOrientationLeft: { // EXIF #8 CGAffineTransform txTranslate = CGAffineTransformMakeTranslation( imageSize.height, 0.0); CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate, M_PI_2); return CGRectApplyAffineTransform(source, txCompound); } case UIImageOrientationDown: { // EXIF #3 CGAffineTransform txTranslate = CGAffineTransformMakeTranslation( imageSize.width, imageSize.height); CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate, M_PI); return CGRectApplyAffineTransform(source, txCompound); } case UIImageOrientationRight: { // EXIF #6 CGAffineTransform txTranslate = CGAffineTransformMakeTranslation( 0.0, imageSize.width); CGAffineTransform txCompound = CGAffineTransformRotate(txTranslate, M_PI + M_PI_2); return CGRectApplyAffineTransform(source, txCompound); } case UIImageOrientationUp: // EXIF #1 - do nothing default: // EXIF 2,4,5,7 - ignore return source; } } ... UIImage *sourceImage = ... // Our image CGRect selectionRect = CGRectMake(100.0, 100.0, 300.0, 400.0); CGRect transformedRect = TransformCGRectForUIImageOrientation( selectionRect, sourceImage.imageOrientation, sourceImage.size); CGImageRef resultImageRef = CGImageCreateWithImageInRect( sourceImage.CGImage, transformedRect); UIImage *resultImage = [[UIImage alloc] initWithCGImage:resultImageRef];
The selections now look like this:
That's better! This particular application doesn't need to rotate these as Ben's solution demonstrated, which is why I factored out the CGRect transforming into the above function.
I hope this helps people who encounter this or a similar problem. If you do copy either the function or the content of this article for republishing elsewhere on the web, I ask that you credit this blog entry as the source.
The original images can be downloaded here.









