I_S5T_single_view.m 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. % I_S5T_single_view.m
  2. %
  3. % This is a stripped down implememtation of the I_S5T algorithm that uses a
  4. % single view (and thus fewer speed thresholds). The single view can be either
  5. % FOV or eye+head (E+H). This implementation has resemblances to the I-VVT
  6. % algorithm.
  7. %
  8. % input:
  9. % arffFile - file to process
  10. % saccParams - file holding the saccade parameters
  11. % typeOfMotion- 1 -> eye FOV, 2 -> E+H
  12. % outFile - file to store detected eye movements
  13. % detectExtra - (optional) default: false. Detect blinks and OKN
  14. function I_S5T_single_view(arffFile, saccParams, typeOfMotion, outFile, detectExtra, speedLow, speedMed)
  15. if (nargin < 4)
  16. detectExtra = false;
  17. end
  18. c_winDur = 100000; % 100ms
  19. c_speedLow = speedLow;
  20. c_speedMed = speedMed;
  21. c_headSpeedLow = 7;
  22. c_headSpeedMed = 30;
  23. c_confThres = 0.75;
  24. % primary labels
  25. c_prim_fix = 1;
  26. c_prim_sacc = 2;
  27. c_prim_sp = 3;
  28. c_prim_noise = 4;
  29. c_prim_attType = '{unassigned,fixation,saccade,SP,noise}';
  30. % secondary labels
  31. c_sec_unassigned = 0;
  32. c_sec_okn = 1;
  33. c_sec_vor = 2;
  34. c_sec_okn_vor = 3;
  35. c_sec_noise = 4;
  36. c_sec_head_purs = 5;
  37. c_sec_attType = '{unassigned,OKN,VOR,OKN+VOR,noise,head_pursuit}';
  38. [data, metadata, attributes, relation, comments] = LoadArff(arffFile);
  39. params = LoadParams(saccParams);
  40. sacc = DetectSaccades360(data, metadata, attributes, typeOfMotion, params);
  41. primData = zeros(size(data,1),1);
  42. primData(sacc) = c_prim_sacc;
  43. c_primAttName = 'primary_label';
  44. [data, attributes] = AddAttArff(data, attributes, primData, c_primAttName, c_prim_attType);
  45. primAttInd = GetAttPositionArff(attributes, c_primAttName);
  46. c_secAttName = 'secondary_label';
  47. secData = zeros(size(data,1),1);
  48. [data, attributes] = AddAttArff(data, attributes, secData, c_secAttName, c_sec_attType);
  49. secAttInd = GetAttPositionArff(attributes, c_secAttName);
  50. % get unassigned intervals
  51. ints = GetIntervalsIndexArff(data, attributes, c_primAttName, 0);
  52. % Classify intersaccadic intervals
  53. timeInd = GetAttPositionArff(attributes, 'time');
  54. [eyeFovVec, eyeHeadVec, headVec] = GetCartVectors(data, metadata, attributes);
  55. if (typeOfMotion == 1)
  56. gazeVec = eyeFovVec;
  57. elseif (typeOfMotion == 2)
  58. gazeVec = eyeHeadVec;
  59. end
  60. for ind=1:size(ints,1)
  61. winInts = GetWindowInts(ints(ind,1), ints(ind,2));
  62. for winInd=1:size(winInts,1)
  63. ClassifyInt(winInts(winInd,1), winInts(winInd,2));
  64. end
  65. end
  66. confInd = GetAttPositionArff(attributes, 'confidence');
  67. data(data(:,confInd) < c_confThres, primAttInd) = c_prim_noise;
  68. data(data(:,confInd) < c_confThres, secAttInd) = c_sec_noise;
  69. SaveArff(outFile, data, metadata, attributes, relation, comments);
  70. if (~detectExtra)
  71. return;
  72. end
  73. blinks = DetectBlinks360(data, metadata, attributes, typeOfMotion, params);
  74. data(blinks, primAttInd) = c_prim_noise;
  75. okn = DetectOKN360(outFile, c_primAttName, c_prim_sacc);
  76. data(okn & data(:,secAttInd) == c_sec_unassigned, secAttInd) = c_sec_okn_vor; % put OKN+VOR because it is the most movement
  77. data(okn & data(:,secAttInd) == c_sec_vor, secAttInd) = c_sec_okn_vor;
  78. % assign label of OKN+VOR to OKN intervals that lay between OKN+VOR intervals
  79. ints = GetIntervalsIndexArff(data, attributes, c_secAttName, c_sec_okn);
  80. for ind=2:size(ints,1)-1
  81. if (data(ints(ind,1)-1,secAttInd) == c_sec_okn_vor && ...
  82. data(ints(ind,2)+1,secAttInd) == c_sec_okn_vor)
  83. data(ints(ind,1):ints(ind,2),secAttInd) = c_sec_okn_vor;
  84. end
  85. end
  86. SaveArff(outFile, data, metadata, attributes, relation, comments);
  87. %
  88. %-----------------------------------------------------------------------------------
  89. %
  90. % this function returns the indices of non-overlappng windows of 100ms
  91. function [l_ints] = GetWindowInts(startInd, endInd)
  92. l_ints = zeros(0,2);
  93. l_prevInd = startInd;
  94. for l_ind=startInd:endInd
  95. if (data(l_ind,timeInd) - data(l_prevInd,timeInd) > c_winDur)
  96. l_ints = [l_ints; l_prevInd l_ind-1];
  97. l_prevInd = l_ind;
  98. end
  99. end
  100. if (data(endInd,timeInd) - data(l_prevInd,timeInd) > c_winDur / 2 || isempty(l_ints))
  101. l_ints = [l_ints; l_prevInd endInd];
  102. else
  103. l_ints(end,2) = endInd;
  104. end
  105. end
  106. function l_intType = ClassifyInt(startInd, endInd)
  107. if (startInd == endInd)
  108. ProcessOneSampleInt(startInd);
  109. end
  110. eyeSpeed = GetSpeed(gazeVec([startInd, endInd],:), data([startInd, endInd],timeInd));
  111. eyeSpeed = eyeSpeed(1);
  112. % cannot be used because we do not use head
  113. % adaptive eye speed thresholds
  114. % eye speed has to scale in proportion to head speed
  115. %speedLow = c_speedLow * (1 + headSpeed/c_headSpeedMed);
  116. %speedMed = c_speedMed * (1 + headSpeed/c_headSpeedMed);
  117. speedLow = c_speedLow;
  118. speedMed = c_speedMed;
  119. if (eyeSpeed < speedLow)
  120. data(startInd:endInd, primAttInd) = c_prim_fix;
  121. elseif (eyeSpeed < speedMed)
  122. data(startInd:endInd, primAttInd) = c_prim_sp;
  123. else
  124. data(startInd:endInd, primAttInd) = c_prim_noise;
  125. end
  126. end
  127. function ProcessOneSampleInt(index)
  128. if (index < size(data,1))
  129. data(index, primAttInd) = data(index+1, primAttInd);
  130. else
  131. data(index, primAttInd) = data(index-1, primAttInd);
  132. end
  133. end
  134. end