ARKit 4のLiDAR Depth API

iOSデバイスで初めてLiDARスキャナを搭載したiPad Proの発売と同時にリリースされたARKit 3.5では、LiDARを利用してreconstractionした3Dメッシュは得られたが、その計算に用いられているはずの**デプス（深度）データにはアクセスできなかった**。

![rectangle_large_type_2_8e10a807ae6bf848e5d9f98228be7602.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3180/db1dff55-a184-98b2-085f-7fe73ca535e8.png)

その後発表された**ARKit 4 / iOS 14（/ iPadOS 14）でようやく、LiDARで計測したデプスを取得できるように**なった。従来のデプスと区別するため、「シーンデプス（Scene Depth）」とも呼ばれているようだ。

## LiDARデプスの取得方法

LiDAR由来のデプスデータは、`ARWorldTrackingConfiguration`利用時、`frameSemantics`プロパティに`.sceneDepth`オプションを指定することで取得可能になる。

```swift
let session = ARSession()
let configuration = ARWorldTrackingConfiguration()

// sceneDepthオプションが利用可能かをチェック
if type(of: configuration).supportsFrameSemantics(.sceneDepth) {
   // Activate sceneDepth
   configuration.frameSemantics = .sceneDepth
}
session.run(configuration)
```

ARFrameに新たに追加された`sceneDepth`プロパティより、LiDARで計測したデプスデータを取得できる。

```swift
func session(_ session: ARSession, didUpdate frame: ARFrame) {
   guard let depthData = frame.sceneDepth else { return }
   // Use depth data
}
```

## 利用可能なコンフィギュレーション

`ARConfiguration`プロトコルに定義されている`frameSemantics`プロパティに`sceneDepth`を設定するので、API的には利用可能ではあるが、APIリファレンスの`sceneDepth`の項にある記述を読む限り、ワールドトラッキング以外のコンフィギュレーションでは利用できないっぽい。


https://developer.apple.com/documentation/arkit/arconfiguration/framesemantics/3516902-scenedepth


> If you enable this option on a world-tracking configuration's frameSemantics, ARKit includes depth information for regions of the current frame's capturedImage. The world-tracking configuration exposes depth information in the sceneDepth property that it updates every frame.

## ハードウェア的な制約

同じく`sceneDepth`のリファレンスページより。

> ARKit supports scene depth only on LiDAR-capable devices


チップがA12以上とかの条件はないようだ。（LiDARが載る→必然的に高性能チップとなるからだろうか）

## 従来のデプスデータとの違い

これまでも（特定の条件下で）ARKit利用時にデプスデータにアクセスすることは可能だった。`ARFrame`の`capturedDepthData`プロパティ、`estimatedDepth`が従来からあったデプスAPI。今回追加されたデプスAPIと何が違うのか。

### デプスデータの種類と取得条件


まずもちろん、デプスデータの種類が違う。そしてそれに伴い、取得できる条件も違う。`capturedDepthData`はTrue-Depthカメラ由来のデプスデータで、フェイストラッキング利用時のみ取得できる。

`estimatedDepth`は`frameSemantics`に`personSegmentationWithDepth`オプションを指定した場合に取得できるもので、デュアルカメラやTrue-Depthカメラ由来のデプスではなく、そのプロパティ名からわかるとおり推定デプスデータである。要A12チップ以上。

`sceneDepth`は上述した通り、`frameSemantics`に`sceneDepth`を指定した場合に取得でき、ARコンフィギュレーションとしてはワールドトラッキングのみ、デバイスとしてLiDARを搭載している必要がある。


### フレームレート／生成アルゴリズムの違い

`capturedDepthData`はTrue-Depthカメラ由来のデプスデータであるため、`ARFrame`のカラーデータ（`capturedImage`プロパティ）よりも更新頻度が低く、デプスを用いたエフェクトなどをかけていると、被写体（フェイストラッキングなので自分）が速く動いた場合に追従できないことがあった。


一方で`estimatedDepth`は機械学習ベースで推定されるもので、A12以降の高性能なチップを条件としているため、カメラフレームと同等のフレームレートで更新される。

`sceneDepth`もLiDARで取得した深度データと広角カメラから取得したカラーデータをベースに機械学習アルゴリズムを利用して生成される。60fpsで動作する、つまりこちらもARFrameが得られるたびに更新されている。


（WWDC 2020の"Explore ARKit 4"セッションより）

> The colored RGB image from the wide-angle camera and the depth ratings from the LiDAR scanner are fused together using advanced machine learning algorithms to create a dense depth map that is exposed through the API.
（広角カメラからのカラーRGB画像とLiDARスキャナからの深度定格が、高度な機械学習アルゴリズムを使用して融合され、APIを通じて公開される高密度の深度マップが作成されます。）

> This operation runs at 60 times per second with the depth map available on every AR frame.
（この操作は毎秒60回実行され、ARフレームごとに深度マップが利用可能になります。）

### 型の違い

`capturedDepthDataはAVDepthData`型、`estimatedDepthはCVPixelBuffer`型、そして`sceneDepth`は`ARDepthData`型で得られる。


## ARDepthData

上述したとおり`sceneDepth`プロパティから得られるLiDAR由来のデプスデータは`ARDepthData`型で得られる。こちらはiOS 14で追加された新クラス。

https://developer.apple.com/documentation/arkit/ardepthdata?changes=latest_major

これをうまく利用した公式サンプルが出ているので、詳しい解説はそのサンプルを読みつつ別記事で書く。

## LiDARの精度／計測可能な距離は？

WWDC 2020ではないが、ARKit 3.5リリース時に、"Advanced Scene Understanding in AR"というTech Talkが公開されており、その中でLiDARの精度について言及があった。

> The new iPad Pro comes equipped with a LiDAR Scanner. This is used to determine distance by measuring at nanosecond speeds how long it takes for light to reach an object in front of you and reflect back. This is effective up to five meters away and operates both indoors and outdoors. 
（新しいiPad ProにはLiDARスキャナーが搭載されています。これは、目の前の物体に光が到達して反射して戻ってくるまでの時間をナノ秒の速度で測定することで距離を判断するために使用されます。これは5メートル先まで有効で、屋内でも屋外でも使用できます。）


**LiDARで何メートル先まで計測できるのか**、という質問をされたことが何度かあるが、こういうのは公式には言及されない印象だったので、「サンプルで実際に試してみるといいですよ」という回答をしていた。目安にしろ貴重な公式情報ではあるのでメモ。

## personSegmentationWithDepthとシーンデプス

`ARConfiguration`の`frameSemantics`プロパティに`personSegmentationWithDepth`オプションを指定している場合、シーンデプスを取得可能なデバイスであれば、自動的にシーンデプスを得られるらしい。

```swift
let session = ARSession()
let configuration = ARWorldTrackingConfiguration()

// Set required frame semantics
let semantics: ARConfiguration.FrameSemantics = .personSegmentationWithDepth
       
// Check if configuration and device supports the required semantics
if type(of: configuration).supportsFrameSemantics(semantics) {
   // Activate .personSegmentationWithDepth
   configuration.frameSemantics = semantics
}
session.run(configuration)
```

しかも、追加の電力コストもかからないとのこと。

> Additionally if you have an AR app that uses people occlusion feature, and then search the personSegmentationWithDepth frameSemantic, then you will automatically get sceneDepth on devices that support the sceneDepth frameSemantic with no additional power cost to your application.




## smoothedSceneDepth

上述した`sceneDepth`はWWDC 2020開催時点で（iOS 14 beta 1時点で）既に公開されていたが、iOS 14 beta 5で突如`smoothedSceneDepth`なるAPIが追加された。

https://twitter.com/shu223/status/1295968108352479232

このとき（2020年8月）ドキュメントをすぐに見に行ったが詳細は何も書かれてなかった。

今見に行くとちゃんと説明が書かれており、**`sceneDepth`との違い**も明記されている。DeepL利用の日本語訳を併記してここにまとめておく。

### smoothedSceneDepth: ARConfiguration.FrameSemantics

`ARConfiguration.FrameSemantics`の型プロパティ

https://developer.apple.com/documentation/arkit/arconfiguration/framesemantics/3674208-smoothedscenedepth

> An option that provides the distance from the device to real-world objects, averaged across several frames.
　（デバイスから実世界のオブジェクトまでの距離を提供するオプションで、複数のフレームで平均化されます。）


#### Declaration

```swift
static var smoothedSceneDepth: ARConfiguration.FrameSemantics { get }
```

#### Discussion

> Enable this option on a world-tracking configuration (ARWorldTrackingConfiguration) to instruct ARKit to provide your app with the distance between the user’s device and the real-world objects pictured in the frame's capturedImage. ARKit samples this distance using the LiDAR scanner and provides the results through the smoothedSceneDepth property on the session’s currentFrame.
（ワールドトラッキング設定（ARWorldTrackingConfiguration）でこのオプションを有効にすると、ユーザーのデバイスとフレームのキャプチャ画像に写っている現実世界のオブジェクトとの距離をアプリに提供するようにARKitに指示します。ARKit は、LiDAR スキャナーを使用してこの距離をサンプリングし、セッションの currentFrame の smoothedSceneDepth プロパティを通して結果を提供します。）

LiDARスキャナーを使用することがここで明記されている。

> To minimize the difference in LiDAR readings across frames, ARKit processes the data as an average. The averaged readings reduce flickering to create a smoother motion effect when depicting objects with depth, as demonstrated in Creating a Fog Effect Using Scene Depth. Alternatively, to access a discrete LiDAR reading at the instant the framework creates the current frame, use sceneDepth.
（フレーム間の LiDAR 読み取り値の差を最小化するために、ARKit はデータを平均として処理します。平均化された読み取り値は、「シーン深度を使用したフォグ効果の作成」で実証されているように、奥行きのあるオブジェクトを描写する際にフリッカーを低減し、より滑らかなモーション効果を生み出します。また、フレームワークが現在のフレームを作成した瞬間に個別の LiDAR 読み値にアクセスするには、sceneDepth を使用します。）

ここが一番重要。`sceneDepth`との違いが明記されており、メリットや使い分けについても書かれている。

> ARKit supports scene depth only on LiDAR-capable devices, so call supportsFrameSemantics(_:) to ensure device support before attempting to enable scene depth.
（ARKit は、LiDAR 対応デバイスでのみシーン深度をサポートしているので、シーン深度を有効にする前に supportsFrameSemantics(_:) を呼び出してデバイスのサポートを確認してください。）

### smoothedSceneDepth: ARDepthData

ARFrameの`smoothedSceneDepth`プロパティ

https://developer.apple.com/documentation/arkit/arframe/3674209-smoothedscenedepth

> An average of distance measurements between a device's rear camera and real-world objects that creates smoother visuals in an AR experience.
（デバイスの背面カメラと実世界のオブジェクトの間の距離測定の平均値で、AR体験でよりスムーズなビジュアルを作成します。）


#### Declaration

```swift
var smoothedSceneDepth: ARDepthData? { get }
```

#### Discussion

> This property describes the distance between a device's camera and objects or areas in the real world, including ARKit’s confidence in the estimated distance. This is similar to sceneDepth except that the framework smoothes the depth data over time to lessen its frame-to-frame delta.
（このプロパティは、デバイスのカメラと実世界のオブジェクトやエリアとの距離を記述します。これは sceneDepth と似ていますが、フレームワークがフレーム間の差分を減らすために深度データを時間の経過とともに滑らかにします。）


> This property is nil by default. Add the smoothedSceneDepth frame semantic to your configuration’s frameSemantics to instruct the framework to populate this value with ARDepthData captured by the LiDAR scanner.
（このプロパティはデフォルトでは nil です。フレームワークに、LiDAR スキャナによってキャプチャされた ARDepthData でこの値を入力するように指示するために、設定の frameSemantics に smoothedSceneDepth フレームセマンティックを追加します。）

> Call supportsFrameSemantics(_:) on your app’s configuration to support smoothed scene depth on select devices and configurations.
（アプリの設定で supportsFrameSemantics(_:) を呼び出して、選択したデバイスと設定で平滑化されたシーン深度をサポートします。）

## 関連

ARKitの本、iOSデプスの本を書いています。

[実践ARKit - BOOTH](https://shu223.booth.pm/items/1038241)
[Depth in Depth - iOSデプス詳解 - BOOTH](https://shu223.booth.pm/items/1313752)

## アドベントカレンダー

本記事は[iOSアドベントカレンダー](https://qiita.com/advent-calendar/2020/ios)20日目の記事です。2020年12月23日現在、エントリーされていた方が記事をアップされていなかったので、その方に確認の上記事を代理投稿させていただきました。