DetectOKN360.m 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. % DetectOKN360.m
  2. %
  3. % This function detects optokinetic nystagmus for 360 degrees data. It uses a
  4. % two step process. In the first step it detects points were the direction
  5. % difference around a point is greater than 90 degrees. In the second step
  6. % it compares the FOV speed of the intervals. If it is above a threshold then
  7. % it is considered as OKN
  8. %
  9. % NOTE: OKN is calculated only in FOV representation. Also saccades have to be
  10. % annotated in advance.
  11. %
  12. % input:
  13. % arffFile - file with ARFF data
  14. % saccAttName - name of the saccade attribute
  15. % saccValue - integer value representing saccades
  16. %
  17. % output:
  18. % result - a logical vector of the same size as the data in the ARFF with true value
  19. % when OKN occurs
  20. function result = DetectOKN360(arffFile, saccAttName, saccValue)
  21. c_minIntAngle = 90; % minimum angle between saccade and subsequent interval
  22. c_maxIntSaccAngle = 70; % maximum angle between saccades
  23. c_minIntSpeed = 10; % degrees/sec
  24. c_saccAttVal = saccValue;
  25. c_OKNlabel = 1;
  26. [data, metadata, attributes] = LoadArff(arffFile);
  27. timeInd = GetAttPositionArff(attributes, 'time');
  28. [eyeFovVec, eyeHeadVec, headVec] = GetCartVectors(data, metadata, attributes);
  29. saccInts = GetIntervalsIndexArff(data, attributes, saccAttName, c_saccAttVal);
  30. result = zeros(size(data,1),1);
  31. attInd = GetAttPositionArff(attributes, saccAttName);
  32. %result(data(:,attInd) == c_saccAttVal) = c_OKNlabel;
  33. for ind=1:size(saccInts,1)-1
  34. saccDir1 = eyeFovVec(saccInts(ind,2),:) - eyeFovVec(saccInts(ind,1),:);
  35. saccDir2 = eyeFovVec(saccInts(ind+1,2),:) - eyeFovVec(saccInts(ind+1,1),:);
  36. % inter-saccadic direction
  37. intSaccDir = eyeFovVec(saccInts(ind+1,1)-1,:) - eyeFovVec(saccInts(ind,2)+1,:);
  38. if (sum(saccDir1) == 0 || sum(saccDir2) == 0 || sum(intSaccDir) == 0)
  39. continue;
  40. end
  41. saccDir1 = saccDir1 / norm(saccDir1);
  42. saccDir2 = saccDir2 / norm(saccDir2);
  43. intSaccDir = intSaccDir / norm(intSaccDir);
  44. relDirSacc = GetDispersion(saccDir1, saccDir2);
  45. relDirIntSacc1 = GetDispersion(saccDir1, intSaccDir);
  46. relDirIntSacc2 = GetDispersion(saccDir2, intSaccDir);
  47. dur = data(saccInts(ind+1,1)-1, timeInd) - data(saccInts(ind,2)+1, timeInd);
  48. dur = dur / 1000000;
  49. ampl = GetDispersion(eyeFovVec(saccInts(ind+1,1)-1,:), eyeFovVec(saccInts(ind,2)+1,:));
  50. speed = ampl / dur;
  51. if (relDirSacc < c_maxIntSaccAngle && ...
  52. relDirIntSacc1 > c_minIntAngle && ...
  53. relDirIntSacc2 > c_minIntAngle && ...
  54. speed > c_minIntSpeed)
  55. %if ((relDirIntSacc1 > c_minIntAngle || ...
  56. % relDirIntSacc2 > c_minIntAngle) && ...
  57. % speed > c_minIntSpeed)
  58. result(saccInts(ind,2)+1:saccInts(ind+1,1)-1) = c_OKNlabel;
  59. end
  60. end
  61. % Iterate again through saccade intervals and make sure that at least two
  62. % OKN intervals exist continuously.
  63. for ind=2:size(saccInts,1)-1
  64. if (result(saccInts(ind,2)+1) == c_OKNlabel)
  65. if (result(saccInts(ind,1) -1) ~= c_OKNlabel && ... % previous interval
  66. result(saccInts(ind+1,2)+1) ~= c_OKNlabel) % next interval
  67. result(saccInts(ind,2)+1:saccInts(ind+1,1)-1) = 0;
  68. end
  69. end
  70. % assign OKN label to saccades between OKN intervals
  71. if (result(saccInts(ind,1) -1) == c_OKNlabel && ...
  72. result(saccInts(ind,2)+1) == c_OKNlabel)
  73. result(saccInts(ind,1):saccInts(ind,2)) = c_OKNlabel;
  74. end
  75. end
  76. confInd = GetAttPositionArff(attributes, 'confidence');
  77. result(data(:,confInd) < 1) = 0;
  78. result = logical(result);
  79. end