/usr/share/psychtoolbox-3/PsychDemos/GarboriumDemo.m is in psychtoolbox-3-common 3.0.11.20131230.dfsg1-1build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | function GarboriumDemo(ngabors, internalRotation)
% GarboriumDemo([ngabors=200] [, internalRotation=0]) -- An aquarium full of cute little gabors!
%
% This demo shows how to use the Screen('DrawTextures') command to draw a
% large number of similar images quickly - in this case, Gabor patches of
% different position, size and orientation. It also shows how to achieve
% fast, mathematically correct, linear superposition of image patches by
% use of alpha blending and the Psychtoolbox imaging pipeline. While you
% could always achieve proper superposition by precomputing large images in
% Matlab, then drawing them as textures, the use of alpha blending allows
% to offload the computations to your graphics hardware - this allows
% speedups by factors of more than 100x in some cases!
%
% The demo shows "an aquarium" of many cute little gabor patches, each moving
% into a random direction. Sometimes the gabors intersect, in that case
% you'll see a linear superposition of them.
%
% Each frame of the animation contains 'ngabors' patches, ngabors defaults
% to 200. Change the number as first optional parameter 'ngabors' if you want
% to exercise your graphics hardware and cpu.
%
% Normally the gabor paches rotate as a whole, a different method of
% rotation is so called internal texture rotation (see comments in code),
% which can be selected by setting the optional 2nd argument
% 'internalRotation' to a value of 1.
%
% You can exit the demo by pressing any key on the keyboard.
%
% This demo needs recent graphics hardware with floating point framebuffer
% support: ATI Radeon X1000 and later, NVidia GeForce 6000 and later.
% History:
% 07/08/2007 Written (MK).
% 05/18/2008 Rewritten, beautified, adapted to current PTB (MK).
% PTB-3 correctly installed and functional? Abort otherwise.
AssertOpenGL;
% Set number of gabor patches to 200 if no number provided:
if nargin < 1 || isempty(ngabors)
ngabors = 200;
end
fprintf('Will draw %i gabor patches per frame.\n', ngabors);
if nargin < 2 || isempty(internalRotation)
internalRotation = 0;
end
% 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);
% 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);
% Query frame duration: We use it later on to time 'Flips' properly for an
% animation with constant framerate:
ifi = Screen('GetFlipInterval', win);
% Enable alpha-blending, set it to a blend equation useable for linear
% superposition with alpha-weighted source. This allows to linearly
% superimpose gabor patches in the mathematically correct manner, should
% they overlap. Alpha-weighted source means: The 'globalAlpha' parameter in
% the 'DrawTextures' can be used to modulate the intensity of each pixel of
% the drawn patch before it is superimposed to the framebuffer image, ie.,
% it allows to specify a global per-patch contrast value:
Screen('BlendFunction', win, GL_SRC_ALPHA, GL_ONE);
% Define prototypical gabor patch of 65 x 65 pixels default size: si is
% half the wanted size. Later on, the 'DrawTextures' command will simply
% scale this patch up and down to draw individual patches of the different
% wanted sizes. To avoid resampling artifacts, you should choose the size of
% the prototypical patch roughly the mean size of the wanted patch sizes, or
% alternatively the biggest size -- Downscaling with bilinear filtering is
% a graceful operation, it only needs to discard information in a
% artifact-free manner, but upscaling a too low resolution patch will
% create artifacts, as it can't create information about structure from
% nothing, just try to hide the most ugly artifacts:
si = 32;
% 'internalRotation' allows to rotate the gabor patches within the
% rectangular area of each drawn patch -- the texture is rotated
% "internally" to each destination patch rectangle. The default setting is
% to rotate the whole destination patch rectangle itself around its center.
% Whatever suits you best...
if internalRotation
% Need to make patches by squareroot of 2 bigger to avoid border
% artifacts during internal rotation of the sampled texture:
s = ceil(si * sqrt(2));
else
s = si;
end
if 1
% Definition of a Gabor patch as Matlab grayscale matrix 'm' with
% proper sign -- a value of zero in m denotes zero contrast at that
% pixel location: This is ugly cut & copy & paste code which could be
% written much nicer by a person with good taste ;-)
res = 2*[s s];
phase = 0;
sc = 5;
freq = 0.05;
tilt = 0;
contrast = 5;
x=res(1)/2;
y=res(2)/2;
sf = freq;
[gab_x gab_y] = meshgrid(0:(res(1)-1), 0:(res(2)-1));
a=cos(deg2rad(tilt))*sf*360;
b=sin(deg2rad(tilt))*sf*360;
multConst=1/(sqrt(2*pi)*sc);
x_factor=-1*(gab_x-x).^2;
y_factor=-1*(gab_y-y).^2;
sinWave=sin(deg2rad(a*(gab_x - x) + b*(gab_y - y)+phase));
varScale=2*sc^2;
m=contrast*(multConst*exp(x_factor/varScale+y_factor/varScale).*sinWave)';
else
% Old style: Sine gratings with pretty hard contrast:
[x,y]=meshgrid(-s:s, -s:s);
angle=0*pi/180; % 30 deg orientation.
f=0.1*2*pi; % cycles/pixel
a=cos(angle)*f;
b=sin(angle)*f;
m=sin(a*x+b*y);
end
% Build drawable texture from gabor matrix: We set the 'floatprecision' flag to 2,
% so it is internally stored with 32 bits of floating point precision and
% sign. This allows for effectively 8 million (23 bits) of contrast levels
% for both the positive- and the negative "half-lobe" of the patch -- More
% than enough precision for any conceivable display system:
gabortex=Screen('MakeTexture', win, m, [], [], 2);
% Preallocate array with destination rectangles:
% This also defines initial gabor patch orientations, scales and location
% for the very first drawn stimulus frame:
texrect = Screen('Rect', gabortex);
inrect = repmat(texrect', 1, ngabors);
dstRects = zeros(4, ngabors);
for i=1:ngabors
scale(i) = 2*(0.1 + 0.9 * randn);
dstRects(:, i) = CenterRectOnPoint(texrect * scale(i), rand * w, rand * h)';
end
% Preallocate array with rotation angles:
rotAngles = rand(1, ngabors) * 360;
% If internalRotation, we need to explicitely specify the size of a texture
% source rectangle 'srcRect', as it slightly deviates from the default
% size, which would be exactly the size of the texture itself. We also need
% to specify the optional 'sflags' parameter for 'DrawTextures' to tell the
% command it should use internal rotation:
if internalRotation
sflags = kPsychUseTextureMatrixForRotation;
srcRect = CenterRect([0 0 (2*si+1) (2*si+1)], Screen('Rect', gabortex));
else
sflags = 0;
srcRect = [];
end
% Initially sync us to VBL at start of animation loop.
vbl = Screen('Flip', win);
tstart = vbl;
count = 0;
% Animation loop: Run until any keypress:
while ~KbCheck
% Step one: Batch-Draw all gabor patches at the positions and
% orientations computed during last loop iteration: Here we fix
% 'globalAlpha' - and therefore contrast - to 0.5, ie., 50% of
% displayable range. Actually its less than 0.5, depending on the
% inherent contrast of the gabor defined above in matrix 'm'.
Screen('DrawTextures', win, gabortex, srcRect, dstRects, rotAngles, [], 0.5, [], [], sflags);
% Mark drawing ops as finished, so the GPU can do its drawing job while
% we can compute updated parameters for next animation frame. This
% command is not strictly needed, but it may give a slight additional
% speedup, because the CPU can compute new stimulus parameters in
% Matlab, while the GPU is drawing the stimuli for this frame.
% Sometimes it helps more, sometimes less, or not at all, depending on
% your system and code, but it only seldomly hurts.
% performance...
Screen('DrawingFinished', win);
% Compute updated positions and orientations for next frame. This code
% is vectorized, but could be probably optimized even more. Indeed,
% these few lines of Matlab code are the main speed-bottleneck for this
% demos animation loop on modern graphics hardware, not the actual drawing
% of the stimulus. The demo as written here is CPU bound - limited in
% speed by the speed of your main processor.
% Compute new random orientation for each patch in next frame:
rotAngles = rotAngles + 1 * randn(1, ngabors);
% Compute centers of all patches, then shift them in new direction of
% motion 'rotAngles', use the mod() operator to make sure they don't
% leave the window display area. Its important to use RectCenterd and
% CenterRectOnPointd instead of RectCenter and CenterRectOnPoint,
% because the latter ones would round all results to integral pixel
% locations, which would make for an odd and jerky movement. It is
% also important to feed all matrices and vectors in proper format, as
% these routines are internally vectorized for higher speed.
[x y] = RectCenterd(dstRects);
x = mod(x + cos(rotAngles/360*2*pi), w);
y = mod(y + sin(rotAngles/360*2*pi), h);
% Recompute dstRects destination rectangles for each patch, given the
% 'per gabor' scale and new center location (x,y):
dstRects = CenterRectOnPointd(inrect .* repmat(scale,4,1), x, y);
% Done. Flip one video refresh after the last 'Flip', ie. try to
% update the display every video refresh cycle if you can.
% This is the same as Screen('Flip', win);
% but the provided explicit 'when' deadline allows PTB's internal
% frame-skip detector to work more accurately and give a more
% meaningful report of missed deadlines at the end of the script. Not
% important for this demo, but here just in case you didn't know ;-)
vbl = Screen('Flip', win, vbl + 0.5 * ifi);
% Next loop iteration...
count = count + 1;
end
% Done. Last flip to take end timestamp and for stimulus offset:
vbl = Screen('Flip', win);
% Print the stats:
count
avgfps = count / (vbl - tstart)
% Close onscreen window, release all ressources:
Screen('CloseAll');
% 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
|