ARFoundation×CloudVisionAPIで現実空間のオブジェクトにタグ付けする
Play
ARFoundationとCloud Vision APIを組み合わせて、現実空間のオブジェクトを解析してタグ付けしてみました。
4つサンプルのgifがあります。
何をしているのか
調べたらもっと詳しい情報が出てくるので、ざっくりと説明します。
ARFoundation
はUnity公式が作っているARアプリを開発するためのSDKです。
UnityでARを作る際には基本的にはこれを使用することになります。Android
やiOS
などのPlatform固有のARSDKをラップしています。
Cloud Vision API
はGCP
の画像認識サービスです。
REST
などで画像を送信するだけでいい感じに解釈してjson
を返してくれます。文字やランドマークなどいろいろなものを解析できます。今回はその中の複数のオブジェクトを検出するを使いました。
この2つを組み合わせて以下の手順で現実空間のオブジェクトにタグ付けしています。
-
ARFoundation
から現在のカメラ画像を取得 -
Cloud Vision API
のObject Localize
に送信して画像のオブジェクトを分析 - 分析結果からオブジェクトの位置を推定して
Ray
を発射し、衝突判定が存在していたらマーカーを設置
アーキテクチャ図です。かたちある虚無ですがせっかく作ったので載せます。
実装内容
リポジトリはこちら。
動かす場合はREADMEに記載の通り、自前でAPIキーを発行して入れ替えてビルドしてください。
環境
platform | version |
---|---|
Unity | 2020.3.23f1 |
ARFoundation | 4.2.1 |
Cloud Vision | v1 |
実際の細かい処理シーケンス
Assets/SpacialAnalyzer/Scripts/Analyzer/AnalyzeSpatial.csがロジックの全てです。
- カメラ位置からRayを撃って
ARAnchor
を設置 - 画像を取得
- 画像取得時のカメラの
ARAnchor
からの相対位置を保存 -
Cloud Vision API
に画像を送信 - レスポンスを取得
- レスポンスのアノテーションから画面のどの位置にオブジェクトがあるかを計算
- 画像とスクリーンの比率を計算
-
ScreenPointToRay
で計算した比率を元に現在のカメラからオブジェクトに向かってのRayを生成する - 現在のカメラに対するRayの相対位置を計算
- 保存しておいた
ARAnchor
の相対位置から画像取得時のカメラの位置を復元 - 画像取得時のカメラ位置に対してRayの相対位置を適応する
- ARRaycastManagerによってRayを撃ち、オブジェクトの位置を計算する
アノテーションからRayを生成するにはScreenPointToRay
が必要で、ScreenPointToRay
には画像を取得した瞬間のカメラ姿勢が必要で……。
長くて面倒なことになっていますが、要するに「画像を取得した瞬間のカメラ姿勢を保持しておいて、そこから画像のアノテーションに応じたRayを撃つ」というだけです。
ポイント
実装するにあたって遭遇したポイントを書いていきます。
Cloud Vision
RESTがRESTじゃない
公式にcurlでの実行方法が記載されているのですが、GOOGLE_APPLICATION_CREDENTIALS
という環境変数に認証情報が保存されている、かつgcloud
がインストール済みという前提なのでそのままUnityに流用することはできません。
uriにqueryとしてAPIキーを設定することで認証することができます。公式には記載を見つけられなかったのですが、下の記事を参考に突破しました。
(0,0)は左上
オブジェクトが画像のどの位置にあるかは0.0 ~ 1.0で表されます。
が、複数のオブジェクトを検出するの最初にあるアノテーション入りの画像とjson
をよーく見比べるとわかりますが、左上が(0,0)として扱われています。機械学習とか詳しくないのでこれが常識なのかGoogle presentsなのか知らないですがめっちゃびっくりしました。
ARFoundation
画像の向きが違う
上の記事にある通り、ARCameraManager
から取得できる画像は生のデータであり端末の回転は考慮されていません。画像の回転処理なんて自前でやってられないですし、かといって向きがおかしい画像をそのまま送った場合Cloud Vision API
の結果にどんな影響があるかもわからないので、端末の向きを固定することで対処しました。ARアプリで端末の回転ってあんまりしない……しなくない?
画像の縦横比が違う
使っているデバイスの画面は2270×1080ですが、ARFoundation
から取得した画像は640×480です。つまりスマートフォンの画面には映ってないけどカメラが認識している部分が含まれています。具体的に言うと画像の上下は画面に映っている分よりちょっと広めです。幸いにも横幅は画面=画像だったので、そこから比率を計算して縦を合わせました。
今回は自分の端末で動けばいいので力押しできましたが、対象を増やす場合は端末の画面サイズとカメラの環境が多様で吸収がしんどそうな部分です。
この事象は画面外の樹木を認識しているこれとかわかりやすいと思います。
RayがTrackableにHitしない
ARAnchor
を用いることで、カメラが動いてもAR空間に配置したコンテンツの位置を安定させることができます。
ARAnchor
は任意の位置に設置することができますが、Trackable
と関連付けることでより安定します。たぶん。Trackable
というのは、PointCloud
やPlane
など、ARFoundation
が認識するもののベースクラスです。
- Rayを撃つ
-
Trackable
にhit -
Trackable
に関連付けたARAnchor
を配置
というのがARAnchor
をTrackable
に関連付ける基本の流れになります。
……が、使っている端末のカメラの性能がよくないのか、なかなかTrackable
にhitしません。しょうがないので当たらなかったら2m先にTrackable
なしのARAnchor
を配置するようにしました。
画像取得のカメラの姿勢を復元
ScreenPointToRay
を使う場合、画像を取得した瞬間の姿勢のカメラから算出する必要があります。ARだとそんなに端末を動かさないはずですが、ちょっと角度がずれただけで大幅にRayがずれるので、どうしても必要でした。絶対→相対→絶対と繰り返し座標を変換する際、相対座標はひとまず変数名にLocal
と必ず入れることで混乱をしのぎました。
このあたりの座標変換は1ステップ1ステップ分解していけばわかることなのですが、その分解していく作業が大変です。頭の中で考えても厳しいので、素直に絵を描くと楽でした。
改良点
もしこれを発展させるならこんな点かなーと思っています。[1]
Anchorの永続化
レスポンスなどはデータクラスにまとめてシリアライズできるようにしてあります。
CloudAnchorに始まるAnchor
の永続化を行うことで、シリアライズしておいた過去のスキャン情報を復元したり、スキャン情報を共有することができます。
他にも、Immersalとかでスキャン済みの点群データとオブジェクトのタグ付けを組み合わせてどこになにがあるかを表示できるようにするとか。
画質改善による影響
送信している画像はbase64に変換していますが、サイズの関係でjpeg
にしています。他の高画質のフォーマットにしたらCloud Vision API
の認識結果が向上するのかどうかは気になるところ。
タグの重複
同じものに対して二重にアノテーションされた場合、タグがかぶって見づらいです。
画像のアノテーションの時点で弾くか、近くにある場合はよりscoreが高いほうが勝つようにするか。
まとめ
前々からやってみたかったので、実現できて満足です。
かれこれ1月くらいはやっていましたが、期間の半分くらいは不適切なコンテンツを検出する(セーフサーチ)で遊んでたような気がします。
あとARCore
のRecord & Playbackの調査。これも結構調べたので、また記事にして「まさか歩き回ってAR開発してるんですか!!?!?!?!?!?!??!?」ってARKit
を煽ろうと思っています。
Cloud Vision API
は思ったより認識してくれて嬉しいですが、家の中でやるとFurniture
で埋め尽くされて前が見えなくなります。なにをやるかにもよりますが、やっぱり認識したいものを絞ってトレーニングした専用モデルを使う必要が出てくるのかなーと思いました。あと、どうもCloud Vision API
って静的にホストされている高画質の画像に対して実行されるのを想定しているような気がします。なんとなく。
VContainerを導入したのはDIのためじゃなくてARXXManager
たちを配り歩くのが面倒になったからです。いっぱいあってほんとめんどくさい。
なんか感想とかあったらくれるとうれしいです。
おしまい。
参考
Cloud Vision API
ARfoundation
else
-
別にやったりはしない。 ↩︎
Discussion