iPhone 7 Plusのデュアルカメラをアプリで使用する

iPhone 7 Plusのデュアルカメラを使ったアプリ開発のメモです。iPhone 7 Plusの実機（iOS 10.1）とXcode8.1を使用しています。

iOS 10.1から標準カメラアプリに「ポートレート」モード（被写界深度エフェクト）が搭載されましたが、これに相当するAPIは今のところ提供されていないようです。しかし、デュアルカメラをキャプチャデバイスに使うと、2つのレンズから得られた画像のうち最適なほう、もしくは可能であれば合成したアウトプットが自動的に得られる仕組みになっており、画質向上の恩恵は受けられるようです。ドキュメントでは次のように説明されています。

>Choosing the dual camera capture device provides behavior similar to the built-in Camera app—the system automatically chooses which camera to use during capture, and can combine data from both cameras to improve capture output.

#iOS 10の変更点への対応
カメラや写真ライブラリのアクセス許可ダイアログに表示する文言を、info.plist で指定します。

```InfoPlist.strings

"NSPhotoLibraryUsageDescription" = "このアプリは写真を表示・保存するために写真ライブラリを使う必要があります。";
"NSCameraUsageDescription" = "このアプリは写真を撮影するためにカメラを使う必要があります。";

```

#カメラデバイスの取得
iOS 9までは、カメラデバイスの取得には AVCaptureDevice.devices() または AVCaptureDevice.defaultDevice(withMediaType:) を使用していましたが、iOS 10でデュアルカメラを使う場合は新しいAPIを使う必要があります。devicesメソッドはdeprecatedになり、また**defaultDevice(withMediaType:)ではデュアルカメラでなく広角レンズ単体のほうが常に選ばれてしまう**ためです。

>When you use this method to request a camera (using the AVMediaTypeVideo media type), the returned device is always of the builtInWideAngleCamera device type. To request other device types, use the defaultDevice(withDeviceType:mediaType:position:) method instead.

次のような感じで、OSのバージョンで振り分けつつデバイスを取得します。**AVCaptureDeviceDiscoverySession** が新しいAPIで、deviceTypes に渡している **AVCaptureDeviceType.builtInDuoCamera** がデュアルカメラを指しています。

```swift
var targetDevice: AVCaptureDevice?
        
if #available(iOS 10.0, *) {
    let discoverySession = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInDuoCamera],
                                                           mediaType: AVMediaTypeVideo, 
                                                           position: .back)
    for device in discoverySession.devices {
        targetDevice = device
    }
}
else {
    targetDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
}
```

広角レンズやズームレンズをそれぞれ取得するときは、**.builtInWideAngleCamera**、**.builtInTelephotoCamera**を指定します。
また、デバイスの取得にはAVCaptureDeviceDiscoverySessionではなく、AVCaptureDevice.defaultDevice(withDeviceType:mediaType:position:) を使用することもできます。

#アウトプットの追加
AVCaptureStillImageOutputがdeprecatedになったため、アウトプットの作成には **AVCapturePhotoOutput**を使用します。

```swift
if #available(iOS 10.0, *) {
    let output = AVCapturePhotoOutput()
    output.isHighResolutionCaptureEnabled = true
    session?.addOutput(output)
}
else {
    let output = AVCaptureStillImageOutput()
    session?.addOutput(output)
}
```

#キャプチャの実行
撮影時の処理は変更しなければならない箇所が多いです。例えば、AVCaptureDeviceインスタンスのflashModeプロパティなどもiOS 10でdeprecatedになっています。撮影の実行にも新しいAPIであるAVCapturePhotoOutputのcapturePhotoメソッドを使いますが、これに渡すAVCapturePhotoSettingsでフラッシュなどの撮影関連の設定ができるようになっています。

```swift
let settings = AVCapturePhotoSettings()
settings.isHighResolutionPhotoEnabled = true
settings.flashMode = .off
targetPhotoOutput?.capturePhoto(with: settings, delegate: self)
```

撮影プロセスが完了すると次のdelegateメソッドが呼ばれます。これで得られたCMSampleBufferを使って、従来のように写真やメタデータの処理をします。

```swift 
func capture(_ captureOutput: AVCapturePhotoOutput, 
             didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, 
             previewPhotoSampleBuffer: CMSampleBuffer?, 
             resolvedSettings: AVCaptureResolvedPhotoSettings, 
             bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
    // do something
}
```

変更点が多いため、カメラのコントロールや撮影の処理をしているコードは、iOS 9以下 / iOS 10で個別のクラスに分けるなどしたほうがよさそうです。

#撮影結果

デュアルカメラによるアウトプットの最適化を確認するのは難しかったりします。一般的な撮影ではほとんどの場面で広角レンズのみが使われていそうですし、フォーカスやその他の撮影条件によって変わってくる部分も大きいので、builtInWideAngleCameraとの違いに確信が持てないというのが正直な感想です。
ズームレンズ側が明確に使用されることを期待してズーム倍率を上げてみたところ、僅かに解像感に違いが出た気がします。下の写真はAVCaptureDeviceのvideoZoomFactorを8.0にして撮影したものです。Gキーのあたりにフォーカスを合わせて撮影しています。

|builtInDuoCamera|builtInWideAngleCamera|
|:------------:|:------------:|
|![keys_duo.jpg](https://qiita-image-store.s3.amazonaws.com/0/9042/d7587c2e-4ee0-52a4-17df-85499fe7dac1.jpeg)|![keys_wide.jpg](https://qiita-image-store.s3.amazonaws.com/0/9042/88f03374-8512-b815-cab0-81a5c3ae7b52.jpeg)|



#制約
デュアルカメラを使うと、広角レンズの場合にはなかった機能制限が発生します。次のようにドキュメントで説明されています。
>When you use the dual camera capture device, **Optical Image Stabilization (OIS), RAW Photo Capture, High Frame Rate (HFR) / Slow Motion Video, and most manual controls are not available**. To use these features, specifically select either the wide or telephoto capture device. (For OIS, specifically select the wide-angle camera; the telephoto camera does not support optical image stabilization.)

AVCaptureDeviceType.builtInDuoCamera のドキュメントにも説明があります。

>Devices of this type do not support:
> · Custom exposure modes and manual exposure bracketing.
> · Locking focus with a specific lens position.
> · Locking white balance with a specific white balance gains setting.

テストとしてAVCaptureDeviceのisLockingFocusWithCustomLensPositionSupportedプロパティの変化をAVCaptureDeviceTypeごとにチェックしてみたところ、builtInDuoCameraでは無効になっているのが確認できました。