在将图像转换为CGImage时丢失图像方向

当从矩形原始图像中裁剪图像的正方形部分时,我正面临图像方向问题。 当图像处于风景中时,它很好。 但是当它是纵向时,似乎没有保留图像方向,这导致图像具有错误的方向和糟糕的裁剪:

func cropImage(cropRectangleCoordinates: CGRect) { let croppedImage = originalImage let finalCroppedImage : CGImageRef = CGImageCreateWithImageInRect(croppedImage.CGImage, cropRectangleCoordinates) finalImage = UIImage(CGImage: finalCroppedImage)! } 

我认为问题出在croppedImage.CGImage 。 这里图像转换为CGImage ,但似乎不保留方向。 通过仅使用UIImage很容易保留方向,但要制作裁剪,图像需要暂时是CGImage ,这就是问题所在。 即使我在转换回UIImage时重新定向图像,它可能是正确的方向,但在裁剪CGImage时已经完成了损坏。

这是一个很快的问题,所以请快速回答,因为Objective-C中的解决方案可能有所不同。

SWIFT 3:

通过此方法将旋转的cgImage转换为UIImage

 UIImage(cgImage:croppedCGImage, scale:originalImage.scale, orientation:originalImage.imageOrientation) 

来源@David Berry回答

我找到了一个解决方案….时间会告诉它是否足够强大,但它似乎适用于所有情况。 这是一个需要修复的恶性错误。

所以问题是UIImage在某些情况下只在转换为CGImage时失去了它的方向。 它会影响人像图像,这些图像会自动进入横向模式。 因此默认情况下的横向图像不受影响。 但是虫子恶毒的地方是它不会影响所有肖像图像!! 此外,imageorientation值对某些图像没有帮助。 那些有问题的图像是用户在其库中通过电子邮件,消息或从网络上保存的图像,因此不能使用相机拍摄。 这些图像可能没有方向信息,因此在某些情况下,图像处于纵向状态….转换为CGImage时仍保留纵向图像。 我真的陷入困境,直到我意识到我的设备库中的一些图像是从消息或电子邮件中保存的。

因此,我发现猜测哪个图像将被重定向的唯一可靠方法是创建给定图像的两个版本:UIImage和CGImage,并比较它们的高度值。 如果它们相等,那么CGImage版本将不会被旋转,您可以按预期使用它。 但是如果它们的高度不同,你可以确定来自CGImageCreateWithImageInRect的CGImage转换会使图像美化。 仅在这种情况下,我交换原点的x / y坐标,我将其作为矩形坐标传递给裁剪,它正确处理这些特殊图像。

这是一个很长的post,但主要的想法是将CGImage高度与UIImage宽度进行比较,如果它们不同,则期望原点被反转。

这是我在查看其他人编写的几段旧代码后编写的UIImage扩展。 它是用Swift 3编写的,使用iOS方向属性和CGAffineTransform以正确的方向重新绘制图像。

SWIFT 3:

 public extension UIImage { /// Extension to fix orientation of an UIImage without EXIF func fixOrientation() -> UIImage { guard let cgImage = cgImage else { return self } if imageOrientation == .up { return self } var transform = CGAffineTransform.identity switch imageOrientation { case .down, .downMirrored: transform = transform.translatedBy(x: size.width, y: size.height) transform = transform.rotated(by: CGFloat(M_PI)) case .left, .leftMirrored: transform = transform.translatedBy(x: size.width, y: 0) transform = transform.rotated(by: CGFloat(M_PI_2)) case .right, .rightMirrored: transform = transform.translatedBy(x: 0, y: size.height) transform = transform.rotated(by: CGFloat(-M_PI_2)) case .up, .upMirrored: break } switch imageOrientation { case .upMirrored, .downMirrored: transform.translatedBy(x: size.width, y: 0) transform.scaledBy(x: -1, y: 1) case .leftMirrored, .rightMirrored: transform.translatedBy(x: size.height, y: 0) transform.scaledBy(x: -1, y: 1) case .up, .down, .left, .right: break } if let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: cgImage.colorSpace!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) { ctx.concatenate(transform) switch imageOrientation { case .left, .leftMirrored, .right, .rightMirrored: ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.height, height: size.width)) default: ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) } if let finalImage = ctx.makeImage() { return (UIImage(cgImage: finalImage)) } } // something failed -- return original return self } } 

将您的UIImage创建调用更改为:

 finalImage = UIImage(CGImage:finalCroppedImage, scale:originalImage.scale, orientation:originalImage.orientation) 

保持图像的原始方向(和比例)。

这是答案,归功于@awolf( 裁剪UIImage )。 完美地处理比例和方向。 只需在要裁剪的图像上调用此方法,然后传入裁剪CGRect而不必担心缩放或方向。 随意检查cgImage是否为nil而不是像我在这里那样用力展开它。

 extension UIImage { func croppedInRect(rect: CGRect) -> UIImage { func rad(_ degree: Double) -> CGFloat { return CGFloat(degree / 180.0 * .pi) } var rectTransform: CGAffineTransform switch imageOrientation { case .left: rectTransform = CGAffineTransform(rotationAngle: rad(90)).translatedBy(x: 0, y: -self.size.height) case .right: rectTransform = CGAffineTransform(rotationAngle: rad(-90)).translatedBy(x: -self.size.width, y: 0) case .down: rectTransform = CGAffineTransform(rotationAngle: rad(-180)).translatedBy(x: -self.size.width, y: -self.size.height) default: rectTransform = .identity } rectTransform = rectTransform.scaledBy(x: self.scale, y: self.scale) let imageRef = self.cgImage!.cropping(to: rect.applying(rectTransform)) let result = UIImage(cgImage: imageRef!, scale: self.scale, orientation: self.imageOrientation) return result } } 

另一个注意事项:如果您正在使用嵌入在scrollView中的scrollView ,还有一个额外的步骤,您必须考虑缩放因子。 假设您的scrollView跨越scrollView的整个内容视图,并使用scrollView的边界作为裁剪框,可以获得裁剪后的图像

 let ratio = imageView.image!.size.height / scrollView.contentSize.height let origin = CGPoint(x: scrollView.contentOffset.x * ratio, y: scrollView.contentOffset.y * ratio) let size = CGSize(width: scrollView.bounds.size.width * ratio, let height: scrollView.bounds.size.height * ratio) let cropFrame = CGRect(origin: origin, size: size) let croppedImage = imageView.image!.croppedInRect(rect: cropFrame)