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

Rob*_*rax 14 uiimage cgimage ios swift

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

 func cropImage(cropRectangleCoordinates: CGRect) {

        let croppedImage = originalImage

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

        finalImage =  UIImage(CGImage: finalCroppedImage)!


    }
Run Code Online (Sandbox Code Playgroud)

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

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

jef*_*igy 12

这是我在查看其他人编写的几段旧代码后编写的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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在转换为调用image.cgImage之前先调用此方法 (2认同)

Adn*_*suf 12

SWIFT 3:

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

UIImage(cgImage:croppedCGImage, scale:originalImage.scale, orientation:originalImage.imageOrientation)
Run Code Online (Sandbox Code Playgroud)

来源@David Berry回答


JGu*_*Guo 7

这就是答案,归功于@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
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个注意事项:如果您正在使用imageView内嵌在 a 中scrollView,则还有一个额外的步骤,您必须考虑缩放系数。假设您imageView跨越 的整个内容视图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)
Run Code Online (Sandbox Code Playgroud)


Rob*_*rax 6

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

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

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

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

  • 但如果图像被平方,这对于你的解决方案将是一个大问题:[] (2认同)

Dav*_*rry 5

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

finalImage = UIImage(CGImage:finalCroppedImage, scale:originalImage.scale, orientation:originalImage.orientation)
Run Code Online (Sandbox Code Playgroud)

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


Kac*_* Li 5

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

func forceSameOrientation() -> UIImage {
    UIGraphicsBeginImageContext(self.size)
    self.draw(in: CGRect(origin: CGPoint.zero, size: self.size))
    guard let image = UIGraphicsGetImageFromCurrentImageContext() else {
        UIGraphicsEndImageContext()
        return self
    }
    UIGraphicsEndImageContext()
    return image
}
Run Code Online (Sandbox Code Playgroud)