UIImage在转换为CIImage,然后转换为CGImage并再次转换为UIImage时会失去其方向。

3

我知道有很多类似的问题,但是它们的解决方案对我不起作用。

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

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

结果,保存的图像方向是错误的。

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

UIImage 转换为 CIImage

最初,我是通过以下方式简单地转换图像的:

let let ciImage = CIImage(image: image)

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

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

以下是方法imageOrientationToTiffOrientation的实现(我在这里找到它):
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 - undefined
3个回答

5
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而不需要中间对象。实际上有这样一种方法!尽管我在互联网上没有找到,而且苹果公司也没有清晰地记录它,所以我在这里发布它。 @matt朝着正确的方向迈出了一步,但这还不够。

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

图像可能包含揭示拍摄时方向的元数据。当捕获的图像包含方向元数据时,可以使用imageWithContentsOfURL:或init(data:)将此元数据加载到CIImage中。如果提供了属性(元数据属性的NSDictionary)选项,则使用任何initWith:options:方法。

重要部分已经强调了。 这是关键。在这里不要混淆optionsproperties。 )


3
创建CIImage时,从原始UIImage中提取方向的方法是:
let ciImage = CIImage(image: image, options: [.applyOrientationProperty:true])!

当 CIImage 处理完成后,使用 CIContext().writeJPEGRepresentation 将数据保存到磁盘上,此时方向信息将会被保留。


尝试过但没有成功。我已经从CMSampleBuffer创建了一个CIImage。我想在上面放置另一张图片(类似贴纸),所以使用了CIImage的composited(over:)方法。但是贴纸的方向不正确。 - undefined
@nr5 我建议你应该正式提出这个问题,作为一个新的问题。也许有人可以帮助你。 - undefined
我尝试过在iOS中使用https://stackoverflow.com/questions/55574172/avcapturevideopreviewlayer-add-overlays-and-capture-photo添加叠加层和拍摄照片。 - undefined
@nr5 我看不到关于方向的任何信息。 - undefined

0
你可以尝试这个方法。
 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)

这里的image是你想要更改的UIImage对象,将其传递给这个方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接