jit.gl.pix でメディアンフィルターを実装してみた:#max6 #jitter #cycling74

Before

lena noise

After – 3×3 メディアンフィルターで処理

lena median

ノイズが載った画像を綺麗にする必要が出てきたので、jit.gl.pix のシェーダーを使ってメディアンフィルター(Median Filter)を実装してみました。下の画像は、上記のノイズ画像をメディアンフィルターで処理した結果です。結構凄い!

jit.gl.pix で実装する上でのポイント

メディアンフィルターのアルゴリズムは、こちらにある通り、近傍の画素値を拾ってきて、並び替えた上で中央値を採用するという単純なものです。例えば、3×3で行うと中央のピクセルに対して周辺と自分を含めた9つの画素値を、明るさで並べ替えて、4番目の値を出力すればOK。

しかしjit.gl.pixでは実装に問題がありました。for文を使って近傍画素を取得し配列へ保存したいのですが、配列が使えないです。そのため変数(m0 ~ m8)を9つ用意し、愚直にベタ書きをしました。また並べ替えに関しても同様の問題がありバブルソートをベタ書きしています。

この問題点は、jit.gl.pixの仕様なのか、シェーダーの仕様なのかはわからないですが、非線形な処理を書こうとすると苦労しそうです。もし、簡単・軽量・カッコよく実装する方法があれば、是非教えてください!

jit.gl.pix Codebox – 3×3 Median Filter

[javascript]

sort(x,y){

return min(x,y),max(x,y);
};

xmin = 1/(dim.x – 1);
ymin = 1/(dim.y – 1);

//近傍画素の取得
//0
x = (-1) * xmin;
y = (-1) * ymin;
m0 = sample(in1,norm + vec(x,y),boundmode="clamp");

//1
x = (0) * xmin;
y = (-1) * ymin;
m1 = sample(in1,norm + vec(x,y),boundmode="clamp");

//2
x = (1) * xmin;
y = (-1) * ymin;
m2 = sample(in1,norm + vec(x,y),boundmode="clamp");

//3
x = (-1) * xmin;
y = (0) * ymin;
m3 = sample(in1,norm + vec(x,y),boundmode="clamp");

//4
x = (0) * xmin;
y = (0) * ymin;
m4 = sample(in1,norm + vec(x,y),boundmode="clamp");

//5
x = (1) * xmin;
y = (0) * ymin;
m5 = sample(in1,norm + vec(x,y),boundmode="clamp");

//6
x = (-1) * xmin;
y = (1) * ymin;
m6 = sample(in1,norm + vec(x,y),boundmode="clamp");

//7
x = (0) * xmin;
y = (1) * ymin;
m7 = sample(in1,norm + vec(x,y),boundmode="clamp");

//8
x = (1) * xmin;
y = (1) * ymin;
m8 = sample(in1,norm + vec(x,y),boundmode="clamp");

//バブルソート
m0,m1 = sort(m0,m1);
m0,m2 = sort(m0,m2);
m0,m3 = sort(m0,m3);
m0,m4 = sort(m0,m4);
m0,m5 = sort(m0,m5);
m0,m6 = sort(m0,m6);
m0,m7 = sort(m0,m7);
m0,m8 = sort(m0,m8);

m1,m2 = sort(m1,m2);
m1,m3 = sort(m1,m3);
m1,m4 = sort(m1,m4);
m1,m5 = sort(m1,m5);
m1,m6 = sort(m1,m6);
m1,m7 = sort(m1,m7);
m1,m8 = sort(m1,m8);

m2,m3 = sort(m2,m3);
m2,m4 = sort(m2,m4);
m2,m5 = sort(m2,m5);
m2,m6 = sort(m2,m6);
m2,m7 = sort(m2,m7);
m2,m8 = sort(m2,m8);

m3,m4 = sort(m3,m4);
m3,m5 = sort(m3,m5);
m3,m6 = sort(m3,m6);
m3,m7 = sort(m3,m7);
m3,m8 = sort(m3,m8);

m4,m5 = sort(m4,m5);
m4,m6 = sort(m4,m6);
m4,m7 = sort(m4,m7);
m4,m8 = sort(m4,m8);

m5,m6 = sort(m5,m6);
m5,m7 = sort(m5,m7);
m5,m8 = sort(m5,m8);

m6,m7 = sort(m6,m7);
m6,m8 = sort(m6,m8);

m7,m8 = sort(m7,m8);

//中央値の出力
out1 = m4;

[/javascript]

参考

上記画像は、画像処理の分野で良く使われているLenaという女性の画像です。

Maxのパーティクルシステム その2: jit.p.shiva jit.p.vishnu jit.gen jit.gl.multiple #max6 #maxmsp #jitter #cycling74

※編集中 スクリーンショット 2014-07-07 21.25.02

Rotate planeの追加

パーティクルに回転処理を加えたいという事で、前回調べた結果パーティクルを格納するmatrixに対して、rotate の値を管理するplaneを実装する必要がありました。 plane数を6にして、6番目の要素をrotateとする事にします。ただし、受け付けられるplaneがjit.p.shiva は 5か8 jit.p.vishnuが5と限られているため、拡張したmatrixを単純にjit.p.vishnuに突っ込めないです。そのためにはjit.matrixのplane数を調整するために jit.unpack / jit.pack を駆使して、分割したMatrixに対して処理を書く必要があります。

jit.p.shivaの初期化タイミングを取得する

回転の初期化をするのも問題がありました。初めは回転の初期化をするタイミングをLifeの値を見ることで、取得しようとしました。

jit.p.shivaでパーティクルが生まれた瞬間Lifeの値が0以外の数値に埋められるのですが、この値が何故かmatrix row 1 の pre  の要素も初期値が入れられてしまって、preのnowの要素を比較してパーティクルが生まれた瞬間を取得できません。(クソい)

解決方法としては、jit.p.vishnuに指定するアトリビュートであるlifeを、回転処理をするjit.genにも与えて、lifeが最大値の時を初期化タイミングと見なして、回転の初期化をするようにしました。 そうしてめでたくRotateの初期値にバラつきをもった状態ができるようになりました。

Maxのパーティクルシステム面倒くさすぎて、貧弱すぎじゃないかと思ったり・・・。 スクリーンショット 2014-07-07 21.03.03 加えて前回のパッチでは、パーティクルの描画オブジェクトである jit.gl.multiple に matrix row 1 (row 1 には、パーティクルの1フレーム前の状態が含まれている)も与えていたため、2重に描画してしまっていました。今回は、jit.scissors により row 1 を切って row 0 のみ渡しておりパフォーマンスがUPしました。といっても1000パーティクルでフレームレートが30fps程度でした。

NC帝國によるシンリャク君ロゴ

スクリーンショット 2014-07-07 21.11.16

いきいきナードコア感謝祭’14

スクリーンショット 2014-07-07 21.09.59 VJで参加させて頂いた いきいきナードコア感謝祭’14 ナードコアの開祖・神々のプレーを拝めて、非常に興奮しました。コバルト爆弾αΩなんかは、とてもナードコアに影響を受けているので、今後の活動を考える上でも良かった。VJ的には、カッカのアニメVJにアニメ的なエフェクトを載せてみようということで集中線や♡等を散らしました。今後もうちょっと演出を詰めていくとアニメ的な演出を解体・再構築したようなVJが出来そうで、個人的に面白くなりそうでした。

ロジスティクス写像を使ったジェネレ−ティブVJ : #max6 #jitter #cycling74

ジェネレ−ティブなVJの方々って、どうやってノイズと関数の中間みたいな画像を次々につくりだしてるのか。時々疑問に思っていました。ふと、ロジスティクス写像なんかの変数をちょっといじると値の収束する場所が次々に変わるような、カオス理論の本などで出てくる数式を使ってるのかなと思い、簡単そうだったので実装してみました。

結果、単なるrandom乱数だと難しそうな値を取得できていると思います。割りと似たような画像が良くでてきちゃったり、あまり音との連動性が良くないので、もう少し工夫が必要そうですが。

jit.gen コード
スクリーンショット 2014-07-05 8.53.01

ロジスティクス写像の数式は、Wikiのカオス理論に載って要る通り、とても簡単です。

再帰呼び出しをして実装しようとしたのですが、jit.genではコンパイルが通らなかったのでfor文にしています。jit.gen のエラーコードは、glslとluaのエラー(っぽいの)が生で出てくるので原因が全然分からない。Param aの値に音量をぶち込んで音との連動を試みています。実装間違っていたら教えて下さい(笑)

カオス理論で使われる数式は、他にも色々式があるみたいです。次はローレンツ方程式を試してみたいです。

jit.gl.pix を jit.gl.lua 内で利用する

スクリーンショット 2014-06-06 16.27.18

[javascript]

autowatch = 1
gc = 1

local mov = jit.new("jit.qt.movie",this.drawto)
mov.adapt = 1;
mov:read("bball.mov");

local frame = jit.matrix();
local plane = jit.new("jit.gl.videoplane",this.drawto);
local pix = jit.new("jit.gl.pix",this.drawto)
pix.gen = "aer.line.genjit"; — readでは無く、genでファイルを読み込みます。
 

function draw()

mov:matrixcalc(frame.name,frame.name)

pix:jit_matrix({frame.name})
pix:draw()

plane:jit_gl_texture(pix.out_name)
plane:draw();

end

[/javascript]

jit.gl.lua で jit.gl.slab シェーダーをかけるサンプル:#jitter #max6

スクリーンショット 2014-05-28 22.31.18

jit.gl.luaの内部で、シェーダーをかけるサンプルを書いてみました。

[javascript]

autowatch = 1
gc = 1

local mov = jit.new("jit.qt.movie",this.drawto)
mov.adapt = 1;
mov:read("bball.mov");

local frame = jit.matrix()

local plane = jit.new("jit.gl.videoplane",this.drawto);
plane.scale = 0.8
plane.automatic = 1;

local slab = jit.new("jit.gl.slab",this.drawto)
slab.file = "td.kaleido.jxs";
slab:param("div",4)

function draw()

mov:matrixcalc(frame.name,frame.name)

slab:jit_matrix({frame.name,frame.name})
slab:draw()

plane:jit_gl_texture(slab.out_name)
plane:draw();

end

[/javascript]

ちなみに初めは、jit.gl.pixを使おうと思ったのですが、.genjitを読みこませようとすると

jit_xml_document: error reading file at byte offset 0 / not well-formed (invalid token)

というエラーがでてしまい、上手く読み込ませる事ができなかったです。