这里遇到了奇怪的CoreImage裁剪问题

4

我遇到了一个奇怪的问题,即在从照片库中裁剪照片后,应用程序无法显示它。运行以下代码后,它会给出以下错误:

        self.correctedImageView.image = UIImage(ciImage: correctedImage)  

[api] -[CIContext(CIRenderDestination) _startTaskToRender:toDestination:forPrepareRender:error:] The image extent and destination extent do not intersect.

以下是我用来裁剪和显示图片的代码。(inputImage 是 CIImage 类型)
    let imageSize = inputImage.extent.size
    let correctedImage = inputImage
        .cropped(to: textObvBox.boundingBox.scaled(to: imageSize) )
    DispatchQueue.main.async {
        self.correctedImageView.image = UIImage(ciImage: correctedImage)  
    }
更多信息:调试打印输入图像和更正后的图像的范围。
以下是self.inputImage的描述:
CIImage: 0x1c42047a0 的范围为 [0 0 3024 4032]。
剪裁 [430 3955 31 32] 的范围为 [430 3955 31 32],仿射变换 [0 -1 1 0 0 4032] 的范围为 [0 0 3024 4032],不透明。仿射变换 [1 0 0 -1 0 3024] 的范围为 [0 0 4032 3024],不透明。颜色匹配“sRGB IEC61966-2.1”_to_workingspace 范围为 [0 0 4032 3024],不透明。IOSurface 0x1c4204790(501) seed:1 YCC420f 601 alpha_one 的范围为 [0 0 4032 3024],不透明。
有趣的是,当我设置断点时,使用Xcode,我能够正确地预览裁剪后的图像。我不确定CIImage中的这个范围是什么意思,但当我将裁剪后的图像分配给UIImageView时,它似乎不太喜欢它。您知道这个范围是做什么的吗?

CoreImage 在一个无限平面上操作。范围是该平面中实际包含您关心的图像数据的矩形。 - Lily Ballard
调用.cropped(to:)是在设置范围。您将其设置为textObvBox.boundingBox.scaled(to: imageSize) - Lily Ballard
好的,您可以随时使用 CIContextCIImage 渲染为位图。 - Lily Ballard
2
我不确定我是否与@KevinBallard完全相同,但我创建了一个CIContext - 注意,这些很昂贵(即:创建一个需要处理时间),尝试只创建一次并将其用于所有操作 - 然后我使用CIContext.createCGImage(CIImage, from: CIExtent),最后使用UIImage(cgImage:)。我选择这种方法是因为我使用GLKView(使用GPU)而不是UIImageView(使用CPU)将东西渲染到屏幕上。如果编码正确,单个CIContext就足够了,不需要UIImage - user7014451
1
我在裁剪图像后重复使用给定的范围。 - mskw
显示剩余11条评论
3个回答

10

我遇到了你描述的相同问题。由于UIKit/CoreImage中的一些奇怪行为,我需要先将CIImage转换为CGImage。我注意到这只在我对CIImage应用了一些下面所述的滤镜时发生。

let image = /* my CIImage */
let goodImage = UIImage(ciImage: image)
uiImageView.image = goodImage // works great!


let image = /* my CIImage after applying 5-10 filters */
let badImage = UIImage(ciImage: image)
uiImageView.image = badImage // empty view!

这是我是如何解决它的。

let ciContext = CIContext()
let cgImage = self.ciContext.createCGImage(image, from: image.extent)
uiImageView.image = UIImage(cgImage: cgImage) // works great!!!

正如其他评论者所说,要注意不要频繁创建CIContext,因为这是一个昂贵的操作。


2
在处理裁剪和平移滤镜时,CIImage中的Extent可能会有些棘手。实际上,它允许新图像的实际位置与其所取自旧图像的部分直接相关。如果你从一幅图像中裁剪出中心部分,你会发现extent的原点不为零,就会出现问题。我相信这个功能是用于制作支持图层的艺术应用程序等方面的。

正如上面所述,你可以将CIImage转换为CGImage,然后再转换为UIImage,但这样做速度慢且浪费资源。这种方法之所以可行是因为CGImage不保留extent,而UIImage可以。但是,正如上面所述,需要创建一个具有很大开销的CIContext

幸运的是,有一种更好的方法。只需创建一个CIAffineTransformFilter,并将其填充为等于extent原点的负值的仿射变换即可:

let transformFilter = CIFilter(name: "CIAffineTransform")!
let translate = CGAffineTransform(translationX: -image.extent.minX, y: -image.extent.minY)
let value = NSValue(cgAffineTransform: translate)
transformFilter.setValue(value, forKey: kCIInputTransformKey)
transformFilter.setValue(image, forKey: kCIInputImageKey)
let newImage = transformFilter.outputImage

现在,newImage 应该与 image 完全相同,但其 extent 原点为零。您可以直接将其传递给 UIView(ciimage:)。我测试过,发现这比创建一个 CIContext 并制作一个 CGImage 快得多。仅供参考:
使用 CGImage 方法:0.135 秒。
使用 CIFilter 方法:0.00002 秒。
(在 iPhone XS Max 上运行)

0

在 iOS 10.X 或更高版本中,您可以使用高级的 UIGraphicsImageRenderer 来绘制您的图像。

let img:CIImage = /* my ciimage */

let renderer = UIGraphicsImageRenderer(size: img.extent.size)

uiImageView.image = renderer.image { (context) in
    UIImage(ciImage: img).draw(in: .init(origin: .zero, size: img.extent.size))
}


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