Tech Sketch Bucket of Technical Chips by TIS Inc.

マーカーをタップすると3Dアバターがしゃべって動くARアプリ 2

Pocket

前回 の記事に引き続き、AndARを改造して「マーカーをタップすると3Dアバターがしゃべって動くARアプリ」の解説をします。
今回は「マーカーをタップする」というイベントをハンドリングするためにAndARに施した改造について、ソースコードを交えて解説します。

・・・AndARの内部構造の話やJNIバリバリのC言語のソースコードの話が出てきたりと、かなり濃いですが、ツイテキテネ!


AndARの処理の流れ

AndARは、だいたい下記のような流れでAR処理を行なっています。

  1. カメラプレビュー画像を取得
  2. カメラプレビュー画像をRGB形式へ変換
  3. カメラプレビュー画像内に、AndAR初期化時に定義したマーカー群と特徴が一致する部分がいくつ存在するかを数える
  4. マーカーが一つ以上存在する場合、マーカー上に対応する3Dオブジェクトをレンダリング
  5. 最初に戻ってカメラプレビューの次のフレームを処理(上記の処理はそれなりに時間がかかるため、処理中にカメラから送られてきたフレームは捨てられる)

今回の改造では、3番目の「マーカーの存在チェック」に割り込み、「検出したマーカーの数」だけでなく「検出したマーカーの(カメラプレビュー座標系での)位置」も返すようにします。

マーカー認識部分の改造

「マーカー認識部分」は速度が必要なため、C言語でネイティブ実装されています。それがarToolkit.cに記述されている「Java_edu_dhbw_andar_ARToolkit_artoolkit_1detectmarkers」メソッドです。またこのネイティブ実装をラップしActivityから使いやすくするためにあるのが、ARToolkit.javaです。

まずはこの二つのファイルから始めましょう。

ARToolkit.java

マーカー情報を保持するMapを追加する

ARToolkitのインスタンスは、AndARのActivityにインスタンス変数として保持されています。そのため検出したマーカー情報をARToolkitに保管しておけば、Activityのメソッドから好きなタイミングで「今検出されているマーカーの位置」を得ることができるようになります。

そこで下記のように、検出したマーカーを保持するMapをARToolkitに追加します。キーとなるIntegerは、AndARが採番したマーカーを一意に特定する番号です。また値となるMarkerInfoは、マーカー検出ネイティブメソッドから得られた文字列を正規表現で解析し、マーカーの位置情報を保持するためのクラスです。

マーカー検出ネイティブメソッドのシグネチャを変更する

ARToolkitクラスでは、下記のネイティブメソッドを呼び出すことでマーカーの認識を行なっています。

一つめの引数の in はカメラプレビューのyuv420sp画像をRGB変換した画像です。
(もう一つの引数 transMatMonitor は、3Dオブジェクトを適切な位置へレンダリングするために用いられる変換行列を作成する際に、操作を同期化するためのロックモニタです。が、今の実装(r205)では、このロックモニタは使われていません・・・)
このメソッドの戻り値は、検出したマーカーの数です。この戻り値が1以上ならば、マーカーが検出されたということになります。

このネイティブメソッドに3番目のOUT引数 markerInfos を追加します。

C言語のネイティブ実装側は、このString配列にマーカーの位置情報を文字列として格納することとします。

マーカー検出時に、マーカー情報をMapへ格納する

実際のマーカー認識処理は、Threadクラスを継承した「DetectMarkerWorker」内部クラスで行われています。ここではartoolkit_detectmarkersを呼び出し、検出されたマーカーの数が1以上であれば3Dオブジェクトのレンダリングを指示していますが、そこにマーカーの位置情報の保存処理を追加します。

arToolkit.c

マーカー検出のネイティブ実装のシグネチャを変更する

javahを用いて、変更したマーカー検出メソッドのシグネチャを生成し変更します。具体的には次のようになります。

  • 変更前:

  • 変更後:

マーカー位置を文字列化する

マーカー検出関数では、冒頭に「arDetectMarker」関数を呼び出すことで marker_num に検出したマーカーの数、 marker_info にマーカーの情報を格納しています。そこでそれらの変数から「検出したマーカーの中心位置」の情報を取り出し、C文字列化します。

実は文字列の配列を返すのではなく、JNIを用いてJavaクラスを直接インスタンス化し、そのクラスの配列を返す実装をするほうが速いし美しいと思います。
が、今回は取り扱いとデバッグが簡単なように、要素数を固定してJava側でメモリを確保してある配列に、文字列を詰め込む形で情報を受け渡すことにしています。時間があれば改善したい部分ですね。

その他の変更

マーカー認識部分以外にも、「マーカーをタップすると3Dアバターがしゃべって動くARアプリ」を構築するために必要な改造をいくつか施しています。

AndARActivity.java

AndARアプリのActivityは、このクラスを継承して作ります。ただし「マーカーをタップした」ということを判断するためは、カメラ座標系の設定情報が必要となるため、cameraインスタンスのアクセス修飾子を private から protected に変更します。

  • 変更前

  • 変更後

「マーカーをタップした」ことを判断するロジックは、次回解説します!

Model3D.java

Model3Dは、Wavefront形式のファイルから構築した3Dオブジェクトを保持し、必要に応じてマーカー上の適切な位置に3Dオブジェクトをレンダリングするためのクラスです。
実際にレンダリングする処理はdrawメソッドに記述されていますが、マーカーをタップした際に各3Dオブジェクトに独自のアニメーションを行わせることができるように、animateメソッドをフックする形に変更しています。

animateメソッドは抽象メソッドのため、実際のアニメーション処理はModel3Dを継承した具象クラスで実装しなければなりません。この部分も、次回解説します!

次回は

今回改造したAndARを用いて、「マーカーをタップすると3Dアバターがしゃべって動くARアプリ」の具体的な実装を解説します。

エンジニア採用中!私たちと一緒に働いてみませんか?