ラズパイ4をVuforia Engineで分身させてみた ~Object Recognition編~

今回は、Vuforia EngineのObject Recognitionという機能を用いて、Raspberry Pi 4を分身させてみました。

ラズパイ4の物体形状を認識して、AR表現がどれくらい簡単にできるのか検証してみました。

実装も非常に簡単で、ほとんどぶれることがなかったので開発の知見を残していきたいと思います！

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Vuforia Englineを使って最近買ったRaspberry Pi4をターゲットにして遊んでみました。<br><br>分身して集まってキングスライム化して、分からないパーツがあれば説明もしてくれます。<br><br>コードも公開してるので、ARで確認したい方はどうぞ！<a href="https://t.co/2kfuYuHp5Z">https://t.co/2kfuYuHp5Z</a> <a href="https://t.co/pMI7BxbWwI">pic.twitter.com/pMI7BxbWwI</a></p>&mdash; こーや (@koyataroo) <a href="https://twitter.com/koyataroo/status/1259373673854599168?ref_src=twsrc%5Etfw">May 10, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ラズパイが分身して集まって、ドラクエのキングスライム感を出してみました笑

Vuforia Engineの`Image Target`はよく使っていたのですが、物体に対しての精度がすごくてかなりびっくりしました！

# 実行環境

- Unity 2019.3.0f3
- Vuforia Engine 9.0
- iPhone 11 Pro
- iTween (アニメーションができるUnity Asset)

# Vuforia Engineとは

Vuforia Engineは、iOSやAndroid、PC、HoloLensといったAR/MRデバイスに対応したAR開発プラットフォームです。

Vuforia EngineはPTCが提供していますが、元々はQualcommが開発しており、2015年にPTCがVuforia事業を6500万ドルで買収しています。

マーカートラッキングを非常に得意としており、特徴点の多い(複雑な)画像であればほとんどぶれることなく3Dオブジェクトを配置することができます。

詳しくはこちらをご覧ください！

<a href="https://qiita.com/kot10n5tn/items/b0cc615206e806dd2904">今一番ホットなARライブラリ「Vuforia」とは</a>

今回はスキャンしたオブジェクトをターゲットにする`Object Recognition`を使っていきたいと思います。

# Object Recognitionとは

Vuforia Engineには`Object Recognition`というスキャンした物体をマーカーにする機能があります。

Androidで`Vuforia Object Scanner`を使用することで物体をスキャンすることができます。

詳しくはこちら
https://library.vuforia.com/articles/Training/Object-Recognition

今回は、4GBのメモリが載ったラズパイ4を最近購入したのでラズパイをターゲットにしてみました！

# スキャンの手順

次のような画像がScannerアプリと同じフォルダに入っているので、印刷します。

![A4-ObjectScanningTarget.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/eac7ed00-5520-3e61-a4d0-fec70be3a211.jpeg)

模様がたくさんあるところがマーカーになっているので、マス目部分におきます。

![scanner.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/76c8327d-0704-0cda-c3b9-002e78e3e1f2.png)

あとは`Vuforia Object Scanner`アプリを起動して360度カメラを動かすだけでスキャンが完了します！

スキャンが完了したあと、右上のシェアボタンから`.od`ファイルを取得します。

![94925362_251665045887943_1038141200860708864_n.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/1d81ec57-d34d-44fd-5f36-f05e21cda652.png)

![95096169_2445353042391883_2792907199821643776_n.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/56661c90-3b2b-7367-019a-3ab81fa16c6a.png)

何らかの手段でPCに`.od`ファイルを送ってください！

# Developer Portalでターゲットの登録

次に<a href="https://developer.vuforia.com/">Vuforia Developer Portal</a>で先ほどスキャンしたターゲットの情報を登録していきます。(無料で会員登録できます！)

ライセンスキーとターゲットを登録していきます。

## ライセンスキーの取得

Vuforia Engineを使うには、ライセンスキーを取得する必要があります。

開発で使う分には無料で使用できるので以下の手順で取得します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/0ee13d3e-9478-f5a0-e410-6249bbaa4c95.png)

ログイン後、右上の「Get Development Key」から無料ライセンスキーを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/f75990e2-9a65-bd65-9f69-b89648a361ed.png)

License Nameは特になんでも良いです！

これでライセンスキーを取得できました。

## ターゲットの登録

ダウンロードした`.od`ファイルを、Vuforia DeveloperサイトでObject Targetとして登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/81968eb9-6bb8-d13b-c9b6-2c7f3684e33d.png)

「Target Manager」タブの「Add Database」で、ターゲットを格納するデータベースを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/04e6e61c-7a73-2933-2cec-b4e0898043f5.png)

データベースの名前とDeviceにチェックを入れて作成します。
ちなみにCloudを使用すると、リリース後であっても管理画面から簡単にターゲットの追加ができたりします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/7b8adfb6-b5c2-a79d-58f2-dbce7ef012b9.png)

最後にデータベースの「Add Target」を選択し、先ほどダウンロードした`.od`ファイルを登録します。

これでターゲットの登録は完成です！

# モデルの用意と配置

ターゲットの登録が完了したので、次はUnityでオブジェクトを表示していきます！

今回は<a href="https://sketchfab.com/3d-models/raspberry-pi-4-e0f6ec5cb4f140f2bbf2bdb263b759ae">無料でダウンロード可能なラズパイの3Dモデル</a>を使用しました。

## Vuforia Engineをプロジェクトに追加する

https://developer.vuforia.com/downloads/sdk

まず、先ほどスキャンしたラズパイを認識するために、こちらから`.unitypackage`をダウンロードして展開します。

まず、ARCameraを追加し、「Open Vuforia Engine configuration」を押して「App License Key」に先ほど取得したライセンスキーを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/4ef726d5-bb41-b7fb-cfcc-1274331a4c2d.png)

これで、Vuforia Engineのアクティベーションが完了したので、UnityでObjectTargetを配置し、その配下に3Dモデルを配置します。
ラズパイのスケールは`0.102`にするとちょうどよい大きさになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/228af62b-276a-52eb-5ee6-2080c1c1e98c.png)

このまま実行したところ、3Dモデルが実物とかなりずれていたので、ポジションの調整をしていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/0b891511-8372-7b64-c6f2-0070693a6cd5.png)


スキャンしたラズパイと重なるようにスケールとポジションの調整をします。
実行しながらリアルタイムに値を調整していくときれいに重なります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/c7faaa80-7079-15d4-d951-2d066c08893e.png)

すると、このようになりました。

![ezgif.com-optimize.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/1a65519b-2e3f-ce08-dad7-e3b5ca7cca45.gif)

いつからARに切り替わったのか分からないくらいスムーズに表示されています！！

これだけでも十分なのですが、今回はもう少し遊んでみるために、分身9個のラズパイ3Dモデルを配置してみました。

それぞれにアニメーションをつけていきます！

# アニメーション

## 分身アニメーション

今回のアニメーションは<a href="https://assetstore.unity.com/packages/tools/animation/itween-84">iTween</a>を用いて実装しました。

Object Targetがラズパイを認識したときに、startAnimationを呼び出す実装にしています。
Object Target側からディレイを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/f8a99934-cff8-6a2a-8508-9771b9fd7c63.png)


``` RaspiBehaviour.cs
public class RaspiBehaviour : MonoBehaviour {

    public bool isMain = false;

    public void showAnimation(float waitTime = 0f) {
        Invoke("move", waitTime);
    }

    void move() {
        var hash = iTween.Hash("islocal", true);
        hash.Add("x", 0.071f);
        hash.Add("y", 0.003f);
        hash.Add("z", 0.049f);
        hash.Add("time", 1f);
        iTween.MoveFrom(this.gameObject, hash);
        iTween.ScaleTo(this.gameObject, new Vector3(0.102f, 0.102f, 0.102f), 0.5f);

        Invoke("hideAnimation", 2.0f);
    }

    void hideAnimation() {
        var hash = iTween.Hash("islocal", true);
        hash.Add("x", 0.071f);
        hash.Add("y", 0.003f);
        hash.Add("z", 0.049f);
        hash.Add("time", 1f);
        iTween.MoveTo(this.gameObject, hash);

        if (isMain) {
            Invoke("expand", 1.2f);
        } else {
            Invoke("setDeactive", 0.6f);
        }
    }

    void expand() {
        iTween.ScaleBy(this.gameObject, new Vector3(2.0f, 2.0f, 2.0f), 0.4f);
        Invoke("explode", 0.6f);
    }

    void explode() {
        iTween.PunchScale(this.gameObject, new Vector3(0.25f, 0.25f, 0.25f), 0.6f);
        foreach (var _transform in gameObject.GetComponentsInChildren<Transform>()) {
            Debug.Log(_transform.name);
            var particle = _transform.GetComponentInChildren<ParticleSystem>();
            particle.Play();
        }
    }

    void setDeactive() {
        this.gameObject.SetActive(false);
    }
}
```

こちらの`RaspiBehaviour`を9個のラズパイモデルにコンポーネントとして追加していきます。

初回のアニメーションで9個に分散、2秒のディレイののち中心部にあつまり、メインのラズパイ以外は非表示にし、メインのラズパイ を2倍に拡大させています。

## 情報表示アニメーション

情報表示の部分は、パーツ部分にタッチを受付けるためと、情報表示のための`Quad`を用意しています。

情報表示の部分は`TextMesh Pro`を使っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/3931bc8c-4468-1107-ac12-3f1d71138e88.png)


``` InfoBehaviour.cs
public class InfoBehaviour : MonoBehaviour {

    private bool isShown = false;

    public Vector3 from;
    public Vector3 to;

    public void show() {
        isShown = !isShown;
        if (isShown) {
            var hash = iTween.Hash("islocal", true);
            hash.Add("x", to.x);
            hash.Add("y", to.y);
            hash.Add("z", to.z);
            hash.Add("time", 0.2f);
            iTween.MoveTo(this.gameObject, hash);
            iTween.ScaleTo(this.gameObject, new Vector3(1f, 1f, 0.01f), 0.4f);
        } else {
            var hash = iTween.Hash("islocal", true);
            hash.Add("x", from.x);
            hash.Add("y", from.y);
            hash.Add("z", from.z);
            hash.Add("time", 0.2f);
            iTween.MoveTo(this.gameObject, hash);
            iTween.ScaleTo(this.gameObject, new Vector3(0f, 0f, 0f), 0.4f);
        }
    }
}
```

この`InfoBehaviour`コンポーネントをそれぞれの情報表示用のQuadに追加しています。


# Object Recognitionの検証

## ブレ耐性

ラズパイに3Dモデルをあてた状態で水平方向に軽く振ってみましたがちゃんとトラッキングできていました、すごい。

![ezgif.com-video-to-gif (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/6ec9c894-86e8-3d74-b946-b8ec80b21d1b.gif)

## 横からの撮影

時折横からの撮影では時折ずれてしまうことがありました。
金属部分の反射の関係が大きくなってしまうからなんでしょうか。。

![side.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/7dcd1d26-af62-d394-1a6f-bdae3e236cba.gif)

## 半分隠してみた

ラズパイを半分隠してみたところ少しだけ3Dモデルが揺れたものの、問題なく表示できていました
特徴点が半分くらい認識できれば良さそうですね！

![half.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/b86a02a2-aba5-f2d6-4a4e-66de45c1e405.gif)

## 遠くから撮ってみた

60センチくらいラズパイから離してトラッキングしてみました。

![long.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/fcf901fb-5ea0-9937-aea6-40d4748daa5c.gif)

ちょっと傾いていますね。。

使ってみたところ30センチ以内からの撮影であれば安定していました。

# まとめ

Vuforia Engineは円柱型や直方体のそれぞれの面の画像を用意することでトラッキンッグすることができますが、ラズパイのような特殊な形をしていてもトラッキングをすることができました。

商品にARでエフェクトを作成したり、説明を加えたりするのは面白そうだな〜と感じました。

開発者向けの<a href="https://www.it-ex.com/promo/vuforia/to-developers/">Vuforia Slackコミュニティサイト</a>もあるようです。
Vuforiaの新情報を取得できたり、実装で困ったことがあれば質問できるのでおすすめです！

また今回のコードはGithubに載せているので是非クローンしてお手元で試してみてください！

https://github.com/kot10n5tn/ObjectRecognition