【AVFoundation】Apple ProRAW/RAWイメージを撮影する


iOS 14.3以降を搭載したiPhone 12 Pro、iPhone 12 Pro MaxでApple ProRAWを撮影することができるようになりました。AVCaptureSession周りを構築して通常の写真撮影を行う方法に関する記事は既にたくさんありますので、ここではApple ProRAW/RAWを撮影するための設定と撮影/保存のハンドリング部分に絞って書きます。

AVCapturePhotoOutputの設定

Apple ProRAWを撮影するためにはAVCaptureSessionに追加したAVCapturePhotoOutputのisAppleProRAWEnabledを有効にする必要があります。Apple ProRAWではなく、通常のRAWイメージを撮影する時はこちらの項目の処理は不要です。
また、iPhone 12 Pro/iPhone 12 Pro Max以外の機種だと、isAppleProRAWSupportedはfalseになります。

   photoOutput.isAppleProRAWEnabled = photoOutput.isAppleProRAWSupported

AVCapturePhotoSettingsの設定

Apple ProRAW/RAWイメージを撮影してフォトライブラリに保存する場合は、「Apple ProRAW/RAWイメージ」 + 「加工済みイメージ」を一つのアセットとして保存する必要があります(以下の例だと「Apple ProRAW/RAWイメージ」 +「JPEGイメージ」)。そのため、AVCapturePhotoSettingsに「Apple ProRAW/RAWイメージ」と「加工済みイメージ」のフォーマットをそれぞれ指定する必要があります。

   let query = photoOutput.isAppleProRAWEnabled ?
       { AVCapturePhotoOutput.isAppleProRAWPixelFormat($0) } :
       { AVCapturePhotoOutput.isBayerRAWPixelFormat($0) }

   // Apple ProRAW/RAWイメージのフォーマットを指定
   guard let rawFormat =
           photoOutput.availableRawPhotoPixelFormatTypes.first(where: query) else {
       fatalError("No RAW format found.")
   }

   // 加工済みイメージのフォーマットを指定
   let processedFormat = [AVVideoCodecKey: AVVideoCodecType.jpeg]

   // 撮影するイメージのフォーマットをAVCapturePhotoSettingsに指定
   let photoSettings = AVCapturePhotoSettings(rawPixelFormatType: rawFormat,
                                              processedFormat: processedFormat)

   photoOutput.capturePhoto(with: photoSettings, delegate: self)

Apple ProRAW/RAWイメージの撮影

撮影を開始すると以下のデリゲートメソッドが2回コールされます。一度目のコールではApple ProRAW/RAWイメージが配信されるタイミング、2回目は加工済みイメージが配信されるタイミングでコールされます。ここではApple ProRAW/RAWイメージを一時的にtmpディレクトリに保存しておき、加工済みイメージを変数で保持しています。

   func photoOutput(_ output: AVCapturePhotoOutput,
                    didFinishProcessingPhoto photo: AVCapturePhoto,
                    error: Error?) {
       // 配信されるイメージデータの種類を判定する
       if photo.isRawPhoto {
           self.rawImageFileURL = self.makeUniqueTempFileURL(extension: "dng")
           do {
               // Apple ProRAW/RAWイメージをtmpディレクトリに保存
               try photo.fileDataRepresentation()!.write(to: rawImageFileURL!)
           } catch {
               fatalError("couldn't write DNG file to URL")
           }
       } else {
           // 加工済みイメージを変数で保持
           self.compressedData = photoData
       }
   }

   /**
    @brief Apple ProRAW/イメージファイルの保存先URLを生成する
    */
   private func makeUniqueDNGFileURL() -> URL {
       let tempDir = FileManager.default.temporaryDirectory
       let fileName = ProcessInfo.processInfo.globallyUniqueString
       return tempDir.appendingPathComponent(fileName).appendingPathExtension("dng")
   }

Apple ProRAW/RAWイメージの保存

撮影が完了したタイミングで以下のデリゲートメソッドがコールされます。「AVCapturePhotoSettingsの設定」で記載したように、Apple ProRAW/RAWイメージを撮影してフォトライブラリに保存する場合は「Apple ProRAW/RAWイメージ」 + 「加工済みイメージ」を一つのアセットとして保存する必要があります。加工済みイメージをメインイメージリソース、Apple ProRAW/RAWイメージを代替イメージリソースとしてアセットを保存するとApple ProRAW/RAWイメージの保存が完了します。

    func photoOutput(_ output: AVCapturePhotoOutput,
                     didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings,
                     error: Error?) {       
        PHPhotoLibrary.requestAuthorization(for: .addOnly) { status in

            guard status == .authorized else { return }

            PHPhotoLibrary.shared().performChanges {
                // 加工済みイメージをメインイメージリソースとして保存
                let creationRequest = PHAssetCreationRequest.forAsset()
                creationRequest.addResource(with: .photo, data: compressedData, options: nil)

                // Apple ProRAW/RAWイメージを代替イメージリソースとして保存
                let options = PHAssetResourceCreationOptions()
                options.shouldMoveFile = true
                creationRequest.addResource(with: .alternatePhoto, fileURL: rawFileURL, options: options)

            } completionHandler: { success, error in
            }
        }
    }

写真アプリを起動して撮影した写真を選択した時、左上に「JPEG + RAW」と表示されていれば撮影成功です🎉

以上になります。
それでは