18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TouchDesignerAdvent Calendar 2022

Day 3

高速な人物切り抜きをTouchDesignerに実装してみた

Posted at

シトロンです。
TouchDesignerの記事は初めて書きます!(ハードル下げ)
普段は、大学や研究所でTouchDesignerの力を借りて研究システムの開発や結果解析などをしています。
今回の執筆の趣旨は、TouchDesignerにonnx簡単に導入できるっぽいからみんな使おうぜ!です。

RobustVideoMatting(RVM)とは

YouTubeのショートリール動画がわかりやすいです。

いわゆる人物切り抜きのネットワークモデルです。
RobustVideoMatting(RVM) はそのうちの一つですが、高品質かつ高速に動作するということで注目されています。
Cedroさんのツイート(より詳細な説明のブログあり)

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のバージョンを確認する

 2022-12-01 18.24.39.png

左上から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をダウンロード

インストールを実行して進めていると

スクリーンショット (4).png

インストールオプションで高速(推奨)とカスタムがあるので、カスタムを選択する。

高速の説明を読むとわかるが、高速はDisplay driverを上書きしてしまう。既存の開発システムの依存関係を破壊してしまう可能性があるので、カスタムが良い

スクリーンショット (5).png

次のページのカスタムインストールオプションでは、CUDAにだけチェックを入れて、次へ進む

インストールが終わったら、次はcuDNNのダウンロード&インストール

上のページから8.5.0をダウンロード

ここで、Nvidiaのアカウントが必要になります!持ってない人は作る必要があります!

スクリーンショット (8).png
ダウンロードして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のチャネルになります。
image.png
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

実行結果はこんな感じで、60fpsで高精度に実行できました。
Nvidia Backgroundよりきれいかつ悪い撮影環境でもきれいな切り抜きが期待できそうです。
実はこの結果は1fだけ遅延していますが、
scriptOp.inputs[0].numpyArray(delayed=True)
delayed=Flaseとすれば遅延はなくなります。(すこしパフォーマンスが悪くなる。)

一方で、それでもシングルプロセスのTouchDesignerでは、この処理により律速してしまいfpsが低下する可能性があります。
その場合は、刻みTDやEngineCOMPでマルチプロセスかすると良さそうです!

toxファイルを置いておきます!(環境を構築したら使えるはず!)
本当は、他のonnxも回してみたかったけど、とりあえずここまでで失礼します!

18
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?