Onnx RuntimeをUnityで動かす

Onnx Runtimeをネイティブプラグインとして、Unity上で動かす実験とサンプルを公開しています。

github.com

開発の動機

4年前に、TensorFlow LiteをUnityで動かす実験を初めて、 はじめは全くの趣味で始めたものが、今では海外からいただく相談の半分以上が機械学習関連になっています。

四年前に始めた実験↓ asus4.hatenablog.com

ところが、実際にシェアを見ると、研究関連ではPytorchのシェアが圧倒的。Unityの公式推論ライブラリBarracudaやTensorFlow Liteで動かすために一旦Onnxに変換するなどの事例なども増え始め、速度的にはTFLiteは非常に満足していますが、サクッとモデルを試してみたいという時に、変換するのが億劫になってきていました。公式ツールで変換しようにもOnnxやPytorchのNCHWからTFLiteのNHWCに変換するときに大量のTransposeが挟まり速度が逆に遅くなることがあるのが不満でもありました。(この辺の高速化は PINTOさんのonnx2tfなどのツールでも対応されています)

Unity SentisのOnnx対応

Unityの公式ML推論ライブラリBarracudaもOnnxフォーマットを読み込みます。今年リニューアルしてSentisという名前になりました。 unity.com

実際試したことがある方はわかるかも知れませんが、SentisではOnnxフォーマットを読み込みますが、実際の実行エンジンはUnityが独自開発しているため、対応オペレーターに結構差があります。Onnx Runtimeで動いてもSentisで動かないことがままあります。(体感的には読み込み成功の打率は半分以下な気がします。)

Sentisの前身、Barracudaでの説明ではありますが、KeijiroさんによるCEDEC公演でも、後半、結構トリッキーなことをして、Onnxモデルの非対応オペラーターをBrracuda上で対応する構造に書き換えるということをしています。

youtu.be

Sentis自体はマルチプラットフォーム対応を謳い、今後対応オペレーターの互換性も増えていくと思いますが、Onnx自体の進化も早く、今後完全なOnnx互換となることは難しいように思います。

もちろんNintendo Switch, PlaystationからWeb Playerまでを対応しなくては行けないUnityが、独自推論エンジンSentis開発を進めることは正しく思いますが、私のようにiOS,Android,PC,macOSくらいで動けば良いユーザーからすると、Onnxの互換性が高くなると嬉しいなと思っていました。

Onnx Runtimeの対応プラットフォーム

一方、Onnx Runtimeの方の進化も早く、最新のHardware Acceleration対応を見ると、

と、はじめからPC, Mobile, さらにRaspberry PiのようなEdge Deviceまでを考慮に入れたプラットフォーム対応に見えます。
Googleの開発するTensorFlow Liteが、PC上でのHardware Accelerationが未だに公式にサポートされてないことを考えると、Onnx Runtimeのマルチプラットフォーム対応は、もしUnityでそのまま動けば大きな強みになりそうです。

またMicrosoftが開発していることもありC#によるC FFIライブラリのWrapperが、はじめからほぼ全て整備されていることも魅力でした。
TensorFlow Liteのときは半分以上自分でC#FFI Wrapperを作っていたので、関心しました。

実際のサンプル

という経緯から、OnnxがUnity上で動くか試し始めたのですが、
Onnx RuntimeのC#設計が良いのかTFLiteでの経験が生きているのか、Unityで動かすのは、すんなりいきました。
現在、macOS, iOS, Androidでの動作を確認しています。

AppleMobile OneというImage Classificationが100fpsで動く例↓

Yoloxが60fps以上で動く例↓

またライブラリに含めるサンプルも4年前からアップデートして、新し目のモデルを選んだので、4年間でのモデルの精度、高速化技術の向上に驚きました。

Onnx Runtime for Unityの使い方

シンプルな事例、Image ClassificationモデルMobile Oneで説明します。

MobileOne Network

Netronでモデルの入出力をみてみるとMobileOneは224x224のRGB画像を受取り、ImageNetの1000種類の画像分類それぞれの確率を返すシンプルなモデルです。

画像を入力として受け取るクラスでやることは大体同じなので、ImageInference.csというPreprocessingをやってくれる抽象クラスを用意しました。

Preprocess

通常はImageInference内でやってくれるので、気にする必要はありませんが、概要だけ書きます。

  • UnityのWebカメラはPC上では問題ないが、スマートフォンでは回転したままなので、デバイスの向きに応じて回転を補正する。TextureSource内で自動でやってくれます。
  • モデルの入力に合わせて、画像をリサイズする。3種類のリサイズ方法を用意しているので、用途に合わせて使い分けてください。
    Aspect Mode
  • Onnxモデルの入力に合わせたTensorを作る。
    • mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]の正規化をする。
    • カメラ画像のテクスチャはNHWC(N=1)のメモリ配列なので、Onnxのメモリ配列NCHWに並べ替える。

Onnx実行

SessionOptions.Run()を実行するだけです。 いくつかInput/Output Tensorの取得方法があるのですが、生のbyte配列を受け渡すより、OrtValueを使う方法がおすすめされているようです。 こちらも通常はImageInference内でやってくれます。

// 事前にモデルから読み込んでOrtValueを作っておく
string[] inputNames;
OrtValue[] inputs;
string[] outputNames;
OrtValue[] outputs;

public void Run()
{
   // inputsで画像をTensorへいれる。
   PreProcess();

   // 実行
   session.Run(null, inputNames, inputs, outputNames, outputs);

   // outputsから値を取り出す。
   PostProcess();
}

Postprocess

MobieOne.cs内でPostProcessメソッドをoverrideしています。

protected override void PostProcess()
{
    // Output Tensorから値を読み込み
    var output = outputs[0].GetTensorDataAsSpan<float>();
    for (int i = 0; i < output.Length; i++)
    {
        labels[i].score = output[i];
    }
    // スコア順に並べる
    TopKLabels = labels.OrderByDescending(x => x.score).Take(topK);
}

以上です。C#APIがよく出来ているので、カスタマイズも色々出来そうですが、ひとまずは一番シンプルな方法を使いました。

まとめ

Onnx RuntimeをネイティブプラグインとしてUnityで動かしているプロジェクトをこちらで公開しています。 github.com

細かい開発記録はZenn Scrapへ残しています。 zenn.dev

もし需要がありそうなら時間を見つけて、Windows, Linux対応も追加したいと思います。
また少し難易度は高そうですが、Web Player対応も出来るといいなと考えています。

Unity ARFoundaitonを録画してUnity Editorで再生する方法

ARFoundationReplayというARを録画してUnity Editor上で再生するライブラリを開発中です。

GitHubリポジトリはこちら↓ github.com

ARFoundaitonReplayで出来ること

Unity Editor内で動作するAR Foundationです。ARを録画した専用動画をEditorで再生することが出来ます。私が普段参加するARプロジェクトでは、Unity Editor上でAR Foundaitonが動かないため、 #if UNITY_EDITORなどを用いて処理をまるっと分けることが思いです。しかしこの分岐が原因で実機でテストすると思わぬバグに出くわすことも多々あります。

このライブラリではUnity Editor上で録画したARをAR Foundationの上で動かすことで、そのような分岐をまるっとなくすことを目的としています。

仕組みはARを独自の映像フォーマットとして保存します。mp4は動画や音声を保存するフォーマットですが、字幕データを保存したり出来るように、実はバイナリデータも同時に保存できます。
UnityのKeijiroさん作Bibcamをベースに、タイムラインと同期する任意のバイナリを保存出来るようにしました。これは、ARCoreやARKitで行われているAR録画と似たような仕組みです。

Video Format for ARFoundaitonReplay

この動画フォーマットには6DOFのカメラ座標、認識した床や壁などの平面メッシュなどのARの再生に必要なメタデータが含まれているので、以下の動画のように、Unity Editor上で再生して、シミュレーションすることが出来ます。

どうして作った?

開発にあたりいくつかのライブラリを調査しました。

- AR Foundation Simuraiton

実はARFoundation5からは、ARFoundation内にARシュミレーション環境がつきました。 Unityで、3Dモデルで部屋を作って、その部屋の中でARを動かしているシュミレーションが出来ます。

Fig for ARFoundation Simulation

以前のUnity公式xRシミュレーションUnity Maseは一年600ドルという、中々手を出しづらい金額でしたが、こちらは無料です。AR Foundation公式という安心感もあります。

しかしVPS(ビジュアルポジショニングシステム)という屋外の自分のいる位置をGPSとカメラ画像などのセンサから推定するARが流行り始めて、私の実際の案件でもARFoundation Simulationでは対応できないケースが増え始めました。

- AR Foundation Remote 2.0

多くのAR開発者に実際に使われているソリューションとしては、AR Foundation Remote 2.0でしょうか。ほとんどの機能をサポートしていて、VPSも使えます。実際私も使っています。

かなり理想形なのですが、Unity Editorとつなげて録画が必要なので、開発者以外の人がARを録画するのが難しいという難点がありました。TestFlightで配布したアプリだけで録画できるとベストです。

- ARCore, ARKit ReplayData

ARFoundationは、Android ARCore, iOS ARKitのラッパーでもあります。そして、
iOS ARKitではReality ComposerというアプリからReplay Dataという動画ファイルを録画して、実機で再生できます。
Android ARCoreにも録画再生の仕組みがあります。更にAnrdoidでは独自の追加トラックもいれることが出来ます。

もちろんUnity製アプリでもビルドした状態で、これらの機能を使うことは出来ます。しかしこれらの機能はEditorからは使えません。Unityの良さのEditor上でアプリを動かしながら、いろんなパラメーターをいじって、開発のイテレーションを回すということができませんでした。


このような理由から、自分の理想に近かったUnity JapanのKeijiroさんの開発していたBibcamをベースに開発を始めました。

開発大変

去年の12月くらいにふと思いたち、手を動かし初めてみたものの、一歩でもUnityの内部挙動に踏み込んだ機能を作ろうとするとUnityのバグに突き当たり、開発が中断したりと大変でした…。私のライセンスではUnity内部のソースコードにアクセス出来なかったので、ただ修正を待つしかないのが辛かったです。Unreal Engineなら自分で直す余地もあるのですが。

いくつか起こった出来事を抜粋します。

  • M1 Macでフリーズする (修正済)

当初はM1 Mac上でを使うとフリーズする状態でした。AR Foundation Remoteの作者&コミュニティと私くらいしか困っていなかったけど、1年以上も進展がなかった後(AR Foundation Simulation機能を開発して気づいたのかもしれません)、修正されました。

  • UnityのVideoPlayerクラスがデグレーション (修正済)

URP14.0.2のバグ対応のためにUnityのバージョンを上げなくてはいけないけれども、アップデートしたUnityバージョンではVideo Playerがリニューアルされ、今まで動いていた動画ファイルが再生できない…という進むにも戻るにもできない状態になりました。 再現可能なミニマムプロジェクトを添付したバグレポを提出して2ヶ月修正を待ちました。(自分のターゲットにしている2022LTSにバックポートされるまでには半年待ちました。) - Unity Issue Tracker上のバグ詳細

  • XR SDKのドキュメントにない仕様

AR FoundationはUnity XR SDKというXR用のネイティブライブラリの元で動いているのですが、サンプルプログラムは動作するのに、自分のプロジェクトにサンプルプログラムをコピペすると全く動かない…という問題がありました。数日あーだこーだ悩んだ後、EditorでXR SDKを動かす場合は、package.jsonkeywordsxreditorsubsystemという文字が入っていないと認識しないという仕様でした…。XR SDKのドキュメントを見渡しても一切書いてなく、そのキーワードに言及しているのはAR Foundation Remoteの作者とNeedle Toolesの作者だけでした。package.jsonの設定項目じゃなくて検索用のkeywordsにこんな重要な設定項目をいれるなんて思いませんでした。

キーワード知ってないとこのリンクにもたどり着かないし、キーワードを知ってるということは解決済みという悲しいやつです。せめてこの記事を見つけた方に届きますように。

もし開発記録に興味があれば、Zennのスクラップに残しているのでご覧ください。 zenn.dev

まとめ

現在開発中のARFoundationReplayを紹介しました。

前回の記事で作ったARアプリでは、このライブラリを実際に使っています。別のAR案件では、Editor上では動くのに実機では動かないということが頻発することがよくありましたが、このプロジェクトではほぼその問題が起こりませんでした。実機のみで起こったの現象としてはAndroidでメモリが足りなくて落ちるくらいでした。

asus4.hatenablog.com

まだ自分が使うSubsystem部分しか作ってはいませんが、Unity Editor上で実機ARと同じデータがみれることで、ARアプリであるあるの実機で現場にテストした時に動かない…。現場でラップトップを開いてビルドし直す…。みたいなケースがほんの少しだけ減る気がします。 今後も、AR案件をやるたびにちまちま自分で使う機能を追加しようと思います。

GoogleのARハッカソンでBest賞をもらいました

GoogleのGeospatial APIを使ったARハッカソンGoogle’s Immersive Geospatial Challengeで、友人の音楽家 Twothと一緒に制作したARアプリが、AR | Best of Entertainment & Events というARのエンタメ部門で1位の賞をもらいました。

GoogleのDeveloperブログに取り上げてもらいました。一番目のコンテンツ、World Ensembleが私達のプロジェクトです。
developers.googleblog.com

説明動画も作りました。(Adobe全部アンイストールしてたので、初めてDaVinci Resolveで編集しました。)
www.youtube.com

他の受賞プロジェクトも面白いアイディアが沢山ありました↓
Google’s Immersive Geospatial Challenge: Go beyond the map in Google’s Immersive Geospatial Challenge - Devpost

Behind the Scenes

普段のフリーランスとしての受託プロジェクトでは中々制作過程を見せることは出来ないのですが、このプロジェクトではソースコードを公開することが参加ルールの一つでした。
折角なので少し制作の裏側を残します。

またクリエイティブ・コモンズ ライセンスでGitHubにプロジェクト一式を公開しています。(音も一緒にCCライセンス公開に同意してくれてありがとうございました。)

github.com

Visual Effects

AnroidでARアプリを作るとき、最新ARCore NativeではついにVulkan対応が入りましたが、Unityから使う際のARFoundaitonは未だにOpenGLESでの実行になります。そのためにUnityの新しいVFX機能であるVFX Graphを使えませんでした。VFX GraphはOpenGLESに対応していないのです。ARアプリでリッチなVFXを作りたいときの一つの障壁とも言えます。
そのため、今回はシンプルなShiriken Partilceを基本に構成しています。その代わり、パーティクル一粒一粒のシェーダーをすべてカスタムすることにしました。
シンプルなShuriken Particleを使っても、一つ一つのパーティクルの見た目にこだわれば結構行ける感触がありました↓

シェーダーは基本的にはShaderGraphで作りました。

今回はAR空間で映える見た目と言うことで、Scene Color Nodeという、現在カメラにレンダリングされているテクスチャにアクセスするノードを多用し、ガラスっぽかったり(光学的な計算はまったく間違っています)現実空間の建物をグリッジ風にしたりしてみました。

Bubble shader graph

モバイル向けに重いShaderは、以下のように、ShaderGarphの中でHLSLのコードを叩くという方法を使ってます。
URPが主流になってからというもの、いろいろな種類のPassやSingle-pass instanced renderingの対応などが面倒くさくなって、生のShaderを書かずに、こんな感じのハイブリッドな構成が多くなりました。複雑なロジックはHLSLで書きつつ、複数の実行環境への対応をShaderGraphがよしなに追記してくれるので結構気に入っています。

Cecium TileSet Shader optimization

3Dサウンド

音はTowthに作ってもらった音をARの3D空間上に配置しています。内部的に16 stepシーケンサーを動かして、3D上でタイミングが来ると音がなるようになっています。
実験的な機能として、屋外のでかいARコンテンツなので音速が考慮に入れて、聴取者までの距離によって音速の遅延をいれるなど、色々遊びました。
この時に使いにくかった、SFX Reverbの使い方をZennに記事を残しています。

仮想空撮 → 現実空間ARへのトランジション

Aerial to AR

今回、実験したものの一つに、Cesiumで読み込んだ3D tilesから AR画面へのトランジションでした。空をタップすると、上空100mへジャンプして、自分のAR画面へ戻って来ます。これは試しに、GeoSpatialPoseのGPS座標とEast-Up-North rotation 、 を元に、Cesium上の3Dモデルに重ね合わせてみると思ったより精度が高く、面白い演出になりそうだったので、急遽取り入れた機能でした。
コア部分は以下のような3行で実現可能です。

var geoPose = _earthManager.CameraGeospatialPose;
_globeAnchor.longitudeLatitudeHeight = new(geoPose.Longitude, geoPose.Latitude, geoPose.Altitude);
_globeAnchor.rotationEastUpNorth = geoPose.EunRotation * _rotationOffset;

AR シミュレーション in UnityEditor

今回、11月20日の締切に対して、空のUnityプロジェクト作って手を付け始めたのがは11月7日、2週間前でした。仕事なら炎上どころではないスケジュールです。…し、別のAR仕事が忙しかったので…。

いくつかのVPS (Visual Positioning System) ARアプリを開発をして来ましたが、必ず出くわす問題が、Unity Editorでは動くのに実機で街に出て動かすと全然動かない…。というやつです。

もちろん普通のアプリでも起きますが、VPSは、GPSの精度、ネットワーク速度、遠くのメッシュは読み込まれてない…。などなど現実の空間でしか起こらない環境要因が盛り沢山です。

そんななか、今回の高速開発を支えたのが、去年から空き時間にちまちまR&Dしていた、ARFoundation Replayです。これは4年前に開発していたARKitStreamerの後継ライブラリです。

以前のものと同じく、Unity Editor上でAR FoundationのSubsystemを動かしますが、以前はPCとWi-FiもしくはUSBで接続したiPhoneから送られてくるリアルタイムのデータしか対応していなかったものが、今回は、VPS対応のために、出先でiPhoneアプリ上でARを録画、Photoに保存して、家に戻って、PCで動画ファイルをUnityで読み込むとAR環境が再現されるようになりました。
ARCore Geospatial APIから取得できるの地球上のGPS座標、近隣のStreetscape Meshを録画し、Unity Editor上で再生することで、家に引きこもりながらARアプリの開発が出来るようになりました。詳しい仕組みについては別記事で解説する機会を作ろうと思います。

この機能により、ほぼ実機で起きるGPS精度起因の問題などをEditorでパラメーターを調整しながら事前に修正することができました。このライブラリの目標の一つであった、 Editorで動くものは実機でも動くEditorで動かないないものは実機でも動かない。 を達成できたと思います。唯一実機で起きたエラーは、メモリが少なくて落ちるくらいでした。

ちなみにARFoundation 5.0以降では、XR Simulationという機能があります。
Simulation Simple AR
任意の3DモデルをUnity Editor上でAR空間としてシミュレーション出来るものです。通常の屋内ARコンテンツを作るときは非常におすすめです。

最後に

普段の仕事で2週間(仕事しながらなのでもっと少ない) でARアプリを作るようなことはないですが、
体験のコア部分だけに集中して作ることで大幅に時間を節約。
実験して良かったものは仕様になくても取り入れる。
バグは夜更け過ぎに仕様に変わる。

というハッカソン特有の制作スタイルでしたが、なにか形にのこり良かったです。
受賞特典の一つであるGoogle AR開発チームとのMTG緊張するな。

Gorillaz presents… Skinny Ape

お仕事報告。Nexus Studiosのかたに声をかけてもらい、NYタイムズスクエアとLondonピカデリーサーカスでみれる、GorillazのARコンサートアプリの技術調査を少しだけお手伝いされていただきました。

www.youtube.com

フルクレジットはYoutubeのDescriptionに。

KAGAMI

坂本龍一とTin DrumのxRコンサートKAGAMIのVFXを担当させていただきました。

https://theshed.org/program/299-kagami-by-ryuichi-sakamoto-and-tin-drum

factoryinternational.org

コンサートはアメリカ、ニューヨークとイギリス、マンチェスターで開かれます。また2024にはオーストラリア、シドニーでも公演がある予定です。

去年から制作に携わってきましたが、コンサート発表前に亡くなり、意味合いも全然違うものになりました。ご冥福をお祈りします。

レビュー記事

英語ではありますが、いくつかレビュー記事を見かけたのでリンクを張ります。

www.nytimes.com

www.forbes.com

www.wired.co.uk

medium.com

日本語記事は有料でした。

bijutsutecho.com