NatML + Unity + Local DL Modelでクロスプラットフォームアプリ実装 ~unity project解説編~　(4/n)

# 最終目標
android/iOS/OSX/windowsでDL推論アプリを単一プラットフォーム・単一言語でつくる。

# これまで
https://qiita.com/totti0223/items/b0407d2dfb323c9c0890

https://qiita.com/totti0223/items/7918e4392cb378aa052c

https://qiita.com/totti0223/items/fe28f5667469021683e1

# 今回の内容

一連の記事でLocaL DL Model + NatMLを利用したクロスプラットフォームアプリの開発を進めてきました。
中間まとめとして、カメラ入力推論をリアルタイムで行うUnity Projectをつくったので共有します。
https://github.com/phytometrics/NatML_playground/tree/main

*上のレポは開発途上で記事の内容と乖離していく可能性があります。執筆時点でのレポジトリは以下のブランチから確認することができます。なお、バグなどあった場合でもこのブランチでは修正されませんので注意してください。*
https://github.com/phytometrics/NatML_playground/tree/qiita


# 解説
- mobilenetv3largeをベースとしてOSXとiOS実機によるcoreml、Android実機によるtfliteの動作をそれぞれ確認しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156483/e3802998-3884-8a8f-6728-b9c36299bd4a.png)

## 環境設定
用意したunity projectでは既に必要なパッケージを記載しているため、初回起動時に必要なものが読み込まれる設定となっている（はず）ですが、０から再現するための手順も備忘録のため残しておきます。

### Unityモジュールの追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156483/d3380a03-ca28-7641-3f5a-b52cab50705b.png)

Add Modulesから以下のものを必要に応じて追加

- Android Build Support
- iOS Build Support
- Mac Build Support (IL2CPP)
- WebGL Build Support

### 新規プロジェクトの作成
- 2D Mobile Core プリセットで新規プロジェクト作成

### プロジェクトからNatMLの追加
Unityのプロジェクトからはmanifest.jsonが見えないので、別途プロジェクトのディレクトリを開いてmanifest.jsonを開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156483/d69b122f-2d2b-1b20-9e50-84198d73f776.png)

以下をmanifest.jsonに追記
```json
{
  "scopedRegistries": [
   {
    "name": "NatML",
    "url": "https://registry.npmjs.com",
    "scopes": ["ai.natml"]
   }
  ],
  "dependencies": {
    "ai.natml.natml": "1.1.8",
    ...
  }
}
```
保存してunityに戻ると必要なパッケージがダウンロードされはじめます。

## Android/iOSビルド用環境構築
- 個々の環境によって異なるので他のドキュメントを参照してください。
- 筆者の環境（OSX, M1 Mac）の場合、AndroidビルドのためにAndroid Studioから、SDKの最新版をインストールし、Google Pixel5のシミュレーター（AVD）もインストールしました。iPhone13の実機ビルドをするためにXcode 15 betaが必要で、App Storeからはまだ入手できなかったのでAppleの[Developer Store](https://developer.apple.com/jp/xcode/resources/)から最新版をダウンロードしてインストールしました。

## 今回用意したUnityプロジェクト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156483/9d215056-ceaf-b210-9639-49c404119de7.png)

playボタンを押すと開発PCのカメラとプラットフォームにあわせたモデルが選択され、推論が始まります。（windowsは未検証です）。ビルドプラットフォームを変えればandroidやiOSでの実機テストも可能なこと確認しています。それぞれの環境構築方法は本テーマと関係ないので記述しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156483/650aaec7-6560-ad81-62fc-73d9f58409bb.png)

## ファイル解説
- Assetsフォルダ直下に必要なスクリプトが全て含まれています。
- Assets/StreamingAssetsにMobileNetV3Largeを各種形式に変換したファイルが格納されています。

### スクリプトファイル
- CameraController.cs
    - カメラ入力取得・モデル推論・画面描画のコード。
- MLClassificationModelPredictor.cs
    - NatMLのモデル定義コード。[NatML + Unity + Local DL Modelでクロスプラットフォームアプリ実装 ~コアコード編~（1/n）](https://qiita.com/totti0223/items/b0407d2dfb323c9c0890)を参照。
- PermissionController.cs
    - AndroidやiPhoneでカメラ・マイクパーミッションを取得する周りのコード。utibenkei氏のqiita記事[UnityでiOSとAndroidのランタイムパーミッションの確認と要求を行う方法](https://qiita.com/utibenkei/items/65b56c13f43ce5809561)をそのまま利用させてもらいました。先人に感謝。
- PathManager.cs
    - モデルファイル(onnx, mlmodel, tflite)のパスを管理し、tfliteの場合はモデル読み込みに先立ってstreamingassetsからpersistent data pathにファイルをコピーし、そのパスを返すスクリプト。
- Rotate90.shader / Rotate90.material
    - 画像を90度回転させるシェーダ。Androidやiphoneの場合、縦向きに持ったスマホに対して90度にカメラが取り付けられている場合が多いため、そのままカメラ入力をとると90度傾いたまま表示されてしまう。対応策として
        - 描画先のオブジェクト(RawImage)を90度回転させる
        - 毎フレームtexture2Dを回転させる
        があげられるものの、前者は画像自体が回転したわけでないので、モデル推論では画像が回転前のまま。後者はCPU負荷が高いのでおすすめされないらしい。
    - 代替案として、90回転させるシェーダを書き、CameraController.cs内部でGraphics.Blitで画像を回転させるようにした。GPUで全部処理できるので速いとのこと。**ここらへんはChatGPTに相談しながら実装したのでベストプラクティスかどうかは全くわかりません。**

## 展望

- 現状のFPSはフレーム更新計算なのでボトルネックであるカメラのFPS（20~30）付近の値になっていると思われる。推論にかかった時間も別途表示したい。
- カメラ画像を無理やりMNV3Largeが要求する128x128にリサイズしていて、さらにはアスペクト比も設定を確認していない。もしかしたらかなりdistortedな写真が推論されているかもしれない。cropするなりなんなりの前処理る実装をしたい。
- classificationは実装できたのでdetection / segmentation / image to image translationあたりに取り組みたい。画像が帰ってくるanime filterとかテーマとしてよさそう、、かも？
- NatMLが扱うinput outputの型式についてもうちょっと勉強する