当我们按下快门之后,相机才创建图形前会进行一系列的操作。包括调整白平衡,降噪,调色,锐化,然后压缩成一张高质量的图形后用于存储和显示。

但是如果我们推迟这种操作,只存储传感器获取的原始数据,在后期就可以获得更多的处理空间。

拍摄 RAW 照片的过程

在 iOS 中拍摄 RAW 时必须先选择以 RAW 文件保存,并且会占用更多的存储空间。 但是由于 RAW 内容无法直接显示,所以在拍摄时会同时保存一张 JPEG/HEIF/HEVC 的图片,然后将 RAW 数据保存在其中。

Capturing Photos in RAW Format 详细的说明了如何将拍摄的照片保存为 RAW 格式。

  1. 首先要为 AVCapturePhotoOutput 照片输出过程中增加以 RAW 格式和普通照片格式的选项;
  2. 由于会输出两张照片,所以 AVCapturePhotoOutputphotoOutput(_:didFinishProcessingPhoto:error:) 会被调用两次 (RAW 和 HEVC 各一次)。 每次都可以通过 photo.fileDataRepresentation() 获取到照片的数据 (Data类型)。
  3. 最后我们需要将两次的数据存入一个 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

显示相册中的流程大致是这样的:

com.adobe.raw-image

所以代码上大体是这样的

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 {

                    }
                })
相关文章