I_S5T.m 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. % I_S5T.m
  2. %
  3. % This function combines the eye+head (E+H) and eye within head (FOV) views in
  4. % order to detect more complex eye movements. It proceeds in the following
  5. % steps:
  6. % 1. detect saccades in the eye+head. More reliabe than FOV
  7. % 2. get unassigned intervals from the returned values of the previous step
  8. % (this excludes noise intervals too)
  9. % 3. get a 100ms windows and classify it based on 5 speed criteria
  10. % 4. detect blinks and OKN with other algorithms if needed and merge them to
  11. % the results
  12. %
  13. % input:
  14. % arffFile - file to process
  15. % saccParams - file holding the saccade parameters
  16. % outFile - file to store detected eye movements
  17. % detectExtra - (optional) default: false. Detect blinks and OKN
  18. % c_speedLow - (optional) default: 10. Low speed threshold for gaze
  19. % c_speedMed - (optional) default: 65. Medium speed threshold for gaze
  20. function I_S5T(arffFile, saccParams, outFile, detectExtra, c_speedLow, c_speedMed)
  21. if (nargin < 6)
  22. c_speedMed = 65;
  23. end
  24. if (nargin < 5)
  25. c_speedLow = 10;
  26. end
  27. if (nargin < 4)
  28. detectExtra = false;
  29. end
  30. c_winDur = 100000; % 100ms
  31. c_headSpeedLow = 7;
  32. c_headSpeedMed = 30;
  33. c_confThres = 0.75;
  34. % primary labels
  35. c_prim_fix = 1;
  36. c_prim_sacc = 2;
  37. c_prim_sp = 3;
  38. c_prim_noise = 4;
  39. c_prim_attType = '{unassigned,fixation,saccade,SP,noise}';
  40. % secondary labels
  41. c_sec_unassigned = 0;
  42. c_sec_okn = 1;
  43. c_sec_vor = 2;
  44. c_sec_okn_vor = 3;
  45. c_sec_noise = 4;
  46. c_sec_head_purs = 5;
  47. c_sec_attType = '{unassigned,OKN,VOR,OKN+VOR,noise,head_pursuit}';
  48. [data, metadata, attributes, relation, comments] = LoadArff(arffFile);
  49. params = LoadParams(saccParams);
  50. % add 2 level detection
  51. % saccades in FOV
  52. %typeOfMotion = 1;
  53. % saccades in eye+head
  54. typeOfMotion = 2;
  55. sacc = DetectSaccades360(data, metadata, attributes, typeOfMotion, params);
  56. primData = zeros(size(data,1),1);
  57. primData(sacc) = c_prim_sacc;
  58. c_primAttName = 'primary_label';
  59. [data, attributes] = AddAttArff(data, attributes, primData, c_primAttName, c_prim_attType);
  60. primAttInd = GetAttPositionArff(attributes, c_primAttName);
  61. c_secAttName = 'secondary_label';
  62. secData = zeros(size(data,1),1);
  63. [data, attributes] = AddAttArff(data, attributes, secData, c_secAttName, c_sec_attType);
  64. secAttInd = GetAttPositionArff(attributes, c_secAttName);
  65. % get unassigned intervals
  66. ints = GetIntervalsIndexArff(data, attributes, c_primAttName, 0);
  67. % Classify intersaccadic intervals
  68. timeInd = GetAttPositionArff(attributes, 'time');
  69. [eyeFovVec, eyeHeadVec, headVec] = GetCartVectors(data, metadata, attributes);
  70. for ind=1:size(ints,1)
  71. winInts = GetWindowInts(ints(ind,1), ints(ind,2));
  72. for winInd=1:size(winInts,1)
  73. ClassifyInt(winInts(winInd,1), winInts(winInd,2));
  74. end
  75. end
  76. confInd = GetAttPositionArff(attributes, 'confidence');
  77. data(data(:,confInd) < c_confThres, primAttInd) = c_prim_noise;
  78. data(data(:,confInd) < c_confThres, secAttInd) = c_sec_noise;
  79. SaveArff(outFile, data, metadata, attributes, relation, comments);
  80. if (~detectExtra)
  81. return;
  82. end
  83. blinks = DetectBlinks360(data, metadata, attributes, typeOfMotion, params);
  84. data(blinks, primAttInd) = c_prim_noise;
  85. okn = DetectOKN360(outFile, c_primAttName, c_prim_sacc);
  86. data(okn & data(:,secAttInd) == c_sec_unassigned, secAttInd) = c_sec_okn;
  87. data(okn & data(:,secAttInd) == c_sec_vor, secAttInd) = c_sec_okn_vor;
  88. % assign label of OKN+VOR to OKN intervals that lay between OKN+VOR intervals
  89. ints = GetIntervalsIndexArff(data, attributes, c_secAttName, c_sec_okn);
  90. for ind=2:size(ints,1)-1
  91. if (data(ints(ind,1)-1,secAttInd) == c_sec_okn_vor && ...
  92. data(ints(ind,2)+1,secAttInd) == c_sec_okn_vor)
  93. data(ints(ind,1):ints(ind,2),secAttInd) = c_sec_okn_vor;
  94. end
  95. end
  96. SaveArff(outFile, data, metadata, attributes, relation, comments);
  97. %
  98. %-----------------------------------------------------------------------------------
  99. %
  100. % this function returns the indices of non-overlappng windows of 100ms
  101. function [l_ints] = GetWindowInts(startInd, endInd)
  102. l_ints = zeros(0,2);
  103. l_prevInd = startInd;
  104. for l_ind=startInd:endInd
  105. if (data(l_ind,timeInd) - data(l_prevInd,timeInd) > c_winDur)
  106. l_ints = [l_ints; l_prevInd l_ind-1];
  107. l_prevInd = l_ind;
  108. end
  109. end
  110. if (data(endInd,timeInd) - data(l_prevInd,timeInd) > c_winDur / 2 || isempty(l_ints))
  111. l_ints = [l_ints; l_prevInd endInd];
  112. else
  113. l_ints(end,2) = endInd;
  114. end
  115. end
  116. function l_intType = ClassifyInt(startInd, endInd)
  117. if (startInd == endInd)
  118. ProcessOneSampleInt(startInd);
  119. end
  120. eyeHeadSpeed = GetSpeed(eyeHeadVec([startInd, endInd],:), data([startInd, endInd],timeInd));
  121. eyeHeadSpeed = eyeHeadSpeed(1);
  122. eyeFovSpeed = GetSpeed(eyeFovVec([startInd, endInd],:), data([startInd, endInd],timeInd));
  123. eyeFovSpeed = eyeFovSpeed(1);
  124. headSpeed = GetSpeed(headVec([startInd, endInd],:), data([startInd, endInd],timeInd));
  125. headSpeed = headSpeed(1);
  126. % adaptive eye speed thresholds
  127. % eye speed has to scale in proportion to head speed
  128. speedLow = c_speedLow * (1 + 0.5*headSpeed/c_headSpeedMed);
  129. speedMed = c_speedMed * (1 + 0.5*headSpeed/c_headSpeedMed);
  130. if (eyeHeadSpeed < speedLow && ...
  131. eyeFovSpeed < speedLow)
  132. data(startInd:endInd, primAttInd) = c_prim_fix;
  133. elseif (eyeHeadSpeed < speedLow && ...
  134. eyeFovSpeed >= speedLow)
  135. data(startInd:endInd, primAttInd) = c_prim_fix;
  136. if (headSpeed >= c_headSpeedLow)
  137. data(startInd:endInd, secAttInd) = c_sec_vor;
  138. end
  139. elseif (eyeHeadSpeed >= speedLow && eyeHeadSpeed < speedMed && ...
  140. eyeFovSpeed < speedLow)
  141. if (headSpeed < c_headSpeedLow)
  142. data(startInd:endInd, primAttInd) = c_prim_sp;
  143. else
  144. data(startInd:endInd, primAttInd) = c_prim_fix;
  145. data(startInd:endInd, secAttInd) = c_sec_head_purs;
  146. end
  147. elseif (eyeHeadSpeed >= speedLow && eyeHeadSpeed < speedMed && ...
  148. eyeFovSpeed >= speedLow && eyeFovSpeed < speedMed)
  149. data(startInd:endInd, primAttInd) = c_prim_sp;
  150. if (headSpeed >= c_headSpeedLow)
  151. data(startInd:endInd, secAttInd) = c_sec_vor;
  152. end
  153. else
  154. data(startInd:endInd, primAttInd) = c_prim_noise;
  155. end
  156. end
  157. function ProcessOneSampleInt(index)
  158. if (index < size(data,1))
  159. data(index, primAttInd) = data(index+1, primAttInd);
  160. else
  161. data(index, primAttInd) = data(index-1, primAttInd);
  162. end
  163. end
  164. end