スターティアラボ WebVR+AI入門勉強会 Part2

すぎどん
2020-12-07
すぎどん
2020-12-07

前回のおさらい

先日、スターティアラボ主催で初心者向けWebVR+AI勉強会を開催しました。

この記事は、前回同様に勉強会の内容を説明していきます。前回は、カメラ画像の取り込みからAI推論モデルの実行までを行いました。今回は、いよいよ推論モデルの出力結果を元に3D空間へ手を表現し、ジェスチャーを認識する実装内容を説明いたします。

前回をご覧になっていない方は、先に「スターティアラボ 初心者向けWebAVR+AI勉強会 part1」の記事をご覧ください。

検出した手の2D表示(デバッグ用)

デバッグ用にカメラ画像に対する手の位置を2Dで表示します。
3Dでおかしな動きをする場合や認識が悪い場合に、2Dでどうなっているのか確認することができます。

実装内容

手の2D表示用のCanvasを新たに追加して、今まで表示していたvideoタグを非表示にします。
Videoタグは推論モデルの入力として利用しますので非表示にするだけとします。



2D表示用Canvasの初期設定をおこないます。
初期設定メソッドは最初に取り込んだスクリプトに実装しています。



2D表示用CanvasにVideo画像と推論結果データを元に手の位置を2D表示します。
手の2D表示用メソッドは最初に取り込んだスクリプトに実装しています。




動作確認

左下のカメラ画像を出力していたところに、赤い線と点で手の位置を表示しています。



検出した手の3D表示

推論結果データを元に、3D空間に手を表現します。

推論結果のデータ構造

取得した推論結果には様々なデータがありますが、今回「landmarks」データを利用します。
landmarksには0〜20の21個のデータがあり、それぞれ下記画像に示す手の箇所の3D位置情報を保持しています。

実装内容

手の3Dオブジェクトを配置する親オブジェクトのタグを作成します。
推論結果から取得した3D位置情報のスケールと、A-Frameのスケールが約200倍違うため、このタグのスケールを調整することでA-Frameで表現できる大きさの手を表現しています。同様に、ポジションで全体位置調整を、ローテーションで手の向きを調整しています。



推論結果から取得できる21箇所の手の3D位置情報を格納する配列を作成します。



手の各3D位置情報に応じて表示するオブジェクトを新規作成するメソッドを定義します。
作成したオブジェクトは先ほど追加した親オブジェクトの子要素として登録すると共に、配列にも格納します。



上で作成した手のオブジェクトを作成するメソッドを呼び出します。



推論結果のlandmarksデータを元に、配列に格納した手の各オブジェクトに対して位置情報を反映します。
landmarksに格納された3D位置情報はx、y、zの軸で位置情報を保持しており、そのまま各オブジェクトのポジションに反映します。





動作確認

カメラに写った手に応じて手の形をした青いオブジェクトが表示します。
認識しなかったり動きがおかしい場合は2D画像を見て、手を正しく認識しているか確認しましょう。

ジェスチャー検出

親指と人差し指の位置情報から、ピンチイン/アウトジェスチャーを識別します。

ジェスチャー説明

状態(ピンチイン/アウト)と、指の開き具合(親指<->人差し指の間の距離)で判断します。
ピンチイン状態からある程度開くとピンチアウト、ピンチアウト状態からある程度閉じるとピンチインとする。開き具合の閾値を別で設けることでチャタリングを防いでいる。




親指と人差し指の距離情報を簡単に取得するため、Vector3クラスを利用しています。
Vector3とは、3Dコンテンツをブラウザで扱うライブラリであるThree.jsのクラスです。
A-Frameは内部的にThree.jsを利用しています。
Vector3は、x, y, zの数値を元に3次元ベクトルを扱えるクラスです。
便利なメソッドが用意されています。今回はジェスチャーでは、3次元空間上の二点間距離を算出するメソッド「distanceTo」を利用します。
詳しい情報は以下を参考ください。

https://threejs.org/docs/#api/en/math/Vector3

実装内容

ピンチ状態と生成したBOXを格納する変数を作成します。



推論結果データのlandmarksの内、親指の先はlandmarks[4]、人差し指の先はlandmarks[8]に格納されています。
それぞれVector3オブジェクトを生成し、distanceToメソッドを使うことで距離値をしています。
一旦、この距離情報をコンソールに出力し、ピンチイン/アウト時の距離を把握しておきましょう。



ピンチイン/アウト状態の切り替え判定を追加します。
 ピンチイン→アウト: ピンチイン状態から親指と人差し指の距離が100以上離れた時
 ピンチアウト→イン: ピンチアウト状態から親指と人差し指の距離が50以下に近づいた時
この時の距離値は人によって違いますので合わない場合は設定値を調整しましょう。

ピンチアウト→インの時に、人差し指の位置にBOXを生成して配置しています。




動作確認

つまむ動作を行うたびに、BOXを生成しています。

物理演算の追加

物理演算ライブラリを利用して、生成したBOXを離した時に落ちる表現を追加します。

aframe-physics-system

物と物の衝突や重力の影響などを表現する、物理演算に関するライブラリ。
A-Frameのタグやオブジェクトに属性として設定することができます。設定できるパラメータは以下の2つです。

dynamic-body: 自由に動くオブジェクト。衝突による跳ね返りや重力による落下などを表現する。
static-body: 位置固定のオブジェクト。衝突に影響するが位置の変動がなく、重力に影響されないオブジェクト。床や建物などの表現に利用する。

詳細は以下のサイトをご覧ください。

https://github.com/n5ro/aframe-physics-system

実装内容

aframe-physics-systemライブラリを読み込みます。



物理演算を利用するには以下の2つが必要です。
 ・a-sceneタグに物理演算を適用する属性「physics」を設定します。
 ・物理演算対象のオブジェクトに「dynamic-body」 もしくは 「static-body」 属性を設定します。

床を表現しているa-planeに重力に影響せず位置が変わらない「static-body」を設定し、それ以外は「dynamic-body」を設定しています。



BOXに対して、離した時に重力にしたがって落ちる動きを取り入れるため、親指と人差し指を離したタイミングで「dynamic-body」属性を設定しています。加えて、手から離れてしまった(コントロール外)とするため、BOXにundefinedを設定して変数を空にしています。



親指と人差し指で摘んだ状態(=box変数にBOXオブジェクトが登録されている状態)中は摘んでいる表現をおこなうため、BOXの位置を人差し指の位置で更新し続けます。




動作確認

ここまでで実装は完了です。part1の始めに記載した「https://startialab-webvr-handpose-handson.glitch.me/」と同じ動きになっていると思います。
ソースの全容については以下を確認願います。

https://glitch.com/edit/#!/startialab-webvr-handpose-handson?path=index.html

最後に

勉強会の内容は以上になります。

スターティアラボ勉強会 WebVR、WebAR、AIと全3回で行いましたが、いかがだったでしょうか?
今までのWebとは思えない表現ができたかと思います。
この分野はどんどん新技術が出てきており、これから発展していく分野だと思います。

同じ内容になりますが、11月以降で再度勉強会を開催する予定です。
参加されていない方や、もう一度聞きたい方がいらっしゃいましたら、ご参加お願いいたします。