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

8

47

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

Menashem answered 1/6, 2012 at 12:23 Comment(5)
1.image clicked from iPhone in Portrait mode gets rotated by 90 degree 2. Camera image changes orientationHerron
Nope buddy. You paste all the links regarding this issue. I went through them all!Menashem
Anonymous, if you downmark this question then you haven't understood the question! I could see that you have this issue here - #3554744 :-) Did you solve it?Menashem
Possible duplicate of iphone image captured from camera rotate -90 degree automaticallyLicastro
Hugely useful: 1. natural output is landscape 2. .width / .height ARE affected by .imageOrientationUziel
D
75

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.

Decuple answered 1/6, 2012 at 12:35 Comment(1)
I would recommend reading this answer. Specifically the answer by 'an0' seems to be the most concise.Blackburn
B
30

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.

Barcroft answered 20/9, 2016 at 13:29 Comment(7)
Very helpful. Thank you!!Petition
Probably the best answer so far that doesn't use hacking fuctions together. Very beautiful.Sane
@return0 Thanks! Please consider upvoting the answer.Barcroft
error Cannot assign to value: function call returns immutable valueUndergird
Upvoted because this works, but it would be nice if you could add an explanation of why that works, because I can´t understand.Nicosia
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.Birgit
Explanation added. @Birgit & xleon.Barcroft
D
29

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

Douglas answered 3/4, 2017 at 8:55 Comment(2)
For swift 4-5: just replace UIImageOrientation.up to UIImage.Orientation.upZehe
Neat solution packaged in a Extension, saved me a bunch of tests, thanks @DouglasKindig
U
6

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.

Uziel answered 9/3, 2014 at 13:48 Comment(1)
I suppose that in the last example you should use shortDimension somewhere (maybe instead of im.size.width in the second line?)Wachter
L
3

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 :)

Licastro answered 8/12, 2015 at 23:11 Comment(4)
It works fine, but compresses the image quality. the quality of the image after rotation is not good.Shambles
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 ?Cauterant
@Ananta Prasad hmm, I’ll check it out and get back to you.Licastro
@Karapetyan I will look into what causes you the issue. What Xcode are you using?Licastro
K
3

Try this, You can use

NSData *somenewImageData = UIImageJPEGRepresentation(newimg,1.0);

instead of

NSData *somenewImageData = UIImagePNGRepresentation(newimg);
Krasnodar answered 31/1, 2017 at 5:39 Comment(0)
C
1

Pls Try the following 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];

And

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];

I have referanced from following link have look for more detail

Best Regards :-)

Creese answered 1/6, 2012 at 12:40 Comment(2)
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.Menashem
From where you are passing the value of “orientation” in “CGRect TransformCGRectForUIImageOrientation(CGRect source, UIImageOrientation orientation, CGSize imageSize)Bagpipe
C
0

Try this code:

NSData *imageData = UIImageJPEGRepresentation(delegate.originalPhoto,100);
Carmine answered 26/5, 2015 at 12:35 Comment(2)
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.Phosphorus
rotation problem come when you store image as PNGRepresentation. if you store image as JPEGRepresentation,image will not rotate!Carmine

© 2022 - 2024 — McMap. All rights reserved.