8thWall上でTensorflowを使ったコンテンツの制作方法
8thWallとTensorflowを組み合わせたWebARアプリの制作方法について紹介します。
目次
- 要件
- 8thWallのImageTargetとの違い
- 実装の流れ
- Canvasの用意
- Tensorflow.jsを使ったObject Detectionの処理
- 画像認識したオブジェクトの3D位置を推定
- 3Dモデルの表示
- まとめ
要件
WebAR上でロゴマークを読み取って、そこを基準として3Dモデルを表示するアプリの制作
- ロゴマークの読み取り、識別にTensorflowのObjectDetectionを採用
- モデル制作の開発コストを抑えるために、GoogleCloudのVertexAIを採用
- WebARとして定番の8thWallを使用。
Tensorflowのモデルデータを用意する方法はここでは紹介しません。
今回使用するモデルデータはVertexAI上で画像複数枚を使って作りました。
8thWallのImageTargetとの違い
8thWallのImageTargetは、あらかじめ指定した数枚の画像を登録しておき、毎フレームその画像の姿勢やサイズを推定するという機能です。
しかし撮影時の光の環境や画像の大きさが認識精度に大きく影響を与えます。
今回の要件は、
- 1㎝サイズの小さなロゴマークを識別する
- マークには様々なバリエーションが存在する
- ユーザーが撮影する環境をコントロールできない
これらの条件から画像認識に優れ、学習させる画像をコントロールしやすいTensorflowを使用することになりました。
しかし、ImageTargetでは使えていた、毎フレームの画像の姿勢推定は現実的では無くなる点には注意するべきかと思います。
Image Target (8thWall) | Tensorflow.js | |
---|---|---|
認識精度 | 精度をよくするために、特徴点が多くとれる画像を用意する必要がある。 | 小さい画像や、シンプルなマークのような画像もマーカーにできる |
姿勢推定 | 位置・回転・スケール | 位置のみ |
ターゲット数 | 5-10画像 | モデルデータによるが現実的に、最大100パターンほど |
実装の流れ
Canvasの用意
カメラ画像のCanvasからTensorflowに通すためのクロップされたCanvasを作成
VertexAIで作成したモデルデータは224x224なので、そのサイズのキャンバスを作成。
// <canvas id="cropped"></canvas>
croppedCanvas = document.getElementById('cropped')
croppedCanvas.width = 224
croppedCanvas.height = 224
Tensorflow.jsを使ったObject Detectionの処理
VertexAIのモデルを使うためのスクリプト tfjs-automl.js
を使用する。
//head.html
<script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
<script src="https://unpkg.com/@tensorflow/tfjs-automl"></script>
//tf-exec.js
const model = await tf.automl.loadObjectDetection(/* Model URL */)
const options = {score: 0.8, iou: 0.5, topk: 1}
const predictions = await model.detect(croppedCanvas, options)
predictionは下記のような構造体で帰ってくるので、各々処理します。
[
{
box: {
left: 105.1,
top: 22.2,
width: 70.6,
height: 55.7
},
label: "label",
score: 0.972
},
...
]
⚠️ここで注意点があり、Tensorflowの仕組み上一回目の処理だけかなり時間がかかります。
これはGPUでの計算処理に必要なシェーダープログラムをビルドしているのが原因のようです。
Could you please elaborate the slowness of the first inference · Issue #1715 · tensorflow/tfjs
なので、ローディング時などで推論を空回ししておくとそのあとの体験がスムーズになるかと思います。
また、何回も推論をかけると、GPUのメモリリークが起きてしまうので、使用後確保したメモリを開放してあげるように注意しましょう。
画像認識したオブジェクトの3D位置を推定
8thWallのSLAMのデータにアクセスしてマークの3Dの位置を推定します。
3Dの位置の推定にはhitTestを使います。2Dの座標(画面の中央)を比較して、SLAMで認識された特徴点の一番近い点を取得できるようです。
XR8.XrController.hitTest() | 8th Wall
const x = (canvas.width / 2) - (croppedCanvas.width/ 2) + prediction.box.left + (prediction.box.width / 2)
const y = (canvas.height / 2) - (croppedCanvas.height/ 2) + prediction.box.top + (prediction.box.height/ 2)
const hitTestResults = XR8.XrController.hitTest(x, y, ['FEATURE_POINT'])
3Dモデルの表示
8thWallのSLAMのデータを使って、3Dモデルを表示します。
const gltfModel = require('./assets/cactus.glb')
const loader = new THREE.GLTFLoader()
const gltf = await loader.load('model.gltf')
model.add(gltf.scene)
model.position.copy(hitTestResult.position)
3Dモデルを読み込み、hitTestの結果を使って3Dの位置を調整しています。
これでロゴマークを読み取って、WebAR上で3Dモデルを表示するアプリの制作が完了しました。
まとめ
今回の実装は8thWallとTensorflow.jsを組み合わせた実装により、ImageTargetでは難しかった機能を実現することができました。
8thWall上では、元々SLAMが動いているので使えるスクリプトには制限がある環境のように思えます。今回代替案として検証していた、OpenCV.jsはWASMを使っていたのが原因なのか、8thWall上では動かすことが出来ませんでした。
以下参考にした記事になります。
Discussion