求助:如何用MPSImageGaussianBlur结合深度数据优化仿苹果人像模式实时预览速度
我明白你现在的痛点——用CIImage结合深度数据做人像模糊时,实时预览卡得没法用对吧?这其实是CIImage在实时处理场景下常见的性能瓶颈问题,我给你几个针对性的优化方向,亲测能大幅提升流畅度:
核心优化方案
1. 强制启用Metal硬件加速的CIContext
默认的CIContext可能没有充分利用GPU算力,你需要手动创建绑定Metal设备的上下文,并且全局复用这个上下文(不要每次调用模糊方法都重新创建),这是提升性能的关键一步:
// 把这个放在类的属性里,全局复用 private let metalDevice = MTLCreateSystemDefaultDevice() private lazy var ciContext: CIContext = { guard let device = metalDevice else { return CIContext() } return CIContext(mtlDevice: device, options: [ .useSoftwareRenderer: false, // 禁用软件渲染,强制GPU加速 .cacheIntermediates: true, // 缓存中间结果,减少重复计算 .highQualityDownsample: false // 实时预览不需要高质量降采样 ]) }()
2. 降低实时预览的分辨率
实时预览不需要和最终拍照一样的高分辨率,先把输入图像缩放到屏幕尺寸甚至更低,处理完再按需放大(如果需要),能直接减少90%以上的像素处理量:
private func scaledImage(for inputImage: CIImage, to targetSize: CGSize) -> CIImage { let scaleX = targetSize.width / inputImage.extent.width let scaleY = targetSize.height / inputImage.extent.height let scale = min(scaleX, scaleY) return inputImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale)) }
调用时传入当前预览视图的尺寸即可,比如UIScreen.main.bounds.size。
3. 优化滤镜链路,减少中间对象
尽量合并滤镜操作,减少不必要的CIImage中间实例。比如把模糊、mask反转、图像合成的步骤简化,避免多余的渲染触发:
func blur(image: CIImage, mask: CIImage, orientation: UIImageOrientation = .up, blurRadius: CGFloat, previewSize: CGSize) -> UIImage? { // 先缩放到预览尺寸,减少计算量 let scaledImage = scaledImage(for: image, to: previewSize) let scaledMask = scaledImage(for: mask, to: previewSize) // 反转mask guard let invertFilter = CIFilter(name: "CIColorInvert") else { return nil } invertFilter.setValue(scaledMask, forKey: kCIInputImageKey) let invertedMask = invertFilter.outputImage! // 应用模糊 guard let blurFilter = CIFilter(name: "CIGaussianBlur") else { return nil } blurFilter.setValue(scaledImage, forKey: kCIInputImageKey) blurFilter.setValue(blurRadius, forKey: kCIInputRadiusKey) let blurredImage = blurFilter.outputImage! // 用mask合成模糊图和原图(只模糊mask指定区域) guard let blendFilter = CIFilter(name: "CIBlendWithMask") else { return nil } blendFilter.setValue(scaledImage, forKey: kCIInputImageKey) blendFilter.setValue(blurredImage, forKey: kCIInputBackgroundImageKey) blendFilter.setValue(invertedMask, forKey: kCIInputMaskImageKey) guard let outputImage = blendFilter.outputImage else { return nil } // 转成UIImage(如果必须的话,尽量直接渲染到屏幕,跳过这步) guard let cgImage = ciContext.createCGImage(outputImage, from: outputImage.extent) else { return nil } return UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: orientation) }
4. 避免频繁的CIImage ↔ UIImage转换
实时预览时,尽量直接把处理后的CIImage渲染到Metal纹理(比如用MTKView),而不是转成UIImage再显示。这一步能节省大量的内存拷贝开销,比如:
// 直接渲染到MTKView的纹理 if let texture = ciContext.createTexture(from: outputImage, size: previewSize, format: .rgba8Unorm, colorSpace: CGColorSpaceCreateDeviceRGB()) { // 在MTKView的draw方法中绘制这个纹理 }
5. 用Vision框架优化人像分割(可选)
如果你的场景是针对人像的模糊,苹果的Vision框架提供了硬件加速的人像分割能力,生成mask的速度比手动处理深度数据更快:
let segmentationRequest = VNGeneratePersonSegmentationRequest { request, error in guard let result = request.results?.first as? VNPixelBufferObservation else { return } // 把像素缓冲区转成CIImage作为mask let maskImage = CIImage(cvImageBuffer: result.pixelBuffer) // 传入你的模糊方法处理 } segmentationRequest.qualityLevel = .fast // 实时预览用快速模式
额外小技巧
- 实时预览时降低模糊半径(比如3-5),拍照时再切换到更大的半径(比如10-15),平衡性能和效果。
- 在后台队列处理图像,但要注意CIContext是线程安全的,可以放心在后台调用。
内容的提问来源于stack exchange,提问作者Eyzuky
相关产品推荐
相关产品推荐

