创建基于 Apple 芯片的图像处理应用
Create image processing apps powered by Apple silicon
2021年6月9日
一句话判断
在 Apple Silicon 上做图像处理,你应该把 Core Image、Metal Performance Shaders 和 vImage 视为一个连续的工具谱——从”零代码滤镜”到”极致性能自定义算子”,覆盖所有场景。
这场 Session 讲了什么
Session 讲解了如何在 Apple Silicon(M1、A14/A15)上构建高性能的图像处理应用。覆盖了三个层次的工具:Core Image(高层滤镜 API)、Metal Performance Shaders(中层 GPU 计算)、vImage(底层 SIMD 图像处理),以及它们之间如何协作。
Apple Silicon 的统一内存架构(Unified Memory Architecture)是性能基础——CPU 和 GPU 共享同一块物理内存,图像数据不需要在 CPU 和 GPU 之间拷贝。Core Image 在 iOS 15 / macOS 12 中的改进让它可以更高效地利用这个特性:新的 CIImage 创建方式可以直接从 Metal texture 或 IOSurface 构建图像,零拷贝传递给滤镜管线。Session 还展示了 vImage 的新功能——支持在 Swift 中直接调用(不再需要忍受 C API 的繁琐),新增了对多通道浮点图像的处理能力。
值得深挖的点
Core Image 与 Metal 的零拷贝集成
以前把 Core Image 的结果传给 Metal 需要先渲染到 CIContext 的 CGImage,再上传到 Metal texture——两次数据拷贝。现在通过 CIContext.render(_, to: MTLTexture) 方法,Core Image 直接渲染到 Metal texture,不经过 CPU。反向也一样:Metal 渲染的结果可以通过 CIImage(mtlTexture:) 直接变成 Core Image 的输入。整个管线可以做到零拷贝:Metal 渲染 -> Core Image 滤镜 -> Metal 显示,数据始终在 GPU 内存中。
vImage 的 Swift 友好 API
vImage 在 Accelerate 框架中一直是 C 风格的函数 API,参数多且命名冗长。今年 Apple 为 vImage 新增了 Swift 风格的包装:vImage.PixelBuffer 类型提供了链式调用的图像处理方法。比如 buffer.convert(to: .gray8) 就能把 RGBA 图像转成灰度,比以前的 vImageMatrixMultiply_ARGB8888ToPlanar8 好读一百倍。性能没有损失——Swift 包装仍然是直接调用底层的 SIMD 实现。
代码片段
// Core Image 零拷贝滤镜管线
// 从 Metal texture 创建 CIImage,处理后直接渲染回 Metal texture
func applyFilters(
inputTexture: MTLTexture,
outputTexture: MTLTexture,
ciContext: CIContext
) {
// 从 Metal texture 创建 CIImage(零拷贝)
let inputImage = CIImage(mtlTexture: inputTexture)!
// 构建滤镜链
let filtered = inputImage
.applyingFilter("CIGaussianBlur",
parameters: [kCIInputRadiusKey: 5.0])
.applyingFilter("CIColorControls",
parameters: [kCIInputContrastKey: 1.2])
.applyingFilter("CIVignette",
parameters: [kCIInputIntensityKey: 0.8])
// 直接渲染到 Metal texture(零拷贝)
ciContext.render(filtered,
to: outputTexture,
commandBuffer: nil,
bounds: filtered.extent,
colorSpace: CGColorSpaceCreateDeviceRGB())
}
// vImage 的 Swift API:批量处理图像
func processImages(pixelBuffer: vImage.PixelBuffer<vImage.Interleaved8x4>,
destination: vImage.PixelBuffer<vImage.Interleaved8x4>) {
// 色彩空间转换:sRGB -> Display P3
pixelBuffer.convert(to: destination,
destinationColorSpace: CGColorSpace(name: CGColorSpace.displayP3)!)
// 调整亮度和对比度
destination.multiply(by: 1.1) // 增加亮度 10%
destination.addCrossoverClamping(10) // 加一点偏移
// 高斯模糊(vImage 内置实现)
destination.gaussianBlur(radius: 3.0,
destination: destination)
}
// 完整的 Metal + Core Image + vImage 混合管线
func hybridProcessingPipeline(
inputBuffer: CVPixelBuffer,
device: MTLDevice
) -> MTLTexture {
let ciContext = CIContext(mtlDevice: device)
// 从相机缓冲区创建 CIImage
let ciImage = CIImage(cvPixelBuffer: inputBuffer)
// Core Image 做色彩校正和白平衡
let colorCorrected = ciImage
.applyingFilter("CIWhiteBalance",
parameters: [kCIInputTemperatureKey: 5500])
// 转换为 vImage 做直方图均衡化(Core Image 不擅长的操作)
var format = vImage_CGImageFormat(bitsPerComponent: 8,
bitsPerPixel: 32,
colorSpace: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue))!
var sourceBuffer = try! vImage.PixelBuffer(ciImage: colorCorrected,
cgImageFormat: format)
sourceBuffer.equalizeHistogram()
// 转回 CIImage 并最终渲染到 Metal texture
let result = try! sourceBuffer.makeCIImage(cgImageFormat: format)
let descriptor = MTLTextureDescriptor.texture2DDescriptor(
pixelFormat: .rgba8Unorm, width: Int(result.extent.width),
height: Int(result.extent.height), mipmapped: false)
descriptor.usage = .shaderRead
let outputTexture = device.makeTexture(descriptor: descriptor)!
ciContext.render(result, to: outputTexture, commandBuffer: nil,
bounds: result.extent,
colorSpace: format.colorSpace!)
return outputTexture
}
最佳实践
- 图像处理管线的架构选择:如果 Core Image 内置的 200+ 滤镜能满足需求,不要用 Metal 自己实现。Core Image 的内部调度已经针对 Apple Silicon 做了极限优化。
- 需要自定义算子时,用 Core Image 的
CIImageProcessorKernel或直接写 Metal compute shader,避免用 CPU 做 pixel-by-pixel 的处理。 - 对于视频帧处理(相机或视频流),确保
CVPixelBuffer在整个管线中保持 IOSurface 支持,这样可以利用统一内存避免拷贝。
还有什么值得关注
CIImage现在支持直接从CMSampleBuffer创建,视频处理管线可以更简洁。- vImage 新增了对 16-bit 浮点图像格式(
vImage.Interleaved16Fx4)的支持,适合 HDR 图像处理。 - Core Image 的
CIFilter现在可以在 Xcode 预览中实时调试,调整参数时直接看到效果。