iOS 中 RAW 照片的拍摄和显示
date
Jul 12, 2020
slug
raw-in-ios
status
Published
tags
summary
type
Post
当我们按下快门之后,相机才创建图形前会进行一系列的操作。包括调整白平衡,降噪,调色,锐化,然后压缩成一张高质量的图形后用于存储和显示。
但是如果我们推迟这种操作,只存储传感器获取的原始数据,在后期就可以获得更多的处理空间。
拍摄 RAW 照片的过程
在 iOS 中拍摄 RAW 时必须先选择以 RAW 文件保存,并且会占用更多的存储空间。 但是由于 RAW 内容无法直接显示,所以在拍摄时会同时保存一张
JPEG/HEIF/HEVC
的图片,然后将 RAW 数据保存在其中。Capturing Photos in RAW Format 详细的说明了如何将拍摄的照片保存为 RAW 格式。
- 首先要为
AVCapturePhotoOutput
照片输出过程中增加以 RAW 格式和普通照片格式的选项;
- 由于会输出两张照片,所以
AVCapturePhotoOutput
的photoOutput(_:didFinishProcessingPhoto:error:)
会被调用两次 (RAW 和 HEVC 各一次)。 每次都可以通过photo.fileDataRepresentation()
获取到照片的数据 (Data类型)。
- 最后我们需要将两次的数据存入一个
PHAsset
。
官方文档上,是将 RAW 的格式内容先保存到一个临时文件上,然后等待全部照片都获取完成后,使用
PHAssetCreationRequest
创建一个 PHAsset。他包含两个 Resource, 一个是压缩后的照片数据(JPEG/HEIF/HEVC
之类的格式),另一个是 RAW 数据。PHPhotoLibrary.shared().performChanges({
// Add the compressed (HEIF) data as the main resource for the Photos asset.
let creationRequest = PHAssetCreationRequest.forAsset()
creationRequest.addResource(with: .photo, data: compressedData, options: nil)
// Add the RAW (DNG) file as an altenate resource.
let options = PHAssetResourceCreationOptions()
options.shouldMoveFile = true
creationRequest.addResource(with: .alternatePhoto, fileURL: rawURL, options: options)
}, completionHandler: self.handlePhotoLibraryError)
完整的代码请参考前面的官方文档。
显示 RAW 照片
在 iOS 显示 RAW 比存储他更加麻烦,因为 iOS 的系统相册根本没法显示出来。
在拍摄的时候, RAW 数据是以
alternatePhoto
保存在 PHAsset 中的,所以系统相册显示的其实是那张压缩后的照片(JPEG/HEIF/HEVC)。 从系统相册中我们甚至都不知道这是一张以 RAW 格式拍摄的照片。我们可以将他理解为未完成显影的胶片。将 RAW 显示出来的过程就好像是显影的过程。所以,如何显示 RAW 取决于解析数据的程序。
在 macOS 上显示 RAW 照片
要显示和获取到 RAW 文件的最方便的方法是在 macOS 上的相册 App 中打开这张相片。 他会在右上角显示
J
或者 R
。 前者表示现在显示的是 JPEG格式,后者表示现在正在显示的是 RAW
格式。 在 macOS 的系统相册中可以对着这张照片点击右键选择将 JPEG 作为原片
或者 将 RAW 作为原片
。 并且可以将此照片作为 dng
格式导出。 然后就可以用后期软件打开进行编辑了。在 iOS 上显示 RAW 照片
虽然系统相册无法显示 RAW 格式,但是很多第三方的 App 是可以正确的现实和编辑 RAW 照片的,比如知名相机 App: Halide。 我自己开发的手动相机 App (Clara) 也可以正确的拍摄和显示 RAW 格式照片。
在 iOS 中,正确的现实 RAW 数据只有一个方法,即使使用
CIFilter
。显示相册中的流程大致是这样的:
- 获取 PHAsset, 每个 PHAsset 表示一张照片;
- 使用 PHCachingImageManager 获取这个 PHAsset 的内容,他的数据包含在 Data 类型中,同时还可以获取到对应的 UTI (用于表示这个数据的格式),如果这个照片是 RAW 格式,他会是
com.adobe.raw-image
;
- 用 CIFilter 来解析 RAW 数据,输出用于显示的图片;
所以代码上大体是这样的
self.imageManager.requestImageDataAndOrientation(for: asset, options: rawImageRequestOptions, resultHandler: { [weak self](imageData, uti, orientation, info) in
guard let _self = self else {
return
}
let options = [CIRAWFilterOption(rawValue: kCGImageSourceTypeIdentifierHint as String) : uti!]
let filter = CIFilter(imageData: imageData, options: options)
if let rawCiImage = filter?.outputImage {
// 获取到用于输出的图片
}
else {
}
})