Liu et al. (2014). Seeing Jesus in toast: Neural and behavioral correlates of face pareidolia で使われた刺激を作成するプログラムです。
論文を元に、そして不明な点は論文の著者にメールでたずねながらプログラムを作成しましたが、以下に公開するコードの動作を論文の著者が保証しているわけではありません。利用される場合はその点にご注意ください。
1枚の画像を作成するのに1分近くかかると思います。ノイズと合成したい顔画像、あるいは文字画像をフォルダに保存して、inputFolderを書き換えてください。
LinuxのOctaveで動作することを確認しています。Matlabでは、mvnpdf関数を使うために Statistics and Machine Learning Toolbox が、fspecial, imfilter関数を使うために Image Processing Toolbox が必要になりますが、Octaveでは pkg load statistics image とすることで無料でこれらの関数を使用することができます。
画像をぼかすだけであれば(ノイズを追加しないのであれば)次のコードだけで大丈夫です。
gaussSigma = 7; filtersize = 2*ceil(2*gaussSigma)+1 % https://jp.mathworks.com/help/images/ref/imgaussfilt.html GF = fspecial('gaussian', filtersize, gaussSigma); % Gaussian Filter TL = imfilter(TF, GF);以下が、すべてのプログラムです。
% Original paper% Seeing Jesus in toast: Neural and behavioral correlates of face pareidolia% https://www.sciencedirect.com/science/article/pii/S0010945214000288%% Please be careful that this code was not written by the authors.% Daiichiro Kuroki who worked as a technical staff in Kyushu University% made it by reading the paper and asking the authors.% The authors and I do not grantee that the program works properly.%% Before running the program, face and letter images without noise must be% saved in two separate folders.% Be careful that the face-noise and letter-noise images could not be made% at the same time. You have to set the inputFolder respectively.%% You have to change the extension of image files according to your% stimuli. i.e., imgFileList.% Moreover, please also check 'stimType' and 'difficulty'.%% This program could run in Octave under Ubuntu.% If you use Matlab, both 'the Statistics and Machine Learning Toolbox' and% 'the Image Processing Toolbox' are needed.%% It takes about 1 minute to make a noise image! function makeImgWithNoise4 pkg load statistics image%pkg load statisticsclear all; % Be careful. Please check also Sigma, lambda, and gammainputFolder = ['images' filesep]; % folder nameimgFileList = dir([inputFolder '*.jpg']); % bmp, jpg, jpeg, or png?stimType = 1; % 1=face, 2=charactor, 3=checker boarddifficulty = 1; % 1=easy to detect, 2=hard to detect outputFolder = 'output';if ~exist(outputFolder, 'dir') mkdir(outputFolder);endcheckerBoardWidth = 40; % mod(noiseWidth, checkerBoardWidth) must be zero.checkerColor1 = [100 100 100];checkerColor2 = [200 200 200]; % These width/height should be greater than the width/heigth of images.noiseWidth = 480;noiseHeight = 480;%noiseWidth = 800;%noiseHeight = 700; % PTB-3 correctly installed and functional? Abort otherwise.AssertOpenGL;%Screen('Preference', 'SkipSyncTests', 1); % Select screen with maximum id for output window:screenid = max(Screen('Screens')); % Open a fullscreen, onscreen window with gray background. Enable 32bpc% floating point framebuffer via imaging pipeline on it, if this is possible% on your hardware while alpha-blending is enabled. Otherwise use a 16bpc% precision framebuffer together with alpha-blending. We need alpha-blending% here to implement the nice superposition of overlapping gabors. The demo will% abort if your graphics hardware is not capable of any of this.PsychImaging('PrepareConfiguration');PsychImaging('AddTask', 'General', 'FloatingPoint32BitIfPossible');%[win winRect] = PsychImaging('OpenWindow', screenid, 128); % Color value seems to be from 0 to 255 not from 0.0 to 1.0 at least in Linux.% To present a sin-wave gabor, the background color should be grey.%[win winRect] = PsychImaging('OpenWindow', screenid, 0);[win, winRect] = PsychImaging('OpenWindow', screenid, 0, [0 0 1000 800] ); % Retrieve size of window in pixels, need it later to make sure that our% moving gabors don't move out of the visible screen area:[w, h] = RectSize(winRect);centerX = w/2;centerY = h/2;% Query frame duration: We use it later on to time 'Flips' properly for an% animation with constant framerate:ifi = Screen('GetFlipInterval', win); nImages = size(imgFileList, 1) if stimType == 3 % checker board if mod(noiseWidth, checkerBoardWidth) ~= 0 error('Please check the checkerBoardWidth'); end if mod(noiseHeight, checkerBoardWidth) ~= 0 error('Please check the checkerBoardWidth'); end for j = 1:noiseHeight/checkerBoardWidth if mod(j,2) == 0 colorType = 1; else colorType = 2; end for i = 1:noiseWidth/checkerBoardWidth tmpRect = [(i-1)*checkerBoardWidth, (j-1)*checkerBoardWidth, i*checkerBoardWidth, j*checkerBoardWidth]; if colorType == 1 tmpColor = checkerColor1; colorType = 2; else tmpColor = checkerColor2; colorType = 1; end Screen('FillRect', win, tmpColor, tmpRect); end end Screen('Flip', win); imageArray = Screen('GetImage', win, [0, 0, noiseWidth, noiseHeight]); imwrite(imageArray, [outputFolder filesep 'checkr-board.jpg']); sca; returnend for k = 1:nImages imgFileName = [inputFolder imgFileList(k).name] x1 = 1:noiseWidth; x2 = 1:noiseHeight; [X1, X2] = meshgrid(x1, x2); vNoise = zeros(length(X1(:)), 1); % Initialize (vNoise is equal to zeros(length(X2(:)), 1) for i = 1:3 % The 'sd' written in the paper seems to be actually covariance. if i == 1 sd = 64; contrast = 1; elseif i == 2 sd = 256; contrast = 5; else sd = 1024; contrast = 25; end Sigma = eye(2).*sd; ngabors = round((noiseWidth/(sd*0.15))^2); %ngabors = 10; for j = 1:ngabors mu = [rand(1)*noiseWidth, rand(1)*noiseHeight]; F = contrast.*mvnpdf([X1(:), X2(:)], mu, Sigma); vNoise = vNoise + F; end % for checking% noiseMatrix = reshape(vNoise, length(x2), length(x1))./max(vNoise);% gabortex=Screen('MakeTexture', win, noiseMatrix, [], [], 2);% Screen('DrawTextures', win, gabortex); % Screen('Flip', win);% KbStrokeWait; end noiseMatrix = reshape(vNoise, length(x2), length(x1))./max(vNoise); % normalize from 0 to 1 NI = (1 - noiseMatrix).*255; % reverse imdata = imread(imgFileName); TF = zeros(noiseHeight, noiseWidth); % True Face LX = round((noiseWidth - size(imdata,2))/2)+1; LY = round((noiseHeight - size(imdata,1))/2)+1; %TF(LY:LY+size(imdata,1)-1, LX:LX+size(imdata,2)-1) = imdata(:,:,1); TF(LY:LY+size(imdata,1)-1, LX:LX+size(imdata,2)-1) = imdata; %imagetex = Screen('MakeTexture', win, TF); %Screen('DrawTexture', win, imagetex); %Screen('Flip', win); %KbStrokeWait; if stimType == 1 % face if difficulty == 1 % easy to detect Sigma = eye(2).*2400; lambda = 0.3; else % hard to detect Sigma = eye(2).*(10^9); lambda = 0.9999997; end mu = [noiseWidth/2, noiseHeight/2]; F = mvnpdf([X1(:), X2(:)], mu, Sigma); F = F./max(F); F = reshape(F, length(x2), length(x1)); gabor_brob = F; whos m gabor_brob NI TF; B = lambda.*(1-gabor_brob)+(1-lambda).*gabor_brob; FN = lambda.*NI.*(1-gabor_brob) + (1-lambda).*TF.*gabor_brob; FN = FN./B; outputData = FN; elseif stimType == 2 % charactor if difficulty == 1 % easy to detect gamma = 0.6; else gamma = 0.35; % hard to detect (still easy) end % Blurring a letter. Although this is not written in the paper, the authors told me directly. gaussSigma = 7; filtersize = 2*ceil(2*gaussSigma)+1 % https://jp.mathworks.com/help/images/ref/imgaussfilt.html GF = fspecial('gaussian', filtersize, gaussSigma); % Gaussian Filter TL = imfilter(TF, GF); %TL = TF; % without blurring LN = NI - (158-TL).*gamma; % Negative values are possible. outputData = uint8(LN); else end newTex = Screen('MakeTexture', win, outputData); Screen('DrawTexture', win, newTex); Screen('Flip', win); % image with gabor %KbStrokeWait; %imageArray = Screen('GetImage', win, [centerX-noiseWidth/2, centerY-noiseHeight/2, centerX+noiseWidth/2, centerY+noiseHeight/2]); [pathstr, imgFilename2, ext] = fileparts(imgFileName); imwrite(uint8(outputData), [outputFolder filesep imgFilename2 '-withNoise.jpg']); Screen('Close'); %error('stop');end % Close onscreen window, release all ressources:sca; % Done.return; % Little helper function: Converts degrees to radians, included for% backward compatibility with old Matlab versions:% function radians = deg2rad(degrees)% radians = degrees * pi / 180;% return