/**/

チュートリアル 39:
空間マッピング

このチュートリアルでは、Jitterマトリックスのセルの空間的レイアウトを、他のマトリックスを空間マップとして用いてリマップ(再配置)する方法について見てみましょう。その中では、数式によってJitterマトリックスを満たす方法と同様、小さなデータの集まりを補完することによって完全なマップを生成する方法について調べてみます。

Jitterマトリックスは任意の複雑さを持ったルックアップテーブルを用いて、空間的にリマップすることができます。チュートリアル12では、jit.charmapオブジェクトが、あるJitterセルのマトリックスのセルの値(色の情報)を、他のJitter マトリックスによって提供されるルックアップテーブルに基づいてマッピングすることについて学びました。同じように、jit.repos オブジェクトは位置の情報の値を持つ Jitter マトリックスをルックアップテーブルとして使用し、他のマトリックスのセルの空間位置を再配置します。

・Jitter Tutorials フォルダの 39jSpatialMapping.pat というチュートリアルパッチを開いて下さい。

このチュートリアルパッチでは、jit.repos と呼ばれる新しいオブジェクトによって処理されているjit.qtmovie オブジェクトからの出力マトリックスが示されています。jit.repos の右インレットは、jit.xfadeオブジェクトの出力を受け取っていますが、このjit.xfadeオブジェクトは、名前を持ったマトリックスを2つ供給されていて、この2つにはそれぞれ ( stretchcartopol という)名前が与えられているため、パッチの他の場所にある jit.matrix オブジェクトと値を共有しています。メインの処理チェーンの右側には2つの別々の領域があって、そこでは、このチュートリアルの空間マップとして用いられる2つのマトリックスが作られています。

read colorswatch.pict jit という.メッセージボックスをクリックして、jit.qt.movie オブジェクトにイメージを読み込ませて下さい。Display と表示された toggle をクリックして、metro オブジェクトをスタートさせて下さい。

この時点では、パッチの左下にある jit.pwindow オブジェクトには白一色のフレームしか表示されないことに注意して下さい。これは、まだ jit.repos オブジェクトに空間マップを読み込ませていないためです。

・パッチの中の、Generate "stretch" Matrix と表示された領域にある preset オブジェクトの最初のボックスをクリックして下さい。pictslider オブジェクトが様々な位置に移動し、この領域の下部にある jit.pwindow オブジェクトにマゼンタから緑へ移り変わるグラデーションが現れるのを見ることができます。

これで、パッチ下部の jit.pwindow に正常な形の画像が表示されるのを見ることができます。

Repos の広大な世界

pictslider オブジェクトを動かして、stretch マトリックスに値をセットしてみて下さい。それによって生じる効果が、画像を伸ばしたり、捻ったりするものであるという点に注意して下さい。

pictslider オブジェクトを1つずつ操作することによって、スクリーンを 3 X 3 のグリッドに分割するような領域の各々で画像が歪められているのがわかると思います。preset オブジェクトの最初のボックスをクリックすることによって、画像は正常なものに戻ります。


鏡の部屋

パッチの pictslider オブジェクトは、2プレーン、3×3の大きさで、long型のJitter マトリックスの個々のセルに値をセットします。その後、このマトリックスは補間によって320×240 のマトリックスにアップサンプリングされ、stretch という名前を与えられています。このマトリックスには、jit.repos に入力されるマトリックスに対してマッピングを行うための命令が含まれています。ここでは、ちょうど9つの指定された座標値から完全な320×240のマップを作成するため、補間が必要となります。

jit.repos オブジェクトの右インレットに送られる空間マップは、入力されたセルがどのセルに出力されるかをセルごとに記述したものです。このマトリックスの最初のプレーン(ブレーン0)は、マッピングのための x 座標を指定し、2番目のプレーン(プレーン1)は y 座標を指定します。例えば、左上隅のセルが 50 75 という値を持っている場合、jit.repos オブジェクトの左インレットに入力されたマトリックスの 50 75 という座標値を持つセルは出力マトリックスの左上隅に配置されます。

以下の例では、jit.repos がこの命令をどのように実行するかを感覚的にわかりやすく説明しています。


シンプルな空間マップ

この例では、増加する数値を持った、単純な1プレーン、5×5 のマトリックスが与えられ、これが空間マップでリマップ(再配置)されています。空間マップの値と、それに対応する出力マトリックスのセルとの相互関係に注意して下さい。出力マトリックスのセルの値は入力マトリックスのセルの値が元になっています。例えば、空間マップ用マトリックスの左下隅のセルからは、4 0 という値が読み取れます。入力マトリックスの座標 {4,0} のセルの値は5にセットされています。そして、見てわかるとおり、出力マトリックスの左下隅のセルは、この値を持っています。

同様に、出力マトリックスの中央の行に含まれるセルはすべて同じ値(19)にセットされていることがわかるでしょう。これは、空間マップの対応するセルがすべて同じ値(3 3)にセットされているためです。入力マトリックスの{ 3, 3}というセルは19という値を持っています。この値が出力マトリックスの行全体に置かれているのです。

このチュートリアルパッチで、空間マップは、処理されるマトリックス全体のサイズ(または dim)を参照することができるような範囲をもつ値を格納できるものである必要があります。ここでディストーション効果を与えようとしている画像は320×240 の大きさを持っているため、空間マップとしてはlong型のマトリックスを使う必要があります。これが、char型であった場合、0〜255の値しか格納できないため、空間マップの中で選ぶことのできるセルが限られてしまいます。この long型マトリックスをjit.pwindowオブジェクトで適切に表示するために、表示を行う前にjit.clipオブジェクトを使ってマトリックスの値をchar型と同じ範囲に制限していることに注意して下さい。

技術的な注:jit.reposのデフォルトの動作では、空間マップを絶対的な座標値として解釈します。すなわち、どの入力セルがどの出力セルにマップされるかという文字通りの仕様になっています。jit.reposmode アトリビュートが 1にセットされると、オブジェクトは相対モード(relative mode)の状態になり、空間マップの持つ座標値は、通常の位置からの相対的な位置を表すようになります。例えば、空間マップのセル{15,10}が、-3,7 という値を持っていたとすると、相対モードで動作する jit.repos オブジェクトは出力マトリックスのセル{15,10}に入力マトリックスのセル{12,17}(すなわち、15-3,10+7)の値をセットします。

jit.reposの動作のイメージを把握するために、もう少し pictslider オブジェクトで遊んでみて下さい。行(あるいは列)全体の pictslider オブジェクトをほぼ同じ位置にセットすると、画像のある領域が、出力画像では水平、あるいは垂直の帯状に全体をカバーするように「引き伸ば」される点に注意して下さい。左の列の pictslider オブジェクトを通常右の列が持つ値にセット(そして、その逆)すると、画像を水平軸方向に反転させることができます。これを途中まで行うか、あるいはスライダの値を「通常」の(あらかじめ preset オブジェクトによって設定されている)位置との間のどこかに設定することによって、よくある「びっくりハウス」の鏡(ガラスの歪みによって反射光を屈折させ、空間的な誇張を行ったりするもの)のような空間のディストーションを行うことができます。

空間的表現(空間を表す式)

これまで、空間マップを手動で設定することを試みてきましたが、同様に、これをアルゴリズムによって生成する興味深い方法について見ていきたいと思います。

・チュートリアルパッチの右下の部分で、Generateと表示された button をクリックして下さい。Crossfade と表示されたナンバーボックス(パッチの主要部分にある trigger オブジェクトに接続されています)を 1.0に設定して下さい。


「棒つきキャンディ」状の空間.

cartopol 空間マップは、jit.gencoord オブジェクトの出力を数式によって処理する jit.exprオブジェクトによって設定されています。

float32型のJitterマトリックス上で実行されるこの数式の結果は、空間マップとして用いられるlong型のマトリックスにコピーされます。この空間マップにより、jit.repos は入力されてくるマトリックスをデカルト座標から極座標へとリマップしますが、これにより入力マトリックスの最左端は出力されるマトリックスの中心になり、入力マトリックスの右端によって描かれる外周の輪に至るらせん状のパターンに包み込まれます。jit.repos boundmode アトリビュートは、オブジェクトにこの範囲を超える座標を内部に戻すように指示するため、出力画像の最終端は入力画像の左端に向かって折り返しを始めます。

jit.gencoord オブジェクトは浮動小数点数値による単純なデカルト座標のマップを出力しますが、マトリックスの値にマトリックスの dim を掛けることによって、次に示すように、容易にに実際のセルの場所に対応する座標値を得ることができます。


通常のデカルト座標マップ

jit.gencoordscale および offset アトリビュートは、オブジェクトによって出力される値の範囲を操作することを可能にしますが、これによって必要な範囲に適合したデカルト座標マップを得るために複数の jit.op オブジェクトを利用するといったステップを省略することができます。方程式を機能させるために、最初のマップは両方のディメンション共に { -1,1} の領域にあることが必要になります(そのために、通常のデカルト座標空間に 2を掛け、-1だけオフセットしています)

Maxの vexpr オブジェクトが数値のリストに対して働くのと同様、jit.expr オブジェクトは jit.gencoord から送られる入力マトリックスを受け取り、それを数式によって計算します。jit.expr オブジェクトの expr アトリビュートは使用される数式を定義します。in[i].p[j] という表記の仕方は、jit.exprに対し、インレットiで受け取ったマトリックスのプレーンjに含まれる値について計算を行うよう指示することを意味します。キーワード dim[i] は、マトリックスのディメンション [i] の大きさを返すことを意味します。この場合では、dim[0] という表記の結果は320になり、dim[1] では240になります。

expr アトリビュートを設定するためのメッセージボックスで、実際には2つの数式が定義されている点に注意して下さい。最初のものは、プレーン 0 (ここでの空間マップのx 座標)に対して適用される数式を、2番目のものはプレーン 1(y 座標)に対して適用される数式を定義しています。

もう少し読みやすいように書いてみると、プレーン 0 のための jit.expr の式:

hypot(in[0].p[0]*in[1]\,in[0].p[1]*in[2]) * dim[0]

は次のように計算されます。いま、入力マトリックスのプレーン0にある値を x、プレーン1にある値を y とし、a と b をそれぞれ jit.expr オブジェクトの第2、第3インレットに接続されたナンバーボックスオブジェクトの値であるとすると、式は、

sqrt (ax2 + by2) * 320

になります。同様に、プレーン1のための式:

(((atan2(in[0].p[1]*in[2]\,in[0].p[0]*in[1])/PI)*0.5)+0.5)*dim[1]

は、このようになります。

(((atan(by/ax)/)/2)+0.5)*240

したがって、ここでは空間マップの x (プレーン0)の値を座標の斜辺の長さによる数値(すなわち、マップの中央からの距離)で、y (プレーン1)の値を原点の周囲の角度(Φ)で表した値で埋めているのがわかると思います。

・チュートリアルパッチの右下の部分で、jit.expr オブジェクトに接続されているナンバーボックスオブジェクトの値を変化させてみて下さい。これによって、空間マップをx軸方向やy軸方向に拡大、縮小できる点に注目して下さい。

jit.exprオブジェクトはインレットから、マトリックスの代わりにMaxの数値(intまたはfloat)を受け取ることができます。そのため、exprアトリビュートにおけるin[1]やin[2]といった表記では、オブジェクトの中央インレット、および右インレットに送られてくる数値を参照します。

技術的な注:jit.expr オブジェクトは expr アトリビュートに含まれる数式を個々の文字列ごとに(それぞれをプレーンに対応させて)解析します。その結果、個々の式はクォーテーションマーク(")によって囲まれなければなりません。さらに、カンマ(,)はバックスラッシュ(\)文字によってエスケープされる必要があります。これは、式が Max によって 「スペースで区切られたリスト」や「コンマで区切られた一連のメッセージ」に細分化されてしまうのを防ぐためです。 jit.expr オブジェクトの verbose アトリビュートをオンにすることによって、(このチュートリアルで行っているように) jit.expr がどのように式を解析しているかについて推察することができます。expr アトリビュートが評価されると、このオブジェクトは式の評価の階層を Maxウィンドウに表示します。これによって、式の様々な部分がどのようにトークン化され、解釈されているかを正確に知ることができます。

・steps と書かれた patcher オブジェクトを開いて下さい。メッセージボックスに含まれるそれぞれの式をクリックして下さい。これによって cartopol マップを計算する方法を段階を追って見ることができます。norm[i]snorm[i] という表記は、基本的に jit.gencoord の機能をここで模倣しているもので、指定された範囲全体でのデカルト座標値(値の範囲0〜1で)や、符号付のデカルト座標値(値の範囲-1〜1)によるマップを生成しています。


デカルト座標 -> 極座標マッピングの様々な段階:(a) 通常のマップ(b)通常のマップにスケールとオフ
セットを施して符号付きの[-1, 1]の範囲にしたもの (c)距離のみによるもの (d)角度のみによるもの

このサブパッチで注意してほしい重要な点の1つは、メッセージボックスオブジェクトが、直接、メインパッチの jit.matrix オブジェクトに接続されているということです。jit.matrixexprfill メッセージを送ることによって、jit.expr のような別のオブジェクトの助けを借りずに、Jitterマトリックスを数式による値で満たすことができます。expfill を使う場合の構文は、 jit.exprexpr アトリビュートとやや異なっています。exprfill の最初のアーギュメントは値を満たすプレーンを示します。このため、この方法を用いて空間マップを生成させる場合には(コンマで区切られた)2つの exprfill メッセージが必要になります。

そして「マトリックス」

・パッチの左側部分にある trigger オブジェクトに接続されたナンバーボックスの値を、1から0へ、さらに1へ戻すように、ゆっくりと変化させてみて下さい。


このチュートリアルで用いられた2つの空間マップの部分的なクロスフェード

ナンバーボックスは、この処理チェーン全体の心臓部である jit.repos オブジェクトに直接結び付けられた jit.xfade オブジェクトの xfade アトリビュートをコントロールしています。結果として、手動で定義された空間マップ(stretch マトリックス)から、アルゴリズムによって定義された空間マップ(cartopol マトリックス)へ滑らかに変化させることができます。Jitterのプロセッシングオブジェクトを用いた、様々な空間マップを合成する能力によって、潜在的な柔軟性をもつレイヤーをさらに付け加えることができます。私たちのマップが納められた2つのマトリックスは同じタイポロジー(型、大きさ、プレーン数)を持っているので、容易に結合させることができ、無限の多様性を持った合成を行うことができます。

まとめ

jit.repos オブジェクトは、第2インレットに送られてくるマトリックスとしてコード化された空間マップに基づいて、入力マトリックスを処理します。デフォルトでは、jit.repos は絶対座標を用いてマトリックスの空間的な処理を行いますが、この場合、2番目のマトリックスのセルの値を座標値とする入力マトリックスのセルの値が、出力マトリックスの同じ場所(訳注:2番目 のマトリックスのセルの位置)にコピーされます。

空間マップはマトリックスのセルを明示的にセットして作ることができますが、必要に応じ、より小さなマトリックスから補間によるアップサンプリングを行って作ることも可能です。マップはまた、アルゴリズムを用いて生成されたマトリックスから作ることもできます。具体的には、jit.gencoord オブジェクトによって生成される「ノーマル」なデカルト座標マップに対し、 jit.expr オブジェクトによって評価される数式で処理を行うことで生成できます。 jit.expr オブジェクトは、数式の文字列をプレーンごとに1つずつ受け取り、キーワードを用いてこれを解析します。キーワードは、入力されるセルの値だけでなく、入力マトリックスのサイズといった他の有用な情報をも示しています。数式の計算結果で直接jitterマトリックスを満たしたい場合、 jit.matrix オブジェクトに対してexprfillメッセージを使用することで、 jit.expr のような別のオブジェクトを使わなくても同じ結果を得ることができます。

jit.repos オブジェクトを用いることによって、イメージやデータ処理の様々な用途のために、多種多様な空間マップを生成することが可能です。Maxの'examples' フォルダの中の'jitter-examples/video/spatial'フォルダには jit.reposの可能性を示す数多くのパッチが含まれています。