Highlight
Apple 在 iOS/iPadOS/macOS/visionOS 27 中推出 RAW 9 解码管线,用基于 Apple Neural Engine 的 CoreML 模型同时处理去马赛克和降噪,第三方 App 只需将 CIRAWFilter 的 decoderVersion 设为 version9 即可获得大幅画质提升,并可通过 CIImageProcessor 的新 API 精确控制 tile 尺寸和临时缓冲区生命周期。
核心内容
老照片也能变清晰
RAW 文件的魅力在于:你今天拍的照片,五年后可以用更好的算法重新处理。Apple 从 2006 年的 21 款相机校准,一路扩展到如今的 784 款。每次管线升级,去马赛克、降噪、色彩都在进步。(02:48)
但以前有个痛点:高 ISO 场景下,RAW 8 的降噪和色彩恢复总差一口气。ISO 51200 的照片里,蜡笔的颜色糊成一片,细小文字几乎不可读。富士 X-T5 的非传统传感器布局更是容易出色彩伪影。(04:27)
RAW 9 的解法是把去马赛克和降噪合并成一个基于 tile 的 CoreML 模型,跑在 Apple Neural Engine 上。结果是:低噪照片的文字更清晰,高噪照片的色彩更准确,连蜡笔上的高光反射都能还原出来。(03:33)
交互编辑与批量导出,两种场景两种策略
RAW 9 画质好了,代价是更吃性能。同一张照片反复渲染时,Core Image 会缓存中间结果,所以用户拖动曝光滑块的反馈依然很快。但导出场景完全不同:一次性全分辨率输出,缓存只会浪费内存。(08:40)
Apple 明确给出了两套最佳实践:
- 交互编辑:用
scaleFactor降采样,每个视图一个CIContext,开启cacheIntermediates,直接渲染到 Metal-backed view。加上 Extended Virtual Addressing entitlement 让系统分配更多缓存内存。(09:39) - 批量导出:
cacheIntermediates设为 false,memoryLimit从默认 256MB 提升到 512MB 或 1024MB,用CIContext的heifRepresentation或jpegRepresentation方法直接输出。(10:56)
搞混这两套配置,App 要么卡顿,要么闪退。
详细内容
启用 RAW 9
RAW 9 不会自动开启,需要手动检查并设置。(05:50)
let rawFilter = CIRAWFilter(imageURL: rawURL)!
// 检查当前设备是否支持 RAW 9
if rawFilter.supportedDecoderVersions.contains(.version9) {
rawFilter.decoderVersion = .version9
}
// 查询某版本支持的相机型号列表
let models = CIRAWFilter.supportedCameraModels(for: .version9)
关键点:
supportedDecoderVersions返回当前 RAW 文件可用的解码版本列表decoderVersion默认不是 version9,必须显式设置supportedCameraModels(for:)是类方法,返回支持指定版本的所有相机型号数组- 支持列表会通过系统 OTA 更新持续扩充
- 原生拍摄 DNG 的设备(包括 iPhone)自动兼容 RAW 9
CIRAWFilter 的可调参数
RAW 9 保留了 20 个校准参数供用户微调,但废弃了三个旧参数。(07:31)
保留的核心参数:
exposure— 控制整体明暗luminanceNoiseReductionAmount— 控制亮度噪点sharpnessAmount— 控制边缘锐化程度contrastAmount— 控制边缘附近的局部对比度
RAW 9 中失效的参数:
colorNoiseReductionAmount— CoreML 模型自动处理色彩降噪detailAmount— 不再需要moireReductionAmount— 不再需要
调用 isSupported 可检查某个属性对当前 filter 实例是否有效。
批量导出的 CIContext 配置
(11:08)
let exportCtx = CIContext(options: [
.cacheIntermediates: false,
.memoryLimit: 512
])
关键点:
cacheIntermediates: false— 导出是一次性操作,缓存无意义memoryLimit单位是 MB,iOS 默认 256MB,提升到 512 或 1024 可显著加速- 用
exportCtx.heifRepresentation(of:)或jpegRepresentation(of:)比直接调 Image IO 更省内存
CIImageProcessor 显式输出 Tile 尺寸
内存紧张时,Core Image 会自动把图像拆成小块处理。RAW 9 新增了让开发者自己控制 tile 大小的能力。(12:23)
import CoreImage
class MyProcessor: CIImageProcessorKernel {
override class func roi(forInput input: Int32,
arguments: [String : Any]?,
outputRect: CGRect) -> CGRect { return outputRect }
override class func process(with inputs: [CIImageProcessorInput]?,
arguments: [String : Any]?,
output: CIImageProcessorOutput) throws {
guard let input = inputs?.first,
let iBuffer = input.pixelBuffer,
let oBuffer = output.pixelBuffer else { return }
let iRegion = input.region
let oRegion = output.region
// 在这里处理像素数据
}
}
let extent = inImg.extent
let tileSize = 512.0
var tiles: [CIVector] = []
for y in stride(from: extent.minY, to: extent.maxY, by: tileSize) {
for x in stride(from: extent.minX, to: extent.maxX, by: tileSize) {
let tile = CGRect(x: x, y: y,
width: min(tileSize, extent.maxX - x),
height: min(tileSize, extent.maxY - y))
tiles.append(CIVector(cgRect: tile))
}
}
let result = try MyProcessor.apply(withTiledExtent: tiles, inputs: [inImg], arguments: [:])
关键点:
roi(forInput:arguments:outputRect:)定义输入区域与输出区域的映射关系process回调中的input.region和output.region是 Core Image 分配的实际处理区域- 手动生成 tile 数组后,通过
apply(withTiledExtent:inputs:arguments:)传入 - tile 大小可根据 GPU 显存和算法需求灵活调整,不限于 512x512
CIImageProcessor 临时缓冲区
处理多 tile 时,反复创建和销毁临时缓冲区会影响性能。RAW 9 新增了由 Core Image 管理的临时 PixelBuffer。(14:24)
import CoreImage
class MyProcessor: CIImageProcessorKernel {
override class func process(with inputs: [CIImageProcessorInput]?,
arguments: [String: Any]?,
output: CIImageProcessorOutput) throws {
guard let input = inputs?.first,
let srcPixelBuffer = input.pixelBuffer,
let dstPixelBuffer = output.pixelBuffer else { return }
// 从 Core Image 缓存池申请临时缓冲区
guard let scratch = output.temporaryPixelBuffer(
identifier: "myScratch",
format: kCVPixelFormatType_64RGBAHalf,
width: Int(output.region.width),
height: Int(output.region.height),
pixelBufferAttributes: nil
) else { return }
// Step 1: 把输入数据拷贝到 scratch
// Step 2: 在 scratch 中处理像素
// Step 3: 把结果拷贝到输出 buffer
}
}
关键点:
temporaryPixelBuffer(identifier:format:width:height:pixelBufferAttributes:)从 Core Image 内部缓存池获取缓冲区identifier用于区分多个临时缓冲区,同一处理器内必须唯一- Core Image 自动管理生命周期:当前 tile 处理完后回收,下一个 tile 复用
- 典型用途是将 Core Image 的交错式图像数据转换为 CoreML 需要的平面式数据
核心启发
-
做一款 RAW 浏览与轻量编辑 App:利用 RAW 9 的画质提升和
CIRAWFilter的 20 个可调参数,做一个专注快速预览和参数微调的 App。入口是CIRAWFilter(imageURL:),核心交互是拖动 exposure/sharpness 滑块实时预览。(07:15) -
批量 RAW 转码工具:针对摄影师的工作流,做一个后台批量将 RAW 导出为 HEIF/JPEG 的工具。关键配置是
cacheIntermediates: false+memoryLimit: 1024,配合heifRepresentation(of:colorSpace:)直接输出。(10:56) -
自定义 RAW 后期滤镜:用
CIImageProcessorKernel接入自己的 Metal 或 CoreML 算法,在 RAW 9 解码后叠加自定义效果。用显式 tile 尺寸控制大图的内存占用,用临时缓冲区避免重复分配。(12:17) -
老照片重处理功能:扫描用户相册中的旧 RAW 文件,用 RAW 9 重新解码后对比展示画质差异。用
supportedCameraModels(for:)提前判断哪些照片可以受益。(06:27)
关联 Session
- Capture photos and video with the Camera app — Camera app 的新拍摄能力,与 RAW 处理形成完整工作流
- Take your photo capture to the next level — 更底层的照片捕获 API,适合需要同时控制拍摄和后期处理的 App
- Display EDR content with Core Image, Metal, and SwiftUI — RAW 9 推荐直接渲染到 Metal-backed view,这篇是相关基础
- SwiftUI graphics — 在 SwiftUI 中集成 Core Image 渲染结果
- Metal performance — 深入理解 Metal 渲染性能,与 RAW 9 的 CIContext 调优互补
评论
GitHub Issues · utterances