-
Notifications
You must be signed in to change notification settings - Fork 12
/
rej_eyecontin.m
196 lines (180 loc) · 9.49 KB
/
rej_eyecontin.m
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
% rej_eyecontin() - reject intervals of continuous data during which
% the eye tracker recorded extreme (out-of-range) values.
% This function is useful for several purposes: to reject
% data with eye blinks, to control fixation position, and to
% reject intervals bad eye tracking data prior to saccade or
% fixation detection.
%
% There are two possibilities:
% METHOD 1: reject bad intervals and insert boundary events
% (=data breaks) at that position. This actually removes the
% intervals with bad data from the dataset.
% EEGLAB function pop_select() is used to do the actual
% rejection. A new boundary event is added at the position
% of each of the resulting data breaks. To perform a similar
% rejection for epoched data, use pop_rej_eyeepoch.
%
% METHOD 2: detect bad intervals in the eye tracking data,
% and insert a "bad_ET" marker in the EEG.event structure for
% every bad interval. These events will be considered by
% pop_detecteyemovements(), so that the bad ET data will not
% distort the detection of saccades and fixations
%
% Usage:
% >> EEG = rej_eyecontin(EEG,chns,minvals,maxvals,windowsize,rejectionmethod)
%
% Inputs:
% EEG - [string] EEG struct, also containing synchronized eye
% tracking data
% chns - [vector of channel indices] Indices of ET channels to
% check for out-of-range values
% minvals - [vector of length(chns)] minimum allowed values for the
% corresponding data channel in 'chns'. Data points
% with values < minvals will be rejected. minvals needs to
% have the same length as chns and maxvals. If you only
% want to test for maxvals, minvals can be left empty [].
% maxvals - [vector of length(chns)] maximum allowed values for the
% corresponding data channel in 'chns'. Data points with
% with values > maxvals will be rejected. maxvals needs to
% have the same length as chns and minvals. If you only
% want to test for minvals, maxvals can be left empty [].
% windowsize - [integer] if windowsize is set to a value > 0, an
% additional plusminus 'windowsize' data points will be
% removed before and after each interval of out-of-range
% data. We recommended to set this to a reasonable value
% (e.g., 50 samples in case of 500 Hz data) because eye
% blinks (usually characterized by zeros or negative
% values in the eye track) are often flanked by
% additional bad samples (recorded during
% the partial occlusion of the pupil while the eye is
% closing and re-opening) that may otherwise not exceed
% the minvals/maxvals thresholds.
% rejectionnmethod - [1 or 2]
% 1: reject bad data intervals (cuts intervals from data!)
% and inserts boundary events at the breaks
% 2: add special marker "bad_ET" to EEG.event for each bad
% data interval. These markers are considered by the
% eye movement detection function (pop_detecteyemovements.m)
% The dataset is not cut.
%
% Outputs:
% EEG - EEG structure with bad intervals removed or detected.
% For each removed interval of bad data, a new boundary
% event is inserted into EEG.event.
%
% An example call of the function might look like this:
% >> EEG = rej_eyecontin(EEG,[33 34],[1 1],[1024 768],50, 2)
%
% In this example, the data of channels 33 and 34, containing the
% horizontal (33) and vertical (34) position of the left eye, are tested
% for pixel values smaller than 1 pixel or larger than 1024 pixels
% (channel 33) and smaller than 1 pixel or larger than 768 pixels
% (channel 34). Screen resolution during the experiment was 1024 x 768
% pixel. Values outside this range likely reflect eye blinks
% or intervals during which the eye was not properly tracked. Around each
% interval of out-of-range data an addtional plusminus 50 samples of
% data are marked as well. For each interval of bad data, a "bad_ET"
% event is added to EEG.event. These bad intervals will not be considered
% by the saccade detection algorithm (pop_detecteyemovements.m)
%
% See also: pop_rej_eyecontin, pop_rej_eyeepoch, pop_select
%
% Author: od
% Copyright (C) 2009-2021 Olaf Dimigen & Ulrich Reinacher, HU Berlin
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, 51 Franklin Street, Boston, MA 02110-1301, USA
function [EEG,seq_bad] = rej_eyecontin(EEG,chns,minvals,maxvals,windowsize,rejectionmethod)
%% input checks
if isempty(chns)
error('\n%s(): Please provide indices of channels to test for out-of-range values.',mfilename)
end
if isempty(minvals) && isempty(maxvals)
error('\n%s(): Please provide either minimum (minvals) or maximum (maxvals) values (or both) to test for out-of-range samples.',mfilename)
end
% Either minvals or maxvals can be left empty; in this case only the other
% criterion is used. If both minvals and maxvals are specified, both
% vectors need to have the same length (length(chns))
checkminima = true;
checkmaxima = true;
if length(minvals) ~= length(maxvals)
if isempty(minvals), checkminima = false; end % only test for minvals
if isempty(maxvals), checkmaxima = false; end % only test for maxvals
if checkminima && checkmaxima
error('%s(): If minimum and maximum values are specified, both vectors need to have the same length!',mfilename)
end
elseif length(chns) ~= length(minvals)
error('%s(): For each channel to test for out-or-range data, please specify one value in minvals and maxvals!',mfilename)
end
%% collect indices of out-of-range samples across all channels
fprintf('\n%s(): Rejecting intervals with out-of-range eye track',mfilename)
ix_bad = [];
for c = 1:length(chns)
[chnid chntxt] = eeg_decodechan(EEG.chanlocs,chns(c)); % get channel names
if checkminima && checkmaxima
fprintf('\nChannel %i \"%s\": rejecting values < %i or > %i',chnid,chntxt{:},minvals(c),maxvals(c));
ix = find(EEG.data(chns(c),:) < minvals(c) | EEG.data(chns(c),:) > maxvals(c));
elseif checkminima
fprintf('\nChannel %i \"%s\": rejecting values < %i',chnid,chntxt{:},minvals(c));
ix = find(EEG.data(chns(c),:) > maxvals(c));
else
fprintf('\nChannel %i \"%s\": rejecting values > %i',chnid,chntxt{:},maxvals(c));
ix = find(EEG.data(chns(c),:) < minvals(c) );
end
ix_bad = [ix_bad ix];
end
ix_bad = unique(ix_bad);
if windowsize > 0
fprintf('\nRejecting an extra plusminus %i samples (%.0f ms) around each out-of-range interval.',windowsize,windowsize*1000/EEG.srate)
end
%% bad samples found?
if isempty(ix_bad)
fprintf('\n\nNo out-of-range samples found.\n')
else
% identify start and end indices of contiguous blocks of bad data
seq_bad = findsequence2(ix_bad');
% add extra rejection zone around bad data
% e.g., to remove sub-threshold bad samples at beginning/end of blinks
seq_bad(:,1) = seq_bad(:,1) - windowsize;
seq_bad(:,2) = seq_bad(:,2) + windowsize;
% remove indices outside data boundaries
seq_bad(seq_bad(:,1) < 1,1) = 1;
seq_bad(seq_bad(:,2) > size(EEG.data,2),2) = size(EEG.data,2);
% to handle overlapping blocks of bad data
% translate again into vector of bad samples (0 = good, 1 = bad)
badvector = zeros(size(EEG.data,2),1);
for n = 1:size(seq_bad,1);
badvector(seq_bad(n,1):seq_bad(n,2)) = 1;
end
% get start/end of bad blocks again
seq_bad = findsequence2(find(badvector));
fprintf('\n\nFound %i out-of-range samples in %i continuous intervals of bad data',length(ix_bad),size(seq_bad,1));
% reject bad data
switch rejectionmethod
case 1 % remove using pop_select
fprintf('\nRemoving bad intervals from continuous data...\n\n')
EEG = pop_select(EEG,'nopoint',seq_bad(:,1:2));
case 2 % add "bad_ET" marker for each bad interval
% Update march 2018 (OD)
% Do not remove data, but introduce new bad_ET events in EEG.event
fprintf('\nAdding %i bad_ET markers to the EEG.event structure...\nNot removing any data.\n\n',size(seq_bad,1))
% add duration of bad interval (in samples)
seq_bad(:,3) = seq_bad(:,2)-seq_bad(:,1) + 1;
% add bad ET markers
EEG = addevents(EEG,[seq_bad(:,1) seq_bad(:,3)],{'latency','duration'},'bad_ET');
otherwise
error('%s(): rejection method input not recognized',mfilename)
end
end
end