UIImage 在将其转换为 CIImage,然后转换为 CGImage 并返回 UIImage 时失去其方向

Posted

技术标签:

【中文标题】UIImage 在将其转换为 CIImage,然后转换为 CGImage 并返回 UIImage 时失去其方向【英文标题】:UIImage looses its orientation while converting it into CIImage, then into CGImage and back to UIImage 【发布时间】:2018-08-08 09:52:31 【问题描述】:

我知道有很多这样的问题,但他们的解决方案对我不起作用。

注意:图片是从互联网下载的。它不是从iPhone 的相机拍摄的。

我有一个UIImage。为了进行一些图像处理操作,我使用Metal 并将图像呈现在MTKView 中。因此,我将图像转换为CIImage。 在对图像进行所有修改后,我将其转换回CGImage,然后再转换为UIImage 并保存。

因此保存的图片方向错误。

我不确定它在哪一步失去了方向(从UIImage 转换为CIImage 以添加到MTKView,或从CIImage 转换为CGImage 然后转换为UIImage保存)。因为我不确定,所以我在下面提供了我为每个步骤尝试过的所有内容:

用于从UIImage 转换为CIImage

最初我只是以这种方式转换图像:

let let ciImage = CIImage(image: image)

然后我尝试使用以下方式:

  let ciImage = CIImage(image: image)?.oriented(forExifOrientation: imageOrientationToTiffOrientation(value: image.imageOrientation))

这里是方法imageOrientationToTiffOrientation的实现(我找到了here):

func imageOrientationToTiffOrientation(value: UIImageOrientation) -> Int32

    switch (value)
    
    case .up:
        return 1
    case .down:
        return 3
    case .left:
        return 8
    case .right:
        return 6
    case .upMirrored:
        return 2
    case .downMirrored:
        return 4
    case .leftMirrored:
        return 5
    case .rightMirrored:
        return 7
    

用于从CIImage 转换为CGImage,然后再转换为UIImage

我像往常一样将CIImage 转换为CGImage

let cgImage = context?.createCGImage(ciImage, from: ciImage.extent)

然后我以这种方式将cgImage 转换为UIImage (imageToSave):

let imageToSave = UIImage(cgImage: cgImage, scale: image.scale, orientation: imageOrientation)

imageOrientationimage.scale 是原始图像的方向和比例。

因此,保存的图像仍然有错误的方向。

有趣的是,当我打印这些图像方向的原始值时,它们与0 相同,即.up,但是正如我所说,保存后方向是错误的。

我很困惑为什么会这样。如果您对如何解决此问题有任何建议,我将不胜感激。

【问题讨论】:

请出示您的实际代码并说明您是如何“知道”您迷失方向的。没有“保存” UIImage 这样的东西,所以不清楚你在做什么。您只能保存图像数据。如果您的目标是保存 CIImage 中的图像数据,您应该调用 CIContext().writeJPEGRepresentation 【参考方案1】:

TLDR:

let uiImage: UIImage = ...
let ciImage = CIImage(image: uiImage, options:
    [.applyOrientationProperty: true,
     .properties: [kCGImagePropertyOrientation: CGImagePropertyOrientation(uiImage.imageOrientation).rawValue]])

要将UIImageOrientation 转换为CGImagePropertyOrientation,此代码使用https://developer.apple.com/documentation/imageio/cgimagepropertyorientation 的扩展名

extension CGImagePropertyOrientation 
    init(_ uiOrientation: UIImageOrientation) 
        switch uiOrientation 
            case .up: self = .up
            case .upMirrored: self = .upMirrored
            case .down: self = .down
            case .downMirrored: self = .downMirrored
            case .left: self = .left
            case .leftMirrored: self = .leftMirrored
            case .right: self = .right
            case .rightMirrored: self = .rightMirrored
        
    

加长版:

我刚刚遇到了类似的问题。将 UIImage 转换为 CIImage 时,图像可能出现方向错误:

let uiImage: UIImage = ...
let ciImage CIImage(image: uiImage) /* does not use uiImage orientation */

最简单的解决方案是使用oriented(_:) 方法来改变方向:

let ciImage = CIImage(image: uiImage)?.oriented(CGImagePropertyOrientation(uiImage.imageOrientation))
/* uses CGImagePropertyOrientation extension (see above) */

这可能没问题,但是oriented(_:) 方法会创建一个新图像。我认为必须有一种方法可以在没有中间对象的情况下创建正确定向的CIImage。还有!虽然我在互联网上的任何地方都没有找到它,而且 Apple 的文档记录不够清楚,所以我把它贴在这里。 @matt 朝着正确的方向迈出了一步,但这还不够。

解决方案在applyOrientationProperty 描述中被“加密”。

图像可以包含在捕获时显示方向的元数据。 您可以加载 使用 imageWithContentsOfURL: 或 init(data:) 将此元数据放入 CIImage 当捕获的图像包含方向元数据时。使用任何 initWith:options: 方法如果 属性(元数据的 NSDictionary properties) 选项也提供了

强调重要部分。那是一把钥匙。 同样重要的是不要在这里混淆optionsproperties。 )

【讨论】:

【参考方案2】:

在创建 CIImage 时从原始 UIImage 中获取方向的方法是:

let ciImage = CIImage(image: image, options: [.applyOrientationProperty:true])!

当您完成 CIImage 后,使用 CIContext().writeJPEGRepresentation 将数据保存到磁盘,并且将保留方向元数据。

【讨论】:

试过但没用。我已经有一个由 CMSampleBuffer 制作的 CIIMage。我想在上面放置另一个图像(有点贴纸),所以使用了 CIImage 中的复合(over :) 方法。但是方向不适合贴纸。 @nr5 我建议您将其作为一个新问题正式提出。也许有人可以帮忙。 我试过了:***.com/questions/55574172/… @nr5 据我所知,没有关于方向的内容。【参考方案3】:

你可以试试这个方法

 func image(with image: UIImage?, scaledToSizeKeepingAspect newSize: CGSize) -> UIImage? 
    var newSize = newSize
    var imgHeight = Float(image?.size.height ?? 0.0)
    var imgWeight = Float(image?.size.width ?? 0.0)
    var fact: Float = 0.0
    if imgHeight > imgWeight 
        fact = Float(CGFloat(imgHeight) / newSize.width)
     else 
        fact = Float(CGFloat(imgWeight) / newSize.height)
    
    imgHeight = imgHeight / fact
    imgWeight = imgWeight / fact
    newSize = CGSize(width: CGFloat(imgWeight), height: CGFloat(imgHeight))
    UIGraphicsBeginImageContext(newSize)
    // Tell the old image to draw in this new context, with the desired
    // new size
    image?.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
        // Get the new image from the context
    let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
    // End the context
    UIGraphicsEndImageContext()
    // Return the new image.
    return newImage

这样调用方法:

image(withImage: image, scaledToSizeKeepingAspect: image.size)

这里的图像是 UIImage 对象,您要更改的图像在此方法中传递。

【讨论】:

以上是关于UIImage 在将其转换为 CIImage,然后转换为 CGImage 并返回 UIImage 时失去其方向的主要内容,如果未能解决你的问题,请参考以下文章

有啥方法可以在不将 CIImage 转换为 UIImage 或 CGImage 的情况下将 CIImage 转换为 NSData?

Objective c 在转换为 UIImage 时丢失了 CIImage 内存

无法使用CIFilter从CIImage中提取UIImage

从 UIImage (Swift) 获取 CIImage

将UIImage转换为CVPixelBufferRef

在 iOS 7 之前的项目中编译警告“不兼容的指针类型初始化 'UIImage *' 使用类型为'CIImage * _Nullable”的表达式