iOS: Image get rotated 90 degree after saved as PNG representation data

53,188

Solution 1

Starting with iOS 4.0 when the camera takes a photo it does not rotate it before saving, it

simply sets a rotation flag in the EXIF data of the JPEG.If you save a UIImage as a JPEG, it

will set the rotation flag.PNGs do not support a rotation flag, so if you save a UIImage as a

PNG, it will be rotated incorrectly and not have a flag set to fix it. So if you want PNG

images you must rotate them yourself, for that check this link.

Solution 2

Swift 3.1 version of the UIImage extension posted by Rao:

extension UIImage {
    func fixOrientation() -> UIImage {
        if self.imageOrientation == UIImageOrientation.up {
            return self
        }
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        self.draw(in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
        if let normalizedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() {
            UIGraphicsEndImageContext()
            return normalizedImage
        } else {
            return self
        }
    }
}

Usage:

let cameraImage = //image captured from camera
let orientationFixedImage = cameraImage.fixOrientation()

Swift 4/5:

UIImageOrientation.up has been renamed to UIImage.Orientation.up

Solution 3

Swift 4.2

Add the following as UIImage extension,

extension UIImage {
    func fixOrientation() -> UIImage? {
        if self.imageOrientation == UIImage.Orientation.up {
            return self
        }

        UIGraphicsBeginImageContext(self.size)
        self.draw(in: CGRect(origin: .zero, size: self.size))
        let normalizedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return normalizedImage
    }
}

Example usage:

let cameraImage = //image captured from camera
let orientationFixedImage = cameraImage.fixOrientation()

Explanation:

The magic happens when you call UIImage's draw(in:) function, which redraws the image respecting originally captured orientation settings. [link to docs]

Make sure to call UIGraphicsBeginImageContext with image's size before calling draw to let draw rewrite the UIImage in current context's space.

UIGraphicsGetImageFromCurrentImageContext lets you capture whatever is the result of the redrawn image, & UIGraphicsEndImageContext ends and frees up the graphics context.

Solution 4

I have found the following tips to be hugely useful:

1. natural output is landscape

2. .width / .height ARE affected by .imageOrientation

3 use short dimension rather than .width


(1) the 'natural' output of the camera for stills IS LANDSCAPE.

this is counterintuitive. portrait is the only way offered by UIImagePickerController etc. But you will get UIImageOrientationRight as the "normal" orientation when using the "normal" portrait camera

(The way I remember this - the natural output for video is (of course) landscape; so stills are the same - even though the iPhone is all about portrait.)


(2) .width and .height are indeed affected by the .imageOrientation!!!!!!!!!

Be sure to do this, and try it both ways on your iPhone,

NSLog(@"fromImage.imageOrientation is %d", fromImage.imageOrientation);
NSLog(@"fromImage.size.width  %f   fromImage.size.height  %f",
            fromImage.size.width, fromImage.size.height);

you'll see that the .height and .width swap, "even though" the real pixels are landscape.


(3) simply using the "short dimension" rather than .width, can often solve many problems

I found this to be incredibly helpful. Say you want maybe the top square of the image:

CGRect topRectOfOriginal = CGRectMake(0,0, im.size.width,im.size.width);

that actually won't work, you'll get a squished image, when the camera is ("really") being held landscape.

however if you very simply do this

float shortDimension = fminf(im.size.width, im.size.height);
CGRect topRectOfOriginal = CGRectMake(0,0, shortDimension,shortDimension);

then "everything is fixed" and you actually "do not need to worry about" the orientation flag. Again point (3) is not a cure-all, but it very often does solve all problems.

Hope it helps someone save some time.

Solution 5

I found this code here, which actually fixed it for me. For my app, I took a picture and saved it, and everytime I loaded it, it would have the annoying rotation attached (I looked up, and it's apparently something to do with the EXIF and the way iPhone takes and stores images). This code fixed it for me. I have to say, it was originally as an addition to a class / an extension /category (you can find the original from the link. I used it like below as a simple method, as I didn't really want to make a whole class or category for just this. I only used portrait, but I think the code works for any orientation. I'm not sure though

Rant over, here's the code:

- (UIImage *)fixOrientationForImage:(UIImage*)neededImage {

    // No-op if the orientation is already correct
    if (neededImage.imageOrientation == UIImageOrientationUp) return neededImage;

    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
    CGAffineTransform transform = CGAffineTransformIdentity;

    switch (neededImage.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, neededImage.size.width, neededImage.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, neededImage.size.width, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;

        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, neededImage.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            break;
    }

    switch (neededImage.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, neededImage.size.width, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;

        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, neededImage.size.height, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            break;
    }

    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, neededImage.size.width, neededImage.size.height,
                                             CGImageGetBitsPerComponent(neededImage.CGImage), 0,
                                             CGImageGetColorSpace(neededImage.CGImage),
                                             CGImageGetBitmapInfo(neededImage.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (neededImage.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr...
            CGContextDrawImage(ctx, CGRectMake(0,0,neededImage.size.height,neededImage.size.width), neededImage.CGImage);
            break;

        default:
            CGContextDrawImage(ctx, CGRectMake(0,0,neededImage.size.width,neededImage.size.height), neededImage.CGImage);
            break;
    }

    // And now we just create a new UIImage from the drawing context
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;
}

I'm not sure how useful this will be for you, but I hope it helped :)

Share:
53,188

Related videos on Youtube

Paresh Masani
Author by

Paresh Masani

Updated on July 09, 2022

Comments

  • Paresh Masani
    Paresh Masani almost 2 years

    I have researched enough to get this working but not able to fix it. After taking picture from camera as long as I have image stored as UIImage, it's fine but as soon as I stored this image as PNG representation, its get rotated 90 degree.

    Following is my code and all things I tried:

    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info 
    {
        NSString *mediaType = [info valueForKey:UIImagePickerControllerMediaType];
    
        if([mediaType isEqualToString:(NSString*)kUTTypeImage]) 
        {
            AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
            delegate.originalPhoto  = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
            NSLog(@"Saving photo");
            [self saveImage];
            NSLog(@"Fixing orientation");
            delegate.fixOrientationPhoto  = [self fixOrientation:[UIImage imageWithContentsOfFile:[delegate filePath:imageName]]];      
            NSLog(@"Scaling photo");
            delegate.scaledAndRotatedPhoto  =  [self scaleAndRotateImage:[UIImage imageWithContentsOfFile:[delegate filePath:imageName]]];
        }
        [picker dismissModalViewControllerAnimated:YES];
        [picker release];
    }
    
    
    - (void)saveImage
    {
        AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        NSData *imageData = UIImagePNGRepresentation(delegate.originalPhoto);
        [imageData writeToFile:[delegate filePath:imageName] atomically:YES];
    }
    

    Here fixOrientation and scaleAndRotateImage functions taken from here and here respectively. They works fine and rotate image when I apply them on UIImage but doesn't work if I save image as PNG representation and apply them.

    Please refere the following picture after executing above functions:

    The first photo is original, second is saved, and third and fourth after applying fixorientation and scaleandrotate functions on saved image

  • Paresh Masani
    Paresh Masani almost 12 years
    Thanks but it doesn't worked either. I used CGRect selectionRect = CGRectMake(0, 0, sourceImage.size.width, sourceImage.size.height); to rotate full image but still it appears 90 degree rotated. Please note that I do this operation after storing image locally in documents folder. Thanks.
  • Mike Casa
    Mike Casa almost 10 years
    I would recommend reading this answer. Specifically the answer by 'an0' seems to be the most concise.
  • furins
    furins over 9 years
    I suppose that in the last example you should use shortDimension somewhere (maybe instead of im.size.width in the second line?)
  • Developer
    Developer about 9 years
    From where you are passing the value of “orientation” in “CGRect TransformCGRectForUIImageOrientation(CGRect source, UIImageOrientation orientation, CGSize imageSize)
  • ericbn
    ericbn almost 9 years
    This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post - you can always comment on your own posts, and once you have sufficient reputation you will be able to comment on any post.
  • Mubasher
    Mubasher almost 9 years
    rotation problem come when you store image as PNGRepresentation. if you store image as JPEGRepresentation,image will not rotate!
  • Baylor Mitchell
    Baylor Mitchell over 7 years
    Very helpful. Thank you!!
  • Munib
    Munib about 7 years
    Probably the best answer so far that doesn't use hacking fuctions together. Very beautiful.
  • Nagendra Rao
    Nagendra Rao about 7 years
    @return0 Thanks! Please consider upvoting the answer.
  • Ananta Prasad
    Ananta Prasad almost 6 years
    It works fine, but compresses the image quality. the quality of the image after rotation is not good.
  • Karen  Karapetyan
    Karen Karapetyan almost 6 years
    Thank you for your answer. I use your code in my app but I get crash reports, the image returned from this function somehow gets corrupted it seems this function makes memory leak that's why. Do you get the same issues and how can I resolve it ?
  • Septronic
    Septronic almost 6 years
    @Ananta Prasad hmm, I’ll check it out and get back to you.
  • Septronic
    Septronic almost 6 years
    @Karapetyan I will look into what causes you the issue. What Xcode are you using?
  • King
    King over 5 years
    error Cannot assign to value: function call returns immutable value
  • xleon
    xleon about 5 years
    Upvoted because this works, but it would be nice if you could add an explanation of why that works, because I can´t understand.
  • Ganpat
    Ganpat over 4 years
    For swift 4-5: just replace UIImageOrientation.up to UIImage.Orientation.up
  • bluewraith
    bluewraith over 4 years
    Thank you, this worked great! But like others, if you could provide some insight into exactly how it's working I'd greatly appreciate it. If I recall correctly, Core Graphics uses a different coordinate scheme (drawn from the origin differently) than other View elements in iOS. I believe that's what's being made use of here, I'm just not sure on the specifics.
  • Nagendra Rao
    Nagendra Rao about 4 years
    Explanation added. @bluewraith & xleon.