顔画像の印象を評定する心理学実験では、髪の毛や服の影響を避けるために顔画像を楕円に切り取ることがよくあります。これをPsychtoolboxで行う方法について解説します。
基本的なアイデアは下図の通りで、楕円に切り抜いたマスクを顔画像の上にかぶせます。
ただし、実際には楕円に切り取るのではなく、楕円の形状で透明度を設定します。
サンプルプログラムをGitHubで公開しています。 どうぞご自由にご利用ください。
以下では重要な点を補足しておきます。
色を混合する
透明度(アルファ)を設定しただけでは、透明度は反映されません。以下のようにして、色の混合処理を有効にします。
Screen(windowPtr,'BlendFunction',GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
混合処理では、描画元と描画先を意識することが大切です。上の行が意味するのは、描画元の色をα倍、描画先の色を(1-α)倍して、足し合わせるということです。例えば、α=0.7のとき、描画元の色が70%、描画先の色が30%になって、混合されます。
meshgrid
MATLABにmeshgridという関数があります。ちょっと直感的には分かりにくいのですが、今回のケースのように、刺激の (x,y) 座標に基づいて何かを行いたいときに便利です。
[x, y] = meshgrid(-imageWidth/2:imageWidth/2, -imageHeight/2:imageHeight/2);
xが画像の水平方向(width)、yが画像の垂直方向(height)に対応している点に注意してください。2で割っている理由は、原点(0,0) を刺激の中心とするためです。こうすることで、楕円の計算をやりやすくなります。
マスクの色情報を設定する
Psychtoolboxでは、刺激の色情報を下図のように4層構造で扱っています。RGBは赤緑青に対応、A(アルファ)は透明度に対応。
まず、この4層構造に対して、すべて1で初期化します。ここで間違いやすいのは、onesの引数の一番目が垂直方向のheightであるという点です。onesの第一引数は行数になります。行は垂直方向に並ぶということを思い出すと理解しやすいかもしれません。またwidthとheightに1を足しているのは、meshgridの数と一致させるためです。例えば水平方向では、原点を中心に、左側にwidth/2個、右側にwidth/2個のデータがあるため、合計でwidth+1個になります。
mask = ones(imageHeight+1, imageWidth+1, 4); % 4の意味は、RGBAの4種類
初期化後に、RGBの色を設定します。ここでは背景色のRGBと対応するようにしています。
mask(:, :, 1) = bgColor(1); % R (赤)
mask(:, :, 2) = bgColor(2); % G (緑)
mask(:, :, 3) = bgColor(3); % B (青)
楕円の外側の透明度を1、内側の透明度を0にする
まずは、こちらで楕円の方程式について確認をしてください。解説サイトの図1のaとbの値を次のように設定します。
ellipse_a = 100;
ellipse_b = 120;
上のほうで説明したように、maskの4番目の層で透明度を設定します。
mask(:, :, 4) = (x.^2)./(ellipse_a.^2) + (y.^2)./(ellipse_b.^2) > 1;
ちょっと分かりにくいかと思いますが、楕円の方程式をそのまま書いています。(x, y)はmeshgridが返してきた値で、刺激の (x, y)座標を意味します。コードとしては1行ですが、この1行で、すべての (x, y) 座標について、楕円の方程式に当てはめた計算を行うことができます。(この点がmeshgridを使うメリットです)その計算結果が、1よりも大きい場合には、楕円の外側の座標であることを意味します。
重要な点なので、言い方を替えてもう一度説明します。刺激(顔画像)の (x, y) 座標は分かっています。このすべての座標を楕円の方程式に代入して計算します。その計算結果が1より大きいかどうかを比較します。1よりも大きい場合は透明度が1(完全に不透明)になります。1以下の場合は透明度が0(完全に透明)になります。比較演算子の結果が正しいときには1が、正しくないときには0になることを利用して、透明度を設定しています。
顔画像とマスクの描画
最後に、顔画像とマスクを描画します。これは順番が重要で、顔画像を先に描画します。顔画像の上にマスクをかぶせることを考えるとイメージしやすいです。
% 画像の呈示
tRect = Screen('Rect', imagetex); % テクスチャのレクトを調べる。
dstRect = CenterRectOnPoint(tRect, centerX, centerY);
Screen('DrawTexture', windowPtr, imagetex, [], dstRect);
% マスクの呈示
tRect = Screen('Rect', masktex);
dstRect = CenterRectOnPoint(tRect, centerX, centerY);
Screen('DrawTexture', windowPtr, masktex, [], dstRect);
Screen('Flip', windowPtr);
解説は以上となります。