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

Ios 将图像转换为CGImage时丢失图像方向,ios,swift,uiimage,cgimage,Ios,Swift,Uiimage,Cgimage,在从矩形原始图像中裁剪图像的方形部分时,我面临图像方向问题。当图像在风景中时,它是好的。但在纵向时,似乎没有保留图像方向,导致图像方向错误,裁剪不良: func cropImage(cropRectangleCoordinates: CGRect) { let croppedImage = originalImage let finalCroppedImage : CGImageRef = CGImageCreateWithImageInRect(cropp

在从矩形原始图像中裁剪图像的方形部分时,我面临图像方向问题。当图像在风景中时,它是好的。但在纵向时,似乎没有保留图像方向,导致图像方向错误,裁剪不良:

 func cropImage(cropRectangleCoordinates: CGRect) {

        let croppedImage = originalImage

        let finalCroppedImage : CGImageRef = CGImageCreateWithImageInRect(croppedImage.CGImage, cropRectangleCoordinates)

        finalImage =  UIImage(CGImage: finalCroppedImage)!


    }
我认为问题在于
cropedimage.CGImage
。在这里,图像被转换为
CGImage
,但似乎没有保留方向。 仅使用
UIImage
很容易保持方向,但要进行裁剪,图像需要临时
CGImage
,这就是问题所在。即使我在转换回
UIImage
时重新调整了图像的方向,它也可能处于正确的方向,但在剪切
CGImage
时已经造成了损坏


这是一个swift问题,请用swift回答,因为在Objective-C中解决方案可能不同。

将您的UIImage创建呼叫更改为:

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

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

我找到了一个解决方案。。。。时间会证明它是否足够强大,但它似乎在所有情况下都有效。这是一个需要修复的恶意bug

所以问题是UIImage,仅在某些情况下,在转换为CGImage时会丢失方向。它会影响自动置于横向模式的肖像图像。因此,默认情况下为横向的图像不会受到影响。 但是这个bug的恶意之处在于它不会影响所有的肖像图像!!另外,imageorientation值对某些图像也没有帮助。 这些有问题的图像是用户在其库中的图像,这些图像是他从电子邮件、消息或从网络上保存的,所以不是用相机拍摄的。这些图像可能没有方向信息,因此在某些情况下,图像是纵向的。。。。转换为CGImage时仍保持纵向。直到我意识到我的设备库中的一些图像是从消息或电子邮件中保存的,我才意识到这一点

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


这是一篇很长的文章,但主要思想是比较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
    }
}

SWIFT 3:

使用此方法将旋转的cgImage转换为UIImage

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

Source@David Berry answer

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

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
    }
}
另一个注意事项:如果您使用嵌入在
滚动视图中的
imageView
,还有一个额外的步骤,您必须考虑缩放因子。假设您的
图像视图
跨越
滚动视图
的整个内容视图,并且您使用
滚动视图
的边界作为裁剪帧,则裁剪后的图像可以如下方式获得:

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)

@JGuo有唯一一个真正有效的答案。我只更新了一点,以返回可选的
UIImage?
和swifter语法。我宁愿从不含蓄地打开包装

extension UIImage {

    func crop(to 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)

        guard let imageRef = self.cgImage?.cropping(to: rect.applying(rectTransform)) else { return nil }
        let result = UIImage(cgImage: imageRef, scale: self.scale, orientation: self.imageOrientation)
        return result
    }
}
下面是它在我的ViewController中作为计算属性的实现

var croppedImage: UIImage? {
    guard let image = self.image else { return nil }

    let ratio = image.size.height / self.contentSize.height
    let origin = CGPoint(x: self.contentOffset.x * ratio, y: self.contentOffset.y * ratio)
    let size = CGSize(width: self.bounds.size.width * ratio, height: self.bounds.size.height * ratio)
    let cropFrame = CGRect(origin: origin, size: size)
    let croppedImage = image.crop(to: cropFrame)

    return croppedImage
}

斯威夫特5。我添加了以下内容作为UIImage的扩展。其想法是强制UIImage中的图像与UIImage方向相匹配(这仅在其显示方式中起作用)。重新绘制UIImage“容器”内的实际图像数据将使相应的CGImage具有相同的方向

func forceSameOrientation()->UIImage{
UIGraphicsBeginImageContext(self.size)
self.draw(in:CGRect(原点:CGPoint.zero,大小:self.size))
guard let image=UIGraphicsGetImageFromCurrentImageContext()其他{
UIGraphicsSendImageContext()
回归自我
}
UIGraphicsSendImageContext()
返回图像
}

我试过了,但问题更严重:finalimage已经被裁剪,方向丢失发生在较早的阶段:crappedimage.CGImage。您的建议可以正确旋转图像,但在损坏完成后。非常感谢。这对我很有帮助。但是如果图像是平方的,这对我们的解决方案来说将是一个大问题:[]只需放置一个像下面视图那样的图像路径,然后上传图像UIImageView*imgViewTemp=[[UIImageView alloc]init];[imgViewTemp setImage:[UIImage imageWithCGImage:[rep fullScreenImage]];UIImage*compressedImage=[mYGlobal funResizing:imgViewTemp.image];我也有同样的问题,但修复方法如上所述。谢谢:)这比我在其他答案中看到的100行旋转代码要好得多:D在转换为调用image之前调用此代码。cgImage工作了很多。谢谢,我使用了您的扩展名和我自己的一些代码,这工作非常好,伙计!