シトロンです。
TouchDesignerの記事は初めて書きます!(ハードル下げ)
普段は、大学や研究所でTouchDesignerの力を借りて研究システムの開発や結果解析などをしています。
今回の執筆の趣旨は、TouchDesignerにonnx簡単に導入できるっぽいからみんな使おうぜ!です。
RobustVideoMatting(RVM)とは
YouTubeのショートリール動画がわかりやすいです。
いわゆる人物切り抜きのネットワークモデルです。
RobustVideoMatting(RVM) はそのうちの一つですが、高品質かつ高速に動作するということで注目されています。
Cedroさんのツイート(より詳細な説明のブログあり)
ブログを書きました!
— cedro (@jun40vn) November 22, 2021
通常、動画の背景を後で編集したい場合は、その動画を撮影するときに背景をグリーンバックにしますが、これは結構面倒です。そこで、今回ご紹介するのは、撮影済みの動画の背景をグリーンバックに変更するRVMという技術です。
ブログ:https://t.co/PPI1FIdh0I pic.twitter.com/pZktORRFGc
Githubのプロジェクトページを見ると、このモデルのonnxが公開されているのでonnxruntimeを使用して、TouchDesigner上で動かしてみました!
このブログでは、全くゼロの状態からRVMを動かすまでを説明します。
だれでも簡単に導入できるっぽいから遊ぼうぜ!という意図で作りました。
onnxruntimeとは
onnxruntimeとはDeepLearningとして学習されたモデルを動かすためのライブラリ(推論エンジン)です。
特徴として
- onnx形式に変換されたあらゆる学習モデルはonnxruntime上で動かせる
- onnxruntimeはWindows, Mac, Linux, Android, iOSに対応しているので、onnxに変換されたモデルはどのデバイスでも実行できる
(ただし、現実には性能要件などに制限されます。) - CUDAに対応しているonnxruntime-gpuもあるので、NVIDIA GPUのパワーで高速に動かせる
- onnxruntimeはC++, C#, Pythonから利用できるため、使いやすい
と、強力なランタイムとなっています。
今回は、Python上からonnxruntimeを利用できるようにし、さらにTouchDesigner上でそのPythonを動かしたいと思います!
pyenvとvenvとは (使用する理由)
pyenvとは、Pythonのバージョンを切り替えられる仕組みです。
また、特定のバージョンのPythonを導入するのにコマンド一発でインストールできるため便利です。今回は、TouchDesigner上で動いているPythonのバージョンと揃える必要があるため、利用します。
venvとは、virtual environment、仮想環境を作る仕組みです。
Pythonで使用するライブラリ(numpyやonnxruntimeなど)をどかっと揃えられます。さらに、プロジェクトごとに使用するライブラリ環境を切り替えられるため、複数のonnxを走らせたいときや違うTouchDesignerとの競合を発生させないために重宝します。
Python(pyenv + venv)導入
最新のTouchdesignerをインストールする(2022.29850)
TouchDesigner上のpythonのバージョンを確認する
左上からTextport and DATを押してPython version確認
渡しの場合は最新のTouchDesignerで、3.9.5でした。
pyenvをインストールする
pyenvとは、複数versionPython共存させるシステムです。
pyenvを使ってPython 3.9.5をダウンロードします。
にアクセスして、Quick startの章に従ってPowershell上でpyenvをインストールします。
手順5のpyenv install <version>
は
pyenv install 3.9.5
と実行し、python 3.9.5をインストールします。
インストールが終わったら、
pyenv global 3.9.5
を実行し、python 3.9.5をアクティベートする。
Pythonを起動して無事3.9.5であることを確認しましょう!
venvを入れる
まず、自分のプロジェクトフォルダをエクスプローラーなどで適当に作成して、そこに移動(cd)します。
cd ディレクトリパス
Powershell上でディレクトリを移動したら、
python -m venv RVM
などとして、RVM用の仮想環境を用意する
RVMの仮想環境は作成したので、それをPythonが利用するようにします。(アクティベート)
.\RVM\Scripts\activate
仮想環境の使用をやめるときは、
deactivate
で抜けられます。
onnxruntime環境を構築
onnxruntimeをinstallしてversionを確認する
まず、pipでonnxruntime-gpuを入れましょう(仮想環境をアクティベートした上でね!)
pip install onnxruntime-gpu
そしたら、インストールしたonnxruntimeのversionを確認します。(多分1.13)
pythonを起動して
import onnxruntime
onnxruntime.__version__
でバージョンが確認できます。
Tips
pip install onnxruntime-gpu==1.12
などとすることで、指定のバージョンのonnxruntimeをインストールできます。
対応したCUDAとcuDNNを導入
onnxruntimeとcudaとcudnnには依存関係があるので、公式のドキュメントを見ます。
私の環境では、onnxruntimeのバージョンは1.13だったので、CUDA11.6とcuDNN8.5.0.96を入れる必要があると分かりました。
上のページから11.6.2のCUDAをダウンロード
インストールを実行して進めていると
インストールオプションで高速(推奨)とカスタムがあるので、カスタムを選択する。
高速の説明を読むとわかるが、高速はDisplay driverを上書きしてしまう。既存の開発システムの依存関係を破壊してしまう可能性があるので、カスタムが良い
次のページのカスタムインストールオプションでは、CUDAにだけチェックを入れて、次へ進む
インストールが終わったら、次はcuDNNのダウンロード&インストール
上のページから8.5.0をダウンロード
ここで、Nvidiaのアカウントが必要になります!持ってない人は作る必要があります!
ダウンロードしてZIPを展開したら、bin, include, libの3つのフォルダをCUDAの所定の場所に移動します。
お世話になったサイト
https://self-development.info/onnx-runtime(gpu版)のインストール/
https://self-development.info/tensorflow-1系gpu版のためにcuda-10-0をインストール/
RVMに必要なmodelを導入する
onnxファイルをダウンロードします。
のダウンロードの章からrvm_resnet50_fp16.onnxを自分のプロジェクトフォルダにダウンロード
TouchDesignerとPython仮想環境をつなげる
作成した仮想環境にTouchDesignerからアクセスできるようにします。
TouchDesignerを起動して、左上のEditからPreferenceを開く。
GeneralのページにAdd External Python to Search Pathというチェックがあるので、チェックされていることを確認する。
下にある、Python 64-bit Module Pathにvenvで作成した仮想環境フォルダのパスを入れる。
例えば、RVMという仮想環境名だったら、RVM/Lib/site-packagesまでの絶対パスを入れる。
SaveしてPreferenceを閉じる。
site-packagesが有効であることを確認するために、TouchDesignerを再起動し、textport DATを開き、
import onnxruntime
を実行してみる。なんのエラーも出なけれOK!
以下のtoxをダウンロードして、カメラ画像などを突っ込めば、結果が見れると思います!
以下、toxの解説part(本編)
onnxを実行するプログラムを書く
https://github.com/PeterL1n/RobustVideoMatting/blob/master/documentation/inference.md#onnx
Robust Video Mattingのプロジェクトページを見ると、モデルを読んで、推論ループを回すだけで良さそう。(かんたんでありがとう🙏)
大事なのは、specに書いてあるsrcのshapeすなわち次元構成を適切に用意してあげること。
numpyArrayとなったテクスチャは[h, w, c]の三次元でデータが格納されています。ここでhがy座標、wがx座標、cがRGBAのチャネルになります。
RVMのドキュメントを読むと、これを[B, C, H, W]の次元形式に再配置します。
そのために、以下のような変換をします。
1行目では、[h, w, c] -> [c, h, w]に変換しています。(同時に、16bitfloatにキャスト)
2行目では空のバッチ次元Bを追加し、c次元についてRGBAの4チャネルからRGBの3チャネルに変更しています。
(...はtempの残りの次元を表現する省略記法)
temp = np.transpose(npArray, [2, 0, 1]).astype(np.float16)
src = temp[np.newaxis, [0,1,2], ...]
結果として、onnxを回すスクリプトは以下のようになりました。
import numpy as np
import onnxruntime as ort
res = [op('script_gpu').inputs[0].width, op('script_gpu').inputs[0].height]
# load onnx file
sess = ort.InferenceSession('\\RVM\\model\\rvm_resnet50_fp16.onnx',
providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
io = sess.io_binding()
rec = [ ort.OrtValue.ortvalue_from_numpy(np.zeros([1, 1, 1, 1], dtype=np.float16) , 'cuda')] * 4 # Must match dtype of the model.
downsample_ratio = ort.OrtValue.ortvalue_from_numpy(np.array([1.0], dtype= np.float32), 'cuda')
for name in ['fgr', 'pha', 'r1o', 'r2o', 'r3o', 'r4o']:
io.bind_output(name, 'cuda')
def onCook(scriptOp):
global rec
# download the input texture to the script TOP into a NumPy array
npArray = scriptOp.inputs[0].numpyArray(delayed=True)
# transpose for RVM
temp = np.transpose(npArray, [2, 0, 1]).astype(np.float16)
src = temp[np.newaxis, [0,1,2], ...]
# run RVM
io.bind_cpu_input('src', src)
io.bind_ortvalue_input('r1i', rec[0])
io.bind_ortvalue_input('r2i', rec[1])
io.bind_ortvalue_input('r3i', rec[2])
io.bind_ortvalue_input('r4i', rec[3])
io.bind_ortvalue_input('downsample_ratio', downsample_ratio)
sess.run_with_iobinding(io)
# get output
fgr, pha, *rec = io.get_outputs()
# convert to numpy array
pha = pha.numpy()
# copy the NumPy array into the texture
scriptOp.copyNumpyArray(np.transpose(pha[0], [1,2,0]).astype(np.float32))
return
Result
今回投稿するTouchDesignerのAdC
— シトロン (@CitronSeason) December 2, 2022
こんな感じの背景切り抜きができます! pic.twitter.com/TGpZvEwZJL
実行結果はこんな感じで、60fpsで高精度に実行できました。
Nvidia Backgroundよりきれいかつ悪い撮影環境でもきれいな切り抜きが期待できそうです。
実はこの結果は1fだけ遅延していますが、
scriptOp.inputs[0].numpyArray(delayed=True)
をdelayed=Flase
とすれば遅延はなくなります。(すこしパフォーマンスが悪くなる。)
一方で、それでもシングルプロセスのTouchDesignerでは、この処理により律速してしまいfpsが低下する可能性があります。
その場合は、刻みTDやEngineCOMPでマルチプロセスかすると良さそうです!
toxファイルを置いておきます!(環境を構築したら使えるはず!)
本当は、他のonnxも回してみたかったけど、とりあえずここまでで失礼します!