Attachment 'calibrateEyeTracker.m'
Download 1 % Calibration routine for SMI IViewX eyetracker and Psychtoolbox,
2 % using the serial port for communication.
3 % This function is fairly robust - if calibration fails for some
4 % reason it will quietly print out some warnings and return. If
5 % calibration failures are catastrophic for your experiment, you will
6 % need to check the output flag for success==1 in your own script.
7 % Syntax:
8 % success = calibrateEyeTracker(window,[ET_serial],[ET_params])
9 % Inputs (all optional except the Psychtoolbox window):
10 % window - Psychtoolbox screen handle
11 % ET_serial - Opened serial port object for scanner. Initialise with e.g.
12 % ET_serial = serial('COM1','BaudRate',9600,'Databits',8);
13 % fopen(ET_serial);
14 % If left undefined, we open a serial port with the above settings.
15 % ET_params - struct with eye tracking parameters. All are optional.
16 % npoints - (13) number of calibration points
17 % calibarea - ([screenx screeny]) calibration area on screen
18 % bgcolour - ([128 128 128]) background colour (RGB)
19 % targcolour - ([255 255 255]) target colour (RGB)
20 % targsize - (20) target height/width in pixels
21 % acceptkey - ([spacebar]) key for forcing point acceptance
22 % quitkey - ([escapekey]) key for aborting calibration
23 % Use KbName('UnifyKeyNames') to get names for other keys
24 % waitforvalid - (1) wait for valid data during calibration
25 % randompointorder - (0) randomise point order during calibration
26 % autoaccept - (1) Auto-accept points after some fixation dur.
27 % checklevel - (2) Fussiness when accepting points (0-3). SMI recommends
28 % 2 for every-day use. Drop if subject is problematic.
29 % 13/4/2010 J Carlin, heavily indebted to Maarten van Caasteren's
30 % VB script for E-Prime
31
32 % TODO
33 % Drift correction
34 % ET_SAV - no problem!
35
36 function [ready,points] = calibrateEyeTracker(window,ET_serial,ET_params)
37
38 % If no serial object entered, try to set one up
39 if ~exist('ET_serial','var') || isempty(ET_serial)
40 ET_serial = serial('COM1','BaudRate',9600,'Databits',8);
41 fopen(ET_serial);
42 end
43
44 % By default, calls time out in 10 SECONDS.
45 % This is clearly unacceptably slow for our
46 % purposes. Now 50 ms.
47 set(ET_serial,'timeout',.1);
48 % The downside is that Matlab spits out a lot of
49 % warnings. Let's disable these...
50 wstate=warning('off','MATLAB:serial:fgetl:unsuccessfulRead');
51
52
53
54
55 % If you don't know what you want, we will fill this in with
56 % defaults.
57 if ~exist('ET_params','var')
58 ET_params = struct;
59 end
60
61 % Screen settings
62 sc = Screen('Resolution',window);
63 schw = [sc.width sc.height];
64
65 KbName('UnifyKeyNames');
66
67 % These are the default settings
68 default_params = struct(...
69 'npoints',13,...
70 'calibarea',schw,... % Full screen size
71 'bgcolour',[128 128 128],...
72 'targcolour',[255 255 255],...
73 'targsize',20, ...
74 'acceptkey',KbName('space'), ...
75 'quitkey',KbName('escape'), ...
76 'waitforvalid',1,...
77 'randompointorder',0,...
78 'autoaccept',1,...
79 'checklevel',2 ...
80 );
81
82 % Now put in defaults for whatever was left undefined
83 fns = fieldnames(default_params);
84 for fn = fns'
85 if ~isfield(ET_params,fn{1})
86 ET_params.(fn{1}) = default_params.(fn{1});
87 end
88 end
89
90 % Quick sanity check
91 if ~sum(ET_params.npoints == [2,5,9,13])
92 error('SMI eye trackers only support 2,5,9 or 13 point calibration')
93 end
94
95 % Start and stop calibration once. This somehow
96 % solves a lot of problems
97 fprintf(ET_serial,sprintf('ET_CAL %d',ET_params.npoints));
98 fprintf(ET_serial,'ET_BRK');
99 % Wait for various crap to go through
100 w = 0;
101 while w == 0
102 if isempty(fgetl(ET_serial))
103 w = 1;
104 end
105 end
106
107 % Draw background
108 Screen(window,'FillRect',ET_params.bgcolour);
109
110 % Display settings for targets
111 % Make a cross - studiously avoiding alpha blending here to
112 % maximise compatibility (but you will need to im processing toolbox)
113 % Settings
114 cross_orgsize = 100;
115 cross_linewidth = .05;
116 % Build cross
117 cs = round((cross_orgsize / 2) - (cross_orgsize * cross_linewidth));
118 ce = round((cross_orgsize / 2) + (cross_orgsize * cross_linewidth));
119 cr = zeros(cross_orgsize);
120 cr(:,cs:ce) = 1;
121 cr(cs:ce,:) = 1;
122 % Resize - Since square, no point to bicubic interpolation
123 cr_rs = imresize(cr,[ET_params.targsize ET_params.targsize],'nearest');
124 % Make target uint8, colour
125 rgb_t = ET_params.targcolour;
126 cros = uint8(cat(3,cr_rs*rgb_t(1),cr_rs*rgb_t(2),cr_rs*rgb_t(3)));
127 % Make an appropriately-coloured background
128 rgb = ET_params.bgcolour;
129 bg = uint8(ones(ET_params.targsize));
130 bg_rgb =cat(3,bg*rgb(1),bg*rgb(2),bg*rgb(3));
131 % Put background and target together
132 target = bg_rgb;
133 target(find(cros)) = cros(find(cros));
134 % Draw texture
135 targetbuf = Screen('MakeTexture',window,target);
136 % Set up basic rect
137 targetrect = [0 0 size(target,1) size(target,2)];
138
139 % Various calibration settings
140 fprintf(ET_serial,sprintf('ET_CPA %d %d',0,ET_params.waitforvalid));
141 fprintf(ET_serial,sprintf('ET_CPA %d %d',1,ET_params.randompointorder));
142 fprintf(ET_serial,sprintf('ET_CPA %d %d',2,ET_params.autoaccept));
143 fprintf(ET_serial,sprintf('ET_LEV %d',ET_params.checklevel));
144
145 % Set calibration area (ie screen res)
146 fprintf(ET_serial,sprintf('ET_CSZ %d %d',schw(1),schw(2)));
147
148 % These are the default ET points for a 13 point calibration on
149 % a 1280x1024 screen. We can tweak this according to our needs...
150 standardpoints = [640 512;
151 64 51;
152 1216 51;
153 64 973;
154 1216 973;
155 64 512;
156 640 51;
157 1216 512;
158 640 973;
159 352 282;
160 928 282;
161 352 743;
162 928 743];
163
164 % Scale up/down to match calibration area
165 scaledpoints = standardpoints .* repmat(ET_params.calibarea ...
166 ./ [1280 1024],13,1);
167
168 % If the calibration area doesn't match the screen res,
169 % need to shift everything to centre
170 shift = @(xy) round(xy + ([sc.width sc.height]/2) - (ET_params.calibarea/2));
171
172 % Shift the calibration points to centre on the screen
173 shiftedpoints = round(scaledpoints + repmat(schw/2,13,1) ...
174 - repmat(ET_params.calibarea/2,13,1));
175
176 % Set to appropriate npoints
177 shiftedpoints = shiftedpoints(1:ET_params.npoints,:);
178
179 % Send custom points to eye tracker
180 for p = 1:length(shiftedpoints)
181 fprintf(ET_serial,sprintf('ET_PNT %d %d %d',p, ...
182 shiftedpoints(p,1),shiftedpoints(p,2)));
183 end
184 % Start calibration
185 fprintf(ET_serial,sprintf('ET_CAL %d',ET_params.npoints));
186
187 ready = 0;
188 ntries = 0;
189
190 % Point coordinates go here - just to validate
191 points = zeros(ET_params.npoints,2);
192
193 rc = 0;
194 while ~ready
195 ntries = ntries+1;
196
197 % If no connection with serial, return anyway
198 if ntries > 5000
199 fprintf('Serial port communication failure!\n')
200 break
201 end
202
203 % Check for manual attempts to move things along
204 [keyisdown, secs, keyCode] = KbCheck;
205 if keyisdown
206 k = find(keyCode);
207 k = k(1);
208 % Force acceptance of current point
209 if k == ET_params.acceptkey
210 fprintf('Accepting point...\n')
211 %WaitSecs(.2); % Time to let go of key...
212 % Now stop execution until the key is released
213 while KbCheck
214 WaitSecs(.01);
215 end
216 fprintf(ET_serial,'ET_ACC');
217 % Give up on calibration
218 elseif k == ET_params.quitkey
219 fprintf('Calibration attempt aborted!\n')
220 fprintf(ET_serial,'ET_BRK');
221 break
222 end
223 end
224
225 % Check if the eye tracker has something to say
226 response = fgetl(ET_serial);
227
228 % What might the eye tracker have to say?
229 if ~isempty(response)
230 % Save each response - mainly for debugging
231 rc = rc+1;
232 resplog{rc} = response;
233 % Split by spaces
234 command_etc = strread(regexprep(response,' ',' '),'%s');
235 command = command_etc{1};
236
237 %%% What we do next depends on the command we got:
238 % Calibration point change
239 if strcmp(command,'ET_CHG')
240 % Coordinates for point
241 xy = points(str2num(command_etc{2}),:);
242 % Rect for point
243 pointrect = CenterRectOnPoint(targetrect,xy(1),xy(2));
244 % Draw into rect
245 Screen('DrawTexture',window,targetbuf,[],pointrect);
246 Screen(window,'Flip');
247 % Reset timeout counter
248 ntries = 0;
249
250 % Calibation point definition
251 elseif strcmp(command,'ET_PNT')
252 points(str2num(command_etc{2}),:) = ...
253 [str2num(command_etc{3}) str2num(command_etc{4})];
254
255 % Screen size verification
256 %elseif strcmp(command,'ET_CSZ')
257 % Actually, we don't want calibration area
258 % to match screen size.
259 % So arguments are X and Y size
260 %sc = Screen('Resolution',window);
261 %if str2num(command_etc{2}) ~= sc.width
262 %fprintf('Calibration failed - Screen width mismatch\n')
263 %break
264 %elseif str2num(command_etc{3}) ~= sc.height
265 %fprintf('Calibration failed - Screen height mismatch\n')
266 %break
267 %end
268
269 % Calibration finished
270 elseif strcmp(command,'ET_FIN')
271 ready = 1;
272
273 % Various commands we don't care about
274 elseif strcmp(command,'ET_REC') || ...
275 strcmp(command,'ET_CLR') || ...
276 strcmp(command,'ET_CAL') || ...
277 strcmp(command,'ET_CSZ') || ...
278 strcmp(command,'ET_ACC') || ...
279 strcmp(command,'ET_CPA') || ...
280 strcmp(command,'ET_LEV')
281 continue
282
283 % Catch all
284 else
285 fprintf(sprintf(['Calibration failed - received unrecognised '...
286 'input: %s\n'],response));
287 fprintf(ET_serial,'ET_BRK');
288 break % DEBUG
289 end % Resp interpretation
290 end % Resp check
291 end % While
292
293 % Clear the target texture from memory
294 Screen('Close',targetbuf);
295 % Return warning state to whatever it started as
296 warning(wstate.state,wstate.identifier);
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.