OnePointDriftCorrection.m 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. % OnePointDriftCorrection.m
  2. %
  3. % This function applies drift correction for 360 degree videos. For correcting
  4. % the drift it has to know when the point was displayed and at which video
  5. % position (equirectangular) was displayed. It then calculates the mean head and
  6. % gaze directions during this time. It basically follows the inverse of the drift
  7. % correction during recording and applies the new displacements.
  8. %
  9. % NOTE: Be careful to provide a time interval in which gaze is not noisy
  10. %
  11. % input:
  12. % arffFile - path to ARFF file
  13. % targetStartTime - target diplay starting time in us
  14. % targetEndTime - target diplay starting time in us
  15. % targetPos - [x, y] equirectangular position
  16. % saveFile - file to save data
  17. function OnePointDriftCorrection(arffFile, targetStartTime, targetEndTime, targetPos, saveFile)
  18. c_timeName = 'time';
  19. c_xName = 'x';
  20. c_yName = 'y';
  21. c_confName = 'confidence';
  22. c_xHeadName = 'x_head';
  23. c_yHeadName = 'y_head';
  24. c_angleHeadName = 'angle_deg_head';
  25. c_confThreshold = 0.8;
  26. [data, metadata, attributes, relation, comments] = LoadArff(arffFile);
  27. timeInd = GetAttPositionArff(attributes, c_timeName);
  28. xInd = GetAttPositionArff(attributes, c_xName);
  29. yInd = GetAttPositionArff(attributes, c_yName);
  30. confInd = GetAttPositionArff(attributes, c_confName);
  31. xHeadInd = GetAttPositionArff(attributes, c_xHeadName);
  32. yHeadInd = GetAttPositionArff(attributes, c_yHeadName);
  33. % convert target position to cartesian
  34. [horTargetRads, verTargetRads] = EquirectToSpherical(targetPos(1), targetPos(2), metadata.width_px, metadata.height_px);
  35. targetVec = SphericalToCart(horTargetRads, verTargetRads);
  36. % find start/end ime position
  37. indStart = find(data(:,timeInd) > targetStartTime);
  38. assert(~isempty(indStart), 'Could not find provided start time');
  39. indStart = indStart(1);
  40. indEnd = find(data(:,timeInd) > targetEndTime);
  41. assert(~isempty(indEnd), 'Could not find provided end time');
  42. indEnd = indEnd(1);
  43. % find mean head and gaze vector
  44. gazeVecMean = zeros(3,1);
  45. for ind=indStart:indEnd
  46. if (data(ind, confInd) < c_confThreshold)
  47. continue;
  48. end
  49. % convert gaze to reference vector
  50. [horGazeRads, verGazeRads] = EquirectToSpherical(data(ind, xInd), data(ind, yInd), metadata.width_px, metadata.height_px);
  51. gazeVec = SphericalToCart(horGazeRads, verGazeRads);
  52. gazeVecMean = gazeVecMean + gazeVec;
  53. end
  54. gazeVecMean = gazeVecMean / norm(gazeVecMean);
  55. [horRads, verRads] = CartToSpherical(gazeVecMean);
  56. [xMean, yMean] = SphericalToEquirect(horRads, verRads, metadata.width_px, metadata.height_px);
  57. dispX = targetPos(1) - xMean;
  58. dispY = targetPos(2) - yMean;
  59. dispXInd = GetMetaExtraPosArff(metadata, 'displacement_x');
  60. newDispX = str2num(metadata.extra{dispXInd,2}) + dispX;
  61. metadata.extra{dispXInd,2} = num2str(newDispX, '%.2f');
  62. dispYInd = GetMetaExtraPosArff(metadata, 'displacement_y');
  63. newDispY = str2num(metadata.extra{dispYInd,2}) + dispY;
  64. metadata.extra{dispYInd,2} = num2str(newDispY, '%.2f');
  65. [data(:,xInd), data(:,yInd)] = UndoLimits(data(:,xInd), data(:,yInd), data(:,confInd));
  66. [data(:,xHeadInd), data(:,yHeadInd)] = UndoLimits(data(:,xHeadInd), data(:,yHeadInd), data(:,confInd));
  67. for ind=1:size(data,1)
  68. % Check if the limits were wrongly undone
  69. angle = GetAngle(data(ind,xInd), data(ind,xHeadInd));
  70. if (angle > 360/3)
  71. [data(ind,xInd), data(ind,yInd)] = BringWithinLimits(data(ind,xInd), data(ind,yInd));
  72. end
  73. % Check if difference comes from errors during recordings
  74. angle = GetAngle(data(ind,xInd), data(ind,xHeadInd));
  75. if (angle > 360/3 && angle < 2*360/3)
  76. data(ind,xInd) = data(ind,xInd) - metadata.width_px/2;
  77. [data(ind,xInd), data(ind,yInd)] = BringWithinLimits(data(ind,xInd), data(ind,yInd));
  78. end
  79. data(ind,xInd) = data(ind,xInd) + dispX;
  80. data(ind,yInd) = data(ind,yInd) + dispY;
  81. [data(ind,xInd), data(ind,yInd)] = BringWithinLimits(data(ind,xInd), data(ind,yInd));
  82. data(ind,xHeadInd) = data(ind,xHeadInd) + dispX;
  83. data(ind,yHeadInd) = data(ind,yHeadInd) + dispY;
  84. [data(ind,xHeadInd), data(ind,yHeadInd)] = BringWithinLimits(data(ind,xHeadInd), data(ind,yHeadInd));
  85. if(data(ind, confInd) < 0.3)
  86. data(ind,xInd) = 0;
  87. data(ind,yInd) = 0;
  88. end
  89. end
  90. SaveArff(saveFile, data, metadata, attributes, relation, comments);
  91. % Returns angle between two x coordinates
  92. function angle = GetAngle(x1, x2)
  93. horRads1 = EquirectToSpherical(x1, 0, metadata.width_px, 1);
  94. horRads2 = EquirectToSpherical(x2, 0, metadata.width_px, 1);
  95. angle = abs(horRads1 - horRads2) * 180 / pi;
  96. end
  97. function [x, y] = BringWithinLimits(x, y)
  98. if (y < 0)
  99. y = -y;
  100. x = x + metadata.width_px / 2;
  101. end
  102. if (y > metadata.height_px)
  103. y = 2 * metadata.height_px - y - 1;
  104. x = x + metadata.width_px / 2;
  105. end
  106. if (x < 0)
  107. x = ceil(abs(x)/metadata.width_px)*metadata.width_px + x - 1;
  108. %x = metadata.width_px + x - 1;
  109. elseif (x > metadata.width_px)
  110. x = x - floor(x/metadata.width_px)*metadata.width_px;
  111. %x = x - metadata.width_px;
  112. end
  113. end
  114. % x,y are list vectors
  115. function [xConv, yConv] = UndoLimits(x, y, confidence)
  116. xConv = zeros(size(x));
  117. yConv = zeros(size(y));
  118. countHor = 0;
  119. xConv(1) = x(1);
  120. yConv(1) = y(1);
  121. % remove horizontal transitions
  122. for i=2:size(x,1)
  123. totConf = confidence(i) + confidence(i-1);
  124. if (totConf > 1.7)
  125. if (x(i) - x(i-1) > metadata.width_px - 100)
  126. countHor = countHor - 1;
  127. elseif (x(i) - x(i-1) < -(metadata.width_px - 100))
  128. countHor = countHor + 1;
  129. end
  130. end
  131. if (countHor > 0)
  132. xConv(i) = x(i) + metadata.width_px;
  133. elseif (countHor < 0)
  134. xConv(i) = x(i) - metadata.width_px;
  135. else
  136. xConv(i) = x(i);
  137. end
  138. yConv(i) = y(i);
  139. end
  140. % remove vertical transitions
  141. countVert = 0;
  142. c_perc = 0.1;
  143. for i=2:size(xConv,1)
  144. diff = abs(x(i) - x(i-1));
  145. yCloseToLim = y(i) > metadata.height_px - 100 | y(i) < 100;
  146. if (diff > (1-c_perc)*metadata.width_px/2 && diff < (1+c_perc)*metadata.width_px/2 && yCloseToLim)
  147. countVert = countVert + 1;
  148. end
  149. if (mod(countVert,2) == 1)
  150. xConv(i) = xConv(i) + metadata.width_px/2;
  151. if (yConv(i) >= metadata.height_px/2)
  152. yConv(i) = 2*metadata.height_px - yConv(i) - 1;
  153. else
  154. yConv(i) = -yConv(i);
  155. end
  156. end
  157. end
  158. end
  159. end