iOS图像取向具有奇怪的行为

在过去的几个星期里,我一直在用objective-c处理图像,并注意到很多奇怪的行为。 首先,像其他许多人一样,我一直在使用相机拍摄的图像(或者将其他人的相机和彩信拍摄给我的图像)旋转90度。 我不知道为什么在这个世界上发生这个事情(因此我的问题 ),但我能够拿出一个便宜的工作。

这次我的问题是为什么会发生这种情况 ? 苹果为什么旋转图像? 当我用相机正确地拍摄照片时,除非我执行上面提到的代码,否则当我保存照片时,它会被保存为旋转状态。 现在,我的解决方法是好的,直到几天前。

我的应用程序修改了图像的各个像素,特别是PNG的alpha通道(因此,任何JPEG转换都会从我的场景的窗口中抛出)。 几天前,我注意到即使图像在我的应用程序中正确显示,这要归功于我的解决方法代码,当我的algorithm修改图像的各个像素时,它认为图像是旋转的。 所以不是修改图像顶部的像素,而是修改图像边上的像素(因为它认为它应该旋转)! 我无法弄清楚如何在内存中旋转图像 – 理想情况下,我宁愿只擦掉imageOrientation标志。

这里还有一些让我感到困惑的东西…当我拍照时, imageOrientation被设置为3.我的解决方法代码足够聪明,可以实现这一点,并将其翻转,以便用户不会注意到。 此外,我的代码保存到图书馆的图像意识到这一点,翻转它, 然后保存它,使其出现在相机胶卷适当。

该代码看起来像这样:

 NSData* pngdata = UIImagePNGRepresentation (self.workingImage); //PNG wrap UIImage* img = [self rotateImageAppropriately:[UIImage imageWithData:pngdata]]; UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil); 

当我将这个新保存的图像加载到我的应用程序中时, imageOrientation为0 – 正是我想看到的,我的旋转解决方法甚至不需要运行(注意:从Internet上加载图像时,与使用相机, imageOrientation总是0,导致完美的行为)。 出于某种原因,我的保存代码似乎抹去了这个imageOrientation标志。 我希望只是偷了代码,并用它来擦除我的imageOrientation一旦用户拍摄照片,并将其添加到应用程序,但它似乎并不工作。 UIImageWriteToSavedPhotosAlbum是否使用UIImageWriteToSavedPhotosAlbum做了特别的imageOrientation

对于这个问题,最好的解决办法是在用户完成图像后立即吹走imageOrientation 。 我认为苹果有轮换行为是有原因的,对吧? 有less数人认为这是苹果的缺陷。

(…如果你还没有失去…注2:当我拍一张水平照片时,一切似乎都很完美,就像从互联网上拍摄的照片一样)

编辑:

以下是一些图像和场景实际上的样子。 根据迄今为止的评论,看起来这种奇怪的行为不仅仅是一种iPhone的行为,我认为这很好。

这是我用手机拍摄的照片的照片(注意正确的方向),当拍照时它看起来和我的手机一模一样:

在iPhone上拍摄的实际照片

这是Gmail发送给我自己的图片后的样子(看起来像Gmail处理它):

照片出现在Gmail中

这里是图像看起来像在Windows中的缩略图(看起来不正确处理):

Windows缩略图

以下是用Windows照片查看器打开时的实际图像的样子(仍未正确处理):

Windows照片查看器版本

经过对这个问题的所有评论,这就是我的想法… iPhone拍摄一张照片,并说“要正确显示,需要旋转90度”。 这些信息将在EXIF数据中。 (为什么需要旋转90度,而不是默认为直线,我不知道)。 从这里,Gmail足够聪明地阅读和分析EXIF数据,并正确显示它。 但是,Windows不够聪明,无法读取EXIF数据,因此不正确地显示图像。 我的假设是否正确?

我做了研发,发现每个图像文件都有元数据属性。 如果元数据指定了其他操作系统通常被Mac忽略的图像的方向。 拍摄的大部分图像都将其元数据属性设置为直angular。 所以Mac显示它90度旋转的方式。 你可以在windows操作系统中以正确的方式看到相同的图像。

有关更多详细信息,请阅读此答案http://graphicssoft.about.com/od/digitalphotography/f/sideways-pictures.htm

尝试在这里http://www.exifviewer.org/或http://regex.info/exif.cgi或http://www.addictivetips.com/internet-tips/view-complete-exif -元数据信息-的-任何-JPEG图像,在线/

我有相同的问题,当我从相机的形象,我把下面的代码来解决它..添加从这里的方法scaleAndRotateImage

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

快速复制/粘贴Dilip的优秀答案的快速翻译。

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

由iPhone / iPad生成的任何图像都会以EXIF方向标签(Exif.Image.Orientation)的左侧风景格式保存,指定实际的方向。

它具有以下值:1:左侧风景6:纵向正常3:风景右侧4:竖向放大肖像

在IOS中,正确读取EXIF信息,并以与拍摄相同的方式显示图像。 但在Windows中,不使用EXIF信息。

如果您在GIMP中打开这些图像中的一个,则会说图像具有旋转信息。

这次我的问题是为什么会发生这种情况? 苹果为什么旋转图像?

答案很简单。 苹果不旋转图像。 这是混乱的地方。

CCD相机不旋转,因此总是以横向模式拍照。

苹果做了一个非常聪明的事情 – 而不是花费所有的时间来旋转图像 – 洗牌兆字节的数据 – 只是标记它如何拍摄照片。

OpenGL很容易翻译 – 所以数据永远不会被洗牌 – 只是如何绘制。

因此,方向元数据。

如果你想裁剪,resize等,这会成为一个问题 – 但是一旦你知道发生了什么,你只需要定义你的matrix,一切都可以实现。

斯威夫特3版与Dilip的答案的安全检查。

 public static func rotateCameraImageToProperOrientation(imageSource : UIImage, maxResolution : CGFloat = 320) -> UIImage? { guard let imgRef = imageSource.cgImage else { return nil } let width = CGFloat(imgRef.width) let height = CGFloat(imgRef.height) var bounds = CGRect(x: 0, y: 0, width: width, height: 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 = CGAffineTransform.identity let orient = imageSource.imageOrientation let imageSize = CGSize(width: CGFloat(imgRef.width), height: CGFloat(imgRef.height)) switch(imageSource.imageOrientation) { case .up: transform = .identity case .upMirrored: transform = CGAffineTransform .init(translationX: imageSize.width, y: 0) .scaledBy(x: -1.0, y: 1.0) case .down: transform = CGAffineTransform .init(translationX: imageSize.width, y: imageSize.height) .rotated(by: CGFloat.pi) case .downMirrored: transform = CGAffineTransform .init(translationX: 0, y: imageSize.height) .scaledBy(x: 1.0, y: -1.0) case .left: let storedHeight = bounds.size.height bounds.size.height = bounds.size.width; bounds.size.width = storedHeight; transform = CGAffineTransform .init(translationX: 0, y: imageSize.width) .rotated(by: 3.0 * CGFloat.pi / 2.0) case .leftMirrored: let storedHeight = bounds.size.height bounds.size.height = bounds.size.width; bounds.size.width = storedHeight; transform = CGAffineTransform .init(translationX: imageSize.height, y: imageSize.width) .scaledBy(x: -1.0, y: 1.0) .rotated(by: 3.0 * CGFloat.pi / 2.0) case .right : let storedHeight = bounds.size.height bounds.size.height = bounds.size.width; bounds.size.width = storedHeight; transform = CGAffineTransform .init(translationX: imageSize.height, y: 0) .rotated(by: CGFloat.pi / 2.0) case .rightMirrored: let storedHeight = bounds.size.height bounds.size.height = bounds.size.width; bounds.size.width = storedHeight; transform = CGAffineTransform .init(scaleX: -1.0, y: 1.0) .rotated(by: CGFloat.pi / 2.0) } UIGraphicsBeginImageContext(bounds.size) if let context = UIGraphicsGetCurrentContext() { if orient == .right || orient == .left { context.scaleBy(x: -scaleRatio, y: scaleRatio) context.translateBy(x: -height, y: 0) } else { context.scaleBy(x: scaleRatio, y: -scaleRatio) context.translateBy(x: 0, y: -height) } context.concatenate(transform) context.draw(imgRef, in: CGRect(x: 0, y: 0, width: width, height: height)) } let imageCopy = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return imageCopy } 

对于其他使用Xamarin的人来说,这里是Dilip 伟大的答案的C#翻译,并感谢那个Swift翻译的泰森。

 public static UIImage RotateCameraImageToProperOrientation(UIImage imageSource, nfloat maxResolution) { var imgRef = imageSource.CGImage; var width = (nfloat)imgRef.Width; var height = (nfloat)imgRef.Height; var bounds = new CGRect(0, 0, width, height); nfloat scaleRatio = 1; if (width > maxResolution || height > maxResolution) { scaleRatio = (nfloat)Math.Min(maxResolution / bounds.Width, maxResolution / bounds.Height); bounds.Height = bounds.Height * scaleRatio; bounds.Width = bounds.Width * scaleRatio; } var transform = CGAffineTransform.MakeIdentity(); var orient = imageSource.Orientation; var imageSize = new CGSize(imgRef.Width, imgRef.Height); nfloat storedHeight; switch(imageSource.Orientation) { case UIImageOrientation.Up: transform = CGAffineTransform.MakeIdentity(); break; case UIImageOrientation.UpMirrored : transform = CGAffineTransform.MakeTranslation(imageSize.Width, 0.0f); transform = CGAffineTransform.Scale(transform, -1.0f, 1.0f); break; case UIImageOrientation.Down : transform = CGAffineTransform.MakeTranslation(imageSize.Width, imageSize.Height); transform = CGAffineTransform.Rotate(transform, (nfloat)Math.PI); break; case UIImageOrientation.DownMirrored : transform = CGAffineTransform.MakeTranslation(0.0f, imageSize.Height); transform = CGAffineTransform.Scale(transform, 1.0f, -1.0f); break; case UIImageOrientation.Left: storedHeight = bounds.Height; bounds.Height = bounds.Width; bounds.Width = storedHeight; transform = CGAffineTransform.MakeTranslation(0.0f, imageSize.Width); transform = CGAffineTransform.Rotate(transform, 3.0f * (nfloat)Math.PI / 2.0f); break; case UIImageOrientation.LeftMirrored : storedHeight = bounds.Height; bounds.Height = bounds.Width; bounds.Width = storedHeight; transform = CGAffineTransform.MakeTranslation(imageSize.Height, imageSize.Width); transform = CGAffineTransform.Scale(transform, -1.0f, 1.0f); transform = CGAffineTransform.Rotate(transform, 3.0f * (nfloat)Math.PI / 2.0f); break; case UIImageOrientation.Right : storedHeight = bounds.Height; bounds.Height = bounds.Width; bounds.Width = storedHeight; transform = CGAffineTransform.MakeTranslation(imageSize.Height, 0.0f); transform = CGAffineTransform.Rotate(transform, (nfloat)Math.PI / 2.0f); break; case UIImageOrientation.RightMirrored : storedHeight = bounds.Height; bounds.Height = bounds.Width; bounds.Width = storedHeight; transform = CGAffineTransform.MakeScale(-1.0f, 1.0f); transform = CGAffineTransform.Rotate(transform, (nfloat)Math.PI / 2.0f); break; default : break; } UIGraphics.BeginImageContext(bounds.Size); var context = UIGraphics.GetCurrentContext(); if (orient == UIImageOrientation.Right || orient == UIImageOrientation.Left) { context.ScaleCTM(-scaleRatio, scaleRatio); context.TranslateCTM(-height, 0); } else { context.ScaleCTM(scaleRatio, -scaleRatio); context.TranslateCTM(0, -height); } context.ConcatCTM(transform); context.DrawImage(new CGRect(0, 0, width, height), imgRef); var imageCopy = UIGraphics.GetImageFromCurrentImageContext(); UIGraphics.EndImageContext(); return imageCopy; } 

我遇到这个问题,因为我有一个类似的问题,但使用Swift。 只是想链接到任何其他Swift开发者为我工作的答案: https : //stackoverflow.com/a/26676578/3904581

这是一个可以有效解决问题的Swift代码片段:

 let orientedImage = UIImage(CGImage: initialImage.CGImage, scale: 1, orientation: initialImage.imageOrientation)! 

超级简单。 一行代码。 问题解决了。

我确切知道你的问题是什么。 你正在使用UIImagePicker,这在各方面都很奇怪。 我build议你使用的AVFoundation为相机提供方向以及质量的灵活性。 使用AVCaptureSession。 你可以在这里得到的代码如何保存使用AVFoundation拍摄照片相册?

尝试将图像格式更改为.jpeg。 这对我有效

快速重构Swift 3(可以testing它并确认一切正常吗?):

 static func rotateCameraImageToProperOrientation(imageSource : UIImage, maxResolution : CGFloat) -> UIImage { let imgRef = imageSource.cgImage let width = CGFloat(imgRef!.width) let height = CGFloat(imgRef!.height) var bounds = CGRect(x: 0, y: 0, width: width, height: 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 = CGAffineTransform.identity let orient = imageSource.imageOrientation let imageSize = CGSize(width: imgRef!.width, height: imgRef!.height) switch imageSource.imageOrientation { case .up : transform = CGAffineTransform.identity case .upMirrored : transform = CGAffineTransform(translationX: imageSize.width, y: 0) transform = transform.scaledBy(x: -1, y: 1) case .down : transform = CGAffineTransform(translationX: imageSize.width, y: imageSize.height) transform = transform.rotated(by: CGFloat.pi) case .downMirrored : transform = CGAffineTransform(translationX: 0, y: imageSize.height) transform = transform.scaledBy(x: 1, y: -1) case .left : let storedHeight = bounds.size.height bounds.size.height = bounds.size.width bounds.size.width = storedHeight transform = CGAffineTransform(translationX: 0, y: imageSize.width) transform = transform.rotated(by: 3.0 * CGFloat.pi / 2.0) case .leftMirrored : let storedHeight = bounds.size.height bounds.size.height = bounds.size.width bounds.size.width = storedHeight transform = CGAffineTransform(translationX: imageSize.height, y: imageSize.width) transform = transform.scaledBy(x: -1, y: 1) transform = transform.rotated(by: 3.0 * CGFloat.pi / 2.0) case .right : let storedHeight = bounds.size.height bounds.size.height = bounds.size.width bounds.size.width = storedHeight transform = CGAffineTransform(translationX: imageSize.height, y: 0) transform = transform.rotated(by: CGFloat.pi / 2.0) case .rightMirrored : let storedHeight = bounds.size.height bounds.size.height = bounds.size.width bounds.size.width = storedHeight transform = CGAffineTransform(scaleX: -1, y: 1) transform = transform.rotated(by: CGFloat.pi / 2.0) } UIGraphicsBeginImageContext(bounds.size) let context = UIGraphicsGetCurrentContext() if orient == .right || orient == .left { context!.scaleBy(x: -scaleRatio, y: scaleRatio) context!.translateBy(x: -height, y: 0) } else { context!.scaleBy(x: scaleRatio, y: -scaleRatio) context!.translateBy(x: 0, y: -height) } context!.concatenate(transform) context!.draw(imgRef!, in: CGRect(x: 0, y: 0, width: width, height: height)) let imageCopy = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return imageCopy! }