例題16-2:続・とっても大切な縞模様¶
A: 今回は例題16-1の続きでGaborパッチを描く。といってもVisionEggにはGaborパッチを描く関数があるわけではなく、例題16-1のグレーティングにマスクをかけるという方法をとる。
B: あれ、僕の前フリは? 頑張ってGaborパッチの勉強をしてきたのに。
A: 作者が「気分が乗らないから全面カット」だとさ。最近仕事がうまくいかなくて落ち込んでるみたいだからおやつでも持って遊びに行ってやれ。
B: えー。Aさんが行けばいいじゃないですか。山田屋まんじゅうでも持って行って。
A: ふむ。山田屋まんじゅうは私も好物だが、奴の地元の菓子を持って行ってどーする。
B: じゃあ十万石まんじゅうで。
A: うまい、うますぎるって奴だな。まだ食べたことないんだよなあ。作者に食べさせるくらいならまず私に食べさせてくれ。
B: それこそぼくがAさんにおごってどーするんですか。自分で買ってください。っていうかさっさとサンプルの説明に入ってください。
A: ほいほい。
行番号なしのソースファイルをダウンロード→ 16-3.py
1# -*- coding:shift-jis -*-
2import VisionEgg
3import VisionEgg.Core
4
5import pygame
6import VisionEgg.Gratings
7import VisionEgg.Textures
8
9screen = VisionEgg.Core.get_default_screen()
10sx,sy = screen.size
11
12mask = VisionEgg.Textures.Mask2D(function='gaussian', # also supports 'circle'
13 radius_parameter=25, # sigma for gaussian, radius for circle (units: num_samples)
14 num_samples=(256,256)) # this many texture elements in mask (covers whole size specified below)
15
16stim = [VisionEgg.Gratings.SinGrating2D(mask=mask,
17 position=(sx/2-50,sy/2),
18 size=(300,300),
19 spatial_freq=0.1,
20 temporal_freq_hz=1.0,
21 orientation=45),
22 VisionEgg.Gratings.SinGrating2D(mask=mask,
23 position=(sx/2+50,sy/2),
24 size=(300,300),
25 spatial_freq=0.05,
26 temporal_freq_hz=2.0,
27 orientation=90)]
28
29viewport = VisionEgg.Core.Viewport(screen=screen,stimuli=stim)
30
31ct = st = VisionEgg.time_func()
32stim[0].parameters.t0_time_sec_absolute = st+2.5
33while ct-st < 5:
34 ct = VisionEgg.time_func()
35 e = pygame.event.get()
36 screen.clear()
37 viewport.draw()
38 VisionEgg.Core.swap_buffers()
39
40screen.close()
B: なんだか珍しいですね。英語のコメントが付いてる。
A: あー。VisionEgg付属のデモからコピペしてきたからな。手抜き感バリバリやね。ポイントはひとつ。12行目からのVisionEgg.Textures.Mask2D。 テクスチャを丸く切り抜くマスクを作成する。要するに 例題8-1 、 8-2 でやったような覗き窓に似ているんだが、穴の開いた板をかぶせて周りを隠すのではなく、切り抜くという点が大きく異なる。
B: ?? 違いがよくわかりませんが。
A: サンプルを実行してみたらわかるよ。
B: おお。ふたつのGaborパッチが重なってますね。きれいきれい。
A: 例題8の方法でこれを描くのは不可能なんだが、わかるか?
B: へ? えーっと、えーーっと。
A: 例題8の方法では、まずグレーティングを描いておいて、Gauss関数でαチャンネルを指定した板を上から重ねてGaborパッチにする。OK?
B: ええ、そこまではわかるんですが…。
A: PCになったつもりで丁寧に描画作業を想像するんだ。例題8の方法の場合、右側のグレーティング、右側の穴の開いた板、左側のグレーティング、左側の穴の開いた板の4つの刺激を描画する必要がある。 二つのGaborパッチが離れていれば全然問題ないんだが、この例のように重なっているとどういう順番で4つの刺激を描画しても、どちらかのGaborパッチが不自然に切れてしまう。
B: ああ、なるほど。確かに。
A: まあ実はαチャンネル付きのGaborパッチのテクスチャデータを自前で生成すれば例題8の方法でもできなくはないんだが、 そんな面倒くさいことはしたくない のでVisionEgg.Textures.Mask2Dの出番だ。 これは他のテクスチャ刺激の上に貼りつけて、そのテクスチャを丸く切り抜く働きを持つ。隠しているのではなく切り抜いているので、サンプルプログラムのようにGaborパッチを二つ重なるように配置しても問題なく表示できる。 B: ふむふむ。
A: 12行目にあるように、引数functionに'gaussian'を指定すると二次元Gauss関数に従って次第に薄れていくように切り抜く。'circle'にするとシャープな境界で切り抜くことが出来る。 radius_parameterは13行目のコメントにあるとおり、'gaussian'ならσ、'circle'なら半径を示す。num_samplesはマスクの大きさ。どちらも単位はピクセルだ。
B: ん? ちょっとわかりません。まずマスクされる刺激のサイズを決めて、それに合うように作らないといけないんですか?
A: いや、マスクは刺激に貼りつけられる時に、刺激に合わせて伸縮される。なので、実際に刺激のどのくらいの範囲が切り抜かれるかはnum_samplesとradius_parameterの比で決まる。 num_samplesとradius_parameterの絶対値が大きいほど高品質のマスクが得られるが、その分処理の負担も大きくなる。
B: ふむふむ。こりゃちょっと慣れが必要そうだなぁ。これ、刺激が正方形ですけど、もしかして横長の刺激に貼りつけたらびよーんって切り抜き範囲も横に伸びちゃうんですか?
A: その「もしかして」の通り。狙ってやる分には構わないが、基本的には長方形の刺激に貼りつけるのはお勧めしない。
B: あ!、もしかしてnum_samplesを刺激と同じ縦横比にしたらきれいに丸く切り抜けるとか?
A: そのアイディアは試してみたことがあるんだが、残念ながらエラーになってしまう。もしかしたら数値の組み合わせによってはうまくいくのかも知れないが、ヘルプにはその辺りの事が特に書かれてないんでエラーの内容をきちんと吟味しないと何とも言えん。たぶん縦横の値が同じじゃないとダメだと思う。
B: ふぅん。そうなると結構使いどころが限られるな…。
A: そうだな。形状の柔軟さという点では例題8の方法に軍配が上がる。さて、作成したマスクを刺激に貼りつけるには、貼り付け先の刺激で引数maskにVisionEgg.Textures.Mask2Dのインスタンスを指定する。サンプルの16行目と22行目だな。
B: どんな刺激にも貼りつけられるんですか?
A: 基本的にはさっき言ったようにテクスチャ刺激、すなわちVisionEgg.Textures.TextureStimulusのインスタンスに貼りつけるものなんだ。だからVisionEgg.MoreStimuli.Target2Dとかに貼りつけることは出来ない。 VisionEgg.Gratings.SinGrating2Dはざっとヘルプを見たところVisionEgg.Textures.TextureStimulusの派生クラスではないようなんだが、内部でテクスチャを使っているので貼りつけることが出来る。
B: 派生クラス。久々に聴いたぞ。なんだっけ。
A: 例題5-3 を復習しておくように。さて、今回はこれでおしまい。
B: へ? もう?
A: うん。VisionEgg.Textures.Mask2Dに関してはこれ以上特にいう事はない。次は気が変わらなければVisionEgg.Dots.DotArea2D。もしかしたらDotArea2Dの話は飛ばしてVisionEgg.Core.SimplePerspectiveProjectionに行くかも。
B: めずらしく具体的な次回予告ですな。DotArea2Dってのが何か知りませんが、それを飛ばすかも知れないってのはなぜ?
A: んー。私が自分の実験で使ったことがないから。あんま解説することがないから。かな。
B: 相変わらずやる気レスですな。
A: まーね。というわけで、また。