Add a 2d Gaussian function

Hello! I’m using Matlab_R2020a on a macOS. I’d like to know how to put a 2d gaussian filter on the baseRect to obtain a circle with soft edges. Thank you so much!!

[screenXpixels, screenYpixels] = Screen(‘WindowSize’, window); %1440x900

%% stimulus
baseRect = [0 0 200 200];

%% Variable initialization
g=0;

%% Draw Grating and Mask

for k = 1:dim

g = g + 1;

A = cosd(alpha_angle) * rand_FreqContr(g,1);
B = sind(alpha_angle) * rand_FreqContr(g,1);

gratingMatrix = sin(Ax+By); %Converts meshgrid into a sinusoidal grating

grayscaleImageMatrix = grey + rand_FreqContr(g,2) * gratingMatrix;
grayscaleImageMatrix = grayscaleImageMatrix + 0.1;

centeredRect = CenterRectOnPointd(baseRect, rand_C(k,1), rand_C(k,2));

Screen(‘PutImage’, window, grayscaleImageMatrix, centeredRect);

end

There is a smooth-edged circle stimulus built-in to PTB, see the ProceduralSmoothedDiscsDemo.m. You can also use this disc as a mask for any other stimuli (i.e. the mask will blend the edges of your texture to a background colour, uses OpenGL alpha blending): ProceduralSmoothedDiscMaskDemo.m — There is also a smooth edged grating: ProceduralSmoothedApertureSineGrating.m.

Technically, these use hermite (default) or cosine interpolation, (you can read about GLSL smoothstep function for details), and you can specify the smoothing amount in pixels.

1 Like

I’ve already looked at these files but I don’t know how to create the disc inside each square that appears on the screen randomly. Could you please write me the code? Thank you so much!!

Is there another way to fix it? For example multiply the greyscaleImageMatrix with a 2d Gaussian matrix of the same size. In this case how the 2d Gaussian matrix should be fixed?

There are lots of possible ways to make a mask. I suggested the Procedural methods as they are the simplest and fastest. A procedural texture is a “virtual” texture, it is really instructions for the graphics card for what to do with each pixel. Here is a demo where the procedural mask is toggled on and off. Note that all this demo does is create the procedural disc whose alpha transparency defines what shows through, then draw it on top of the square texture. Remember that we set the OpenGL blending to Screen('BlendFunction', ptb.win, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) for this to work:

function masktest()

	bgColour = 0.5;
	screen = 0;
	screenSize = [0 0 1000 1000];
	size = 5; %degrees
	
	ptb = mySetup(screen,bgColour,screenSize);

	% ---- make random texture stimulus
	pxSize = round(size*ptb.ppd);
	texture = repmat(rand(pxSize,pxSize,1),1,1,3);
	texture = Screen('MakeTexture',ptb.win,texture);
	texRect = Screen('Rect',texture);
	texRect = CenterRect(texRect,ptb.winRect);
	
	% ---- make a procedural mask
	pxSmooth = 31; %pixels to smooth by
	maskRadius = round(pxSize/2);
	[maskTexture, maskRect] = CreateProceduralSmoothedDisc(ptb.win,...
		pxSize+5, pxSize+5, [], maskRadius, pxSmooth, true, 2);
	maskRect = CenterRect(maskRect,ptb.winRect);

	% ---- prepare variables
	CloseWin = false;
	quit = KbName('escape');
	Priority(MaxPriority(ptb.win)); %bump our priority to maximum allowed
	vbl = Screen('Flip', ptb.win);
	loop = 1;
	showMask = true;
	
	while ~CloseWin
		% our texture
		Screen('DrawTexture', ptb.win, texture, [], texRect);
		
		% toggle mask on / off for the demo
		if mod(loop,60) == 0; showMask = ~showMask; end
		if showMask
			Screen('DrawTexture', ptb.win, maskTexture, [], maskRect,...
				[],[],1,[bgColour bgColour bgColour]);
		end
		
		vbl = Screen('Flip', ptb.win, vbl + ptb.ifi/2);
		loop = loop + 1;

		% ---- handle keyboard
		[~,~,keyCode] = KbCheck(-1);
		name = find(keyCode==1);
		if ~isempty(name)  
			switch name
				case quit
					CloseWin = true;
				otherwise
					%disp('Cant match key!')
			end
		end
	end
end

function ptb = mySetup(screen, bgColour, ws)
	ptb.cleanup = onCleanup(@myCleanup);
	PsychDefaultSetup(2);
	KbName('UnifyKeyNames');
	Screen('Preference', 'SkipSyncTests', 2);
	Screen('Preference', 'VisualDebugLevel', 3);
	if isempty(screen); screen = max(Screen('Screens')); end
	ptb.ScreenID = screen;
	PsychImaging('PrepareConfiguration');
	PsychImaging('AddTask', 'General', 'FloatingPoint32BitIfPossible');
	[ptb.win, ptb.winRect] = PsychImaging('OpenWindow', ptb.ScreenID, bgColour, ws, [], [], [], 1); 

	[ptb.w, ptb.h] = RectSize(ptb.winRect);
	screenWidth = 405; % mm change for your monitor
	viewDistance = 573; % mm change for your setup
	ptb.ppd = ptb.w/2/atand(screenWidth/2/viewDistance);
	ptb.ifi = Screen('GetFlipInterval', ptb.win);
	ptb.fps = 1 / ptb.ifi;
	Screen('BlendFunction', ptb.win, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
end

function myCleanup()
	disp('Clearing up...')
	Priority(0)
	sca
end

You can of course create a standard texture and physically draw a gaussian or smoothed disc into the texture. Again for this to work, you have to use the alpha channel and the correct OpenGL blending mode when drawing your mask texture on top of your stimulus textures. You can look at GazeContingentDemo.m for an example generating an alpha mask using a gaussian.

Before I used Procedural shaders for my masks, I used to draw an oval into an offscreen texture, then smooth it with a gaussian kernel. See this for an example of how I do that:

And how it gets drawn:

1 Like