TouchDesignerでAzure KinectのBody TrackingデータをRecordして再生

みんなー！カメラのインタラクティブもの作るとき、まず最初に動作テスト用データのレコーディングしてるー？
ちょっとプログラム修正するたびに立ち上がって手足パタパタしてないよねー？

っていいつつ僕も普段やりがちなんですが、今回はTouchDesignerでAzure Kinectを使う案件で、普段の開発をMacでやりたかったので、まず最初にレコーディングと再生の環境を整えました。

やったことは以下です。

- Azure Kinectのカラー画像とBody Trackingのデータをそれぞれファイルに保存
- 簡単なアバターをつくってBody Trackingデータで動かす
- 動画と同期して表示
- （おまけ）動画と3Dの位置合わせ

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/TouchDesigner?src=hash&amp;ref_src=twsrc%5Etfw">#TouchDesigner</a> で <a href="https://twitter.com/hashtag/AzureKinect?src=hash&amp;ref_src=twsrc%5Etfw">#AzureKinect</a> のBody Trackingデータを記録して再生してみた<a href="https://t.co/jspREYYDEE">https://t.co/jspREYYDEE</a> <a href="https://t.co/evEYbTlJ9R">pic.twitter.com/evEYbTlJ9R</a></p>&mdash; 岩谷成晃 (@nariakiiwatani) <a href="https://twitter.com/nariakiiwatani/status/1295636016376582144?ref_src=twsrc%5Etfw">August 18, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


# レコーディング

こんな感じです。（スクショ）
CHOPの記録をどうやろうかいろいろ試行錯誤しましたが、今回はこの形に落ち着きました。
`Trail　CHOP`の前に`Null CHOP`(CookType: Selective)を置いてるのに同フレームのデータが複数回記録される挙動がクリアできなかったので、レコーディング後にDATに変換して、`Sort DAT`で重複を削除しました。

![capture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/e1045c22-619c-cbf3-19d0-da9ca9dfc1eb.png)

![スクリーンショット 2020-08-18 16.08.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/d549c384-fb6f-8846-addb-da12d8c12c7f.png)

注意することとしては、`Chop to DAT`はCPUを食うので、レコーディング中は切っておくと良いと思います。
今回は`Chop to DAT`のCHOPパラメータ欄に`'trail1' if op('null3')[0] == 0 else ''`と書いておきました。

# 読み込みと整形

`File Out DAT`から書き出されたdatファイルは、TDのウィンドウにD&Dすると`Table DAT`として読み込まれます。
![スクリーンショット 2020-08-18 16.20.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/16ef8ddc-4f55-f281-612d-702f8a2ae81d.png)

- `Dat to CHOP`でCHOPに変換
- `Select CHOP`で不要なデータ（frame, timestamp, p1/id）を削除
- `Lookup CHOP`で1フレームを切り出し（切り出す位置は動画と同期するため`in CHOP`で外から指定）
- `Shuffle CHOP`でjoint毎に1サンプルのデータになるよう整形（後にインスタンシングすることを意識）
- `Rename CHOP`でわかりやすい名前をつける（今回は`t[xyz] r[xyz]`）

これで、外から指定した時刻の姿勢が取り出せました。

整形したデータは以下の形をしています。

- 32サンプル（Azure Kinectから取り出したジョイント数）
- 6チャンネル（位置[xyz] 回転[xyz]）


# アバターをつくる

[前回](https://qiita.com/nariakiiwatani/items/c96b37dd94d894654588)とほぼ同じです。
前回は元データがQuaternionだったのでEulerへの変換が必要でしたが今回は元からEulerなので単純に`Geometry COMP`に突っ込めばOKです。
画像内左上にある`motion_dat`というコンテナにはさっきのdat読み込み部分が入っています。
![スクリーンショット 2020-08-18 16.33.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/34d008e6-6da9-b9de-5a30-dfdb722d66d7.png)

# 動画と同期

`motion_dat`に0〜1の数を突っ込めば、その位置のモーションデータが取り出せる作りにしたので、`Movie File In TOP`を`Info CHOP`でみて、`index_fraction`の値を入れてやればOKです。
ちなみにスクショ内でTOPの名前が`TDMovieOut_0`になってますが、これはloadしたファイル名であって、オペレーターはちゃんと`Movie File In TOP`です。
![スクリーンショット 2020-08-18 16.40.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/2c7d7eb4-1f28-b045-70c2-04d1af3d9e1e.png)

ちなみにこの方法で同期が取れるのは

- 動画とモーションの記録を同時に開始/終了した
- モーションデータが途切れていない

という条件が満たされているからです。
今回のようにテスト用に作るものであればこれで良いんですが、ちゃんとしなきゃいけない場面ではちゃんとしましょう。

# 位置合わせ

Azure Kinectのスペックシートから値を拾って`Camera COMP`の姿勢に反映させたらぴったり合いました。簡単ですね。
カラーカメラの水平FOVは90 => https://docs.microsoft.com/ja-jp/azure/kinect-dk/hardware-specification
デプスカメラはカラーカメラ比で6度傾いている(NFOVの場合） => https://docs.microsoft.com/ja-jp/azure/kinect-dk/coordinate-systems

# 最後に
Windowsで開発を続けるとしての話ですが、Kinect Recorderから書き出したmkvを再生できるようになると良いですね。
https://forum.derivative.ca/t/support-for-mkv-matroska-multiple-video-tracks-such-as-color-depth-metadata-etc/121578