Highlight
Apple 把 ProRAW 的采集、图库存储、RAW 资源读取和 Core Image 编辑接入到 AVFoundation、PhotoKit 与 Core Image,开发者可以在自己的相机和修图 App 中处理带有计算摄影结果的 DNG RAW 文件。
核心内容
相机 App 常见的两条路都不省心。HEIC 和 JPEG 适合立即显示,文件小,系统已经做完 Smart HDR、Deep Fusion 或夜间模式融合。摄影用户后期调整时,余量有限。Bayer RAW 保留更多传感器数据,后期空间大,但开发者要处理去马赛克、噪声和渲染速度问题。
ProRAW 解决的是这条生产链。它仍然放在标准 Adobe DNG 文件里,系统框架 ImageIO 和 Core Image 已有基础支持。它的像素来自线性化、场景参考的数据,可由多帧曝光融合生成,默认用 12-bit RGB 无损压缩,文件通常在 10MB 到 40MB 之间。
对开发者来说,变化发生在三个位置。拍摄阶段用 AVFoundation(音视频采集框架)请求 ProRAW 像素格式;入库阶段用 PhotoKit(照片图库框架)把 DNG 作为主要照片资源保存;编辑阶段用 Core Image(图像处理框架)加载 CIRAWFilter,调整曝光、白平衡、锐化、本地色调映射,必要时取出线性场景数据做分析。
从拍摄开始
以前做 RAW 相机,开发者通常要在 Bayer RAW 和处理后 JPEG 之间取舍。ProRAW 让 App 得到一个 DNG 文件,同时保留 Apple 计算摄影的默认观感。它还可以在文件里带全尺寸 JPEG 预览,减少 RAW + JPEG 双资源带来的图库管理成本。
从图库继续
用户的照片库里可能已经有 RAW 文件。iOS 15 新增 .smartAlbumRAW,开发者可以直接取到系统 Photos 里的 RAW 智能相册,再用 PHAssetResourceManager 读取真正的 RAW 数据。
从编辑落地
Core Image 在 iOS 15 和 macOS 12 增加了更适合 Swift 的 CIRAWFilter API。开发者不必只用字符串 key 调整 RAW 滤镜,可以直接设置属性。ProRAW 的默认观感来自 DNG 标签;关掉这些默认输入后,还能拿到线性场景参考输出,用于直方图、分析或自定义渲染。
详细内容
1. 配置支持 ProRAW 的采集管线
(07:52)ProRAW 只支持最高质量照片格式。最简单的做法是在 AVCaptureSession 上使用 .photo preset。Apple 建议在 session 启动前启用 photoOutput.isAppleProRAWEnabled,否则会触发耗时的管线重配置。
import AVFoundation
private let session = AVCaptureSession()
private let photoOutput = AVCapturePhotoOutput()
private func configureSessionAndPhotoOutput() {
session.beginConfiguration()
session.sessionPreset = .photo
photoOutput.isHighResolutionCaptureEnabled = true
photoOutput.isAppleProRAWEnabled = photoOutput.isAppleProRAWSupported
photoOutput.maxPhotoQualityPrioritization = .quality
session.commitConfiguration()
}
关键点:
AVCaptureSession()创建相机采集会话。AVCapturePhotoOutput()负责静态照片输出。session.beginConfiguration()开始批量修改会话配置。session.sessionPreset = .photo选择支持最高质量照片的 preset。photoOutput.isHighResolutionCaptureEnabled = true开启高分辨率照片采集。photoOutput.isAppleProRAWEnabled = photoOutput.isAppleProRAWSupported只在设备支持时启用 ProRAW。photoOutput.maxPhotoQualityPrioritization = .quality把最高照片质量优先级设为质量优先。session.commitConfiguration()提交配置。
(09:26)启用 ProRAW 后,availableRawPhotoPixelFormatTypes 会包含 ProRAW 像素格式。用 AVCapturePhotoOutput.isAppleProRAWPixelFormat(_:) 区分 ProRAW 和 Bayer RAW。
import AVFoundation
func firstProRAWPixelFormat(from photoOutput: AVCapturePhotoOutput) -> OSType? {
return photoOutput.availableRawPhotoPixelFormatTypes.first { pixelFormat in
AVCapturePhotoOutput.isAppleProRAWPixelFormat(pixelFormat)
}
}
关键点:
availableRawPhotoPixelFormatTypes返回当前输出支持的 RAW 像素格式。first { pixelFormat in ... }从列表里找第一个 ProRAW 格式。isAppleProRAWPixelFormat(pixelFormat)判断该格式是否属于 Apple ProRAW。- 函数返回
OSType?,找不到时返回nil,调用方应把它当作设备或格式不支持处理。
2. 创建 ProRAW 拍摄设置
(10:09)只拍 ProRAW 时,AVCapturePhotoSettings(rawPixelFormatType:) 就够了。如果还要同时得到处理后的照片,可以提供 processedFormat。session 提到,这会让 App 处理多个资源,PhotoKit 里也更难跟踪。
import AVFoundation
func makeProRAWSettings(
photoOutput: AVCapturePhotoOutput,
proRawPixelFormat: OSType
) -> AVCapturePhotoSettings? {
guard let processedPhotoCodecType = photoOutput.availablePhotoCodecTypes.first else {
return nil
}
let photoSettings = AVCapturePhotoSettings(
rawPixelFormatType: proRawPixelFormat,
processedFormat: [AVVideoCodecKey: processedPhotoCodecType]
)
return photoSettings
}
关键点:
availablePhotoCodecTypes.first取当前输出支持的处理后照片编码格式。guard在没有可用编码格式时提前返回。AVCapturePhotoSettings(rawPixelFormatType:processedFormat:)创建一次同时包含 ProRAW 和处理后照片的拍摄请求。AVVideoCodecKey指定处理后照片使用的编码类型。- 返回
AVCapturePhotoSettings?,调用方拿到后再继续补充缩略图、预览和质量优先级。
(10:53)ProRAW 支持在 DNG 里嵌入最高可达全尺寸的 JPEG 缩略图。Apple 在 session 中建议用这个模型替代 RAW + JPEG 双资源,图库占用更小,用户体验也更清晰。
import AVFoundation
func addEmbeddedThumbnail(
to photoSettings: AVCapturePhotoSettings,
device: AVCaptureDevice
) {
guard let thumbnailPhotoCodecType = photoSettings.availableRawEmbeddedThumbnailPhotoCodecTypes.first else {
return
}
let dimensions = device.activeFormat.highResolutionStillImageDimensions
photoSettings.rawEmbeddedThumbnailPhotoFormat = [
AVVideoCodecKey: thumbnailPhotoCodecType,
AVVideoWidthKey: dimensions.width,
AVVideoHeightKey: dimensions.height
]
}
关键点:
availableRawEmbeddedThumbnailPhotoCodecTypes.first取 DNG 内嵌缩略图可用编码。device.activeFormat.highResolutionStillImageDimensions读取当前格式的高分辨率照片尺寸。rawEmbeddedThumbnailPhotoFormat设置内嵌缩略图格式。AVVideoWidthKey和AVVideoHeightKey让缩略图使用指定尺寸。
(11:08)拍摄前还可以给本次请求设置质量优先级和快速预览。这里的 photoQualityPrioritization 不能超过前面 photoOutput.maxPhotoQualityPrioritization。
import AVFoundation
func finishProRAWSettings(
_ photoSettings: AVCapturePhotoSettings,
photoOutput: AVCapturePhotoOutput,
delegate: AVCapturePhotoCaptureDelegate
) {
photoSettings.photoQualityPrioritization = .quality
if let previewPixelFormat = photoSettings.availablePreviewPhotoPixelFormatTypes.first {
photoSettings.previewPhotoFormat = [
kCVPixelBufferPixelFormatTypeKey as String: previewPixelFormat
]
}
photoOutput.capturePhoto(with: photoSettings, delegate: delegate)
}
关键点:
photoQualityPrioritization = .quality让本次拍摄质量优先。availablePreviewPhotoPixelFormatTypes.first取一个可用预览像素格式。previewPhotoFormat请求一个可快速显示的预览图。capturePhoto(with:delegate:)使用同一个静态照片 API 发起 ProRAW 拍摄。
3. 接收 DNG 文件并定制压缩
(11:44)didFinishProcessingPhoto 会收到 AVCapturePhoto。如果请求了预览,可以从 previewPixelBuffer 拿显示用图像。用 isRawPhoto 区分 ProRAW 和处理后照片,再用 fileDataRepresentation() 获取 DNG 数据。
import AVFoundation
final class ProRAWCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
var proRAWFileDataRepresentation: Data?
var proRAWPixelBuffer: CVPixelBuffer?
var previewPixelBuffer: CVPixelBuffer?
func photoOutput(
_ output: AVCapturePhotoOutput,
didFinishProcessingPhoto photo: AVCapturePhoto,
error: Error?
) {
guard error == nil else {
return
}
if let preview = photo.previewPixelBuffer {
previewPixelBuffer = preview
}
if photo.isRawPhoto {
guard let fileData = photo.fileDataRepresentation() else {
return
}
guard let pixelBuffer = photo.pixelBuffer else {
return
}
proRAWFileDataRepresentation = fileData
proRAWPixelBuffer = pixelBuffer
}
}
}
关键点:
AVCapturePhotoCaptureDelegate接收照片处理完成回调。guard error == nil过滤采集失败。photo.previewPixelBuffer提供未压缩预览,可用于快速更新界面。photo.isRawPhoto判断当前回调是否为 RAW 照片。photo.fileDataRepresentation()返回可保存的 ProRAW DNG 文件数据。photo.pixelBuffer返回 RAW 像素数据,适合继续走自定义处理链。
(12:52)ProRAW 默认用 12-bit companding curve 无损编码。通过 AVCapturePhotoFileDataRepresentationCustomizer,开发者可以降低无损压缩位深,或把质量值设到 1 以下使用 8-bit 有损压缩。
import AVFoundation
final class AppleProRAWCustomizer: NSObject, AVCapturePhotoFileDataRepresentationCustomizer {
func replacementAppleProRAWCompressionSettings(
for photo: AVCapturePhoto,
defaultSettings: [String: Any],
maximumBitDepth: Int
) -> [String: Any] {
return [
AVVideoAppleProRAWBitDepthKey: min(10, maximumBitDepth),
AVVideoQualityKey: 1.00
]
}
}
关键点:
AVCapturePhotoFileDataRepresentationCustomizer用于替换 ProRAW 文件数据表示的压缩设置。replacementAppleProRAWCompressionSettings返回新的压缩参数。maximumBitDepth是当前照片允许的最大位深。min(10, maximumBitDepth)把无损压缩位深限制在 10-bit 或更低的可用值。AVVideoQualityKey: 1.00保持无损压缩。
(13:51)生成文件数据时,把定制器传入 fileDataRepresentation(with:),就能按新的压缩设置产出 DNG。
import AVFoundation
func customizedProRAWData(from photo: AVCapturePhoto) -> Data? {
guard photo.isRawPhoto else {
return nil
}
let customizer = AppleProRAWCustomizer()
return photo.fileDataRepresentation(with: customizer)
}
关键点:
guard photo.isRawPhoto确保只处理 RAW 照片。AppleProRAWCustomizer()创建压缩设置定制器。fileDataRepresentation(with:)按定制器生成 ProRAW DNG 数据。- 返回
Data?,生成失败时调用方需要处理nil。
4. 用 PhotoKit 保存和读取 RAW 资源
(15:19)保存 ProRAW 到图库时,把 DNG 文件作为 .photo 主资源加入 PHAssetCreationRequest。session 明确建议 ProRAW 使用内嵌预览,不推荐旧式 RAW + JPEG 双资源模型。
import PhotoKit
func saveProRAWToPhotoLibrary(proRawFileURL: URL) {
PHPhotoLibrary.shared().performChanges {
let creationRequest = PHAssetCreationRequest.forAsset()
creationRequest.addResource(
with: .photo,
fileURL: proRawFileURL,
options: nil
)
} completionHandler: { success, error in
if success {
return
}
_ = error
}
}
关键点:
PHPhotoLibrary.shared().performChanges在照片库事务里修改图库。PHAssetCreationRequest.forAsset()创建一个新照片资源请求。addResource(with:.photo,fileURL:options:)把 ProRAW DNG 作为主照片资源保存。completionHandler接收保存结果和错误。
(15:45)iOS 15 新增 .smartAlbumRAW。它对应 Photos App 里的 RAW 相册,包含带 RAW 资源的资产。
import PhotoKit
func fetchRAWSmartAlbum() -> PHFetchResult<PHAssetCollection> {
return PHAssetCollection.fetchAssetCollections(
with: .smartAlbum,
subtype: .smartAlbumRAW,
options: nil
)
}
关键点:
PHAssetCollection.fetchAssetCollections查询资产集合。.smartAlbum指定系统智能相册类型。.smartAlbumRAW指定 RAW 智能相册。- 返回
PHFetchResult<PHAssetCollection>,调用方可继续取其中的资产。
(17:16)读取某个 PHAsset 的 RAW 数据时,先枚举 PHAssetResource,只看 .photo 和 .alternatePhoto,再用 UTType.rawImage 判断资源类型。
import PhotoKit
import UniformTypeIdentifiers
func requestRAWData(for asset: PHAsset, dataHandler: @escaping (Data) -> Void) {
let resources = PHAssetResource.assetResources(for: asset)
for resource in resources {
if resource.type == .photo || resource.type == .alternatePhoto {
if let resourceUTType = UTType(resource.uniformTypeIdentifier) {
if resourceUTType.conforms(to: UTType.rawImage) {
let resourceManager = PHAssetResourceManager.default()
resourceManager.requestData(for: resource, options: nil) { data in
dataHandler(data)
} completionHandler: { error in
_ = error
}
}
}
}
}
}
关键点:
PHAssetResource.assetResources(for:)取出资产下的所有资源。.photo是主照片资源,.alternatePhoto是旧式 RAW + JPEG 模型里的 RAW 资源位置。UTType(resource.uniformTypeIdentifier)把资源 UTI 转为类型对象。conforms(to: UTType.rawImage)判断资源是否属于 RAW 图像。PHAssetResourceManager.default()负责读取资源数据。requestData(for:options:dataReceivedHandler:completionHandler:)分块返回数据,结束时调用完成回调。
5. 用 Core Image 加载、编辑和导出 ProRAW
(18:36)iOS 15 和 macOS 12 的 CIRAWFilter 增加了读取预览图的便捷 API。它比直接走 CGImageSourceCreateThumbnailAtIndex 更贴近 RAW 编辑流程。
import CoreImage
func previewImage(from url: URL) -> CIImage? {
let rawFilter = CIRAWFilter(imageURL: url)
return rawFilter.previewImage
}
关键点:
CIRAWFilter(imageURL:)从 ProRAW 文件创建 RAW 滤镜。previewImage返回 DNG 内的预览图。- 返回值是
CIImage?,文件没有可用预览时可能为空。
(19:54)如果 App 要提供编辑能力,应该创建 CIRAWFilter 并修改属性。session 展示的常见调整包括曝光、色温、色调、锐化、本地色调映射。
import CoreImage
func adjustedRawImage(url: URL) -> CIImage? {
let rawFilter = CIRAWFilter(imageURL: url)
rawFilter.exposure = -1.0
rawFilter.neutralTemperature = 6500
rawFilter.neutralTint = 0.0
rawFilter.sharpnessAmount = 0.5
rawFilter.localToneMapAmount = 0.5
return rawFilter.outputImage
}
关键点:
CIRAWFilter(imageURL:)创建可修改的 RAW 滤镜。exposure = -1.0把图像亮度调为原来的一半。neutralTemperature = 6500以开尔文设置场景中性点温度。neutralTint = 0.0设置色调偏移。sharpnessAmount = 0.5把默认锐化强度设为一半。localToneMapAmount = 0.5把本地色调映射强度设为一半。outputImage生成应用这些 RAW 参数后的图像。
(21:40)ProRAW 的默认观感由 DNG 标签驱动。要取线性场景参考数据,需要关掉默认观感相关输入,再读取 outputImage。
import CoreImage
func linearSceneReferredImage(url: URL) -> CIImage? {
let rawFilter = CIRAWFilter(imageURL: url)
rawFilter.baselineExposure = 0.0
rawFilter.shadowBias = 0.0
rawFilter.boostAmount = 0.0
rawFilter.localToneMapAmount = 0.0
rawFilter.isGamutMappingEnabled = false
return rawFilter.outputImage
}
关键点:
baselineExposure = 0.0关闭默认基线曝光。shadowBias = 0.0关闭默认阴影偏置。boostAmount = 0.0关闭默认 boost。localToneMapAmount = 0.0关闭本地色调映射。isGamutMappingEnabled = false关闭色域映射。outputImage返回线性场景参考图像,可用于分析或继续输入其他 Core Image 滤镜。
(23:54)编辑完成后,App 可以把 rawFilter.outputImage 写成 8-bit HEIC。session 推荐输出色彩空间使用 Display P3。
import CoreImage
func writeHEIC(
rawFilter: CIRAWFilter,
ciContext: CIContext,
to theURL: URL
) throws {
guard let outputImage = rawFilter.outputImage else {
return
}
try ciContext.writeHEIFRepresentation(
of: outputImage,
to: theURL,
format: .RGBA8,
colorSpace: CGColorSpace(name: CGColorSpace.displayP3)!,
options: [:]
)
}
关键点:
rawFilter.outputImage取编辑后的 RAW 输出。guard在没有输出图像时直接返回。writeHEIFRepresentation写出 HEIC 文件。format: .RGBA8使用 8-bit RGBA 输出格式。CGColorSpace.displayP3使用 Display P3 色彩空间。options: [:]保持默认写出选项。
(25:13)在支持 Extended Dynamic Range(扩展动态范围,EDR)的 Mac 显示器上,Apple 建议用 MTKView 子类显示 ProRAW。视图使用 rgba16Float,CAMetalLayer 打开 EDR 内容,再把 rawFilter.extendedDynamicRangeAmount 设为 1。
import CoreImage
import MetalKit
class MyView: MTKView {
var context: CIContext
var commandQueue: MTLCommandQueue
init(frame frameRect: CGRect, device: MTLDevice, context: CIContext, commandQueue: MTLCommandQueue) {
self.context = context
self.commandQueue = commandQueue
super.init(frame: frameRect, device: device)
colorPixelFormat = MTLPixelFormat.rgba16Float
if let caml = layer as? CAMetalLayer {
caml.wantsExtendedDynamicRangeContent = true
}
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func prepareForEDRDisplay(rawFilter: CIRAWFilter) {
rawFilter.extendedDynamicRangeAmount = 1.0
}
}
关键点:
MTKView适合把 Core Image 输出交给 Metal 渲染。CIContext负责 Core Image 渲染。MTLCommandQueue负责提交 Metal 命令。colorPixelFormat = .rgba16Float让视图使用半浮点像素格式。wantsExtendedDynamicRangeContent = true允许 Metal layer 显示 EDR 内容。extendedDynamicRangeAmount = 1.0请求 RAW 滤镜输出 EDR 版本。
核心启发
-
做什么:做一个面向摄影师的 ProRAW 相机。 为什么值得做:AVFoundation 可以直接请求 ProRAW DNG,并嵌入全尺寸 JPEG 预览。 怎么开始:用
.photopreset 配置AVCaptureSession,启用photoOutput.isAppleProRAWEnabled,选择isAppleProRAWPixelFormat返回 true 的像素格式,再调用capturePhoto(with:delegate:)。 -
做什么:做一个 RAW 图库整理工具,自动找出用户照片库里的 RAW 文件。 为什么值得做:iOS 15 的
.smartAlbumRAW能直接定位带 RAW 资源的资产。 怎么开始:用PHAssetCollection.fetchAssetCollections(with:subtype:options:)获取 RAW 智能相册,再用PHAssetResource.assetResources(for:)和UTType.rawImage找到 DNG 数据。 -
做什么:做一个轻量 ProRAW 调色面板。 为什么值得做:
CIRAWFilter暴露了曝光、色温、色调、锐化、本地色调映射等常用调整属性。 怎么开始:从文件 URL 创建CIRAWFilter(imageURL:),把滑杆值映射到exposure、neutralTemperature、neutralTint、sharpnessAmount、localToneMapAmount,实时显示outputImage。 -
做什么:做一个高动态范围分析工具。 为什么值得做:关闭默认观感输入后,可以拿到线性场景参考图像,用于直方图或亮度统计。 怎么开始:把
baselineExposure、shadowBias、boostAmount、localToneMapAmount设为 0,关闭isGamutMappingEnabled,再把outputImage传给CIFilter.areaHistogram()。 -
做什么:做一个 Mac 上的 EDR ProRAW 查看器。 为什么值得做:ProRAW 的动态范围可以通过 MetalKit View 在支持 EDR 的 Mac 显示器上呈现。 怎么开始:创建
MTKView子类,设置colorPixelFormat = .rgba16Float,对CAMetalLayer打开wantsExtendedDynamicRangeContent,渲染前设置rawFilter.extendedDynamicRangeAmount = 1.0。
关联 Session
- Capture high-quality photos using video formats — 讲解如何用 AVCapture API 选择照片质量和采集格式,适合继续理解 ProRAW 的拍摄前置配置。
- What’s new in camera capture — 介绍 2021 年相机采集更新,补充 AVFoundation capture 的整体变化。
- Explore Core Image kernel improvements — 讲解 Core Image kernel 的改进,适合把 ProRAW 编辑继续扩展到自定义滤镜。
- Explore HDR rendering with EDR — 深入 EDR 渲染管线,适合继续实现 ProRAW 的高动态范围显示。
- Improve access to Photos in your app — 讲 Photos 访问方式更新,适合和 PhotoKit RAW 资源读取一起使用。
评论
GitHub Issues · utterances