AR FoundationをEditorでも動かす その2

【unityプロ技】 Advent Calendar 2019 - Qiita 6日目の記事です。*1

img

Imgur

はじめに - ARFoundationとは

スマートフォンでのAR楽しいですね。それぞれiOSはARKit、AndroidではARCoreというAR用開発キットが公開されています。Unity用SDKもそれぞれUnity-ARKit-Plugin,Google ARCore SDK for Unityと開発がされてきました。今まではそれぞれのSDKをインストールして頑張ってきたのですが、ARCore, ARKit, HoloLens, Magic LeapSDKをラップして、同じAPIでアクセスできるようにする、ARFoundationが発表されました。

ARFoundationの導入は、安定のテラシュールブログさんを御覧ください。 tsubakit1.hateblo.jp

何を作った?

ARFoundationはUnity-ARKit-PluginのときよりもAPIがシンプルでわかりやすいのですが。以前のこちらの日記でも簡単に書いたように、 Editorで動かない。毎回実機ビルドが必要という問題がありました。Unity-ARKit-Pluginのときはできていたんです…。つい昨日、公式からアナウンスがあり、2020年はじめにリモートでつなげてEditorでも動作できる機能をリリース予定です。とはいえ、今すぐ仕事で使うために、自分で使う一部機能だけでもEditorで動かせると便利だと思ったので、ARKit3の新機能だけでもEditorで動作するものを作りました。*2

asus4.hatenablog.com

github.com

今回エディタ側で対応した機能は、 - 人間の体部分を検出 - Face - 座標検出 - Mesh検出 - ブレンドシェイプ推定 です。 以下は作例です。Editorで動作することで、デプスを送りながらVisual Effect Graphでノードのパラメータをいじるなどといったこともできるようになっています。

使い方

簡単に使い方を説明します。

1. NDIのインストール

iPhoneのカメラで移している映像をUnityに送るためにNDIを使っています。iPhoneで使うためには、NDIを https://ndi.tv/sdk/ からインストールする必要があります。メールアドレスを登録してしばらくするとSDKのダウンロード先URLが送られてきます。

2. iPhoneに送信用アプリをインストール

GitHubからUnityプロジェクトをダウンロードして、 iPhone用にビルド。Xcodeから対応端末 (iPhoneX以降〜)へインストールします。

3. maciPhoneをUSBでつなげる

高速のWi-Fiの場合は良いですが、私のオフィスのネットワーク環境ではiPhonemaciPhone USBネットワークで接続したほうが遅延が少なく動作しました。

Imgur

4. 再生する

iPhoneの送信側と対応するシーンをUnityEditorでも開きます。
iPhoneを再生すると、同じネットワークにNDIが飛んでいる場合、NDIの名前が表示させるので、NDI ReceiverコンポーネントのSource Nameにセットしてください。 Imgur

Unity Editorを再生開始するとiPhoneと同じ画面が表示されると思います。

ARFoundationを中身を見てみよう

この開発をするために、ARFoundationの中身を詳しく知る必要があったので、紹介します。

最新のロードマップ、Unite Copenhagen 2019でのスライドにあるように、AR Foundationを抽象化されたAPIの一個深いレイヤーにはSubsystemというものがあります。平面を検出する機能を例に取ると、

f:id:asus4:20191207001738p:plain
こんな感じ

それぞれのプラットフォームフォームがSubsustemを持っています。対応していないplatformではSubsystemがnullを返します。Manager側が何も動作しないので、シーン中にManagerコンポーネントが配置されていても問題ありません。そして、UnityEditorで実行するときは、Subsystemは存在しないので、エラーは返さないものの、動作しません。

そこでUnity Editor用のSubsystemを追加して、ARFoundationとは別の経路でiPhone実機から同等のデータを送信。このEditor用SubsystemをAR FoundationのハイレベルAPIに同じデータを転送。

f:id:asus4:20191207003135p:plain
エディタのSubsystemを追加

一番上のレイヤーから見ると、動作は何も変わっていないので同じコードで動作する気がします。

Subsystemの作り方

ここまでがわかったいくつかのSubsystemを自分で作ってみました。GitHubのリンクはこちらです

セッション管理をしているXRSesseionSubsystemをEditorで対応する例です。

// 仮想コード
namespace ARKitStream.Internal
{
    // PreserveはCode strippingでコードが消されないようにしてるっぽい。
    [Preserve]
    public class ARKitSessionRemoteSubsystem : XRSessionSubsystem
    {
        protected override Provider CreateProvider() => new ARKitRemoteProvider();

        // サブシステムの初期化時にUnityEngineから実行される
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
        static void RegisterDescriptor()
        {
            // UnityEditorのときだけサブシステムを作る
#if UNITY_EDITOR
            const string id = "ARKit-Remote-Session";
            XRSessionSubsystemDescriptor.RegisterDescriptor(new XRSessionSubsystemDescriptor.Cinfo
            {
                id = id,
                subsystemImplementationType = typeof(ARKitSessionRemoteSubsystem),
                supportsInstall = false,
                supportsMatchFrameRate = false
            });
#endif // UNITY_EDITOR
        }
        
        // サブシステム本体
        // 各サブシステムにはProviderと呼ばれる本体のクラスがいる。
        class ARKitRemoteProvider : Provider
        {
            //
        }
    }
}

かなりシンプルに書いていますが、このようにRuntimeInitializeOnLoadMethodアトリビュートでSubsystemを管理しているようです。RuntimeInitializeOnLoadMethodは今回始めて使いましたが、UnityEngineネームスペースにあるので、ARFoundation以外にもSubsystemを使っているライブラリがあるのかもしれません。

まとめ

  • AR Foundationを使うと簡単にマルチプラットフォームなARアプリを作成できます。
  • Editorでも一部の機能を動作させることができた。
  • Unity開発チームが来年はじめに、Editorで動くやつを公開してくれる予定。

以上です。

*1:ML-Agents付属のBarracudaのドキュメントが少ないので、詳細を書こうとしたのですが、玉砕したので内容を変更しています

*2:また別の選択肢とて1→10さんが開発しているZIG SIM PROもいいかもしれません。NDIとOSCで、ARKit3のいくつかの機能を送信できます。有料ですが、時間節約できるので一瞬で元取れます。

TensorFlow LiteをUnityから動かす その2

この記事は Unity #2 Advent Calendar 2019 - Qiita の1日目です。他の日程に面白そうなタイトルが沢山並んでいます。

目次

TL;DR;

TensorFlow LiteのUnity Pluginを使い MNIST, SSD, DeepLab, PoseNetなどの基本的なものを動作させるサンプルを作りました。
github.com

TensorFlow Liteとは

TensorFlowをスマートフォンRaspberry Piなどのデバイスで動かすことを目的としています。学習はTensorFlow本体のpythonなどでモデルを作り、それを*.tfliteというファイルに書き出すことで、色々なデバイスで推論部分のみを小さなプログラムサイズで動かせるようになっています。コア部分はC++でUnity Pluginも非常に簡単に作ることができます。

先日この記事を書きました。 asus4.hatenablog.com

その後、GPUで動かすことによる高速化。複数プラットフォーム対応、入力と出力の型が見えないなどの足りない機能を追加して、使えそうな感じになってきました。TensorFlow の本家リポジトリへも PRを送ってやり取り頑張っています。
Pull Requests · tensorflow/tensorflow · GitHub

TensorFlow Lite Unity Pluginの使い方。

MNISTと呼ばれる0から9の手書き数字識別するプログラムの使い方をサンプルコードで説明します。簡略化しているので実際に動作するコードはGitHubを見てください。

    // 入力の画像 28 x 28のグレースケール
    float[,] inputs = new float[28, 28];

   // 出力の10個の数字の確率
    float[] outputs = new float[10];

    void Start()
    {
        // モデルのbyte配列を読み込んで`Interpreter`を作る
        interpreter = new Interpreter(File.ReadAllBytes(path));

        // 入力のバッファを確保
        interpreter.ResizeInputTensor(0, new int[] { 1, 28, 28, 1 });
        interpreter.AllocateTensors();
    }
    
   void Update()
   {
        // 入力データをセット
        interpreter.SetInputTensorData(0, inputs);

        // なんか計算してくれる!
        interpreter.Invoke();
        
        // 出力のデータを取得
        interpreter.GetOutputTensorData(0, outputs);
    }

    void OnDestroy()
    {
        // Interpreterを開放
        interpreter?.Dispose();
    }

とてもシンプルにできています。サンプルでもコードの多くは、画像をinputsに変換する部分だったりします。
また次のコードの用に、コンストラクタのオプションで、マルチスレッドや、GPUを使った高速化などを設定できます。

    void Start()
    {
        // オプション
        var options = new Interpreter.Options()
        {
            // CPU時にマルチスレッドで動作 
            threads = 2,
            // iPhone, macでメタルGPU上で動かす
            gpuDelegate = new MetalDelegate(new MetalDelegate.Options()
            {
                allowPrecisionLoss = false,
                waitType = MetalDelegate.WaitType.Passive,
            })
        };
        
        // option付きのコンストラクタ
        interpreter = new Interpreter(File.ReadAllBytes(path), options);
    }

Android/iOSで書かれた公式のExamplesのいくつかをUnityへ移植したので、それぞれのサンプルの説明をしていきます。

MNIST

MLのHallo World的存在MNISTです。28 x 28 ピクセルの数字を 0 - 9までのどれが書かれたの確率を教えてくれます。

f:id:asus4:20191128200929p:plain
MNIST ネットワーク図
ネットワーク図を見るとぎょっとしますが、レイヤーの機能は理解ぜずとも、inputs/outputsの部分に注目すれば大丈夫です。

入力

index type dimensions 説明
0 float [28,28,1] 28x28のグレースケール画像です。0.0 ~ 1.0へ正規化します。

出力

index type dimensions 説明
0 float [10] 10個の0.0 ~ 1.0までの確率です

SSD - Single Shot MultiBox Detector

画像上の物体の名前と矩形範囲を推定する。サンプルに使ったモデルでは90種類の物体を認識します。

f:id:asus4:20191128201008p:plain
SSD ネットワーク図

入力

index type dimensions 説明
0 sbyte [1,300,300,3] 300x300のRGB画像です。それぞれのピクセルは 0 - 255

出力

認識されたオブジェクト上位10種の情報を返します。

index type dimensions 説明
0 float [10, 4] 10個のバウンディングボックスがtop, left, bottom, rightの順で入っています
1 float [10] 認識した物体の IDが入っています。floatだけど、intにキャストして使えば大丈夫
2 float [10] それぞれの確率が入っています。
3 float [1] 全体で認識した物体の数

DeepLab

SSDと似て、画像の中の物体を推定しますが、矩形ではなく、ピクセル単位で推定します。

f:id:asus4:20191128201042p:plain
DeepLab ネットワーク図

入力

index type dimensions 説明
0 float [1,257,257,3] 257x257のRGB画像です。それぞれのピクセルは 0.0 ~ 1.0 に正規化しています

出力

すべてのピクセルを21種類のラベルに分類します。

index type dimensions 説明
0 float [1,257,257,21] 257x257のピクセル全てにどのラベルの可能性が高いかのスコアがはいっています

PoseNet

画面上の人間の骨格を、2D座標で取得するプログラムです。

f:id:asus4:20191128201139p:plain
PoseNet ネットワーク図
直列のとてもシンプルなネットワーク図に見えます。その分ポストプロセスでやることがあります。

入力

index type dimensions 説明
0 float [1,257,257,3] 257x257のRGB画像です。それぞれのピクセルは 0.0 ~ 1.0 に正規化しています

出力

index type dimensions 説明
0 float [9,9,17] 9x9のブロックに分割したエリアそれぞれに17個のパーツIDの可能性が入っています。
1 float [9,9,34] 9x9のブロックのそれぞれのパーツIDの本当の場所へのオフセット x,y値
2 float [9,9,32] 今回1人の認識では使わない
3 float [9,9,32] 同上

使うモデルによってブロックの分割数、精度が変わります。

出力したデータの整形が一番面倒なモデルです。このブログが一番わかりやすかったです。 medium.com

正確さを犠牲にシンプルに言えば、

  • 9 x 9に分割したブロックそれぞれに、体のパーツ17種の確率が入っている。
  • 一番確率が高いパーツにオフセット値を足して、正しいXY座標を得る
  • 17x17分割のモデルを使うことで精度を上げることが可能。遅くなる

ようです。

Unityでの実装で注意すること。

  • Height, Widthの順番: Unityでは すべてのAPIwidth, heightで表されますが、TF Liteでは height, widthです。
  • モデルの種類によっては、GPUオプションが使えないものもある

TODOs

  • Android版は私の端末Sony Xperia XZ2で動作中にフリーズするので、原因を調査
    • 熱によるカメラのフリーズ問題のよう
  • Windows, Linuxプラグインを作る

まとめ

  • TensorFlow LiteはUnityでも使えそう
  • 公式のモデルをUnityで動くサンプルを作った

TensorFlow Liteの公式サンプルでは、Android/iOSネイティブは全く別の実装をしています。Unity PluginではAndroid/iOS/Unity Editorを同じプログラムで作れるので便利です。今回使ったのは決して最新ではなく、ある程度こなれたモデルですが、Unityで使えるようになることで、ゲーム組み合わせたりするとまた可能性が広がりそうだと思いました。
Texture2DのピクセルC#上で直接触っているところなどがあり、まだ高速化できる余地があるので、もう少し触っていきます。実案件へは未投入ですが、仕事で使いたいって相談もお待ちしています。

NativeArray<T>を高速にシリアライズ/デシリアライズ

高速化TIPSです。以前、ARKitの顔の頂点のデータをUnityEditorに送るプログラムを書きました。

mobile.twitter.com この様なことができます。

UnityEditorには毎フレーム以下のデータを送っています。

public NativeArray<Vector3> vertices; 
public NativeArray<Vector3> normals; 
public NativeArray<int> indices; 
public NativeArray<Vector2> uvs;

頂点の数が多くシリアライズ、デシリアライズがパフォーマンスのネックになっていました。直接ポインタをゴニョゴニョすると行けるのではないかと調べているとNativeSliceにSliceConvertという型を変換できるメソッドがあることに気づきました。これを使うと以下の様なことができます。

gist.github.com

使い方です。

NativeArray<Vector3> vertices;

// シリアライズ
byte[] verticesBytes = vertices.ToRawBytes();

// デシリアライズ
NativeArray<Vector3> vertices2 = NativeArrayExtension.FromRawBytes<Vector3>(verticesBytes, Allocator.Temp);

Tensorflow LiteをUnityで動かす

前回のBarracudaに続きTensorflow LiteをUnityで動かしてみました。

Unity上で学習するのはML-Agentで十分で。私の用途では、軽量、複数のプラットフォーム(iOS, Android, macOS, Windows)で動作可能なUnityのプラグインが必要でした。Tensorflow Liteはコア部分はシンプルなC言語APIで用途にあっていそうです。

Tensorflow Lite サイトより引用

Tensorflowリポジトリの奥深くに、Unity Pluginが隠されています。 tensorflow/tensorflow/lite/experimental/examples/unity/TensorFlowLitePlugin at master · tensorflow/tensorflow · GitHub

macOS, iOSで動作確認をしてみました。 以下、MNISTとSSDの動作サンプルです。

Barracudaとは別のMNISTモデルをつかったので、純粋な比較ではないのですが、Barracudaよりも高速でした。

GitHubリポジトリはこちら。 GitHub - asus4/tf-lite-unity-sample

Tensorflow Lite のUnity プラグインは非常にシンプルで。*.tfliteモデルのInputs/Outputsの型を確認するAPIがないのですが、NetronというElectron製アプリでいろいろなモデルを読み込み確認できて便利でした。

f:id:asus4:20191118183116p:plain
netron

github.com

またTensorflow LiteにはGPUデリゲートという機能があり、iOSはMetal, AndroidOpenGL ES 3.1 で計算することができます。macOSでもMetalで行けるはずなのですが、ソースコード中にUIKitに依存する場所があり、ちょっと手直し中です。できたらお知らせします。

WindowsGPU Delegate対応Unity Pluginはt-takasakaさんが作っていました。

github.com

追記

その後、macOSでもGPU Delegate動かすことに成功しました。

Add Build Setting TFLite Metal Delegate for macOS bundle · asus4/tensorflow@d0b45bd · GitHub

Unity製MLライブラリBarracuda

Unity上でMachine Learningについてリサーチしています。Unity上でMLというとUnity社が開発しているML-Agentsが最初に出てくると思います。Tensorflowとの連携ができ強化学習などのサンプルが多く見れます。

GitHub - Unity-Technologies/ml-agents: Unity Machine Learning Agents Toolkit

そのml-agentsの子プロジェクトとして最近Barracudaというものをハッケンしました。

ml-agents/Barracuda.md at master · Unity-Technologies/ml-agents · GitHub

説明を読むに、TensorFlowの学習済ファイル (*.pb)やONNXのモデルをBarracudaで読み込んで、Unityで動かしているようです。CPUもサポートしていますが、GPUは全部ComputeShaderで頑張っている模様。実際にMNISTを動作させるサンプルを作ってみました。しかしいまいちパフォーマンスがでず…。ひとまずソースコード乗っけます。

ソースコードはこちら↓

GitHub - asus4/barracuda-samples

MNIST.pbモデルをBarracudaのnnフォーマットに変換するGoogle Colab

colab.research.google.com