/**/
マトリックスの内容を分析する方法は数多くあります。チュートリアルのこの章では、イメージの色情報に注目した非常にシンプルな1つの方法を紹介します。ここでは、どのようにしてイメージの中の特定の色(あるいは色範囲)を見つけるか、そして、ビデオの1つのフレームから次のフレームへ進む間に起こるその色の位置の変化をどのようにしてトラッキング(追跡)するかという問題を考えます。これは、ビデオの中でのある物体の運動についての情報を得たり、身体的な動作をトラッキングしたりする場合に役立ちます。より一般的に言えば、このテクニックはデータのマトリックスの中で特定の数値(または数値の範囲)の位置を見つけるために役立てることができます。
イメージの中の特定の色を見つけるために使用するオブジェクトとして、jit.findbounds があります。ここではビデオの中の色をトラッキングするため、当然ながら4プレーンで2次元のcharデータ型マトリックスを分析しますが、jit.findboundsオブジェクトはどんなデータ型、どんなプレーン数のマトリックスに対してでも使用することができます。
jit.findbounds の動作は次のように行われます。jit.findbounds の min と max というアトリビュートを使って、各々のプレーンで探そうとする値の最小値と最大値を指定します。 jit.findbounds オブジェクトは、マトリックスを受け取ると、そのマトリックス全体を見渡して個々のプレーンで指定された範囲に入る値を探します。そして、jit.findboundsは、指定された値が見つかった範囲を示すセルインデックスを出力します。実際には、値が見つかった範囲の境界のインデックスを出力します。2Dマトリックスの場合、境界領域は矩形になるので、jit.findboundsオブジェクトは指定された値が見つかった領域の左上と右下のセルのインデックスを出力します。
・Jitter Tutorialフォルダの25jColorTracking.patというチュートリアルパッチを開いて下さい。
この例では、jit.qt.movieオブジェクトによって、赤いボールが動き回るムービー(実際にはアニメーション)を再生しています。これは、明らかにほとんどのビデオで見られるものより単純なシチュエーションですが、jit.findbounds オブジェクトがどのように動作するかを見るためには、わかりやすい状況を設定してくれます。jit.findbounds オブジェクトの min および max アトリビュートを初期化するためにアーギュメントがあらかじめ書き込んである点に注意して下さい。
最小値と最大値は4つのプレーンのそれぞれについて指定されています
これらのアトリビュートに対しては4つのアーギュメントがあり、jit.findbounds オブジェクトが受け取るマトリックスの4つのプレーンそれぞれについて1つの値が対応しています。min アトリビュートは個々のプレーンで許容する最小値を、maxアトリビュートはその最大値を設定します。このアーギュメントによって jit.findbounds オブジェクトは、アルファプレーンについては0〜1、赤のプレーンについては 0.75〜1、緑と青のプレーンについては 0〜0.1の間にある、あらゆる値を探します。マトリックスのデータは char型 であるため、探索したい値は 0〜1の小数で指定しなければなりません(charの値がどのように色の表現に用いられるかについてはチュートリアル 5、6の説明を参照してください)。ここでは赤いボールの位置をトラッキングしたいため、jit.findbounds オブジェクトに対して赤のブレーンが非常に大きな値を持ち、緑と青のプレーンが非常に小さい値を持つセルを探すように命じました(アルファプレーンはどんな値であっても構いません)。
・toggle をクリックしてmetro をスタートさせて下さい。赤いボールが動き回っているのに合わせて、jit.findboundsオブジェクトはボールに外接する長方形のセルインデックスを出力します。metro をストップさせ、jit.findbounds が出力した数値を検証して下さい。このような形になっていると思います。
jit.findbounds オブジェクトは指定した色が現れた領域を出力します。
jit.findboundsオブジェクトは、同じセルの全てのプレーンで目標値を発見できた領域を出力します。この図の場合、jit.findbounds オブジェクトは求められる値を、列120〜159、および 行50〜89の範囲内で発見しています。赤いボールの直径はちょうど40ピクセルですからうまく合致しています。このセルインデックスは、ビデオのこのフレームでボールが位置する場所を40×40の正方形のセル領域として示しています。
jit.findbounds オブジェクトの最初の2つのアウトレットからの出力が2つの値から成るリストである点に注意して下さい。最初のアウトレットは値が見つかった領域の最初のセルインデックスを各ディメンションごとに、2番目のアウトレットは領域の最後のセルインデックスを各ディメンションごとに出力しています。(ここで扱っているのは2Dマトリックスですから、各々のリストは2つの値しか持っていません。これらの値を個別に見るために、ここではunpackオブジェクトを使っています。)
ビデオフレーム内のボールの位置を表わす1つの点が必要な場合、jit.findboundsオブジェクトが出力する矩形領域の中心点を求めてこれをボールの「位置」と考えることができます。exprオブジェクトはこの操作を行っています。次元ごとに最初のセルと最後のセルの差を取り、2つの値の中心点を見つけるためにこれを2で割って最初のセルインデックスの値に加えることにより、「位置」を表わす1つの点を求めています。
矩形の中心点の計算
垂直次元の値を求めるために、実際には垂直座標を239から引いていることに注意して下さい。これは、セルインデックスが上から下へ数えられるのに対し、ここでは下から上へという対象の「高さ」を考えたいためです。(これはまたusliderオブジェクトの動作にも関係します。ここでは垂直座標をusliderを用いて表示するつもりであるため、座標を下から上へと増加するように表わす必要があります。)
「位置」の計算結果をhsliderとusliderに送ってボールの中心点がうまくトラッキングできていることを示し、この座標値をナンバーボックスオブジェクトに表示しています。さらに、座標値を0〜1の範囲にスケーリングし、このボールの水平および垂直位置が、すぐにでもMaxパッチの他の部分の動作やアトリビュートの加工に利用できる可能性があることを示しています。例えば、垂直位置によってビデオやMSPのボリュームをコントロールする、あるいは水平座標によってイメージの回転を行うといったことが考えられます。
パッチの他の部分での利用のために位置座標を0〜1の範囲にスケールしています
はっきりとした白い背景の中のくっきりとした赤いボールというシンプルな例ではすべてが完璧にうまくいきました。しかし、「実世界」のビデオでの1つの物体のトラッキングはかなり難しいものです。ここで、起きるであろういくつかの問題点とそれを解決するためのトリックについて述べておきましょう。
・赤いボールのムービーが停止していることを確認して下さい。そして、patcherbballtracking オブジェクトをダブルクリックして、A More Detailed Example の内容を見て下さい。[bballtracking] サブパッチの左上隅にあるStart/Stopと表示された toggleをクリックして、ビデオをスタートさせて下さい。
このムービーには赤いシャツ、緑のパンツ、黄色と青のボールという鮮明な色を持った対象物があります。これはカラートラッキングに使える可能性を秘めています。しかし、このボールのトラッキングを行うためには、前の例に比べてちょっと難しい要素がいくつかあります。
まず第1に、ビデオの最上部のわずかなスキャンライン(マトリックスの最初の数行)には、実際に分析の対象としないゴミ(不要な情報)が含まれています。このゴミは、このビデオの不完全なデジタル化による不幸な産物です。このような不完全さは普通に見られるもので、分析処理を面倒なものにします。第2に、イメージが高い彩度を持っていないため、必要とされるほど色の違いが明確でないという点です。第3に、4秒間のクリップの最後ではボールが完全にフレームから消えてしまうという点です(jit.findboundsオブジェクトは、探している値の存在を見つけることができない場合、最初と最後のセルインデックスとして-1を出力します)。第4に、フレームの中でボールの位置を見つけるために黄色をトラッキングしようと考える場合、ボールが黄色一色ではないことを認識している必要があるという点です。ボールのテクスチャ(表面の模様)やライティング(照明)によって、実際には「黄色味がかった範囲」として見えます。このため、この範囲を注意深く識別して jit.findbounds オブジェクトに設定する必要があります。
これらの問題点を解決してみましょう。チュートリアル 14 で説明したように、いくつかのJitterオブジェクトによってマトリックス全体ではなく、見たい部分の「ソース」矩形領域を表示することができます。チュートリアル14で、jit.matrix オブジェクトの srcdimstart、 srcdimend 、そして usesrcrect アトリビュートについて説明し、また jit.qt.movie オブジェクトにはそれに相当する srcrect、usesrcrect というアトリビュートがあることを述べました。jit.qt.movie オブジェクトのこれらのアトリビュートを使ってビデオイメージをトリミングし、必要のない部分を取り除くことにしましょう。
・Crop source Image と表示されたメッセージボックスをクリックして下さい。これによって、私たちが必要とする新しいソース矩形領域のセルインデックスが jit.qt.movie オブジェクトに送られ、マトリックス全体に代わってそのソース矩形領域を使用するようオブジェクトに指示します。この領域が行4から始まり(これで、マトリックスの5番目の行から始めることになります)、イメージの最初にあるゴミをトリミングしている点に注意して下さい。さらに、ボールの最初のバウンドがちょうど左下隅で行われるため、ソースイメージの左側の20ピクセルもトリミングしています。これで、ビデオの分析したい部分に焦点を当てることができました。
・次に他の問題を考えてみましょう。ウィンドウの右下隅にあるSetupと表示された小さなpresetオブジェクトをクリックして下さい。これによって全てのユーザインターフェイスオブジェクトを望ましい設定にセットします。
ここでは、jit.qt.movieオブジェクトの loop アトリビュートを2の「往復再生」に設定し、ループの終了点を2160(54番目のフレームが表示される瞬間)に設定しています。このため、ムービーはフレーム0からフレーム53まで進み、逆戻りします。ムービーはちょうどボールが最初に舗道にバウンドする瞬間まで再生され、その後逆方向に進みます。
さらに、jit.brcosaオブジェクト(チュートリアル 7 に詳しい説明があります)にいくつかの値を送ってオブジェクトの brightness、contrast、saturation アトリビュートを適した値に設定しています。この設定はイメージとして見るためには必ずしもベストなものではありませんが、色の違いをより明白にし、色の値をより小さな範囲に押し込めることによって jit.findbounds オブジェクトがよりトラッキングを行いやすいようにしています。
加えて、(パッチの中程にある)jit.matrix オブジェクトの usesrcdim アトリビュートをオンにしているため、オブジェクトは jit.findbounds オブジェクトの出力によってソース矩形領域を決定します。Show Tracked Region と表示された jit.pwindow にトラッキングされた領域が表示されるのを見ることができます。
jit.findbounds オブジェクトの出力を jit.matrix オブジェクトの
srcdimstartとsrcdimend アトリビュートの設定に使っています
ボールの下地の黄色は赤と緑の値をほぼ同等に持っているため、jit.findbounds オブジェクトの min および max アトリビュートには、赤と緑のプレーンの値が高く、青のプレーンの値が低いセルを探すように設定しています。jit.brcosa の設定、および jit.findbounds の min、 max アトリビュートをの設定を注意深く行うことによって、ボールの黄色い部分に対する非常に信頼性の高いトラッキング結果が得られるように処理しているのがわかると思います。
注:ここでは実際に言及しなかった非常に重要な項目の1つに、ビデオの特定の色を効果的にトラッキングするために jit.findbounds オブジェクトの min と max というアトリビュートをどのように設定したらよいかということがあります。これにはある程度の試行錯誤を伴う調整が必要ですが、チュートリアル 10 で紹介した suckah オブジェクトを使うことによって特定のピクセルの色についていくつかの非常に明確な情報を得ることができます。分析しようとするビデオを表示する jit.pwindow の上に suckah オブジェクトを重ね、トラッキングを行いたい色の上をクリックして、その時の suckah オブジェクトの出力を利用してそのセルのRGB情報を得ることができます。(suckahオブジェクトから出力される値は0〜255の範囲にありますが、これを255.0で割ることによって0 〜1の範囲に直すことができます。)
このようにして、少なくともこの特定の状況においては、ビデオ内の1つの物体をトラッキングするうえでの問題点をなんとか克服することができました。しかし、これを成し遂げた後、私たちが得たこの情報を使って何をしたらよいでしょうか?ここでは、オブジェクトの位置情報を使ってサウンドをコントロールする2つの方法を紹介します。サウンドにはMIDIノート、あるいはMSPトーンの演奏を用います。どちらの例も音楽的に非常に洗練されているとは言えませんが、位置情報をサウンド情報にマッピングする際の基本的な問題を明らかにするために役立ちます。
ここでは、このパッチの中の Use Tracking Infoと表示された部分に置かれた2つのサブパッチに位置情報を送っています。pack オブジェクトを使って jit.findbounds オブジェクトの全ての出力を4つの要素からなる1つのリストにまとめ、gate オブジェクトを使ってその情報を patcher playnotes サブパッチ(MIDI ノートの演奏)、patcher playtones サブパッチ(MSPトーンの演奏)、またはどちらでもない状態(サウンドを鳴らさない)のいずれかにルーティングしています。
位置情報を2つのサブパッチのうちの1つに送ります
注:チュートリアルにあるMIDIまたはMSPを含む例を動作させるためには、機器を正しく設定しておく必要があります。MIDIを使った例では、OMSがインストールされていて、Maxの仮想MIDIポートa にマルチティンバーシンセサイザ・キーボードが接続されていることを前提としています。MSPを使った例では、MSPがインストールされていて、DSP ステータスウィンドウの中の適切な出力デバイスをセットするためのドライバを持っていることが前提となります。
機器の設定についてのより詳しい情報はMax Getting Started マニュアルの"Setup"セクション、Max Tutorials and Topics マニュアルの"Introduction"セクション、MSPマニュアルの"Audio I/O"セクションを参照して下さい。
訳注:現在のバージョンのMax/MSP では OMS がインストールされている必要はありません。機器の設定については、Maxファンダメンタルの「セットアップ」「MIDIの使用」「ポート」、Maxチュートリアルの「イントロダクション」、MSP チュートリアル「オーディオ I/O」などのドキュメントを参照して下さい。
・Use Tracking Infoと表示された ubumenu の中のメニュー項目 1 = Play MIDI Notes を選択して下さい。patcher playtones オブジェクトをダブルクリックして、[playnotes]サブパッチの内容を見て下さい。何の音も演奏されない場合(そして、ムービーが再生されたままであることが確認できた場合)は、 noteout a オブジェクトをダブルクリックしてデバイスダイアログボックスの他のMIDIシンセサイザを選択してみて下さい。
[playnotes]サブパッチの内容
[playnotes] サブパッチでは、最初の例の中で使用したものと同様なマッピングの式を使っています。最初の例の場合は、ボールの位置座標を計算してその情報を有用な範囲に置くというものでした。ここでは、水平位置を計算し、それを16で割って、0〜19までの範囲の値を得ています。また、重複した数値(すなわち、繰り返されたノート)を無視するために changeオブジェクトを使い、その後 table オブジェクトから演奏するノートを探し出すようにしています。
注:バスケットボールプレーヤの動きは、特定の音階とは何の関係もありません。そのため、生の位置データをMIDIキーナンバとして使った場合には無調のインプロヴィゼーションになります。(そうすることが決して間違っているわけではありません!)ピッチの選択に調性的な意味合いを持たせたい場合、ボールの水平方向の運動によって生成された数値を、ルックアップテーブル(tableオブジェクト)に格納した音階の音を探すためのインデックスナンバとして使うことができます。tableの内容を見たい(あるいは、変えたい)場合には、tableオブジェクトをダブルクリックして、グラフィックエディタウィンドウを開いて下さい。
ボールの垂直位置(0〜119の範囲にマップしてあります)は、ベロシティ値を決めるために使っています。 makenote オブジェクトはノートにデュレーション(200ミリ秒)を割り当て、MIDIノートオフメッセージを出力するよう気をつけています。音楽の最も根底となるパルス(毎秒20パルス)はムービーを再生する metro の速さによって決定されていますが、changeオブジェクトが繰り返しの音を削除するため、全てのパルスにおいてMIDIノートが鳴らされるとは限りません。
・[playnotes] サブパッチウィンドウを閉じて下さい。Crop and Flip source Image と表示されたメッセージボックスをクリックして下さい。これは、jit.qt.movie オブジェクトに新しいソース矩形領域を送り、イメージを水平方向に反転させます。これによって[playnote] サブパッチの音楽効果の音の高低が逆転します。
・Use tracking Infoと表示された ubumenu から、メニューアイテム 2 = Play MSP Tones を選択して下さい。patcher playtones オブジェクトをダブルクリックして、[play tones] サブパッチの内容を見て下さい。
色の位置をMSPオシレータの周波数コントロール情報として使います
ここでは、バスケットボールの水平および垂直位置座標をMSPオシレータの周波数値として使っています。これらの値を計算する方程式はいくぶん恣意的なものですが、2つの座標値を同じ周波数領域にマップするよう工夫されています。水平座標はオーディオの左チャンネルのオシレータをコントロールするために用いられ、垂直座標は右チャンネルのオシレータの周波数をコントロールするために用いられています。
受信されるメッセージの存在自体をMSPオーディオをオンにする(サウンドレベルを徐々に上げる)ために使っていて、メッセージが200ミリ秒以上受信されない場合にはサウンドをフェイドアウトさせ、オーディオをオフにします。
最初のメッセージによってオーディオがスタートされフェードインされます。メッセー
ジが201ミリ秒以上受信されないとフェイドアウトされオーディオがオフになります。
・[playtones] サブパッチウィンドウを閉じて下さい。Crop Source Image および Crop and Flip Source Image と表示されたメッセージボックスをクリックしてビデオイメージを垂直方向に反転し、MSPオシレータに対する効果の違いを聞いて下さい。
このチュートリアルでは、色領域の位置情報を直接サウンドシンセシスやMIDI演奏のパラメータ制御に使うといった非常に簡単な実装例を紹介しました。少し手の込んだMaxプログラミングを用いることによって、物体の動きについての情報をより多く引き出すことができる可能性があります。
例えば、1つのビデオフレームでのオブジェクトの位置を前のフレームでの位置と比較し、ピタゴラスの定理を用いてオブジェクトのフレーム間の移動距離を計算して速度を割り出す、さらにその動きの傾斜(Δy/Δx)を計算し、(三角関数アークタンジェントを使って)移動角を割り出す、ある速度と前の速度を比較して、加速度を計算する、等々のことが可能です。1つのフレームでのオブジェクトの見かけ上の大きさを次のフレームのものと比較して、z 軸(奥行き方向)上にあるカメラに近づく、または遠ざかるといった動きについてのおよその推測をすることさえ可能です。
jit.findbounds オブジェクトはマトリックスの各プレーンで特定の範囲内にある値を探し、各プレーンで指定された範囲内の値を見つけることができたマトリックス上の領域を出力します。これは、どのようなな数値データの範囲を、どのような型のマトリックス状で見つけようとする場合でも役立ちます。特に、4プレーンマトリックスで特定の色の位置を見つけるために使用することができるので、これをビデオで物体の動きをトラッキング(追跡)するために用いることができます。
jit.qt.movie オブジェクトの srcrect アトリビュートによるビデオイメージのトリミングは、ソースイメージの必要な部分に焦点をあてるための手助けとなります。jit.brcosa オブジェクトは、ソースビデオのカラー値の調整をするために役立ちますが、これによって特定な色あるいは色の範囲の分離、探索がしやすくなります。
jit.findbounds オブジェクトの出力を使って物体の位置をトラッキングすることができ、さらにこの出力からオブジェクトの動きについての他の情報、例えば速さ、方向等を計算することができます。このような取り出した情報を使って、MIDI演奏、MSPシンセシス、あるいは他のJitterオブジェクトに関するパラメータをコントロールすることができます。