2009年7月 9日 (木)

「Jitterで動体検知して遊ぶ」を高速化してもらいました!その1

前回の「Jitterで動体検知して遊ぶ」で、いつも参考にさせてもらっているhttp://adsr.jp/のmasato221さんからコメントをいただき、なんと高速処理版を作ってくれました!
しかも2種類。いやーほんとありがたいです。うれしいです。助かります。
データは直リンクOKということでしたので遠慮なくリンクしてみます。
http://adsr.jp/patches/rx8.zip

masato221さんとはうちの同僚&元同僚がaircordのサイト制作で関わったとか。
なんか機会があればよろしくお願いします。。

というわけで今回は作っていただいたパッチの1つめを見てみます。

jitter_rx8

●映像入力
右上のエリアで動画を読み込みます。
読み込んだ動画は「color」という名前をつけたMatrixに入れておきます。
これを後で使います。

●動体検知
真ん中のエリアです。

1)jit.dx.grab 20 15 @unique 1
「@unique 1」というのが追加されました。
ドキュメントを見ると「デジタイザから新しいフレームを受信しなかった場合は bang には応答しません。」と書いてあります。
WEBカメラのフレームレートって低いので、前回のbangのタイミングとフレームが変わってなかったら何もしないってことですかね。処理が軽減されますね。

2)jit.rgb2luma
1プレーンのグレースケールのMatrixに変換します。
動いた部分を調べるだけならグレースケールの1プレーンだけでOKですね。
さらに処理が軽減されますね。

3)jit.dimmap @map 0 1 @invert 1 0
Matrixを(映像を)反転します。鏡っぽくするので。

4)t l l → jit.op @op absdiff
ここがよく分からず。。
2つのMatrixの違う部分を調べてるのは分かるけども、1つ前の「t l l」で違うMatrixを出力できてるということだろうか?
今のフレームと1つ前のフレームを比べることで動いてる部分を調べることができるはずだけどこれだと同じフレームが渡されてる感じがしてしまう。
うーん、もしかすると、両方のインレットにMatrixが送られてくるのはほぼ同時だけど厳密には同時ではないから1つめのインレットにMatrixがきた時点では2つめのインレットに入ってるMatrixは1つ前のだから大丈夫ってことかな。
そういうことにしておこう。

5)jit.expr @inputs 3 @expr \"snorm[0]*in[1]*1.33\" \"snorm[1]*-1*in[1]\" \"(in[2]/100)+in[0]\"
exprはあんまり理解してなかったけど、今回理解が深まりました。
・in[0]の数値はインレットの番号っぽい。
 なのでin[1]は2つめのインレットに入ってくる値。
・cell[0]はMatrixのX座標。1,2,3,4,5,・・・
・cell[1]はMatrixのY座標。1,2,3,4,5,・・・
・norm[0]はMatrixのX座標を0~1の範囲で正規化したもの。ex)0.0,0.1,・・・,0.9,1.0
・norm[1]はMatrixのY座標を0~1の範囲で正規化したもの。ex)0.0,0.1,・・・,0.9,1.0
・snorm[0]はMatrixのX座標を-1~1の範囲で正規化したもの。ex)-1.0,-0.9,-0.8,・・・,-0.1,0.0,0.1,・・・,0.9,1.0
・snorm[0]はMatrixのY座標を-1~1の範囲で正規化したもの。ex)-1.0,-0.9,-0.8,・・・,-0.1,0.0,0.1,・・・,0.9,1.0

norm、snormの意味が今まで分からなかったけどやっと分かった。これは便利だ。

「@inputs 3」としてインレット数を3つに変更。
2つめので●の並ぶ間隔、3つめので●の大きさを調整できるようになってます。

ここでできたMatrixはプレーン数が3つで、X値、Y値、サイズとなってます。

6)jit.iter
Matrixを1セルずつ出力していきます。
1つめのアウトレットではさっきのMatrixのX値、Y値、サイズがリストで出力されます。

「zl slice 2」でそのリストの最初の2つを1つめのアウトレットに出力。
「prepend position」で「position」とくっつけて「position X Y」というメッセージにする。
「zl slice 2」の2つめのアウトレットは残り(この場合はサイズ)が出力されます。
それを「prepend scale」で「scale」とくっつけてメッセージにします。

jit.iterの2つめのアウトレットでは今出力しているセルの座標がリストで出力されます。
その座標を使って「getcell X Y」のメッセージを作ります。
これを最初に作った「color」という名前の映像のMatrixに送ることで、その座標の色を取得できます。
getcellメッセージの出力結果は「jit.matrix color」の2つめのインレットから出力されます。
メッセージ内容は「cell X Y val A R G B」となってます。
必要なのは後半の「R G B」の部分だけなので「zl slice 5」として「cell X Y val A」と「R G B」に分けます。
「R G B」が2つめのインレットから出力されます。
それを「prepend color」としてcolorのメッセージにします。
また、同時に「if $f1+$f2+$f3 > 0. then 1 else 0」で色を調べて、黒だったら●を表示しないようにします。
これは「gate」に黒だったら0、黒じゃなければ1を送ることでbangメッセージが最後の「jit.gl.gridshape」に行かないように制御しています。

7)jit.gl.gridshape sample @shape circle @depth_enable 1 @automatic 0 @blend_enable 1 @blend_mode 3 1 @dim 10 10
●を表示します。


処理もすっきりしたし、処理速度も速くなりました。
前は20x15でもけっこうCPU負荷が高かったけど、これだと48x36でも大丈夫です。
左上のとこで変更できます。

いろいろと勉強になりました。
なんかレベルアップした気がする。。

では次はもう1つのjit.gl.multipleを使ったバージョンを見てみます。こっちはさらに速くなっております。
でもmultipleはまだ理解できてなくて・・・これを機に理解できるようにしよう。
また一通り解読したらブログに書きますので。

| | コメント (2) | トラックバック (0)

2009年6月22日 (月)

Jitterで動体検知して遊ぶ

この前赤色検知でやってみたけどイマイチだったので普通に動体検知をやってみました。
内容としては映像を入力し、色を取得して●に変換。カメラで動体検知して、動いた場所の●を反応させます。

とりあえずは動きいたけど、かなり頭の悪いやり方になってるはず。
●が20x15になってるのも単にCPU負荷的にこのくらいじゃないとダメだっただけで
ほんとはもっと細かくしてみたい。ちゃんとしたやり方でやれば軽くなるはず。。

jitter17

今回は下記のような考え方でやってます。


●動体検知
FLASHでやる時と同じ考え方です。
1つ前のコマと比べて色が違ってる部分が動いた部分として認識します。

1)カメラの取得
jit.dx.grab 20 15

Macだとjit.qt.grabです。
20x15のサイズで取得します。
20x15なのは処理軽減のため。。
最初から20x15にするのと、最初は320x240で後から20x15にするのとでどの程度違いがあるかは分からないけど、これでも一応大丈夫でした。

2)カメラ映像の調整
jit.brcosa

映像の明るさ、コントラストを調整し、検知しやすくします。
環境によって変わると思うので数値をいじれるようにしておきます。

3)滑らかに変化させる
jit.slide @slide_up 4 @slide_down 8

暗い場所だとカメラのフレームレートがかなり落ちるので、反応が悪くなります。
元映像をjit.slideで滑らかに変化させることでごまかそうという考えです。

4)反転
jit.dimmap @map 0 1 @invert 1 0

鏡っぽくしたいので映像を反転します。

5)Matrixへ
jit.matrix 7 float32 20 15

最終的なMatrixは色(ARGB)、座標(X,Y)、大きさ(scale)の7つの要素を持たせたいので
plane数が7のMatrixに入れ込みます。
このへんがまず効率悪そう。

6)現在のコマを取っておく
jit.matrix oldmt 4 float32 20 15

次のフレームで1つ前のコマとの違いを調べるために今のコマを取っておきます。

7)1つ前のコマとの違いを調べる
jit.op @op absdiff @val 0.

「absdiff」で違いを調べます。
FLASHでいうブレンドモードのdifferenceみたいなものです。
右のインレットには「jit.matrix oldmt 4 float32 20 15」を入力します。
これは「6」でやった1つ前のコマになります。
このへんは頭使いました。処理の順番が分かりにくい。
これで合ってるのかも分からないけど動いてるからOK。

8)値の調整
jit.op @op * @val 5.

色の違いを使って●のサイズを決めるので、そのへんの調整ができるようにしときます。

9)Matrixへ
jit.matrix 7 float32 20 15

これいらないんじゃないかな。。

10)Matrixの値の変更
jit.expr @expr 0. 0. 0. 0. (cell[0]/6-2.6) -(cell[1]/6-1.2) (in[0].p[1]+in[0].p[2]+in[0].p[3])*0.1+0.05

左から、アルファ、赤、緑、青、X、Y、サイズとなるようにMatrixの中身を変更します。
アルファ、赤、緑、青は別で処理するのでここでは0にしておきます。
「cell[0]」はセルのX値、「cell[1]」がY値になります。表示位置を調整するために少し値をいじります。
「in[0].p[1]」、「in[0].p[2]」、「in[0].p[3]」はそれぞれRGB値。値が大きいほどたくさん動いた部分になります。

11)Matrixへ
jit.matrix 7 float32 20 15

これもいらないんじゃないかな。。


●映像ファイルの入力
1)映像ファイルの読み込み
jit.qt.movie

「read」メッセージを送ることで映像ファイルを読み込みます。
ほんとは外部のDVD映像とかを読み込みたい。

2)滑らかに変化
jit.slide @slide_up 4 @slide_down 8

滑らかに変わったほうがいい感じだったので。

3)plane7のMatrixへ
jit.matrix 7 float32 20 15

動体検知したMatrixと合体させるためにplane数7のMatrixへ。

4)空いてる部分の数値を初期化
jit.op @op * pass pass pass pass 0. 0. 0.

「3」のままだと後ろの3つのplaneは前のplaneの値が繰り返し入ってしまうのでここで0に初期化します。

●Matrixの合成、表示
1)Matrixの合成
jit.op @op +

映像のMatrixと動体検知のMatrixを合体させます。
映像のはアルファ、赤、緑、青、0、0、0になってます。
動体検知のは0、0、0、0、X、Y、サイズになってます。
それぞれを足します。

2)Matrixの中身を1セルずつリストにしする
jit.iter

Matrixの各セルの値の全plane分をリストで出力します。
アルファ、赤、緑、青、X、Y、サイズのリストがセル数分出力されていきます。

3)黒いとこは表示しない
if $f1+$f2+$f3 > 0. then bang

黒いとこは見た目的にあんまり分からないので、処理の軽減のためにも表示させないようにします。
黒じゃない時だけbangを出力します。

4)●の描画
jit.gl.gridshape jit17 @color 0.2 0.9 0.6 0.1 @scale 0.08 0.08 0.08 @shape circle @depth_enable 1 @smooth_shading @automatic 0 @blend_enable 1 @blend_mode 3 1

「2」の出力結果を使ってjit.gl.gridshapeに色、座標、サイズのメッセージを送り、●を描画します。


こんな効率の悪そうな内容になってます。
ほんと誰かに教えてもらいたい。。

| | コメント (2) | トラックバック (0)

2009年6月 9日 (火)

ねんぴょー

なんか流行っているみたいで。。

http://nenpyo.org/primevision

けっこう夢中で書いちゃうなあ。
昔を思い出すのもボケ防止にいいみたいなので昔のもちょこちょこ追加しよう。

| | コメント (0) | トラックバック (0)

Jitterの赤色検知で遊ぶ。

前回のJitterでの赤色検知を使って、ちょっとした動きを追加。

画面内の赤い部分にパーティクルを発生させたいだけなんだけど、これが難しい。。
Flashだったら簡単にできるのになあ。単に俺が理解してないだけなのでこのソフトが悪いわけではないんだけども。

jit13

jit13_patch

1.処理軽減
MAX/MSPだったらパーティクルたくさんあっても軽い、みたいな先入観があったけど
そのへんはちゃんと作らないとやっぱり重くなるね。
カメラの取得サイズが160x120の場合19200となるわけで、それ全部パーティクルにしたらそれなりに重いか。

前回の赤色検知では160x120でやってたけど、そこはそのまま使いつつ、その後にサイズを小さくします。

jit.matrix 1 float32 20 15 @planemap 1

新しくMatrixを作ります。
赤色検知では最初にカメラの取得があるのでARGBの4つのplaneが存在してたけど、
最終的に使うのは赤の部分だけなので、新しいMatrixはplaneを1個だけにします。
「@planemap 1」で渡されたMatrixのplaneの1のやつ(0から始まるので2番目)がコピーされます。
とりあえずこれでデータ的には4分の1になるのかなと。
サイズは「20 15」としました。8分の1に圧縮されます。セル数的には64分の1。
セル数は300になり、この300個分のパーティクルを作るようにします。

2.パーティクル
「jit.gl.mesh」を使います。
2つあるのは四角を表示する用と、それらをつなぐラインを表示する用になってます。
@draw_mode が違うだけで他は同じです。

3.条件分岐
先ほど小さくしたmatrixの各セルごとに値を調べます。
その値が0.001より大きいときはその座標に向かってパーティクルは動き、
そうでない場合は真ん中に戻るようにしたいので、条件分岐をしたいなと。
最初は「if文なんてねーだろ」って思っていろいろ調べちゃったけど、普通にありました。

if $f1 > 0.001 then $i2 $i3 $f1 bang else 10 7.5 0. bang

$f1=1つめのインレットに渡される値=赤色の値
$i2=セルのX座標
$i3=セルのY座標
です。
「$f1」の「f」はfloatを意味してるっぽいです。
ここを最初「$i1」としてたら出力結果が整数にまるめられちゃって、if文で小数点ダメなのかって諦めかけてしまいました。。

この文は、
$f1が0.001より大きかったら「$i2 $i3 $f1 bang」というリストを返し、
そうでない時は「10 7.5 0. bang」というリストを返します。
「10 7.5 0.」は真ん中に来させるための値です。
「bang」はその後のcounterを動かすためのものです。

4.表示用Matrixの作成
300個のパーティクルに使う座標を格納するためのMatrixを作ります。
サイズは300x1です。XYZの値があるのでplaneは3つです。
1つめのplaneにX座標、2つめにY座標、3つめにZ座標を格納します。

その前に「setcell $1 0 val $2 $3 $4」というメッセージを作り、このMatrixへ渡してあげることで各セルへ値を入れていきます。

その前の部分では各値の調整をし、格納するセルのX座標を「counter」を使って制御して
packでリストにまとめています。
このへんはすごく頭を使いました。もっと楽な方法、速い方法があるはず。

5.動きをスムーズに。
「4」のMatrixを「jit.gl.mesh」に渡す時に「jit.slide」を通すことで、位置がパッと変わるのではなく、滑らかに動くようにします。
@slide_upはおそらく値が大きくなる時の変化の速さ、
@slide_downはおそらく値が小さくなる時の変化の速さだと思います。
よくFlashで簡単なイージングをかける時に、
this._x += (_xmouse - this._x) / 10;
などとしたりしますが、この式の「10」に相当するような感じです。
つまり値が小さいほど動きは速くなり、大きいほど遅くなります。

という感じで作ってみましたが、実際に動かしてみると赤よりもオレンジや黄色に反応しやすい。。
カメラの性能もあるけど映像が暗くなってしまうので、単に赤いものだと暗くなりやすいっぽい。黄色とかオレンジのほうが明るいので取得しやすい。
その前に赤色検知のやり方も微妙なんだけども。

カメラはどういうのがいいのかねー。

| | コメント (0) | トラックバック (0)

2009年6月 5日 (金)

Jitterで赤色検知

jit.exprを使ってカメラの映像の赤色検知をしてみました。
jit.exprはサンプルとかでよく使われてるけど意味が分かりにくい。
実験してみてやっとちょっとだけ分かった気がします。

Jitter12

<カメラの映像取得>
「jit.dx.grab 160 120」というオブジェクトを作って「open」メッセージを
送ってあげるとカメラの映像を取得できます。
ちなみにWindowsとMacでやり方が違ってて、これはWindowsの場合。
MacだとQuicktimeの機能を使うので「jit.qt.grab」となります。
「160 120」の部分はサイズ。

<映像の色データをMatrixへ>
「jit.matrix 4 float32 160 120」としてMatrixを作成。
「160 120」はMatrixのサイズ。これはカメラの取得サイズに合わせます。
「4」という数字はこの場合「160x120のサイズのMatrixを4枚」という感じの意味です。
「plane」というパラメータになります。
なぜ4つかというと、映像の色データ=ARGBの各値を各planeに割り振るわけです。

<赤色検知>
「jit.expr @expr 1 (in[0].p[1]-(in[0].p[2]+in[0].p[3])/2-0.1)*10 0 0」とします。
@exprのとこはスペースで区切って各planeに対する計算値を書きます。

1つめはアルファ値。だと思うんだけど値変えても特に変化ないので違うのかな。。

3つめ、4つめは緑、青の値。そこはいらないので0にします。

2つめがややこしくなってます。
考え方としては赤い部分を検出するにはR値が大きい部分が分かればよさそうですが
白いところか明るい部分は赤くなくてもR値は大きくなります。
なので「赤い部分だけ」を取得するにはR値が大きく、GとBの値が小さい部分を取得すればいいのかなと。

というわけで、計算は適当だけど「R値-(G値+B値)/2」ってしてみました。
これをexprでやる場合にどうしたらいいのか。

ドキュメントを見てみると「in」というのがあって、それが渡ってくるMatrixの値っぽい。
「in[0]」って[]内に数字を書きます。これはなんだろうな。。おそらくMatrixが複数渡ってくる場合があって、どのMatrixかを指定するためなのかと。分かんないけどここは通常0でいいかと。

例えば「jit.expr @expr in[0] in[0] in[0] in[0]」とすると各値に渡ってきたMatrixがそのまま設定されます。
「jit.expr @expr in[0]*0.5 in[0]*0.5 in[0]*0.5 in[0]*0.5」とすると全部半分の値になるので暗くなります。
ちなみに全部同じ計算をするのであれば「jit.expr @expr in[0]*0.5」と1つだけ書けばいいです。

まずは「jit.expr @expr 1 in[0] 0 0」としてみます。
R=赤は渡ってきたMatrixの中身そのままになります。GとBは0になります。
これで赤のチャンネルのみ表示できました。

先ほどの計算「R値-(G値+B値)/2」をしてみます。
「in[0]」で渡ってきたMatrixを取得できるけども、これは2つめに「in[0]」と書いたら渡ってきたMatrixの2つめのplaneの値を受け取ることになります。
なので「in[0]」だけだと赤(2つめ)のとこで緑(3つめ)と青(4つめ)の値が取れません。
他のとこの値をとるには「in[0].p[1]」とします。
「p」ってのが「plane」の略っぽいですね。
「in[0]」が渡ってきたMatrix(の1つめ)、その中にあるplaneを「p[1]」で[]内でplaneの番号を
指定して値を取得できるということです。
「p[1]」を省略したら勝手に対応するplaneの値を取得できるってことですね。
なので「jit.expr @expr in[0] in[0] in[0] in[0]」と書くと、それは勝手に
「jit.expr @expr in[0].p[0] in[0].p[1] in[0].p[2] in[0].p[3]」としたのと
同じになるってことみたいです。

というわけで「R値-(G値+B値)/2」を計算するには
「in[0].p[1]-(in[0].p[2]+in[0].p[3])/2」
とすればOKです。

表示確認用にちょっと計算を変えてます。
0.1を引いて暗い赤は完全に暗くして、残った赤は10倍にしてより明るい赤にしてます。

あとはこの結果のMatrixで、2つめのplaneの値を見れば赤色部分が分かるのでそれを
使えばいいのかなと思います。

| | コメント (0) | トラックバック (0)

2009年5月26日 (火)

MAX/MSP+Jitter勉強中

わけあってJitter勉強中です。
まだ体験版。ほんとに買ってくれるのだろうか??

前にもちょっとさわったことがあったので、MAX/MAPの基本的な使い方は一応理解済み。
Jitterもやったけど結局よく分からず断念してた。
そんでまた最近再勉強中。

↓このへんで基本の勉強
http://www.s-musiclab.jp/mmj_docs/max5/jitter/jitter_tutorial/j17_jttu_top.html
http://yoppa.org/ssaw06.php?itemid=316

Matrixの生成とかいじり方とかをなんとなく理解した。

世の中にあんまりサンプルとか無いんだけど、
↓こちらでカッコイイのをダウンロードできるので参考にしながら勉強してます。
http://adsr.jp/

もっと簡単なのがあるといいんだけどな。。

で、そこを参考にしながら軽く作ったのが以下。

jitter10_window

jitter10_patch

jitter10_subpatch

以下、理解している内容。間違ってる可能性はけっこうあるので気をつけてください。

1.「jit.p.shiva」「jit.p.vishnu」でパーティクルを生成。
AfterEffectsのプラグインのParticularみたいな感じ。

・jit.p.shiva
life:寿命(フレーム数)
life_var:lifeの値を基準にしたランダム範囲かな?
emit:毎フレームの生成量。この数値が収まるmatrixを用意しておく。
   (「jit.matrix pt 5 float32 500 2」のとこの「500」)
emit_var:emitのランダム範囲?

・jit.p.vishnu
pos:生成する座標
force:パーティクルに加わる力。風とか重力みたいなもの。
speed:初期速度?
pitch、yaw:横か縦の広がり具合?

その後matrixのごにょごにょがありまして。。


2.「jit.gl.mesh」で描画
matrixを使って点とかラインとかポリゴンを描画する。

・jit.gl.mesh
draw_mode:描画モード。points、triangle、tri_strip、lines、line_stripとか。
smooth_shading:1にすればキレイになるんだろうね。
auto_colors:これを1にすると勝手に色がつく。
color_mode:「auto_colors」が1の時に使用。何を元に色をつけるか。
lighting_enable:1にするとライト効果。
poly_mode:調べてない。
blend_enable:FLASHでいうブレンドモードを使うかどうか。
blend_mode:ブレンドモードの種類。1 1だとたぶん加算。
point_size:draw_modeがpointsの時に描画される点の大きさ。


3.波形を使ってパーティクルの生成位置を制御
サブパッチ「sinMtrix」の中では波形を生成して、そのsin値をmatrixに入れてoutputしてます。
x,y,zの3つに違う値を反映させたいので、3つ作ってます。

・jit.poke~
波形をmatrixに変換。
「spos」は名前。なんでもいいけど4箇所とも同じにする。
「1 0」はmatrixの番号。

「jit.matrix spos 3 float32 1」で3つのmatrixをまとめる。
「3」はレイヤー数。「float32」は各セル内の型。「1」は行列の数。

サブパッチを出る。

・jit.op
matrixの演算。
op:演算の種類。+-*/とか。
val:元のmatrixに+(-*/)する値。

・jit.iter
matrixの各レイヤーをリストで出力?

処理順は3→1→2だけど、考え方の順で書きました。

その他のオブジェクトについて。

・jit.window jit10
出力用ウィンドウの生成。名前をつける。

・jit.gl.render jit10 @erase_color 0 0 0 1
openGLのレンダリングを行う。

erase_color:毎フレーム(eraseメッセージを送ることで)指定した色で塗りつぶす。ProcessingとかFLASHのBitmapDataと考え方は同じかな。
camera、lookatとか:カメラの位置、向き。うまく調整できない。。

まだまだいいの作れないけど、分かってくるとおもろいなー。

| | コメント (2) | トラックバック (0)

2009年5月18日 (月)

新婚旅行に行ってきました。

4月24日から5月3日まで新婚旅行に行ってきました。

日本を代表するフラッシャーさんたちがFITCのためにTorontoへ向かう中、逆方向へ向かってみました。
行ったのはギリシャのサントリーニ島、アテネ、フランスのパリ、ロワール、ヴェルサイユ。
FLASHのこととか忘れてしまうくらい満喫してきましたよ。おかげで帰ってからしばらく使い物にならないくらい腑抜けに。。だんだん調子戻ってきたかな。

やっとflickrに写真アップできたー。
http://www.flickr.com/photos/30200414@N08/sets/

| | コメント (0) | トラックバック (0)

2009年3月16日 (月)

DeskTopLive.asに行って来た。

会社の近くなので行って来ました。
DeskTopLive.as

FLASHerの身ではありますが、一番見たかったのは 真鍋大度さん。

やっぱりああいうの楽しいなあ。大学でちゃんと学んでればデバイスもそれなりに作れるようになってたかな?(機械システム工学科)
すぐにWEBにはまってしまい、大学どうでもいいやーってなっちゃったからな。。これから勉強しよ。

少し前にMAX/MSPもやったけど、試用期間が過ぎちゃったのでもう実験できず。買おうかどうか悩んでます。
とりあえず無料なんでproccessingをまた始めました。なんかおもしろいの作りたいと思ってます。

ちなみにDeskTopLive.asは仕事があったので2部が終わった後に帰りました。
メインのイベントも見たかったけど、有名どころなんでまた見れるかと。
って、いつまでもこっち側じゃダメだな。。

数ヶ月どっかに篭って修行したい。

| | コメント (0) | トラックバック (0)

2009年2月16日 (月)

RED5をやってみた。

6年位前にFlash Communication Server MXでマルチユーザーの実験をしたことはあったけど、未だに案件でそういうコンテンツを作る機会が無く。。
企画の段階で話が出たりすることもあるけど、結局サーバの問題でNGになることが多いなあ。
(クライアントのサーバをいじれない、他に専用サーバを用意する費用がない、等)

でも今年やっと仕事でマルチユーザのコンテンツができるかもしれない!
その場合はFlash Media Serverという選択肢はあるんだけど、やっぱり高いんで無料でできるRED5を試してみました。

インストールやら設定なんかは他のサイトを参考にやってみました。
ただサーバサイドのプログラムの作成のとこで、参考サイト通りにやってもできずに悩みました。。

<参考>
http://coelacanth.heteml.jp/site/flash_red5/article_4
http://www.sonicjam.co.jp/soniclabs/?p=46
http://www.thinkit.co.jp/article/152/3/3.html

このへんで書かれてるのはスクリプトを書くところまでだけど、実際はjar形式でエクスポートし、「lib」フォルダに格納しなければいけませんでした。
バージョンが違うのかな?それかなんか設定がおかしい?まあ動いたからいいや。

以下、参考サイトに書いてあることだけど個人的なまとめ。
サーバーサイドのJavaです。

クライアントからサーバサイドの関数を呼ぶと引数で「IConnection conn」が渡される。
これを使って呼び出し元のクライアントの関数を実行できる。

if (conn instanceof IServiceCapableConnection) {
	IServiceCapableConnection sc = (IServiceCapableConnection) conn;
	sc.invoke("recvID", new Object[]{id, msg});
}

接続しているクライアント全部の関数を実行するのは下記のとおり。

IScope scope = Red5.getConnectionLocal().getScope();
Set roomClients = scope.getClients();
Iterator iter = roomClients.iterator();
while ( iter.hasNext() ) {
	IClient client = (IClient) iter.next();
	Set connset = client.getConnections();
	Iterator itcon = connset.iterator();
	while (itcon.hasNext()) {
		conn = itcon.next();
		if (conn instanceof IServiceCapableConnection) {
			IServiceCapableConnection sc = (IServiceCapableConnection)conn;
			sc.invoke("recv", new Object[]{getClients().size()});
		}
	}
}

誰かが接続してきた時に呼ばれる関数。
この中で接続者全員に現在の接続者数を渡してあげればリアルタイムで接続人数を更新できる。
接続人数は「getClients().size()」で取得できる。
ただし、この中ではまだ新しく接続した人の分はプラスされてないので+1してから返すといい。

public boolean appConnect( IConnection conn , Object[] params ){}

誰かが切断した時に呼ばれる関数。
この中で接続者全員に現在の接続者数を渡してあげればリアルタイムで接続人数を更新できる。
接続人数は「getClients().size()」で取得できる。
ただし、この中ではまだ切断した人の分はマイナスされてないので-1してから返すといい。

public void appDisconnect(IConnection conn) {}

MySQLを使う時は以下の通り。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

Statement stmt = null;
try {
	Class.forName("com.mysql.jdbc.Driver");
	Connection db_conn = DriverManager.getConnection("jdbc:mysql://localhost/dbname", "username", "password");
	stmt = db_conn.createStatement();
	ResultSet rs = stmt.executeQuery("SELECT * FROM id");
	while(rs.next()) {
		//1行ずつ処理するのかな
		//取得するのがStringなのかIntなのか等で関数が変わる
		id = rs.getInt("num");
	}
	stmt.close();
	stmt = null;
	db_conn.close();
	db_conn = null;
	updateID(id+1);
} catch (SQLException ex) {
	//log.debug("SQLException: " + ex.getMessage());
	//log.debug("SQLState: " + ex.getSQLState());
	//log.debug("VendorError: " + ex.getErrorCode());
} catch (Exception e2) {
	//log.debug("Exception: " + e2.getMessage());
}

UPDATEとかINSERTのときは↓こんな感じで。

import java.sql.PreparedStatement;

PreparedStatement updateNum = db_conn.prepareStatement("UPDATE id SET num = ? ");
//SQL文の中の「?」に対して何を指定するか
//第一引数は「何番目の?」に対してか。
//第2引数は「その?に何を入れるか。
updateNum.setInt(1, id);
updateNum.executeUpdate();

てか今年初だ。今年はがんばります。。

| | コメント (0) | トラックバック (0)

2008年11月13日 (木)

PixelBenderで輪郭抽出&輪郭に雪を積もらせる

最近カメラで取得した映像を使って遊んでます。
映像の中の人とかに雪が積もるといいなーと思ってやってみました。

境界線が分かればなんとかなるので、↓こちらを参考にやってみました。
http://web.sfc.keio.ac.jp/~shokai/archives/2007/05/proce55ing-webcam-edge-detect.html

●まずはActionScriptで

元ソースはProcessingなので、ASに書き直す必要があります。
そのままいけないので試行錯誤した結果が下記のような感じです。
(※省略してます)

考え方としては、まず画像全体の色を4段階にして処理をしやすくし、それから境界線を調べていくようです。

//RGBの各チャンネルを3つのBitmapDataのBLUEチャンネルにコピー
bd1Red.copyChannel(bd1,bd1.rect,pt,BitmapDataChannnel.RED,BitmapDataChannnel.BLUE);
bd1Blue.copyChannel(bd1,bd1.rect,pt,BitmapDataChannnel.GREEN,BitmapDataChannnel.BLUE);
bd1Green.copyChannel(bd1,bd1.rect,pt,BitmapDataChannnel.BLUE,BitmapDataChannnel.BLUE);


//4段階に量子化
//各座標の色を取得し、0x00~0xFFまでの値にし、その中でさらに4段階化する。
//0x00~0xFFまでの値にしやすいように全部BLUEチャンネルにコピー
//その状態でgetPixelしたら0x00~0xFF(0x000000~0x0000FF)の範囲で取得できるかと思いきや・・・
//他のチャンネルも同じ値になるみたい。
//仕方なくgetPixelしてから0xFFFF00で割った余りを使って0x00~0xFFの範囲にする
//一度64で割ってから小数点以下を切り捨てて、また64倍することで4段階の色に分かれる
//(0~255の256段階なので4段階にするには4で割った64で割れば0~3の4段階になる)
for(var yy:int = 0; yy < 240; yy++){
	quants_pixels[yy] = [];
	for(var xx:int = 0; xx < 320; xx++){
		var red:Number = bd1Red.getPixel(xx,yy) % 0xFFFF00;
		var blue:Number = bd1Blue.getPixel(xx,yy) % 0xFFFF00;
		var green:Number = bd1Green.getPixel(xx,yy) % 0xFFFF00;
		var level:Number = Math.floor((red + blue + green)/3/64) * 64;
		quants_pixels[yy][xx] = level; //draw each pixel to the screen
	}
}

// 周囲(4ピクセル)の平均値と比較する
//各ピクセルを上下左右の平均と比べて、それより大きいか小さいかで境界線かどうかを判定するようです
for(yy=1; yy<240-1; yy++) {
	for(xx=1; xx<320-1; xx++) {
		//bd2.setPixel(xx,yy,quants_pixels[yy][xx]);
		var around:Number = (quants_pixels[yy - 1][xx] + quants_pixels[yy][xx - 1] + quants_pixels[yy][xx + 1] + quants_pixels[yy + 1][xx]) / 4;
		if (around < quants_pixels[yy][xx]) {
			bd3.setPixel(xx, yy, 0x000000);
		} else {
			bd3.setPixel(xx, yy, 0xFFFFFF);
		}
	}
}

以上のような考え方でやってます。
後半は元のやつとだいたい同じなのでいいと思うけど、前半の処理はかなり効率が悪そう。。
でもとりあえずは境界線を取得できたので、これで雪を降らそうと思ったけど、処理がかなり重いことに気付きました。。

●PixelBenderで

処理を軽くしないといけないんだけどいい方法も思いつかず、検索するのも面倒だなあと思ってたときに「PixelBender使えばいいんじゃない??」って思いついて試しにやってみました。
考え方は一緒で、色を4段階にして、その後に各ピクセルごとに境界線の判定をします。
この2つを1つのPixelBenderの処理でやりたかったけど、PixelBenderも分け分からないので2つに分けました。
でもこれで処理は全然軽くなりましたー。といってもまだ重いけど、ASだけに比べれば全然速い。
考え方は同じでやってるので、ASでやった時とPixelBenderでやった時で特に違いはなく、ただ処理が速くなりました。

これをもっとキレイにして忘年会で使おうと思います。

↓カメラ映像の境界線に雪が積もります。クリックで画面切り替わります。
雪の表現はこちらを参考にしました。


<PixelBender>


kernel FourColor
<	namespace:			"";
	vendor:				"";
	version:			1;
	description:		"";
>
{
	input image4 source;
	output pixel4 result;
	void evaluatePixel()
	{
		pixel4 color = sampleLinear( source, outCoord() );
		float b1 = (color.r + color.g + color.b) / 3.0;
		
		float r = 0.0;
		float g = 0.0;
		float b2 = floor(b1 * 100.0 / 25.0);
		float b = b2 * 25.0 / 100.0;
	
		result = pixel4(r,g,b,1.0);
				
	}
}

<PixelBender>


kernel OutLine
<	namespace:			"";
	vendor:				"";
	version:			1;
	description:		"";
>
{
	input image4 source;
	output pixel4 result;
	void evaluatePixel()
	{
		//ある座標
		float2 xy = outCoord();
		//その座標の上下左右の座標
		float2 xy1 = float2(xy.x, xy.y-1.0);
		float2 xy2 = float2(xy.x-1.0, xy.y);
		float2 xy3 = float2(xy.x+1.0, xy.y);
		float2 xy4 = float2(xy.x, xy.y+1.0);
		//それぞれの色を取得
		pixel4 cl = sampleLinear(source, xy);
		pixel4 cl1 = sampleLinear(source, xy1);
		pixel4 cl2 = sampleLinear(source, xy2);
		pixel4 cl3 = sampleLinear(source, xy3);
		pixel4 cl4 = sampleLinear(source, xy4);
		//各色のBlueチャンネルの色を取得(渡される画像は青のチャンネルしかないはず)
		pixel1 b = cl.b;
		pixel1 b1 = cl1.b;
		pixel1 b2 = cl2.b;
		pixel1 b3 = cl3.b;
		pixel1 b4 = cl4.b;
		
		pixel4 aaa;
		//上下左右の色の平均値よりも大きいか小さいかで境界線を判定
		if ((b1+b2+b3+b4)/4.0 < b) {
			aaa = pixel4(0.0,0.0,0.0,1.0);
		} else {
			aaa = pixel4(1.0,1.0,1.0,1.0);
		}
		
		result = aaa;
	}
}

<ActionScript>

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.display.Shader;
	import flash.filters.ShaderFilter;
	import flash.filters.BlurFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.display.BlendMode;
	import flash.utils.ByteArray;
	[SWF(width=640,height=480,backgroundColor=0xFFFFFF,frameRate=120)]
	public class Snow extends Sprite {
		//青の4段階色に変換するためのPixelBender
		[Embed(source = "test09.pbj", mimeType = "application/octet-stream")]
		private var shaderData:Class;
		//↑の境界線にするためのPixelBender
		[Embed(source = "test10.pbj", mimeType = "application/octet-stream")]
		private var shaderData2:Class;
				
		private var shader:Shader;
		private var shaderFilter:ShaderFilter;
		private var shader2:Shader;
		private var shaderFilter2:ShaderFilter;

		private var particleList:Array = [];
		
		private var my_cam:Camera;
		private var video:Video;
		private var bd1:BitmapData;
		private var bd2:BitmapData;
		private var bd3:BitmapData;
		private var bdSnow:BitmapData;
		private var bdBlack:BitmapData;
		private var bdSmall:BitmapData;
		private var bmBlack:Bitmap;
		private var pt:Point;
		private var cl:ColorTransform;
		private var mtSmall:Matrix;
		private var mtBig:Matrix;
		public function Snow() {
			addEventListener(Event.ADDED_TO_STAGE, added);
		}
		private function added(e:Event):void {
			removeEventListener(Event.ADDED_TO_STAGE, added);
			init();
		}
		private function init():void {
			//PixelBenderのやつをFilterとして使うための処理
			shader = new Shader(ByteArray(new shaderData()));
			shaderFilter = new ShaderFilter(shader);
			shader2 = new Shader(ByteArray(new shaderData2()));
			shaderFilter2 = new ShaderFilter(shader2);

			pt = new Point();
			cl = new ColorTransform();
			
			//カメラ
			my_cam = Camera.getCamera();
			my_cam.setMode(320, 240, 30, false);
			//カメラの映像の表示
			//デフォルトで全画面、ステージクリックで裏の処理過程のBitmapDataが見れるようにサイズ変更
			video = new Video();
			video.attachCamera(my_cam);
			video.width = 640;
			video.height = 480;
			video.x = 0;
			video.y = 0;
			stage.addEventListener(MouseEvent.CLICK, clickVideo);
			function clickVideo(e:MouseEvent):void {
				if (video.width > 500) {
					video.width = 320;
					video.height = 240;
					bmBlack.alpha = 0.0;
				} else {
					video.width = 640;
					video.height = 480;
					bmBlack.alpha = 0.5;
				}
			}

			//カメラの映像をBitmapData化してBlurを少しかけた状態を出力する用
			bd1 = new BitmapData(320, 240, false);
			var bm1:Bitmap = new Bitmap(bd1);
			bm1.x = 320;
			bm1.y = 0;
			addChild(bm1);
			
			//PixelBenderを使って青の4段階色に変換した状態を出力する用
			bd2 = new BitmapData(320, 240, false);
			var bm2:Bitmap = new Bitmap(bd2);
			bm2.x = 0;
			bm2.y = 240;
			addChild(bm2);

			//PixelBenderを使って境界線の状態にしたのを出力する用
			bd3 = new BitmapData(320, 240, false);
			var bm3:Bitmap = new Bitmap(bd3);
			bm3.x = 320;
			bm3.y = 240;
			addChild(bm3);

			//カメラの映像は上のレイヤーにしたいのでここでaddChild
			addChild(video);
			
			//雪用のBitmapDataを毎回黒で塗りなおすので、それ用
			bdBlack = new BitmapData(640, 480, false, 0x000000);
			//カメラ映像は少し暗めにすると雪が見やすくなるので↑を使いまわしてアルファかけて配置
			bmBlack = new Bitmap(bdBlack);
			addChild(bmBlack);
			bmBlack.blendMode = BlendMode.MULTIPLY;
			bmBlack.alpha = 0.5;
			
			//雪を降らす用のBitmapData
			bdSnow = new BitmapData(640, 480, false, 0x000000);
			var bmSnow:Bitmap = new Bitmap(bdSnow);
			addChild(bmSnow);
			//BitmapDataの背景色(黒)を消すためにBlendModeを使う。
			//ScreenでもいいかもしれないけどADDのほうが明るくなるので。
			bmSnow.blendMode = BlendMode.ADD;
			
			//雪で使うのを作っとく
			bdSmall = new BitmapData(320, 240, false, 0x000000);
			mtSmall = new Matrix(0.5, 0, 0, 0.5, 0, 0);
			mtBig = new Matrix(2, 0, 0, 2, 0, 0);
			
			addEventListener(Event.ENTER_FRAME, loop);
			
			
		}
		
		private function loop(e:Event):void {
			//カメラの映像をdraw
			bd1.draw(video);
			//境界線が複雑になり過ぎる場合はブラーかける。かけすぎると境界線が取得しにくくなる。
			//bd1.applyFilter(bd1, bd1.rect, pt, new BlurFilter(2, 2, 1));
			
			//PixelBenderを使って青の4段階色に変換
			bd2.draw(bd1);
			bd2.applyFilter(bd2, bd2.rect, pt, shaderFilter);
			
			//PixelBenderを使って境界線に変換
			bd3.draw(bd2);
			bd3.applyFilter(bd3, bd3.rect, pt, shaderFilter2);
			//ブラーをかけて境界線を太くする
			//これもPixelBenderでやっちゃうほうが速いかも
			bd3.applyFilter(bd3, bd3.rect, pt, new BlurFilter(2, 2, 1));
			
			//雪のオブジェクト(Particle)をaddし、配列particleListに入れてく
			addParticle();
			addParticle();
			addParticle();
			addParticle();
			addParticle();
			
			//雪の表示用BitmapDataを黒く塗りつぶす
			bdSnow.draw(bdBlack);
			
			//particleListに入ってる雪のオブジェクトを1つずつ座標更新、表示していく
			for (var i:int = particleList.length - 1; i >= 0; i--) {
				//雪オブジェクトを取り出す
				var tw:Particle = Particle(particleList[i]);
				//境界線のBitmapData(bd3)の中で、雪オブジェクトと同じ座標の色を取得
				if (tw.y < 4 || tw.y > 476 || bd3.getPixel(Math.round(tw.x/2), Math.round(tw.y/2)) == 0xFFFFFF) {
					//それが白だったら何もない
					tw.update(false);
				} else {
					//白じゃなかったら境界線上
					//元は白or黒だけど、ブラーかけたのでグレーの部分も境界線となるので「白以外は境界線」とする
					tw.update(true);
				}
				
				//雪オブジェクトと同じ座標のピクセルを白で塗りつぶす
				bdSnow.setPixel(tw.x, tw.y, 0xFFFFFF);
				
				//雪オブジェクトが画面から消えてたら配列から削除
				if (tw.del) {
					particleList.splice(i, 1);
				}
			}
			
			//雪のキラキラをするために1回小さくする
			bdSmall.draw(bdSnow, mtSmall);
			bdSmall.applyFilter(bdSmall, bdSmall.rect, pt, new BlurFilter(2, 2, 1));
			bdSnow.draw(bdSmall, mtBig, cl, BlendMode.SCREEN);
			
			//軽くブラーをかけてみる
			bdSnow.applyFilter(bdSnow, bdSnow.rect, pt, new BlurFilter(2, 2, 1));
		}
		
		private function addParticle():void {
			//雪オブジェクトの追加
			var tw:Particle = new Particle();
			particleList.push(tw);
			tw.x = Math.random() * 640;
			tw.y = 1;
		}
	}
}
import flash.display.Bitmap;
class Particle {
	public var x:Number;
	public var y:Number;
	public var del:Boolean = false;
	private var vy:Number;
	private var ay:Number;
	function Particle() {
		vy = 0;
		ay = 0.03;
	}
	public function update(b:Boolean):void {
		if (b) {
			//境界線上の速度
			vy = 0.01;
		} else {
			//何もないとこの速度
			vy += ay;
			if (vy > 3) {
				vy = 3;
			}
		}
		y += vy;
		if (y > 480) {
			//画面から消えたらdel=trueにすることで削除対象になる
			del = true;
		}
	}
}


| | コメント (0) | トラックバック (0)

«FLASHで「L.A.S.E.R Tag」みたいなやつのテスト