🦊

⑥[Android][Kotlin][Vuforia][AR]VuforiaのVuMarkを動かしてみた。

に公開

第1回 VuforiaでARの初めの一歩
第2回 Vuforiaのサンプルコード(Image Targetのみ)を動かしてみた。
第3回 動画再生を板ポリ上で実行するサンプルコードを作ってみた。
第4回 Vuforiaで動画再生ARを実装してみた。
第5回 Vuforia11.4.4の公式サンプルコードを動かしてみた。
お薦めしません: 第6回 VuforiaのVuMarkを動かしてみた。
お薦めしません: 第7回 Vuforiaで独自VuMarkを作ってみた。←挑戦中 ←無理ゲーすぎた
第8回 Vuforia(Image Target)を独自マーカーで動かしてみた。
第9回 Vuforia(Image Target)を独自マーカーの複数検出させてみた。

「VuMark使えますけどサンプルありません」って。orz

↑分かりにくいけど、VuMarkマーカに反応して動画再生出来ている
テクスチャが前後逆に貼られててキャラがよく分からなくなってるのはご愛敬ってことで。

github: https://github.com/aaaa1597/AndKot-VuforiaVuMarkSample.git

第6回目 VuforiaのVuMarkを動かしてみた。

Abstruct

  • Vuforia VuMarkを動かしてみた話。
  • Vuforiaのバージョン 11.4.4。

※Vuforiaには3Dモデルを認識するModel Targetと、画像を認識するImage Targetってのがあって、この記事はImage Targetの話になります。

背景

VuMarkを動かしたかったので、ここのところ連続して Vuforiaの記事を書いてた。
で、VuMarkのサンプルコードが見つからんくって苦労したので、まとめを残しておきます。

ざっくり手順

  1. VuMarkをデザインしてSVGで出力
  2. 生成したSVGで、Vuforia Portalにログイン → データセット生成
  3. Android プロジェクトに導入 (.xml / .dat)
  4. kotlin/c++で検出処理を書く

準備

  • android端末
  • 開発用PC(Android Studioのインストールは済ませておく。)
  • android端末をつないで、デバッグができる様にしておく。
  • SDKと公式サンプルコードは今回一緒にgithubにコミットしたんで不要。
  • Vuforiaのアカウント作成 前回の説明の準備1をやっておく。
  • Vuforiaのライセンスキー取得 前回の説明の準備2をやって取得しておく。

手順1. VuMarkマーカテンプレートをデザインしてSVG出力

ムズくて断念。
ココの VuMark-Designer-11-4-4.zip から MortonTuxedos.svg を抜き出して使用。
※公式なのに Chateauの方は動かないので気をつけて!!

手順2. 生成したSVGで、Vuforia Portalにログイン → データセット生成

データセット生成の手順はざっくりとは下記の通り。
  1. Vuforia developer portalにログイン
  2. 作成したVuMarkマーカテンプレートを登録
  3. VuMarkを生成 → ダウンロード
  4. データセット(xml / dat)をダウンロード

  1. Vuforia developer portal にログイン

  2. Target Manager → Generate Database 押下

    Database Name: 適当に
    Type: VuMark
    → Generate 押下

  3. aaa 選択

  4. Add Target 押下 → 下記入力 → Add 押下
    File: MortonTuxedos.svg
    width: 5cmとするので0.05
    Name: momo001 ※これも適当に

  5. 待つ。
    1分ぐらい。
    Processingになってるので完了まで待つ。画面下部にRefreshボタンがあるので表示更新はこれを使う。

  6. VuMarkを生成 → ダウンロード
    Generate VuMark 押下 → 下記入力 → Download
    ID: 0056 ※これも適当に
    Graphic Format: PNG

  7. 得られたVuMarkマーカがコレ↓

  8. データセット(xml / dat)をダウンロード
    Android Studio, Xcode or Visual Studio を選ぶ(Androidアプリだからね)

  9. ゲット!!

手順3. ビルドする。

1. 下記に今回のコードをコミットしたのでそれをDL。

https://github.com/aaaa1597/AndKot-VuforiaVuMarkSample

2. Android Studioでプロジェクトを開く。

cloneしたAndKot-VuforiaVideoPlaybackを開く。

3. 認証情報を設定した後、ビルドする。

Vuforiaのライセンス画面を開いて license Key をコピー。

            ↓
Android Studioで、"AppController.cpp"を開き、licenseKeyに、準備2でコピーしたライセンスを貼り付けます。
そして、ビルド。

※もし、この手順を飛ばしたら、アプリ起動時に下記エラーが発生します。

4. マーカを印刷する。

マーカは、VuMarkマーカ。"7. 得られたVuMarkマーカ"のところで表示したやつを印刷しましょう。

手順4. 手順2でDLしたvuforiaのデータセット(.xml/.dat)をプロジェクトに組み込む

ゲットした momo001.zip は解凍するとmomo001.xml / momo001.datの2ファイルが出来るので、その2つをAndroid プロジェクトに追加する。(実際はgithubに追加済なのでやる必要はないです)

app/
└── src/
    └── main/
        └── assets/
            └── VuMark/
                ├── momo001.dat
                └── momo001.xml

手順5. kotlin/c++でVuMark検出処理を書く

  • MainActivity.kt
    VUMARK_IDの定義を追加してそのIDで初期化する。
MainActivity.kt
+ const val VUMARK_ID = 2
・
・
・
    private suspend fun initializeVuforia() {
        return withContext(Dispatchers.Default) {
+           initAR(this@MainActivity, this@MainActivity.assets, VUMARK_ID)
        }
    }
  • VuforiaWrapper.cpp
    VuMarkの検出結果を取り出して、OpenGL描画処理を呼出してるところ。
    検出結果は複数の可能性があるのでループにしている。
VuforiaWrapper.cpp(311-327)
        VuMatrix44F worldOriginProjection;
        VuMatrix44F worldOriginModelView;
        if (controller.getOrigin(worldOriginProjection, worldOriginModelView))
        {
            gWrapperData.renderer.renderWorldOrigin(worldOriginProjection, worldOriginModelView);
        }

+       auto [vuMarkList, CNT] = controller.createVuMarkList();
+       for (int idx = 0; idx < CNT; idx++) {
+           VuMatrix44F trackableVuMarkProjection = {};
+           VuMatrix44F trackableVuMarkModelView = {};
+           VuMatrix44F trackableVuMarkModelViewScaled = {};
+           VuVector2F vuMarkerSize = {};
+           VuObservation *pVuMarkObservation = nullptr;
+           if (vuObservationListGetElement(vuMarkList.get(), 0, &pVuMarkObservation) != VU_SUCCESS)
+               continue;
+           if (controller.getVuMarkResult(idx, pVuMarkObservation, trackableVuMarkProjection, trackableVuMarkModelView, trackableVuMarkModelViewScaled, vuMarkerSize))
+           {
+               gWrapperData.renderer.renderVideoPlayback(trackableVuMarkProjection, trackableVuMarkModelView, trackableVuMarkModelViewScaled, vuMarkerSize);
+               gWrapperData.renderer.renderImageTarget(trackableVuMarkProjection, trackableVuMarkModelView, trackableVuMarkModelViewScaled);
+           }
+       }
+       vuMarkList.reset();

        VuMatrix44F trackableProjection;
        VuMatrix44F trackableModelView;
        VuMatrix44F trackableModelViewScaled;
        VuImageInfo modelTargetGuideViewImage;
        VuBool guideViewImageHasChanged;
        VuVector2F markerSize;
        if (controller.getImageTargetResult(trackableProjection, trackableModelView, trackableModelViewScaled, markerSize))
  • AppController.h
    VUMARK_ID と 関数定義。検出数を取得する関数と、検出結果を取得する関数を追加。
AppController.h(29,109-110)
public:
    // Constants
    static constexpr int IMAGE_TARGET_ID = 0;
    static constexpr int MODEL_TARGET_ID = 1;
+   static constexpr int VUMARK_ID = 2;

    // Type definitions
・
・
・
+   std::pair<std::unique_ptr<VuObservationList, decltype(&vuObservationListDestroy)>, int> createVuMarkList();
+   bool getVuMarkResult(const int idx, const VuObservation *pObservation, VuMatrix44F& projectionMatrix, VuMatrix44F& modelViewMatrix, VuMatrix44F& scaledModelViewMatrix, VuVector2F& markerSize);
  • AppController.cpp
    1番修正したソース。ざっくりとは以下を修正。

    • VuMarkの検出数を取得する関数(新規作成)
    • VuMarkの検出結果を取得する関数(新規作成)
    • VuMark用Observerを生成する部分
  • VuMarkの検出数を取得する関数(新規作成)
    検出数と検出結果リストを返却する様に作成。

AppController.cpp(385-402)
+std::pair<std::unique_ptr<VuObservationList, decltype(&vuObservationListDestroy)>, int>
+AppController::createVuMarkList() {
+   VuObservationList* observationList = nullptr;
+   REQUIRE_SUCCESS(vuObservationListCreate(&observationList));

+   if (vuStateGetVuMarkObservations(mVuforiaState, observationList) != VU_SUCCESS)
+   {
+       LOG("Error getting VuMark observations");
+       REQUIRE_SUCCESS(vuObservationListDestroy(observationList));
+       std::unique_ptr<VuObservationList, decltype(&vuObservationListDestroy)> _null_ptr_(nullptr, &vuObservationListDestroy);
+       return {std::move(_null_ptr_), -1};
+   }

+   int numObservations = 0;
+   REQUIRE_SUCCESS(vuObservationListGetSize(observationList, &numObservations));
+   std::unique_ptr<VuObservationList, decltype(&vuObservationListDestroy)> retptr(observationList, &vuObservationListDestroy);
+   return {std::move(retptr), numObservations};
+}
  • VuMarkの検出結果を取得する関数(新規作成)
    Matrixを返却する。戻り値にせずに出力引数にしたのは特に理由ない。
AppController.cpp(404-449)
+bool
+AppController::getVuMarkResult(const int idx, const VuObservation *pObservation, VuMatrix44F &projectionMatrix, VuMatrix44F &modelViewMatrix,VuMatrix44F &scaledModelViewMatrix, VuVector2F &markerSize) {
+   if (mTarget != VUMARK_ID)
+       return false;

+   assert(pObservation);
+   assert(vuObservationIsType(pObservation, VU_OBSERVATION_VUMARK_TYPE) == VU_TRUE);
+   assert(vuObservationHasPoseInfo(pObservation) == VU_TRUE);

+   VuPoseInfo poseInfo;
+   REQUIRE_SUCCESS(vuObservationGetPoseInfo(pObservation, &poseInfo));

+   VuVuMarkObservationTemplateInfo vuMarkInfo;
+   REQUIRE_SUCCESS(vuVuMarkObservationGetTemplateInfo(pObservation, &vuMarkInfo));

+   if (poseInfo.poseStatus == VU_OBSERVATION_POSE_STATUS_NO_POSE)
+       return false;

+   VuVuMarkObservationInstanceInfo vuMarkInstanceInfo;
+   vuVuMarkObservationGetInstanceInfo(pObservation, &vuMarkInstanceInfo);
+   __android_log_print(ANDROID_LOG_INFO, "aaaaa", "VuMark[%d]: InstanceInfo(dataType=%d numeric=%d, str=%s ) uniqueId=%d, name=%s, userData=%s\n", idx,
+                       vuMarkInstanceInfo.dataType, vuMarkInstanceInfo.numericValue, vuMarkInstanceInfo.buffer, vuMarkInfo.uniqueId, vuMarkInfo.name, vuMarkInfo.userData ? vuMarkInfo.userData : "NULL");

+   markerSize.data[0] = vuMarkInfo.size.data[0];
+   markerSize.data[1] = vuMarkInfo.size.data[1];

+   projectionMatrix = mCurrentRenderState.projectionMatrix;

+   /* Compute model-view matrix */
+   auto modelMatrix = poseInfo.pose;
+   modelViewMatrix = vuMatrix44FMultiplyMatrix(mCurrentRenderState.viewMatrix, modelMatrix);

+   // Calculate a scaled modelViewMatrix for rendering a unit bounding box
+   // z-dimension will be zero for planar target
+   // set it here to the larger dimension so that
+   // a 3D augmentation can be shown
+   VuVector3F scale;
+   scale.data[0] = vuMarkInfo.size.data[0];
+   scale.data[1] = vuMarkInfo.size.data[1];
+   scale.data[2] = std::max(scale.data[0], scale.data[1]);
+   scaledModelViewMatrix = vuMatrix44FScale(scale, modelViewMatrix);

+   return true;
+}
  • VuMark用Observerを生成する部分
    ここで生成したデータセット(.xml/.dat)を設定する。
AppController.cpp(806-822)
+   if (mTarget == VUMARK_ID) {
+       auto vuMarkConfig = vuVuMarkConfigDefault();
+       vuMarkConfig.databasePath = "VuMark/momo001.xml";
+       vuMarkConfig.templateName = "momo001";
+       vuMarkConfig.activate = VU_TRUE;
+
+       VuVuMarkCreationError vuVuMarkCreationError;
+       if (vuEngineCreateVuMarkObserver(mEngine, &mObjectObserver, &vuMarkConfig, &vuVuMarkCreationError) != VU_SUCCESS)
+       {
+           LOG("Error creating VuMark observer: 0x%02x", vuVuMarkCreationError);
+           mErrorMessageCallback("Error creating vumark observer");
+           return false;
+       }
+   }
-   if (mTarget == IMAGE_TARGET_ID)
+   else if (mTarget == IMAGE_TARGET_ID)

手順6. 実行

期待通りに動いた!! なかなか大変だった。
※まだ独自VuMarkマーカが作れてないので道半ば。ふー。

詰まったところと対応策

とにかくVuMarkマーカの作成に苦しんだ。

そもそも VuMark デザインするのにAdobe Illustratorが必要というね。(けっこうお高いのに!) そしてそれが煩雑でムズいという。断念した。
で結局イラレを使わずに、ココから気合で作成したんだけど、見事にVuMarkテンプレート登録出来たんだけど。。。
データセット(.xml/.dat)をプロジェクトに組み込んだら、読み込みエラーって。もうね。

じゃ、公式のがあるじゃんって思い出して、"Chateau.svg"を試すと読込み成功するけど、認識動作させず。。。公式なんじゃないの?

で、MortonTuxedos.svgを試すとやっと動いた。ホントやっと動いた!
これで動くソースは作れた。


けど動くVuMarkマーカの作成方法が全く分かんない。次の課題として今から挑戦するけど、たぶん無理な気がする。挫折する気がする。ここが僕の限界かな。
誰かVuMarkマーカの作り方教えてくれんかな。

                              「誰かの役に立つと思って」

Discussion