|
| 1 | +%% FIXATION DETECTION USING THE IDENTIFICATION BY 2-MEANS CLUSTERING (I2MC) ALGORITHM |
| 2 | +% Description: |
| 3 | +% The I2MC algorithm was designed to accomplish fixation detection in data |
| 4 | +% across a wide range of noise levels and when periods of data loss may |
| 5 | +% occur. |
| 6 | + |
| 7 | +% Cite as: |
| 8 | +% Hessels, R.S., Niehorster, D.C., Kemner, C., & Hooge, I.T.C., (2016). |
| 9 | +% Noise-robust fixation detection in eye-movement data - Identification by |
| 10 | +% 2-means clustering (I2MC). Submitted. |
| 11 | + |
| 12 | +% For more information, questions, or to check whether we have updated to a |
| 13 | +% better version, e-mail: [email protected] / [email protected]. I2MC is |
| 14 | +% available from www.github.com/royhessels/I2MC |
| 15 | + |
| 16 | +% Most parts of the I2MC algorithm are licensed under the Creative Commons |
| 17 | +% Attribution 4.0 (CC BY 4.0) license. Some functions are under MIT |
| 18 | +% license, and some may be under other licenses. |
| 19 | + |
| 20 | +% Quick start guide for adopting this script for your own data: |
| 21 | +% 1) Build an import function specific for your data (see importTobiiTX300 |
| 22 | +% for an example). |
| 23 | + |
| 24 | +% 2) Change line 106 to use your new import function. The format should be: |
| 25 | +% |
| 26 | +% data.time for the timestamp |
| 27 | +% data.left.X & data.left.Y for left gaze coordinates |
| 28 | +% data.right.X & data.right.Y for right gaze coordinates |
| 29 | +% data.average.X & data.average.Y for average gaze coordinates |
| 30 | +% |
| 31 | +% You may provide coordinates from both eyes, only the left, only the |
| 32 | +% right, or only the average. |
| 33 | +% Gaze coordinates should be in pixels, timestamps should be in milliseconds |
| 34 | + |
| 35 | +% 3) Adjust the variables in the "necessary variables" section to match your |
| 36 | +% data |
| 37 | +% 4) Run the algorithm |
| 38 | + |
| 39 | +% Requirements: Signal Processing Toolbox for downsampling. If not |
| 40 | +% available you may set opt.downsample to []. This may, however, degrade |
| 41 | +% performance of the algorithm. |
| 42 | + |
| 43 | +% Tested on MATLAB R2012a, R2014b & R2016a |
| 44 | + |
| 45 | +%% INITIALIZE |
| 46 | +clear variables; clear mex; close all; fclose('all'); clc; |
| 47 | +dbstop if error; |
| 48 | +commandwindow; |
| 49 | + |
| 50 | +%% NECESSARY VARIABLES |
| 51 | + |
| 52 | +% General variables for eye-tracking data |
| 53 | +opt.xres = 1920; % maximum value of horizontal resolution in pixels |
| 54 | +opt.yres = 1080; % maximum value of vertical resolution in pixels |
| 55 | +opt.missingx = -opt.xres; % missing value for horizontal position in eye-tracking data (example data uses -xres). used throughout functions as signal for data loss |
| 56 | +opt.missingy = -opt.yres; % missing value for vertical position in eye-tracking data (example data uses -yres). used throughout functions as signal for data loss |
| 57 | +opt.freq = 300; % sampling frequency of data (check that this value matches with values actually obtained from measurement!) |
| 58 | + |
| 59 | +% Variables for the calculation of visual angle |
| 60 | +% These values are used to calculate noise measures (RMS and BCEA) of |
| 61 | +% fixations. The may be left as is, but don't use the noise measures then. |
| 62 | +opt.scrSz = [50.9174 28.6411]; % screen size in cm |
| 63 | +opt.disttoscreen = 65; % distance to screen in cm. |
| 64 | + |
| 65 | +% Folders |
| 66 | +% Data folder should be structured by one folder for each participant with |
| 67 | +% the eye-tracking data in textfiles in each folder. |
| 68 | +folders.data = 'example data'; % folder in which data is stored (each folder in folders.data is considered 1 subject) |
| 69 | +folders.output = 'output'; % folder for output (will use structure in folders.data for saving output) |
| 70 | + |
| 71 | +% Plot results |
| 72 | +do.plots = 1; % if set to 1, plot of fixation detection for each trial will be saved as png-file in output folder. |
| 73 | +% the figures works best for short trials (up to around 20 seconds) |
| 74 | + |
| 75 | +%% OPTIONAL VARIABLES |
| 76 | +% The settings below may be used to adopt the default settings of the |
| 77 | +% algorithm. Do this only if you know what you're doing. Uncomment the |
| 78 | +% settings below and run the algorithm. |
| 79 | + |
| 80 | +% % STEFFEN INTERPOLATION |
| 81 | +% opt.windowtimeInterp = 0.1; % max duration (s) of missing values for interpolation to occur |
| 82 | +% opt.edgeSampInterp = 2; % amount of data (number of samples) at edges needed for interpolation |
| 83 | +% opt.maxdisp = opt.xres*0.2*sqrt(2); % maximum displacement during missing for interpolation to be possible |
| 84 | +% |
| 85 | +% % K-MEANS CLUSTERING |
| 86 | +% opt.windowtime = 0.2; % time window (s) over which to calculate 2-means clustering (choose value so that max. 1 saccade can occur) |
| 87 | +% opt.steptime = 0.02;% time window shift (s) for each iteration. Use zero for sample by sample processing |
| 88 | +% opt.maxerrors = 100; % maximum number of errors allowed in k-means clustering procedure before proceeding to next file |
| 89 | +% opt.downsamples = [2 5 10]; |
| 90 | +% |
| 91 | +% % FIXATION DETERMINATION |
| 92 | +% opt.cutoffstd = 2; % number of standard deviations above mean k-means weights will be used as fixation cutoff |
| 93 | +% opt.maxMergeDist = 30; % maximum Euclidean distance in pixels between fixations for merging |
| 94 | +% opt.maxMergeTime = 30; % maximum time in ms between fixations for merging |
| 95 | +% opt.minFixDur = 40; % minimum fixation duration after merging, fixations with shorter duration are removed from output |
| 96 | + |
| 97 | +%% SET-UP FOLDERS |
| 98 | + |
| 99 | +folders.func = 'functions'; % folder for functions, will add to matlab path |
| 100 | +addpath(genpath(folders.func)); |
| 101 | + |
| 102 | +%% START ALGORITHM |
| 103 | + |
| 104 | +% create textfile and open for writing fixations |
| 105 | +fid = fopen(fullfile(folders.output,'allfixations.txt'),'w'); |
| 106 | +fprintf(fid,'FixStart\tFixEnd\tFixDur\tXPos\tYPos\tFlankedByDataLoss\tFraction Interpolated\tWeightCutoff\tRMSxy\tBCEA\tFixRangeX\tFixRangeY\tParticipant\tTrial\n'); |
| 107 | + |
| 108 | +% go through all folders in 'data' (one folder is assumed to be one |
| 109 | +% subject) |
| 110 | +[fold,nfold] = FolderFromFolder(folders.data); |
| 111 | + |
| 112 | +for e = 1:nfold |
| 113 | + |
| 114 | + % make output folder |
| 115 | + mkdir(folders.output,fold(e).name); |
| 116 | + |
| 117 | + % go through all files in fold, if fold is empty continue to next |
| 118 | + % folder |
| 119 | + [file,nfile] = FileFromFolder(fullfile(folders.data,fold(e).name),'silent','txt'); |
| 120 | + if ~nfile |
| 121 | + disp('folder is empty, continuing to next folder'); |
| 122 | + continue |
| 123 | + end |
| 124 | + |
| 125 | + for f = 1:nfile |
| 126 | + |
| 127 | + %% IMPORT DATA |
| 128 | + fprintf('Importing and processing %s/%s \n',fold(e).name,file(f).name) |
| 129 | + |
| 130 | + [data.time,data.left.X,data.left.Y,data.right.X,data.right.Y] = importTobiiTX300(fullfile(folders.data,fold(e).name,file(f).name),1,[opt.xres opt.yres],opt.missingx,opt.missingy); |
| 131 | + |
| 132 | + % check whether we have data, if not, continue to next file |
| 133 | + if isempty(data.time) |
| 134 | + fprintf('Empty file encountered, continuing to next file \n'); |
| 135 | + continue |
| 136 | + end |
| 137 | + |
| 138 | + %% RUN FIXATION DETECTION |
| 139 | + fix = I2MCfunc(data,opt); |
| 140 | + |
| 141 | + %% PLOT RESULTS |
| 142 | + |
| 143 | + if do.plots |
| 144 | + % pre-allocate name for saving file |
| 145 | + savefile = fullfile(folders.output, fold(e).name, file(f).name(1:end-4)); |
| 146 | + plotResults(data,fix,savefile,[opt.xres opt.yres]); |
| 147 | + end |
| 148 | + |
| 149 | + for g=1:numel(fix.start) |
| 150 | + fprintf(fid,'%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t%.3f\t%s\t%s\n',[fix.startT(g) fix.endT(g) fix.dur(g) fix.xpos(g) fix.ypos(g) fix.flankdataloss(g) fix.fracinterped(g) fix.cutoff, fix.RMSxy(g), fix.BCEA(g), fix.fixRangeX(g), fix.fixRangeY(g)],fold(e).name,file(f).name(1:end-4)); |
| 151 | + end |
| 152 | + end |
| 153 | +end |
| 154 | + |
| 155 | +%% CLEAN UP |
| 156 | + |
| 157 | +fclose(fid); |
0 commit comments