ARKitで画像検出から画像トラッキングへ書き換えるのって簡単だなと思った話

# はじめに
この投稿は[AR Advent Calendar 2018](https://adventar.org/calendars/2956?fbclid=IwAR182Iikn6JsCTH87KaNEvPLrqPT5feUU5NfPwtCvr9sOOeGHRMTCOvU-8g) 21日目の記事です！

21日目は社内でARギルドというARアプリを開発する活動をしている（弊社のギルド制度については[コチラ](https://www.wantedly.com/companies/abema/post_articles/114103)）iOSエンジニアの[cokaholic](https://github.com/cokaholic)が担当します。

今回はARKitにおける画像検出と画像トラッキングについてざっくりと解説し、画像検出から画像トラッキングへ書き換える方法ってこんなにも簡単なんだなって思った話について話します。

# ARKitの機能の変遷

まずは「画像検出」と「画像トラッキング」の両者がARKitに追加されるまでの流れについて話します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/46227/b35e70a1-6ee1-4b8a-1963-d031916d9bbc.png)
>[Apple WWDC2017 - Introducing ARKit: Augmented Reality for iOSより](https://developer.apple.com/videos/play/wwdc2017/602/)

## ARKit 1.0

ARKitは2018年12月現在、version 2.0までありますので、WWDC 2017時点で発表されたARKitのことをここではARKit 1.0と呼称することにします。
2017年に初めて発表されたARKit 1.0では、以下のような特徴があることが発表されました。

- 早く、安定したモーショントラッキング
- 基本的な境界での平面推定
- 環境光推定
- スケール推定
- UnityやUnreal Engine、Appleの3DフレームワークであるSceneKitをサポート
- Xcodeのテンプレートを用意
- 端末内のカメラ、CPU/GPU、モーションセンサーでAR表現を実現

## ARKit 1.5

その後、2018年3月29日にリリースされた「iOS 11.3」へのアップデートでARKitはversion 1.5を迎えます。
ARKit 1.5では、以下のような新機能の追加とパフォーマンスの向上が行われました。

- 壁などの垂直面の検出機能を追加
- **既知の2次元画像の検出機能を追加**
- 円形テーブルのようなイレギュラーな形をした平面へのマッピング精度の向上
- カメラで映し出される現実世界の解像度を50%向上し、オートフォーカス機能を追加

この時点で「画像検出」機能がARKitに追加されました。

## ARKit 2.0

そして、2018年9月18日。2018年6月にWWDC 2018で発表され、新型のiPhone XS, iPhone XS Max, iPhone XRの発表後に満を持してリリースされた「iOS 12」でARKitはversion 2.0を迎えます。
ARKit 2.0では、以下のような大幅な新機能の追加が行われました。

- 現実世界で検出した特徴点の永続化とその復元機能を追加
- オブジェクトの周りの環境をテクスチャとして写し込ませることができる機能を追加
- **既知の２次元画像のトラッキング機能を追加**
- 3次元の物体をスキャンし、スキャンした3次元オブジェクトを検出できる機能を追加
- フェイストラッキングに目線と舌をトラッキングできる機能を追加

ここでついに、「画像トラッキング」機能がARKitに追加されました。

# ARKit 1.5の「画像検出」とARKit 2.0の「画像トラッキング」の違い

この両者の違いについてですが、ARKit 1.5までの「画像検出」では、画像を検出することはできても、検出された画像側を動かした場合に、**検出位置が固定されたままになり、動かされた画像に検出位置が追随していくことはできませんでした**。
しかし、ARKit 2.0で追加された「画像トラッキング」では、検出された画像側を動かされた場合でも、**検出位置が動かされた画像に追随していくことができるようになりました**。
これが両者の大きな違いです。
また、このテーマについては、@shu223さんの以下の記事がとても参考になりますので、おすすめです。

[ARKit 2.0の画像トラッキングとARKit 1.5の画像検出の違い - Qiita](https://qiita.com/shu223/items/f01b4dc00ceb915b0e61)

# ARKitにおける画像検出

次はARKit 1.5の「画像検出」を行うまでの流れをざっくりと説明していきます。
（Xcodeで「Augmented Reality App」テンプレートとかでARKitのベース部分は用意している前提）

## 1. 検出するための画像を追加するグループを作成する

まずは、Assets.xcassetsを開き、+メニューから**New AR Resource Group**を選択し、**AR Resources**グループを作成しましょう。

<img src="https://qiita-image-store.s3.amazonaws.com/0/46227/2cdcfe05-1fbb-e88c-f945-ccddf25fa771.png" width=500>

## 2. 検出するための画像をグループに追加する

次に、検知させるための画像を「AR Resources」グループ内に ドラッグ&ドロップで追加します。
サンプルとして、私が最近ハマっているバーチャルユーチューバーの田中ヒメちゃんの画像と(株)アクトゼロさんが提供している商用フリーの昔の少女マンガ風イラスト画像を追加してみました。

<img width="800" alt="スクリーンショット 2018-12-21 21.46.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/46227/9d5b2dcc-d5b8-ad0e-04c1-8fd06c89cad0.png">

そして、追加した画像にはそれぞれインスペクターから、検出する画像の現実世界でのサイズを入力してください。

<img src="https://qiita-image-store.s3.amazonaws.com/0/46227/13fce256-7a6e-a5c1-2185-8c443a761e2f.png" width=400>

検出する画像の現実世界でのサイズを入力してもエラーが消えない場合は、 その画像が画像検出に向いていない理由が表示されます。
こうなった場合は、エラー内容に準拠した別の画像を用意しましょう。

<img src="https://qiita-image-store.s3.amazonaws.com/0/46227/99259238-8662-98d8-ee22-15e0833b1533.png" width=800>

### Tips: 画像検出に適した画像について

実際に入れてみたらわかるのですが、結構判定はシビアでした。
背景が白っぽい画像とかはほぼうまくいかなさそうです。
画像検出に適した画像と適さない画像の違いは主に以下の画像の通りです。

<img width="800" alt="スクリーンショット 2018-12-21 21.53.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/46227/fcf41e51-3ed4-c286-03ab-facd63657577.png">

## 3. 検出画像を ARWorldTrackingConfiguration の detectionImages に追加する

検出させるための画像を「AR Resources」グループ内に追加したら、グループ名である「AR Resources」から `referenceImages: Set<ARReferenceImage>` のインスタンスを生成します。
そして、`configuration: ARWorldTrackingConfiguration` の `detectionImages` に生成した `referenceImages` をセットすると、画像を検出できるようになります。

<img width="637" alt="スクリーンショット 2018-12-21 22.00.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/46227/f981ee8b-2771-d4fb-1e49-a1f764d032af.png">

画像の検出は `ARSCNViewDelegate` の `func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)` メソッド内にて行います。
検出した画像の名前によって、どの画像が検出されたかがわかるため、画像ごとに反応を変えることもできます。

<img width="653" alt="スクリーンショット 2018-12-21 22.05.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/46227/e8f9619a-db75-dd4a-970b-095ff2ede279.png">

# ARKitにおける画像トラッキング

さて、次は先ほどの画像検出のコードからARKit 2.0で追加された画像トラッキングに書き換える方法について説明していきます。

## 1. configuration の変更

以下の画像のように、先ほどの[ARKitにおける画像検出の章の第3節](https://qiita.com/cokaholic/items/8fa2bd6105ff3961b55d#3-%E6%A4%9C%E5%87%BA%E7%94%BB%E5%83%8F%E3%82%92-arworldtrackingconfiguration-%E3%81%AE-detectionimages-%E3%81%AB%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B)で解説した`detectionImages`をセットした`configuration`を`ARWorldTrackingConfiguration`から`ARImageTrackingConfiguration`へ変更し、`configuration`へ`referenceImages`をセットするプロパティを`detectionImages`から`trackingImages`へ変更するだけで、なんと、画像トラッキングに変わります。超簡単ですね。半端ないって。

<img width="635" alt="スクリーンショット 2018-12-21 22.13.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/46227/90a0b153-a164-51b4-699b-bcabab950b7f.png">

画像の検出は先ほどと同じく、 `ARSCNViewDelegate` の `func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)` メソッド内にて行います。

そして、検出した画像に追随するためのアンカーの更新はアンカーが変化するたびに呼び出される `ARSCNViewDelegate` の `func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor)` メソッド内で行うことができます。
このとき、お互いのメソッドで検出した画像それぞれを識別するためには、引数で受け取っている `anchor: ARAnchor` の `anchor.identifier` を使用するとそれぞれで識別することができます。

# 最後に

今回の記事はここまでとなります。
今年はARKit 2.0の大幅な機能追加によって、ARKitは大いに盛り上がった一年だったなと思います。
来年はARKit 3.0が来て、オクルージョン処理機能の追加とか画像検出のための画像を動的に追加できる機能とか追加されると嬉しいなぁと思いながら、日々ARKitをいじいじして楽しんでおります。
あ、まだ間に合うので、クリスマスプレゼントとしてARKit 3.0を公開してくれてもいいんですよ、Appleさん？
あれ？返事がない。そうか、[23日~27日まではお休み](https://developer.apple.com/news/?id=11272018&1543349202)でしたね。
無茶言ってすみませんでした〜

以上です！
最後まで読んでいただき、ありがとうございました！