首页 > 解决方案 > 将 UIBezierPath 变成蒙版?

问题描述

不确定我是否正确地问了这个问题,但我有两个组成部分;一个CIImage和一个UIBezierPath。理想情况下,我想创建一个CGRect封装我的UIBezierPath; 路径内的一切都是白色的,路径外的一切都是黑色的。这样,我可以将其渲染CGRect为某种图像,然后我可以将其用作其他目的的蒙版。

我正在努力弄清楚如何以性能为重点来做到这一点。如下所述,我的测试利用UIGraphicsImageRenderer了对于我的需求来说太慢的使用(我将在相机的样本缓冲区上执行此操作)。因此,我想坚持下去CoreImage。这是我的尝试;

// Path
let path = UIBezierPath()
// ... define the path's shape and close it

// My source image
let image = CIImage(cgImage: UIImage(named: "test.jpg")!.cgImage!)

// Renderer
let renderer = UIGraphicsImageRenderer(size: image.extent.size)

// Render path as mask
let img = renderer.image { ctx in
   ctx.cgContext.setFillColor(UIColor.black.cgColor)
   ctx.cgContext.fill(CGRect(x: 0, y: 0, width: image.extent.size.width, height: image.extent.size.height))
   ctx.cgContext.setFillColor(UIColor.white.cgColor)
   ctx.cgContext.addPath(path.cgPath)
   ctx.cgContext.drawPath(using: .fill)
}

// Put a filter on the image
let imageFiltered = image.applyingFilter("CIPhotoEffectNoir")

// Blend with mask
let maskFilter = CIFilter.blendWithMask()
maskFilter.inputImage = imageFiltered
maskFilter.backgroundImage = image
maskFilter.maskImage = CIImage(cgImage: img.cgImage!)

// Output
if let output = maskFilter.outputImage {
   ... use CIContext() to render back to CVPixelBuffer for preview on MTKView.
}

总体而言,目标是定义图像的一部分(不符合正方形或圆形等传统形状),将使用 CIFilter 过滤,然后在原始图像上合成。如果有更好的方法(例如以某种方式获取原始图像,对其进行过滤,将其裁剪到路径(使路径之外的所有内容透明)并进行合成,那可能会更好。

需要注意的是,上面的示例代码会导致崩溃,因为UIGraphicsImageRenderer无法以足够快的速度渲染蒙版。

标签: swiftimage-processinguibezierpathcore-image

解决方案


到目前为止,您的方法看起来不错。我认为缓慢的部分是使用 Core Graphics 生成掩码图像。不幸的是,没有直接的方法可以直接(在 GPU 上)对 Core Image 做同样的事情。但是,您可以尝试以下方法:

(假设您之前的问题中路径始终具有特定的形状,)您可以针对您选择的特定参考尺寸生成一个包含路径的蒙版图像。确保路径不会“接触”边界。

然后,当您想将其用作蒙版时,使用变换将形状图像移动并缩放到正确的位置,并使其边缘无限延伸(以覆盖整个底层图像;这就是形状不应接触边缘的原因)。像这样的东西:

let pathImage = CIImage(cgImage: img.cgImage!)
// scale path to the size of the area you want to mask
var mask = pathImage.transformed(by: CGAffineTransform(scaleX: scaleX, y: scaleY))
// move path to the place you want to cover
mask = mask.transformed(by: CGAffineTransform(translationX: offsetX, y: offsetY))
// let mask fill the rest of the area
mask = mask.clampedToExtent()

// use mask as maskImage...

您应该能够回收pathImage每一帧,从而避免核心图形和 CPU-GPU 同步。


推荐阅读