A Typical Script - BlueYellowTemporalGrating.m


As an introduction to the creation of stimuli with the Bits++/Psychtoolbox system, here is an example of how you might write a script to display a circular, spatially sinusoidal grating whose contrast varies in time, also sinusoidally, between -100% and +100%. I've split it into sections and explained each section in detail.

Note: The purpose of this is to show how you might like to design a particular stimulus and to give a flavour of the way things are done in Matlab and Psychtoolbox. It is by no means the definitive or only way to do it.




Reset all Matlab variables.

% BlueYellowTemporalGrating.m
clear all; close all


Get a screen index.

The Psychtoolbox function Screen('Screens') returns a Matlab matrix containing the indices of each of the screens connected to your computer. So if you have a dual output graphics card it will return [1,2]. If the Bits++ is connected to the second output then max(Screen('Screens')) returns its index. If your Bits++ is not connected to the second output you'll have to change this line.

whichScreen=max(Screen('Screens'));


Open it.

The Screen('OpenWindow'...) Psychtoolbox function gets a window ready to draw into. It can be a rectangle within the screen, but in our case we miss out the 'rect' parameter and draw to the whole screen. The 128 is the value with which we will clear the window, and the 2 means that we will use double-buffering. That means that the stimulus will be prepared on a hidden piece of memory and then flipped onto the visible screen after it has been drawn.

[window,screenRect] = Screen('OpenWindow',whichScreen,128,[],32,2);


Make sure the graphics card doesn't interfere.

The next line is one of the things that has to be done to ensure that the T-Lock system works properly. Graphics cards contain gamma correction tables which alter the values of pixels before they are sent to the screen, or, in our case, the Bits++. But we don't want those pixels to be altered, because it will break T-Lock. So we have to load the gamma correction table in the graphics card with a linear ramp - a conversion table that does nothing. We have two lines here, one of which is commented out, because of a bug in some graphics cards. Some graphics cards expect the gamma correction table values to be between 0 and 1, which seems reasonably logical. But some cards expect the values to be between 0 and 255/256. So if the T-Lock system doesn't work with the script as shown here, you just have to try the other line to see if that fixes it.

%Screen('LoadNormalizedGammaTable',window,linspace(0,(255/256),256)'*ones(1,3));
Screen('LoadNormalizedGammaTable',window,linspace(0,1,256)'*ones(1,3));


Initialise the Bits++'s CLUT.

Next we are going to load the Bits++'s CLUT with a linear greyscale ramp. The BitsPlusSetClut.m file in the common folder can do this for us. It expects an array of 256 RGB values where each Red, Green and Blue value is 0 for minimum luminance and 65535 for maximum luminance.

So we need a 256x3 matrix of numbers to represent 256 RGB values, in which Red=Green=Blue and their values progress in equal steps from 0 to 65535. If we were using the entire palette of 256 entries for our greyscale ramp the step size would be 256. But, to complicate matters slightly, we are going to reserve the first index in the palette for a background colour, which will be mid-grey. So we have 255 palette entries for the greyscale ramp and the step size will be 65536/255 which is, near enough, 257.

This is why the first line in this little section of code perhaps looks a little odd. It creates a 256x3 matrix of numbers stepping up by 257 each row and in which the second row is [0 0 0]. We then set the first row equal to our mid-grey background colour and finally call BitsPlusSetClut to load this palette into the Bits++.

Clut = [-1:254; -1:254; 254:-1:-1]' * 257;
Clut( 1, : ) = [ 128 128 128 ] * 256;
BitsPlusSetClut(window,Clut);


Create the grating.

Next we create a Matlab matrix to represent the grating.

The grating will be circular and its diameter is first set as the width or the height of the screen, whichever is the smaller. i.e. the largest circle that will fit on the screen.

To create the matrix for the grating we first create a single line of values that vary sinusoidally. Then we duplicate that line to create a square matrix.

To make the grating circular we use a 'for' loop to scan through the square matrix, one line at a time. For each line we use Pythagorus's theorum to work out the two points on the line that are on the circumference of a circle centred in the middle of the square matrix. We then fill all the elements in the matrix to the left of the left-hand point and to the right of the right-hand point with zeros, as this is the background colour. In this was we 'cut' a circle out of the square grating.

% Create a matrix to represent a sinusoidal grating in a circular window.
[screenWidth, screenHeight]=Screen('WindowSize', window);
dia = min([screenWidth screenHeight]) - 1;
radius = dia / 2;
numCycles = 5;
% Create the matrix for the grating.
gratingImage = ( cos( ( 0:dia ) * numCycles / dia * 2 * pi ) + 1 ) * 254 / 2 + 1;
gratingImage = repmat( gratingImage, [ ( dia + 1 ) 1 ] );
% Cut out the circular window.
for y = 0 : dia
    ycentred = y - radius;
    x = sqrt( radius * radius - ycentred * ycentred );
    gratingImage( y + 1, 1 : floor( radius - x ) ) = 0;
    gratingImage( y + 1, floor( radius + x + 1 ) : ( dia + 1 ) ) = 0;
end;


Draw the grating.

Now we create a 'texture' using the matrix. A 'texture' is an OpenGL concept. It is basically just a bitmap that can be drawn onto the screen. Psychtoolbox exposes this OpenGL functionality to us in the functions Screen('MakeTexture'...) and Screen('DrawTexture'...). They are used in all of the demos for Bits++ for getting stimuli displayed and for changing the Bits++'s CLUT with the T-Lock system.

When we did Screen('OpenWindow'...) we created a window with double buffering. That is, there are two sets of video memory but only one is actually displayed and we 'flip' between the two. Later in this script, when the Bits++'s CLUT is changed using the T-Lock system, we have to draw the T-Lock code and its associated palette onto the invisible video memory and then flip it onto the screen. To animate the stimulus this is done over and over again. So we need both sets of video memory to contain our grating texture. That is why, in this section of the code, we draw the texture twice, doing a 'flip' in between.

Also, before drawing the texture each time, we fill the screen with the background colour.

Note: When we use the Screen('Flip'...) command here we set its 'dontclear' parameter to 1 because we are writing an image to both screens but, by default, the command clears the screen. There is more discussion of this further down the script. To see the documentation of this command type screen flip? at the Matlab prompt.

% Draw the matrix into a texture and draw it to the middle of the screen AND the back
% screen.
textureIndex = Screen( 'MakeTexture', window, gratingImage );
Screen('FillRect', window, 0 );
xpos = ( screenWidth - dia ) / 2;
Screen( 'DrawTexture', window, textureIndex, [], [ xpos 0 xpos + dia dia ] );
Screen( 'Flip', window, [], 1 );
Screen('FillRect', window, 0 );
Screen( 'DrawTexture', window, textureIndex, [], [ xpos 0 xpos + dia dia ] );
Screen( 'Flip', window, [], 1 );


Animate the grating.

Finally, with everything prepared, we can enter the 'main loop' of the script. This is the loop that actually does the animation.

The contrast of the greyscale ramp in the Bits++'s CLUT is going to be varied sinusoidally with respect to time. If you imagine a graph of the CLUT values before we start this loop - a graph of luminance versus palette index - you will see a straight diagonal line sloping upwards from left to right. Low luminance is on the left and high luminance on the right. If you reversed this palette you would get a straight diagonal line sloping down from left to right.

Sinusoidally varying the luminance between +100% and -100% means oscillating between these two extremes, so the appearance of the graph over time would ressemble a seesaw (aka a teeter-totter).

This is achieved in the 5th line of the code shown below. The values in the 256x3 matrix representing the CLUT are assigned in the same way as they were further up the script, but this time all the values are multiplied by a time dependent sine function. The background colour is then replaced in the first palette index and the CLUT is loaded.

Notice, though, that this time we do not use BitsPlusSetClut. We are using BitsPlusEncodeClutRow, which is a function that is called by BitsPlusSetClut. It simply returns a Matlab matrix containing the T-Lock code and the new CLUT in a form that we can just draw directly onto the video memory. The reason we call BitsPlusEncodeClutRow and do the actual drawing of the T-Lock and CLUT ourselves is because we want to use the command Screen( 'Flip', window, [], 1 ) to ensure that the gratings we have drawn are not erased by the flip command, as they would be by the Screen( 'Flip', window ) used in BitsPlusSetClut.

We do just that, and flip the video memory into view. All this keeps happening forever. The user has to Ctrl-C to stop the script.

while 1
    for a = 0 : 359
        % [Re]create the linear ramp CLUT.
        Clut = [-1:254; -1:254; 254:-1:-1]' * ( sin( a * pi / 180 ) * 128 ) + 32768;
        Clut( 1, : ) = [ 128 128 128 ] * 256;
        ClutEncoded = BitsPlusEncodeClutRow( Clut );

        % Draw the encoded CLUT to the screen.
        ClutTextureIndex = Screen( 'MakeTexture', window, ClutEncoded );
        Screen( 'DrawTexture', window, ClutTextureIndex, [], [0, 0, 524, 1] );
        Screen( 'Flip', window, [], 1 );
    end;
end;


This tidying up code doesn't actually get executed in this demo because the user has to stop execution with Ctrl-C.

% Tidy up.
Screen('CloseAll');


Valid XHTML :: Valid CSS: :: Powered by WikkaWiki