iOS Image Orientation has Strange Behavior
I had the same problem when I get the image from Camera, I put the following code to fix it.. Added the method scaleAndRotateImage from here
- (void) imagePickerController:(UIImagePickerController *)thePicker didFinishPickingMediaWithInfo:(NSDictionary *)imageInfo {
// Images from the camera are always in landscape, so rotate
UIImage *image = [self scaleAndRotateImage: [imageInfo objectForKey:UIImagePickerControllerOriginalImage]];
//then save the image to photo gallery or wherever
}
- (UIImage *)scaleAndRotateImage:(UIImage *) image {
int kMaxResolution = 320;
CGImageRef imgRef = image.CGImage;
CGFloat width = CGImageGetWidth(imgRef);
CGFloat height = CGImageGetHeight(imgRef);
CGAffineTransform transform = CGAffineTransformIdentity;
CGRect bounds = CGRectMake(0, 0, width, height);
if (width > kMaxResolution || height > kMaxResolution) {
CGFloat ratio = width/height;
if (ratio > 1) {
bounds.size.width = kMaxResolution;
bounds.size.height = bounds.size.width / ratio;
}
else {
bounds.size.height = kMaxResolution;
bounds.size.width = bounds.size.height * ratio;
}
}
CGFloat scaleRatio = bounds.size.width / width;
CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
CGFloat boundHeight;
UIImageOrientation orient = image.imageOrientation;
switch(orient) {
case UIImageOrientationUp: //EXIF = 1
transform = CGAffineTransformIdentity;
break;
case UIImageOrientationUpMirrored: //EXIF = 2
transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
break;
case UIImageOrientationDown: //EXIF = 3
transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationDownMirrored: //EXIF = 4
transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
break;
case UIImageOrientationLeftMirrored: //EXIF = 5
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationLeft: //EXIF = 6
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationRightMirrored: //EXIF = 7
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeScale(-1.0, 1.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
case UIImageOrientationRight: //EXIF = 8
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
default:
[NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];
}
UIGraphicsBeginImageContext(bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
CGContextScaleCTM(context, -scaleRatio, scaleRatio);
CGContextTranslateCTM(context, -height, 0);
}
else {
CGContextScaleCTM(context, scaleRatio, -scaleRatio);
CGContextTranslateCTM(context, 0, -height);
}
CGContextConcatCTM(context, transform);
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return imageCopy;
}
I did R&D on it and discovered , every image file has metadata property. If the metadata specifies the orientation of the image which is generally ignored by other OS but Mac. Most of images taken are having their meta data property set to right angle. So Mac shows it 90 degree rotated manner. You can see the same image in proper way in windows OS.
For more detail read this answer http://graphicssoft.about.com/od/digitalphotography/f/sideways-pictures.htm
try reading your image's exif here http://www.exifviewer.org/ , or http://regex.info/exif.cgi , or http://www.addictivetips.com/internet-tips/view-complete-exif-metadata-information-of-any-jpeg-image-online/
Quick copy/paste Swift translation of Dilip's excellent answer.
import Darwin
class func rotateCameraImageToProperOrientation(imageSource : UIImage, maxResolution : CGFloat) -> UIImage {
let imgRef = imageSource.CGImage;
let width = CGFloat(CGImageGetWidth(imgRef));
let height = CGFloat(CGImageGetHeight(imgRef));
var bounds = CGRectMake(0, 0, width, height)
var scaleRatio : CGFloat = 1
if (width > maxResolution || height > maxResolution) {
scaleRatio = min(maxResolution / bounds.size.width, maxResolution / bounds.size.height)
bounds.size.height = bounds.size.height * scaleRatio
bounds.size.width = bounds.size.width * scaleRatio
}
var transform = CGAffineTransformIdentity
let orient = imageSource.imageOrientation
let imageSize = CGSizeMake(CGFloat(CGImageGetWidth(imgRef)), CGFloat(CGImageGetHeight(imgRef)))
switch(imageSource.imageOrientation) {
case .Up :
transform = CGAffineTransformIdentity
case .UpMirrored :
transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
case .Down :
transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
transform = CGAffineTransformRotate(transform, CGFloat(M_PI));
case .DownMirrored :
transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
case .Left :
let storedHeight = bounds.size.height
bounds.size.height = bounds.size.width;
bounds.size.width = storedHeight;
transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
transform = CGAffineTransformRotate(transform, 3.0 * CGFloat(M_PI) / 2.0);
case .LeftMirrored :
let storedHeight = bounds.size.height
bounds.size.height = bounds.size.width;
bounds.size.width = storedHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
transform = CGAffineTransformRotate(transform, 3.0 * CGFloat(M_PI) / 2.0);
case .Right :
let storedHeight = bounds.size.height
bounds.size.height = bounds.size.width;
bounds.size.width = storedHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
transform = CGAffineTransformRotate(transform, CGFloat(M_PI) / 2.0);
case .RightMirrored :
let storedHeight = bounds.size.height
bounds.size.height = bounds.size.width;
bounds.size.width = storedHeight;
transform = CGAffineTransformMakeScale(-1.0, 1.0);
transform = CGAffineTransformRotate(transform, CGFloat(M_PI) / 2.0);
default : ()
}
UIGraphicsBeginImageContext(bounds.size)
let context = UIGraphicsGetCurrentContext()
if orient == .Right || orient == .Left {
CGContextScaleCTM(context, -scaleRatio, scaleRatio);
CGContextTranslateCTM(context, -height, 0);
} else {
CGContextScaleCTM(context, scaleRatio, -scaleRatio);
CGContextTranslateCTM(context, 0, -height);
}
CGContextConcatCTM(context, transform);
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
let imageCopy = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return imageCopy;
}
My question this time is why is this happening? Why is Apple rotating images?
The answer to this is very simple. Apple is NOT rotating the image. That's where the confusion lies.
The CCD camera doesn't rotate, so it's always taking the photo in landscape mode.
Apple did a very smart thing - instead of spending all the time to rotate the image - shuffling megabytes of data around - just tag it with HOW the picture was taken.
OpenGL does translations very easily - so the DATA never gets shuffled - just HOW ITS DRAWN.
Hence the orientation meta data.
This becomes a problem if you want to crop, resize etc - but once you know what's happening, you just define your matrix and everything works out.