UIImagePNGRepresentation问题? / 图像旋转90度

55

我想从UIImagePickerController中加载图像,然后将所选的照片保存到我的应用程序文档目录。

UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSData *data1 = UIImagePNGRepresentation(image);

NSString *fileName = "1.png";
NSString *path = //get Document path, then add fileName
BOOL succ = [data1 writeToFile:path atomically:YES];

但是在我将图像保存到文档后,我发现图像被旋转了90度,然后我更改了方法UIImagePNGRepresentation为UIImageJPEGRepresentation,这一次没问题了。有人知道问题出在哪里吗?


可能是重复的问题:iOS UIImagePickerController上传后的结果图像方向 - Axel Guilmin
6个回答

99

我曾经遇到同样的问题,现在找到了原因:从iOS 4.0开始,相机拍摄照片时不会在保存之前旋转它,而是简单地在JPEG的EXIF数据中设置旋转标志。

如果将UIImage保存为JPEG格式,它将设置旋转标志。

PNG不支持旋转标志,因此如果将UIImage保存为PNG格式,则会错误地旋转,且没有设置标志来修复它。因此,如果您需要使用PNG格式,必须自行旋转它们。

我认为这是PNG保存功能中的一个错误,但这只是我的观点(至少他们应该警告你关于这个问题)。


我花了大约一个小时的时间,试图让我的 PNG CGImageSource(从数据创建)在创建缩略图时正确旋转... 尝试在我的 CGImageSource 创建选项中插入 exif 数据等等。最后 UIImageJPEGRepresentation 竟然立刻解决了它!谢谢! - taber
3
即使将它保存为JPEG也无法解决我的问题,图像仍然会旋转。 - rohan-patel
1
它部分地解决了我的问题!当我在横向模式下拍摄图像时,它会正确保存,并且在检索时是横向的。但如果我在纵向模式下拍摄图像,则在检索时仍然是横向的。这只对纵向模式旋转。 - Bharathi
我遇到了同样的问题,这里提供的类别:https://dev59.com/sm035IYBdhLWcg3wbPY5#5427890 彻底解决了它。 - Axel Guilmin

31

请尝试这个。

func rotateImage(image: UIImage) -> UIImage? {
    if image.imageOrientation == UIImage.Orientation.up {
        return image /// already upright, no need for changes
    }
    UIGraphicsBeginImageContext(image.size)
    image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
    let copy = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return copy
}

愉快的编程 :)


3
非常感谢。在将图像保存为.png格式之前进行此操作对我很有帮助。 - Alexandros
我也尝试了同样的方法,对我来说效果很好,只需要进行 ~3 个小修正就可以升级到 Swift 4!非常感谢 :) - agrippa
完美!在Swift 5和Xcode 12中运行良好。 - aheze

15

我用以下代码解决了这个问题。

- (UIImage *)scaleAndRotateImage:(UIImage *)image
{
    int kMaxResolution = 640; 

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

我喜欢这段代码,而且它是唯一一个可以旋转我的图像的代码。但是,我在iOS 7.1和ARC周期中无法让它正常工作。你有任何想法问题可能出在哪里吗? - Matthias Max
谢谢你!非常好用!我已经将它翻译成了Swift 2.0,并在我的当前项目中使用。这是一个要点:https://gist.github.com/fnk0/2e108700bdbe4a92766c - Marcus Gabilheri
@MarcusGabilheri,我该如何使用你的代码将其旋转90度? - alex

6
以下的Swift函数可以解决这个问题。
func rotateImage(image: UIImage) -> UIImage? {
        if (image.imageOrientation == UIImage.Orientation.up ) {
            return image
        }
        UIGraphicsBeginImageContext(image.size)
        image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
        let copy = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return copy
    }

是的,很简单,因为drawInRect函数会考虑图像方向。

我该如何使用它?目前我的imageHolder代码看起来像这样:invoiceImageHolder.image = loadImageFromPath(fileInDocumentsDirectory(("\(imagePath!)"))),但是我遇到了“未解决的标识符'imageOrientation'”错误。 - alex

2

Stefan的答案已更新至Swift 4:

func rotateImage(image: UIImage) -> UIImage {
        if (image.imageOrientation == UIImage.Orientation.up) {
            return image
        }
        UIGraphicsBeginImageContext(image.size)
        image.draw(in: CGRect(origin: .zero, size: image.size))
        let copy = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return copy!
    }

并且,接着是:
var originalImage = yourImage.image!
image = rotateImage(image: originalImage)

2

我创建了这个UIImage扩展来解决UIImagePNGRepresentation的问题,基于这个回答。因此,我建议使用这个类函数UIImage.PNGRepresentation(img: UIImage)代替UIKit函数UIImagePNGRepresentation。

Swift 3代码:

//  MyUIImage.swift
//  MyEasyMovie-Public-App
//
//  Created by Ahmed Zahraz on 19/12/2016.
//  Copyright © 2016 AhmedZahraz. All rights reserved.    

import Foundation
import UIKit

extension UIImage {


    public class func PNGRepresentation(_ img: UIImage) -> Data? {
        // No-op if the orientation is already correct
        if (img.imageOrientation == UIImageOrientation.up) {
            return UIImagePNGRepresentation(img);
        }
        // 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.
        var transform:CGAffineTransform = CGAffineTransform.identity

        if (img.imageOrientation == UIImageOrientation.down
            || img.imageOrientation == UIImageOrientation.downMirrored) {

            transform = transform.translatedBy(x: img.size.width, y: img.size.height)
            transform = transform.rotated(by: CGFloat(M_PI))
        }

        if (img.imageOrientation == UIImageOrientation.left
            || img.imageOrientation == UIImageOrientation.leftMirrored) {

            transform = transform.translatedBy(x: img.size.width, y: 0)
            transform = transform.rotated(by: CGFloat(M_PI_2))
        }

        if (img.imageOrientation == UIImageOrientation.right
            || img.imageOrientation == UIImageOrientation.rightMirrored) {

            transform = transform.translatedBy(x: 0, y: img.size.height);
            transform = transform.rotated(by: CGFloat(-M_PI_2));
        }

        if (img.imageOrientation == UIImageOrientation.upMirrored
            || img.imageOrientation == UIImageOrientation.downMirrored) {

            transform = transform.translatedBy(x: img.size.width, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)
        }

        if (img.imageOrientation == UIImageOrientation.leftMirrored
            || img.imageOrientation == UIImageOrientation.rightMirrored) {

            transform = transform.translatedBy(x: img.size.height, y: 0);
            transform = transform.scaledBy(x: -1, y: 1);
        }


        // Now we draw the underlying CGImage into a new context, applying the transform
        // calculated above.
        let ctx:CGContext = CGContext(data: nil, width: Int(img.size.width), height: Int(img.size.height),
                                      bitsPerComponent: img.cgImage!.bitsPerComponent, bytesPerRow: 0,
                                      space: img.cgImage!.colorSpace!,
                                      bitmapInfo: img.cgImage!.bitmapInfo.rawValue)!

        ctx.concatenate(transform)


        if (img.imageOrientation == UIImageOrientation.left
            || img.imageOrientation == UIImageOrientation.leftMirrored
            || img.imageOrientation == UIImageOrientation.right
            || img.imageOrientation == UIImageOrientation.rightMirrored
            ) {


            ctx.draw(img.cgImage!, in: CGRect(x:0,y:0,width:img.size.height,height:img.size.width))

        } else {
            ctx.draw(img.cgImage!, in: CGRect(x:0,y:0,width:img.size.width,height:img.size.height))
        }


        // And now we just create a new UIImage from the drawing context
        let cgimg:CGImage = ctx.makeImage()!
        let imgEnd:UIImage = UIImage(cgImage: cgimg)

        return UIImagePNGRepresentation(imgEnd)
    }

}

它似乎返回了一个正确方向的黑色图像...也许我做错了什么? - superandrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接