[TouchDesigner] DATまわりの話

はじめに

# TouchDesigner Advent Calendar 2019

この記事はQiitaに投稿した記事の動画補足つきVerです。

また先日とある大学で行われた制作展のサポートをさせていただき、学生さんにTouchDesignerなどを教える機会がありました。
今後もそういった機会が増えるため、入門者初心者にもなるたけすげーわかりやすく書けるよう努力します、、!!

ここはこういうやり方あるよ、このほうがいいよ、など引っかかることがあれば気軽に教えていただければ幸いです、、!🙏


DATにまつわる話

CSVの読み込み

FileInDATでcsvファイルなどの読み込みができます。(いくらか前は対応してなかったぽいです。)
FileInDATのRefreshトグルをオンにしておくと、読み込み元のファイルの変更を保存ごとに随時反映してくれます。

ちなみにcsvファイルをドラッグアンドドロップすると自動でTableDATに変換して読み込みができます。が、Tableとして読み込まれるためcsvファイルの変更は反映されません。

※FileInでcsvを読み込む際、csvファイルの最終行が読み込まれない場合があるので注意です。
とりあえずcsvファイルを用意する場合は、最終行に空行を追加するなどするのがよいかもです。

5行目の空行がないと @hello, he, helloの行を読み込んでくれません。(動画1:51)

日本語ファイルの読み込み

日本語を含むcsvファイル(ツイートデータ、歌詞データなど)をTouchDesingerに取り込むときにぶつかる問題です。
TouchDesignerではTextに直接日本語を入力したりはできますが、csvなどファイルから読み込んだときにはエンコードの関係で文字化けしてしまいます。
正しく取り込むためにはPythoのスクリプトを書いてutf8として指定して読み込む必要があります。
以下はそんなスクリプトの一例です。

# loadJapaneseFile - Example

import csv
table = op('loadedCSV')
table.clear()

path = project.folder +'/'+ me.parent().par.Csvfile

with open(path, encoding='utf8') as f:
    data = csv.reader(f)
    cn = len(next(data))
    rn = len(list(data))+1
    table.setSize(rn,cn)

with open(path, encoding='utf8') as f:
    data = csv.reader(f)
    r=0
    for row in data:
        c=0
        for col in row:
            print( col )
            table[r,c] = col.strip()
            c+=1
        r+=1

上記のスクリプトでは、一度テーブルをリセットして、csvの数だけセル(空白)を作った後、再度読み込んだcsvの値をセットしています。
なんかいろいろ試した際に思うように行かず、取り急ぎ実装した一例なので、あまり効率的でないかも。。いやゼッタイへん。
よりよい実装わかりしだい更新しますね。

指定フォルダ内の動画を再生する

Palleteの中にmoviePlaylistなんてものもありますが。。もっとシンプルでいいんだって時。
FolderDATで、フォルダ内のファイルリストを取得できます。
この際、拡張子の指定や、サブフォルダ(子フォルダ)の中身も参照するかどうかなど、簡単にプロパティから指定できます。


日付順にソート

最新の動画を取得したいなどの要望を叶えるため、
FolderDATで取得したリストを日付順だったり更新順に並べ替えたいという場合があります。
並べ替えたい時はSortDATというものがあるのですが、基本的には数値順かアルファベット順のみです。Dateを表示させると「Fri Dec 6~」みたいな表示でうまく並べ替えできない場合があります。このためDateをEpochというフォーマットに置き換えソートを掛けます。

上記の動画で操作しているように、ポイントはFolderのDateFormatをEpochにするところ。
これによって正しくSortをかけることができます。
またSortDATにはランダムに並べ替える機能もあり、Seedの値を変更することでランダムな並びを変更できます。
いわゆる配列のシャッフルのように使えるので便利です。(動画2:05)

ついでにSort後、再度日付に変換するtoxを作っていたのですが、、ファイル行方不明のため後ほどアップしておきます。。

TextDAT

Pythonのスクリプトを書き込んで実行することができます。
その他、メモ書きのように利用することもできます。プログラミング言語で言うコメントの役割ですね。

スクリプトを書く

オペレータをアクティブ状態にするとスクリプトを書くことができます。
書き込んだスクリプトはTextDATを右クリック>Run、またはCtrl (Cmd)+Rで実行できます。(アクティブ状態で選択しているとき、つまりオペレータに直接入力可能な状態の場合はスクリプトの実行ができません!)

その他、他のスクリプト内から、op('text1').run()のようにオペレータを指定してrun()とすることでスクリプトを実行することができます。

そんでから、実行結果はTextportというウィンドウで確認することができます。
ツールバー>Dialogs>Textport and DATs、またはAlt (option)+Tでウィンドウを開くことができます。

関数ごとにTextを作る

Execute系のオペレータから実行するとき、直接その中に書くと、デバッグしづらいことがあります。
たとえばChopの0,1(OFF,ON)をフラグとして関数を実行する場合、「Chopの値をもとにDATのTextで書いたスクリプトを実行したい」ので、ChopExcuteを使用することになるかと思います。
このとき直接ChopExecuteの中に処理のスクリプトを書いてもいいのですが、ChopExecuteを選択してCtrl+RとしてもonOffToOn()というイベント関数になっているので実行されません。
作業中は任意のタイミングで処理を実行させたい場合が往々にしてあると思います。そんなときのために処理のスクリプトは別のTextDATにわけて書くと後々融通がききやすいです。

後々知りましたが、このことは、かの先生も自身のサイトで述べられていたので、真理なんだと思います。

また関数をひとつのTextDATに書いた際、

print(me) #とか 
print('---'+me.name+'---') #とか

などを追加しておけばスクリプトの実行順が追いやすいのでデバッグしやすいかもです。

その他ちょっとしたスクリプトTips

  • オペレータのpulseパラメータをスクリプトから叩く
    たとえばMovieFileInのCuepulsewを叩く時
op('moviefilein1').par.cuepulse.pulse()
  • ButtonCOMPをスクリプトからクリックする。
op('button1')click()
  • EvaluateDATのExpressionでスクリプトの簡易チェック
    td_evaluate.png
    単純に値をとってくるときなどに意外と便利かもです。

  • TD高速化Tips
    op('lfo1')['chan1'] よりも op('lfo1')[0] のほうが処理速度は早い(らしい) 

おまけ

オペレータに色を付ける。

自作のtoxなどのオペレータに色を付けたい。調べてもパッと出てこなかったけど、何かのチュートリアル動画を見ていたときにしれっと色をつけているのを見てやり方を知りました。

  1. Cキーを押してカラーパレットを表示
  2. 色を付けたいオペレーターを選択してカラーパレットから色を選択

td_opColor.png

自作toxのロードについて

いい感じのオペレータ群ができたら、オペレータ群をまるっと選択>右クリック>CollapseSelected でひとつのコンテナにまとめる。
このコンテナをtoxとして利用するため、Save Component .toxでtoxファイルに書き出すのですが、このときのもとの自作コンテナは自動ではtoxファイルを参照していることにはなりません。
CommonでExternal.toxを指定でき、これを指定することで初めて、toxファイルを参照して利用することができます。
Reload .tox on Startで起動時にtoxファイルを読み込んできますが、作業中にtoxを編集して再度読み直す場合は、Common>Re-Initが便利です。

また初期値として、ReloadCustomParametersがONになっています。このため、toxを使いまわしそれぞれパラメータを調整しても、ここがONのままだと次回起動時に元になっているtoxファイルの値が入ってしまい、調整が水の泡になってしまいます。適宜OFFにするなど注意してください!

td_base_common.png

ちなみに、Built-inのパラメータはOP.par.cuepulseのようにパラメータ名がオール小文字ですが、カスタムパラメータはOP.par.CustomparameterとParameter名の1文字目のみが必ず大文字になります。
標準パラメータとカスタマイズの干渉を防ぐためだったりすると思うのですが、
そんな些細なことになんとなく気がついていたおかげで僕は何度か幸せになることがありました。

おわりに

DATまわりの知見というか基礎的なところから些細なTipsを書かせていただきました。
なにかひとつでも役に立つものがあれば幸いです。

TouchDesignerはリアルタイム映像ジェネレータとしてとても優秀なソフトでVJ方面での利用が目立つ印象ですが、その一方で、データの管理など裏方処理がバチボコ楽、シリアル通信がバチボコ楽など、
プログラミングしたことないけど何か作りたい学生 にも"うってつけ"だと制作展のサポートで思いました。
(のちのちはプログラミングなどにも興味持ってもらいたいですけど)

ちなみに、今回動画の撮影はFonePawというソフト(無料版は3分間の書き出し制限)を使いました。
入力キーの表示はKeyCastrOWを使いました。KeyCastrOWはGithubから自分でビルドする必要がありますが、ビルド済みのものを配布している方がいらっしゃったのでそちらから拝借。

今後もちょろりちょろり書いていきたいです。

  • ドングルまわりの話(みんなどうしてるんですか?)
  • Instantiateの話
  • MovieFileInの話

ASCII to String変換

ASCII to String 変換

C++の話。

oFでは keyPressed(int key)って関数があって、
キーボードの入力をASCIIコードの値としてkeyに格納され受け取ることができます。
たとえば[a]とキーを叩くと、keyにはASCIIコードで'a'を表す 97 という整数が格納されます。
このため、文字を計算式で扱えるなど、いろんな便利なことができるんですが、反面、単純に文字として受け取るときに変換が必要です。

for(int i=0; i<5; i++){
    string s;
    s = ('A'+i);
    cout<<s<<endl;
}
//---出力
A
B
C
D
E

ポイントはstring変数を用意したあとで、charを代入すること。
string s = char('A'+i);としたいところだけど、コンストラクタの関係とかでエラーになってしまう。1

この他にも、アルファベットの大文字小文字の間隔が32なので変換が簡単にできたりします。
'A'+32とすれば'a'になり、'a'-32とすれば'A'になる。といった具合。

対応一覧

ASCIIコード一覧
10進数 16進数 ASCII
1 0x01 SOH(ヘッダ開始)
2 0x02 STX(テキスト開始)
3 0x03 ETX(テキスト終了)
4 0x04 EOT(転送終了)
5 0x05 ENQ(照会)
6 0x06 ACK(受信確認)
7 0x07 BEL(警告)
8 0x08 BS(後退)
9 0x09 HT(水平タブ)
10 0x0a LF(改行)
11 0x0b VT(垂直タブ)
12 0x0c FF(改頁)
13 0x0d CR(復帰)
14 0x0e SO(シフトアウト)
15 0x0f SI(シフトイン)
16 0x10 DLE(データリンクエスケー プ)
17 0x11 DC1(装置制御1)
18 0x12 DC2(装置制御2)
19 0x13 DC3(装置制御3)
20 0x14 DC4(装置制御4)
21 0x15 NAK(受信失敗)
22 0x16 SYN(同期)
23 0x17 ETB(転送ブロック終了)
24 0x18 CAN(キャンセル)
25 0x19 EM(メディア終了)
26 0x1a SUB(置換)
27 0x1b ESC(エスケープ)
28 0x1c FS(フォーム区切り)
29 0x1d GS(グループ区切り)
30 0x1e RS(レコード区切り)
31 0x1f US(ユニット区切り)
32 0x20 SPC(空白文字)
33 0x21 !
34 0x22 "
35 0x23 #
36 0x24 $
37 0x25 %
38 0x26 &
39 0x27 '
40 0x28 (
41 0x29 )
42 0x2a *
43 0x2b +
44 0x2c ,
45 0x2d -
46 0x2e .
47 0x2f /
48 0x30 0
49 0x31 1
50 0x32 2
51 0x33 3
52 0x34 4
53 0x35 5
54 0x36 6
55 0x37 7
56 0x38 8
57 0x39 9
58 0x3a :
59 0x3b ;
60 0x3c <
61 0x3d =
62 0x3e >
63 0x3f ?
64 0x40 @
65 0x41 A
66 0x42 B
67 0x43 C
68 0x44 D
69 0x45 E
70 0x46 F
71 0x47 G
72 0x48 H
73 0x49 I
74 0x4a J
75 0x4b K
76 0x4c L
77 0x4d M
78 0x4e N
79 0x4f O
80 0x50 P
81 0x51 Q
82 0x52 R
83 0x53 S
84 0x54 T
85 0x55 U
86 0x56 V
87 0x57 W
88 0x58 X
89 0x59 Y
90 0x5a Z
91 0x5b [
92 0x5c \
93 0x5d ]
94 0x5e ^
95 0x5f _
96 0x60 `
97 0x61 a
98 0x62 b
99 0x63 c
100 0x64 d
101 0x65 e
102 0x66 f
103 0x67 g
104 0x68 h
105 0x69 i
106 0x6a j
107 0x6b k
108 0x6c l
109 0x6d m
110 0x6e n
111 0x6f o
112 0x70 p
113 0x71 q
114 0x72 r
115 0x73 s
116 0x74 t
117 0x75 u
118 0x76 v
119 0x77 w
120 0x78 x
121 0x79 y
122 0x7a z
123 0x7b {
124 0x7c
125 0x7d }
126 0x7e ~
127 0x7f DEL(削除)

openCV3/4のメモ

openCV3/4のメモ

要約

openCv4をopenframeworksで使うまでの手順。
プロジェクトの生成については、ほかサイトで見なかった簡単な方法をメモ。
めちゃ雑記です。

概要

oFでopenCV3とか4とか使ってみます。
この記事この記事にそって進めてみる。
opencv4でないといけないことは特にないです。ただただBackgroundSubtractorを試してみたかった。
後日また雑な記事かきます。テストの動画だけ貼っときます。なにかのための証拠。

環境とりあえずMac

openCVのinstall

まずはopenCV3 or 4のインストール。
※ 基本最新で上書きされるっぽい。3,4を共存させる方法もあるらしいが、自分は openCv4.1.0でこれといって不具合はないので、opencv4のみになっています。

brew tap homebrew/scienceは使えなくなったらしい。参考
そのため brew tap brewsci/science こちらのコマンドで環境アップデート。

  • opencv3の場合
    brew install openCV3で入れる。
  • opencv4の場合
    opencv4についてはこちらが詳しい。
    brew upgrade opencv でopenCV4(最新版 ex: 4.1.0)を入れる。
途中xcodeのコマンドラインをインストールしてね的なエラーが出たので、xcode-select --installをして、再度upgradeかけると、エラーなく無事完了した。  
過去に入れた気がするのにと思っていたけど、どうもOSをアップグレードすると再度入れる必要があるらしい。  

普通にすすめると、たいていは/usr/local/Cellar/opencv/が更新されるはず。

Xcodeで新規プロジェクト作成

色んなサイトでは以下の方法があげられていたが、パス間違える可能性があったり、記入が面倒だったりなので、個人的にはおすすめじゃない。

紹介されていたプロジェクト生成方法

open
  1. Project.xcconfigでOPENCV_PATHを設定 パスは適宜変えてください。
//OpenCV3
OPENCV_PATH = /usr/local/Cellar/opencv/3.4.1_2
//OpenCV4
OPENCV_PATH = /usr/local/Cellar/opencv/4.1.0_2  
  1. BuildSettingsのHeader Search PathsにOpenCVのインクルードパスを追加
    $(OPENCV_PATH)/include/opencv4
  2. Build SettingsのOther Linker Flagsに必要なdlybを追加
    ここが面倒、、

openCV4をインストールすると、libフォルダにdylib以外に.aファイルもありました。
これらおそらく中身は同じですが、動的ライブラリか、静的ライブラリかの違いがあります。  
こちらでは動的のほうがファイルサイズ小さく、使用メモリも少なくできるっぽい。
dylibを追加するのが面倒な場合は、
.aファイルをGeneralのLinked Frameworks and Librariesにドラッグドロップしても起動可能です。

(たぶんProject.xcconfigのOTHER_LDFLAGSこのあたりの設定でどうにかなるのだけど、、)

#include <opencv2/core.hpp>ここでリンク貼れてないよってエラー出たけど、単純にheader search pathに$(OPENCV_PATH)/includeを入れてないのが原因でした。

おすすめの生成方法

ProjectGeneratorでプロジェクトを作り、

  1. プロジェクトのSearchPaths > HeaderSearchPathsに/usr/local/Cellar/opencv/4.1.0_2/include/opencv4 を追加。(パスは適宜変更してください。)
  2. /usr/local/Cellar/opencv/4.1.0_2/libから必要なdylibを General>LinkedFrameworks and Libraries にドラッグドロップで追加

どのヘッダ呼び出すときにどのlibが必要になるのか、どこを見ればいいんだろう。。。今の所勘で追加しています。。。
このやり方が簡単安定な気がする。Debug/Releaseで変更の必要ない?
紹介されていたプロジェクト生成方法だと必要かもだけど。


画像は適当に追加したのでごちゃっとしています。どうやら.aの静的ライブラリよりは、.dylibなど動的ライブラリのほうが実行ファイルが軽く、メモリの消費を抑えられるとかどうとか。。。

ofxCvとの連携と利用

oFでopenCVを扱う際に、関数群を使いやすくまとめてくれているofxCvを使うとめっちゃ便利。

ProjectGeneratorでofxCvのみ追加してプロジェクト生成。
あとは、上の1.2.を同様に。
なんかもしかしたら、もうちょうっと設定必要だったかも。わすれてもうた。


おまけめも

ofxOpenCvDnnSegmentation など

やはりどこにでも先人がいるもので、馬場先生のofxOpenCvDnnSegmentationがあった。openCVのdnnライブラリをいろいろまとめてらっしゃる。

配布されていたopencv2.aでうまくいかなかったので、自分でメイクしてみる。参考

が、原因はどうもそこではなかった。ので、makeの手順は隠す。

makeの手順
  1. python build_framework.py osxを実行するが、python3ではうまくいかないみたい。
    自分の場合は python2 build_framework.py osc とするとうまくいった。
    python -Vでバージョンを確認したり、which python, which python2でpythonの2.7以上が使われているか確認するとよさそう。
    参考1 参考2

エラー(returned non-zero exit status 65)について
** BUILD FAILED **

The following build commands failed:
    CompileC /Users/hiraoshugo/openCV/opencv-4.1.0/platforms/osx/osx/build/build-x86_64-macosx/modules/world/OpenCV.build/Release/opencv_world.build/Objects-normal/x86_64/opencl_kernels_video.o /Users/hiraoshugo/openCV/opencv-4.1.0/platforms/osx/osx/build/build-x86_64-macosx/modules/world/opencl_kernels_video.cpp normal x86_64 c++ com.apple.compilers.llvm.clang.1_0.compiler
(1 failure)
============================================================
ERROR: Command '['xcodebuild', 'MACOSX_DEPLOYMENT_TARGET=10.12', 'ARCHS=x86_64', '-sdk', 'macosx', '-configuration', 'Release', '-parallelizeTargets', '-jobs', '4', '-target', 'ALL_BUILD', 'build']' returned non-zero exit status 65

こんなエラーでた。どうやらcmakeのバージョン(3.10.2)がダメらしい。参考
てわけで、cmakeのupgrade

cmake --version
// cmake version 3.10.2
brew upgrade cmake
// .....
cmake --version
// cmake version 3.15.2
python2 build_framework.py osx
// .....

  1. python build_framework.py –contrib <opencv_contribへのパス> osx

そんなこんな。。

結論、こちらのように#include "opencv2/imgproc/imgproc_c.h"をofxCvのWrappers.hに追加した。

modelデータは以下から直接落とした。
http://dl.caffe.berkeleyvision.org/fcn8s-heavy-pascal.caffemodel


最近いろんな出会いがあって楽しい。特に音楽やってる人とよく出会う。
春よりも春らしい。

明日は学生CGコンテスト25周年のパーティーにいってきます。
夏がもうすぐ終わるからね。季節の変わり目。春の訪れ。
秋はいずこへ。

openframeworksでカメラが使えなくなった

openframeworksでカメラが使えなくなった。
ofAVFoundationGrabber.mmの
[self.captureSession addInput:captureInput];
でエラーが出る。
QuickTimeなどではちゃんとカメラが動作するので、oFかXcodeの設定かなと踏んでいた。

open

結論としては、Macのシステム環境設定でセキュリティとプライバシーのカメラの設定で、oFのアプリのチェックが外れていた。
何がきっかけで外れたのか、何を思ったか昔の私がチェック外したのか。。

エラー内容は以下みたいな。

An uncaught exception was raised
~~
Terminating app due to uncaught exception &#039;NSInvalidArgumentException&#039;
~~
libc++abi.dylib: terminating with uncaught exception of type NSException

ちょっと話は違うけど、以下を読んで、ふとプライバシーの設定のことを思い出した。
https://github.com/openframeworks/openFrameworks/issues/6193

コンソールのエラーログにもセキュリティ的な話がなく、
再起動しても、新しくoFの環境作ってVidoGrabberExampleをビルドしても、同様のエラーが出ていたので、悩みました。
気づいてみれば簡単なことなのに、、残念な経験は案外忘れているんですね。


vbsでGmailを送信する

以前にお仕事でvbsを使って、メールを送信しました。
バッチファイルなどで監視しておいて、なにかイベントがあったときにメールで知らせるため。

わりと汎用的に使えるTipsだと思います。

open

手順メモ

  1. 送信元のGmailアカウントを用意する。既存のものでもOK
  2. そのGoogleアカウントは2段階認証にしておく。
  3. Googleアカウント設定のセキュリティからアプリパスワードの設定をする。
    特に決まりがなければ、その他で適当な名前をつけるといいかも。
  4. vbsを書く。

備忘録

  • 複数人に送信する場合は、;で追加できる。
  • どうやらGoogleアカウントのパスワードを更新すると、アプリパスワードもリセットされるっぽい。
    のでので、再度アプリパスワードを生成して、スクリプトに当て直す必要がありそう。
    この辺なんかうまいことできないかな。。

実際のスクリプト

'****************************************************************
' mail sender
'****************************************************************

Set Cdo = WScript.CreateObject("CDO.Message")
Cdo.From = "表示名<送信元メールアドレス>; "
Cdo.To = "ATESAKI1<MAIL_ADDRESS1>; ATESAKI2<MAIL_ADDRESS2>;"
' Cdo.To = "ATESAKI1<MAIL_ADDRESS1>"
' Cdo.Bcc = "ATESAKI1<MAIL_ADDRESS1>"
Cdo.Subject  = "件名"
Cdo.Textbody = "本文〜〜のあとに改行(vbCrLf)と現在時間(NOW)" & vbCrLf & Now
Cdo.BodyPart.Charset = "utf-8"

Cdo.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
Cdo.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.gmail.com"
Cdo.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 465
Cdo.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 30
Cdo.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = true
Cdo.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = true
Cdo.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusername") = "送信元メールアドレス"
Cdo.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "16文字のパスワード"

Cdo.Configuration.Fields.Update
Cdo.Send

Wscript.Quit(Err.Number)

参考にしたリンク

https://blog.saboh.net/smtpgmailcom/
http://serialty.blog117.fc2.com/blog-entry-10.html