この記事はTouchDesigner Advent以下略
TouchDesignerのPython環境には標準でOpenCVのcv2モジュールが入っています。
内部的には Blob Track TOP
で使われているようなのですが、公開されている部分だけだとファイルの入出力を経由しないと使えません、、、
えっ… リアルタイムで使いたい… ので、普通にTOP渡しで自由に使えるようにしましょう!
できました!
ダウンロードはこちらから https://github.com/satoruhiga/TouchDesigner-OpenCV/releases
実際に使ってみる
今回はややこしい事をなるべく隠蔽したかったので OpenCV1
にコールバックを受けるDATを指定するとデータが飛んでくるようにしました。
↓ コールバックのDATの中身
import cv2
def onFrame(data):
cv2.imshow('img', data)
これだけでOpenCVの画像プレビュー用のウィンドウが開きます。
あとは書籍やチュートリアルを黙々と試していくことで色々できますね!
cv2.calcOpticalFlowPyrLKをやってみた様子
以下色々とテッキーな事が書いてありますが、使う分には何も考えなくていいのもTouchDesignerの素晴しい所だと思いますので、興味のある方のみ読んでいただければ!
方針
現在TouchDesignerでは、TOPのピクセルデータにアクセスするのに CPlusPlus TOP
のモジュールを書く必要があります。
そして CPlusPlus TOP
のモジュールを書いてピクセルデータを取り出したら、今度はそれをどうにかしてPythonに渡す必要があります、、
色々探してみたのですが手っ取り早くC++からPythonへデータを渡す仕組みが見当たらなかったので、ややアホっぽいですが nanomsg を使うことにしました
nanomsgとは?
nanomsgは、TCP、UDP、スレッド間通信、プロセス間通信など様々なプロトコルや通信方式に対応した便利なソケットライブラリです。
C++、Pythonの両方で使えるのと、Windows環境でのプロセス間通信に対応していたので試してみることにしました。
CPlusPlus TOP用モジュールの実装
C:\Program Files\Derivative\TouchDesigner099\Samples\CPlusPlus\CPUMemoryTOP\
あたりにあるサンプルを見ながらC++を黙々と書いていきます。
getTOPDataInCPUMemory
関数でTOPのピクセルデータをメインメモリに読み出すのですが、その時のオプションとして OP_TOP_INPUT_DOWNLOAD_DELAYED
を指定しないとひどく遅いのでそこだけ注意です。
OP_TOPInputDownloadOptions options;
options.downloadType = OP_TOP_INPUT_DOWNLOAD_DELAYED;
options.cpuMemPixelType = RGBA8Fixed;
options.verticalFlip = true;
const uint8_t* src = (const uint8_t*)inputs->getTOPDataInCPUMemory(input, &options);
if (!src) return;
送信側のソースコードは こちら
Pythonの受信側
それではPython側でメッセージを受け取る部分です!
nnpy というモジュールが調子よかったのですが、Windows環境でのビルドがすごい大変だったのでgithubにモジュールのバイナリも一緒に上げてあります。
ちなみに、Tipsとして ↓ のような感じのスクリプトを適当なタイミングで呼び出しておくと .toe
と同階層にある python
フォルダを動的にモジュールパスに追加できてひじょうに便利です
import os, sys
PATH = os.path.join(os.path.abspath('.'), 'python')
if not PATH in sys.path:
sys.path.append(PATH)
Cookの制御
途中で言語をまたいだ通信が入っていたりで、内部的に結構ややこしい事になってしまっているのでそれぞれのスクリプトをどのタイミングで走らせればいいかも、だいぶこんがらがっています
TouchDesignerでは基本的に見えていない部分はCookをしない最適化がかかります。
今回のようにTOPに入力したものを外部のライブラリ経由で飛ばして~ というようなことをすると、オペレーターが見えなくなった時にCookが走ってくれない可能性大だったので Null CHOP
の Cook Type
を Always
にして、そこから順繰りに参照していくことで全体的にCookが止まらないような依存関係を組みました。(ちょっとやりすぎかもしれない)
また CPlusPlus TOP
の仕様? で一番最初に強制的にCookをしないと処理が初まってくれなかったのでそこは Execute DAT
の onStart
を使って起動時にCookを走らせるようにしています。
まとめ
TouchDesignerではC++で独自オペレーターが作れるので工夫次第である程度踏み込んだ所でもユーザーレベルでどうにかできる事が多々あります。
現在099のExprementalではSOPもC++で拡張できるようになっているので、Pythonではパフォーマンス的に厳しい所などがあればどんどんC++で補って便利にしていきたいですね!
それでは!