123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- % I_S5T.m
- %
- % This function combines the eye+head (E+H) and eye within head (FOV) views in
- % order to detect more complex eye movements. It proceeds in the following
- % steps:
- % 1. detect saccades in the eye+head. More reliabe than FOV
- % 2. get unassigned intervals from the returned values of the previous step
- % (this excludes noise intervals too)
- % 3. get a 100ms windows and classify it based on 5 speed criteria
- % 4. detect blinks and OKN with other algorithms if needed and merge them to
- % the results
- %
- % input:
- % arffFile - file to process
- % saccParams - file holding the saccade parameters
- % outFile - file to store detected eye movements
- % detectExtra - (optional) default: false. Detect blinks and OKN
- % c_speedLow - (optional) default: 10. Low speed threshold for gaze
- % c_speedMed - (optional) default: 65. Medium speed threshold for gaze
- function I_S5T(arffFile, saccParams, outFile, detectExtra, c_speedLow, c_speedMed)
- if (nargin < 6)
- c_speedMed = 65;
- end
- if (nargin < 5)
- c_speedLow = 10;
- end
- if (nargin < 4)
- detectExtra = false;
- end
- c_winDur = 100000; % 100ms
- c_headSpeedLow = 7;
- c_headSpeedMed = 30;
- c_confThres = 0.75;
- % primary labels
- c_prim_fix = 1;
- c_prim_sacc = 2;
- c_prim_sp = 3;
- c_prim_noise = 4;
- c_prim_attType = '{unassigned,fixation,saccade,SP,noise}';
- % secondary labels
- c_sec_unassigned = 0;
- c_sec_okn = 1;
- c_sec_vor = 2;
- c_sec_okn_vor = 3;
- c_sec_noise = 4;
- c_sec_head_purs = 5;
- c_sec_attType = '{unassigned,OKN,VOR,OKN+VOR,noise,head_pursuit}';
- [data, metadata, attributes, relation, comments] = LoadArff(arffFile);
- params = LoadParams(saccParams);
- % add 2 level detection
- % saccades in FOV
- %typeOfMotion = 1;
- % saccades in eye+head
- typeOfMotion = 2;
- sacc = DetectSaccades360(data, metadata, attributes, typeOfMotion, params);
- primData = zeros(size(data,1),1);
- primData(sacc) = c_prim_sacc;
- c_primAttName = 'primary_label';
- [data, attributes] = AddAttArff(data, attributes, primData, c_primAttName, c_prim_attType);
- primAttInd = GetAttPositionArff(attributes, c_primAttName);
- c_secAttName = 'secondary_label';
- secData = zeros(size(data,1),1);
- [data, attributes] = AddAttArff(data, attributes, secData, c_secAttName, c_sec_attType);
- secAttInd = GetAttPositionArff(attributes, c_secAttName);
- % get unassigned intervals
- ints = GetIntervalsIndexArff(data, attributes, c_primAttName, 0);
- % Classify intersaccadic intervals
- timeInd = GetAttPositionArff(attributes, 'time');
- [eyeFovVec, eyeHeadVec, headVec] = GetCartVectors(data, metadata, attributes);
- for ind=1:size(ints,1)
- winInts = GetWindowInts(ints(ind,1), ints(ind,2));
- for winInd=1:size(winInts,1)
- ClassifyInt(winInts(winInd,1), winInts(winInd,2));
- end
- end
- confInd = GetAttPositionArff(attributes, 'confidence');
- data(data(:,confInd) < c_confThres, primAttInd) = c_prim_noise;
- data(data(:,confInd) < c_confThres, secAttInd) = c_sec_noise;
- SaveArff(outFile, data, metadata, attributes, relation, comments);
- if (~detectExtra)
- return;
- end
- blinks = DetectBlinks360(data, metadata, attributes, typeOfMotion, params);
- data(blinks, primAttInd) = c_prim_noise;
- okn = DetectOKN360(outFile, c_primAttName, c_prim_sacc);
- data(okn & data(:,secAttInd) == c_sec_unassigned, secAttInd) = c_sec_okn;
- data(okn & data(:,secAttInd) == c_sec_vor, secAttInd) = c_sec_okn_vor;
- % assign label of OKN+VOR to OKN intervals that lay between OKN+VOR intervals
- ints = GetIntervalsIndexArff(data, attributes, c_secAttName, c_sec_okn);
- for ind=2:size(ints,1)-1
- if (data(ints(ind,1)-1,secAttInd) == c_sec_okn_vor && ...
- data(ints(ind,2)+1,secAttInd) == c_sec_okn_vor)
- data(ints(ind,1):ints(ind,2),secAttInd) = c_sec_okn_vor;
- end
- end
- SaveArff(outFile, data, metadata, attributes, relation, comments);
- %
- %-----------------------------------------------------------------------------------
- %
- % this function returns the indices of non-overlappng windows of 100ms
- function [l_ints] = GetWindowInts(startInd, endInd)
- l_ints = zeros(0,2);
- l_prevInd = startInd;
- for l_ind=startInd:endInd
- if (data(l_ind,timeInd) - data(l_prevInd,timeInd) > c_winDur)
- l_ints = [l_ints; l_prevInd l_ind-1];
- l_prevInd = l_ind;
- end
- end
-
- if (data(endInd,timeInd) - data(l_prevInd,timeInd) > c_winDur / 2 || isempty(l_ints))
- l_ints = [l_ints; l_prevInd endInd];
- else
- l_ints(end,2) = endInd;
- end
- end
- function l_intType = ClassifyInt(startInd, endInd)
- if (startInd == endInd)
- ProcessOneSampleInt(startInd);
- end
- eyeHeadSpeed = GetSpeed(eyeHeadVec([startInd, endInd],:), data([startInd, endInd],timeInd));
- eyeHeadSpeed = eyeHeadSpeed(1);
- eyeFovSpeed = GetSpeed(eyeFovVec([startInd, endInd],:), data([startInd, endInd],timeInd));
- eyeFovSpeed = eyeFovSpeed(1);
- headSpeed = GetSpeed(headVec([startInd, endInd],:), data([startInd, endInd],timeInd));
- headSpeed = headSpeed(1);
- % adaptive eye speed thresholds
- % eye speed has to scale in proportion to head speed
- speedLow = c_speedLow * (1 + 0.5*headSpeed/c_headSpeedMed);
- speedMed = c_speedMed * (1 + 0.5*headSpeed/c_headSpeedMed);
- if (eyeHeadSpeed < speedLow && ...
- eyeFovSpeed < speedLow)
- data(startInd:endInd, primAttInd) = c_prim_fix;
- elseif (eyeHeadSpeed < speedLow && ...
- eyeFovSpeed >= speedLow)
- data(startInd:endInd, primAttInd) = c_prim_fix;
- if (headSpeed >= c_headSpeedLow)
- data(startInd:endInd, secAttInd) = c_sec_vor;
- end
- elseif (eyeHeadSpeed >= speedLow && eyeHeadSpeed < speedMed && ...
- eyeFovSpeed < speedLow)
- if (headSpeed < c_headSpeedLow)
- data(startInd:endInd, primAttInd) = c_prim_sp;
- else
- data(startInd:endInd, primAttInd) = c_prim_fix;
- data(startInd:endInd, secAttInd) = c_sec_head_purs;
- end
- elseif (eyeHeadSpeed >= speedLow && eyeHeadSpeed < speedMed && ...
- eyeFovSpeed >= speedLow && eyeFovSpeed < speedMed)
- data(startInd:endInd, primAttInd) = c_prim_sp;
- if (headSpeed >= c_headSpeedLow)
- data(startInd:endInd, secAttInd) = c_sec_vor;
- end
- else
- data(startInd:endInd, primAttInd) = c_prim_noise;
- end
- end
- function ProcessOneSampleInt(index)
- if (index < size(data,1))
- data(index, primAttInd) = data(index+1, primAttInd);
- else
- data(index, primAttInd) = data(index-1, primAttInd);
- end
- end
- end
|