/usr/share/psychtoolbox-3/PsychGLImageProcessing/DisplayUndistortionLabRiggerMouseStim.m is in psychtoolbox-3-common 3.0.12.20160126.dfsg1-1ubuntu1.
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 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | function scal = DisplayUndistortionLabRiggerMouseStim(caliboutfilename, screenid)
% scal = DisplayUndistortionLabRiggerMouseStim(caliboutfilename[, screenid])
%
% Hi-Def TV for mice - Courtesy of Labrigger.
%
% CAUTION: This is not a finished, polished routine, but a template for
% actual producers of TV for mice to get started. Make sure you understand
% this code before using it!
%
% Geometric display calibration procedure for undistortion of distorted
% displays. Needs graphics hardware with basic support for the PTB imaging
% pipeline.
%
% This one is an example of how to integrate sample code from LabRigger
% for visual stimulation of a mouse with a single flat screen. It does the
% neccessary undistortion. For explanation and detail go to the original
% code and method presented at:
%
% http://labrigger.com/blog/2012/03/06/mouse-visual-stim/comment-page-1/
%
% Please note this is quickly hacked together throwaway sample code. It
% almost literally copy and pasted the code from LabRigger into the
% subroutine MouseStim() and then converts its output calibration matrices
% into a calibration file useable by Psychtoolbox DisplayUndistortionCSV
% method, so these undistortions can be applied to live visual stimuli
% fast and in realtime. All display and stimulation setup parameters are
% hard-coded in MouseStim(). Somebody should really clean up this routine
% and test it on a real stimulation setup. If you've successfully done so,
% please contribute the enhanced version of this M-File back to the PTB
% for integration into future releases.
%
% General boiler-plate description of working and use:
%
% Psychtoolbox can "undistort" your visual stimuli for you: At stimulus
% onset time, PTB applies a geometric warping transformation to your
% stimulus which is meant to counteract or cancel out the geometric
% distortion caused by your display device. If both, PTB's warp transform
% and the implicit distortion transform of the display match, your stimulus
% will show up undistorted on the display device.
%
% For this to work, PTB needs a non-ancient graphics card with support for
% the PTB imaging pipeline. All ATI/AMD cards starting with Radeon 9500 and
% all NVidia cards of type GeForce-FX5200 and later, as well as the Intel-GMA 950
% and later should be able to do it, although more recent cards will have a higher
% performance.
%
% DisplayUndistortionLabRiggerMouseStim defines a continous mapping
% (x', y') = f(x, y) from uncorrected input pixel locations (x,y) in
% your stimulus image to output locations (x', y') on your display.
% This mapping is defined by a linear mesh of quadrilaterals, as computed
% in the subfunction MouseStim().
%
% How to use:
% -----------
%
% Execute the function with the following parameters:
%
% `caliboutfilename` Name of the file to which calibration results should
% be stored. If no name is provided, the file will be stored inside the
% 'GeometryCalibration' subfolder of your Psychtoolbox configuration
% directory (path is PsychToolboxConfigDir('GeometryCalibration'). The
% filename will contain the screenid of the display that was calibrated.
%
% `screenid` screen id of the target display for calibration. The parameter
% is optional, defaults to zero, and is only used to generate the default
% filename for the output file.
%
% This script will print out a little snippet of code that you can paste
% and include into your experiment script - That will automatically load
% the calibration result file and apply the proper undistortion operation.
%
% You can see an example use of it in ImagingVideoCaptureDemo.m
%
% A quick way to test your calibration created with this script is to
% call ImageUndistortionDemo (caliboutfilename, 'checkerboard'). However,
% for production use you'd rather use your calibration via PsychImaging,
% as shown in ImagingVideoCaptureDemo or the code snippet printed by this
% function.
% History:
%
% 24-Jul-2015 mk Written, based on DisplayUndistortionCSV.
% Setup defaults:
PsychDefaultSetup(0);
if ~exist('screenid', 'var') || isempty(screenid)
screenid = 0;
end
% At this point, screenid contains the final screenid for the screen to
% calibrate. Assign it to scal struct. This will create 'scal' if it
% doesn't exist yet, or override its screenid in some cases:
scal.screenNumber = screenid;
% Define type of mapping for this calibration method: This is used in the
% CreateDisplayWarp() routine when parsing the calibration file to detect
% the type of undistortion method to use, ie. how to interpret the data in
% the calibration file to setup the calibration.
warptype = 'CSVDisplayList'; %#ok<NASGU>
% We won't use normalized coordinates, but absolute pixel coordinates for
% this method: Encoded source and target coordinates are absolute locations
% in units of pixels
scal.useUnitDisplayCoords = 0;
% Compute mapping matrices for the "Mouse visual stimulus" method presented/developed
% by LabRigger under: http://labrigger.com/blog/2012/03/06/mouse-visual-stim/comment-page-1/
%
% Use a subsampling of the dense calibration matrices of 10. Only every 10th
% pixel in x and y direction is used to define the warpmesh, so we cut down
% the amount of geometry to process to 1 / (10*10) = 1/100 th. Bilinear
% interpolation is used for intermediate pixel locations. Tweak the subdivision
% value to your needs aka your calibration accuracy <-> performance tradeoff.
[xi, yi, xS, yS] = MouseStim(10);
% Build 2D source and destination matrices: rows x cols per plane,
% 2 planes for x and y components:
rows = size(xi, 1);
cols = size(xi, 2);
% Vertex coordinates of the rendered output mesh quad vertices:
scal.vcoords = zeros(rows, cols, 2);
% Corresponding texture coordinates for sourcing from user provided input
% image framebuffer:
scal.tcoords = zeros(rows, cols, 2);
% Assign from output of the Labrigger MouseStim implementation
scal.vcoords(:,:,1) = xi;
scal.vcoords(:,:,2) = yi;
scal.tcoords(:,:,1) = xS;
scal.tcoords(:,:,2) = yS;
% 'scal' contains the final results of calibration. Write it out to
% calibfile for later use by the runtime routines:
% Check if name for calibration result file is provided:
if ~exist('caliboutfilename', 'var')
caliboutfilename = [];
end
if isempty(caliboutfilename)
% Nope: Assign default name - Store in dedicated subfolder of users PTB
% config dir, with a well defined name that also encodes the screenid
% for which to calibrate:
caliboutfilename = [ PsychtoolboxConfigDir('GeometryCalibration') 'CSVCalibdata' sprintf('_%i', screenid) '.mat'];
fprintf('\nNo name for calibration file provided. Using default name and location...\n');
end
% Print name of calibfile and check for existence of file:
fprintf('Name of calibration result file: %s\n\n', caliboutfilename);
if exist(caliboutfilename, 'file')
answer = input('This file already exists. Overwrite it [y/n]? ','s');
if ~strcmpi(answer, 'y')
fprintf('\n\nCalibration aborted. Please choose a different name for calibration result file.\n\n');
return;
end
end
% Save all relevant calibration variables to file 'caliboutfilename'. This
% method should work on both, Matlab 6.x, 7.x, ... and GNU/Octave - create
% files that are readable by all runtime environments:
save(caliboutfilename, 'warptype', 'scal', '-mat', '-V6');
fprintf('Creation of Calibration file finished :-)\n\n');
fprintf('You can apply the calibration in your experiment script by replacing your \n')
fprintf('win = Screen(''OpenWindow'', ...); command by the following sequence of \n');
fprintf('commands:\n\n');
fprintf('PsychImaging(''PrepareConfiguration'');\n');
fprintf('PsychImaging(''AddTask'', ''LeftView'', ''GeometryCorrection'', ''%s'');\n', caliboutfilename);
fprintf('win = PsychImaging(''OpenWindow'', ...);\n\n');
fprintf('This would apply the calibration to the left-eye display of a stereo setup.\n');
fprintf('Additional options would be ''RightView'' for the right-eye display of a stereo setup,\n');
fprintf('or ''AllViews'' for both views of a stereo setup or the single display of a mono\n');
fprintf('setup.\n\n');
fprintf('The ''GeometryCorrection'' call has a ''debug'' flag as an additional optional parameter.\n');
fprintf('Set it to a non-zero value for diagnostic output at runtime.\n');
fprintf('E.g., PsychImaging(''AddTask'', ''LeftView'', ''GeometryCorrection'', ''%s'', 1);\n', caliboutfilename);
fprintf('would provide some debug output when actually using the calibration at runtime.\n\n\n');
% Done.
return;
end
% MouseStim is mostly a verbatim copy of the sample code on LabRigger,
% with some modifications to make it work for PTB.
function [xi, yi, xS, yS] = MouseStim(subdivide)
close all;
% Monitor size and position variables
w = 56.69; % width of screen, in cm
h = 34.29; % height of screen, in cm
cx = w/2; % eye x location, in cm
cy = 11.42; % eye y location, in cm
% Distance to bottom of screen, along the horizontal eye line
zdistBottom = 24.49; % in cm
zdistTop = 14.18; % in cm
% Alternatively, you can specify the angle of the screen
%screenAngle = 72.5; % in degrees, measured from table surface in front of screen to plane of screen
%zdistTop = zdistBottom - (h*sin(deg2rad(90-screenAngle)));
%pxXmax = 200; % number of pixels in an image that fills the whole screen, x
%pxYmax = 150; % number of pixels in an image that fills the whole screen, y
% MK: Use bigger input/output framebuffer of a 1680 x 1050 flat panel.
% We add 1 pixel in size, so we don't get cutoff at the bottom and right
% border if 'subdivide' is > 1.
pxXmax = 1681; % number of pixels in an image that fills the whole screen, x
pxYmax = 1051; % number of pixels in an image that fills the whole screen, y
% Internal conversions
top = h-cy;
bottom = -cy;
right = cx;
left = cx - w;
% Convert Cartesian to spherical coord
% In image space, x and y are width and height of monitor and z is the
% distance from the eye. I want Theta to correspond to azimuth and Phi to
% correspond to elevation, but these are measured from the x-axis and x-y
% plane, respectively. So I need to exchange the axes this way, prior to
% converting to spherical coordinates:
% orig (image) -> for conversion to spherical coords
% Z -> X
% X -> Y
% Y -> Z
[xi,yi] = meshgrid(1:pxXmax,1:pxYmax);
% MK: Need to shift - 1 because OpenGL coordinates are 0-based, not 1-based
% as Matlabs, otherwise we'd get artifacts at the top-left corner of the
% display due to omitted data:
xi = xi - 1;
yi = yi - 1;
cart_pointsX = left + (w/pxXmax).*xi;
cart_pointsY = top - (h/pxYmax).*yi;
cart_pointsZ = zdistTop + ((zdistBottom-zdistTop)/pxYmax).*yi;
[sphr_pointsTh sphr_pointsPh sphr_pointsR] ...
= cart2sph(cart_pointsZ,cart_pointsX,cart_pointsY);
% view results
figure
subplot(3,2,1)
imagesc(cart_pointsX);
colorbar
title('image/cart coords, x')
subplot(3,2,3)
imagesc(cart_pointsY);
colorbar
title('image/cart coords, y')
subplot(3,2,5)
imagesc(cart_pointsZ);
colorbar
title('image/cart coords, z')
subplot(3,2,2)
imagesc(rad2deg(sphr_pointsTh));
colorbar
title('mouse/sph coords, theta')
subplot(3,2,4)
imagesc(rad2deg(sphr_pointsPh));
colorbar
title('mouse/sph coords, phi')
subplot(3,2,6)
imagesc(sphr_pointsR);
colorbar
title('mouse/sph coords, radius')
% Rescale the Cartesian maps into dimensions of radians
xmaxRad = max(sphr_pointsTh(:));
ymaxRad = max(sphr_pointsPh(:));
fx = xmaxRad/max(cart_pointsX(:));
fy = ymaxRad/max(cart_pointsY(:));
% Compute matrices with sampling positions, needed for Psychtoolbox:
xS = interp2(cart_pointsX.*fx,cart_pointsY.*fy,xi,sphr_pointsTh,sphr_pointsPh);
yS = interp2(cart_pointsX.*fx,cart_pointsY.*fy,yi,sphr_pointsTh,sphr_pointsPh);
h = figure;
subplot(1,2,1);
imagesc(xS);
colorbar
title('Lookup position input x:')
subplot(1,2,2);
imagesc(yS);
colorbar
title('Lookup position input y:')
% Subsample to only use every subdivide'th sample:
xi = xi(1:subdivide:end, 1:subdivide:end);
yi = yi(1:subdivide:end, 1:subdivide:end);
xS = xS(1:subdivide:end, 1:subdivide:end);
yS = yS(1:subdivide:end, 1:subdivide:end);
% We are done with creating output useable for Psychtoolbox.
%% And here’s the debug code to try the distortion out in Matlab/Octave:
if 1
%% try a distortion
% make source image
checkSize = 105; % pixels per side of each check
w = 1680; % width, in pixels
h = 1050; % height, in pixels
I = double(checkerboard(checkSize,round(h/checkSize/2),round(w/checkSize/2))>0.5);
% alternate source image
%I = zeros(150*4,200*4);
%I(105*4:125*4,:)=0.2;
%I(20*4:40*4,:)=0.4;
if isequal(size(I), size(xi))
% Test apply the distortion via interpolation and plotting in Matlab:
ZI = interp2(cart_pointsX.*fx,cart_pointsY.*fy,I,sphr_pointsTh,sphr_pointsPh);
h=figure;
subplot(1,2,1);
imshow(I);
subplot(1,2,2);
imshow(ZI);
end
end
% Done.
end
% Inline replacement for missing rad2deg() in default Octave 3.8 installation:
function deg = rad2deg(rad)
deg = rad * 180 / pi;
end
|