aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/argaze/utils/tobii_segment_display.py84
1 files changed, 65 insertions, 19 deletions
diff --git a/src/argaze/utils/tobii_segment_display.py b/src/argaze/utils/tobii_segment_display.py
index 86d0057..a106e7b 100644
--- a/src/argaze/utils/tobii_segment_display.py
+++ b/src/argaze/utils/tobii_segment_display.py
@@ -12,14 +12,13 @@ import cv2 as cv
def main():
"""
- Replay Tobii segment video
+ Display Tobii segment video and data
"""
# manage arguments
parser = argparse.ArgumentParser(description=main.__doc__.split('-')[0])
parser.add_argument('-s', '--segment_path', metavar='SEGMENT_PATH', type=str, default=None, help='segment path')
parser.add_argument('-r', '--time_range', metavar=('START_TIME', 'END_TIME'), nargs=2, type=float, default=(0., None), help='start and end time (in second)')
- parser.add_argument('-w', '--window', metavar='DISPLAY', type=bool, default=True, help='enable window display', action=argparse.BooleanOptionalAction)
args = parser.parse_args()
if args.segment_path != None:
@@ -41,12 +40,22 @@ def main():
# Access to timestamped gaze position data buffer
tobii_ts_gaze_positions = tobii_segment_data['GazePosition']
- # Access to timestamped pupil diameter data buffer
- tobii_ts_pupil_diameter = tobii_segment_data['PupilDiameter']
+ # Access to timestamped gaze position 3d data buffer
+ tobii_ts_gaze_positions_3d = tobii_segment_data['GazePosition3D']
+
+ # Access to timestamped head rotations data buffer
+ tobii_ts_head_rotations = tobii_segment_data['Gyroscope']
# Access to timestamped events data buffer
tobii_ts_events = tobii_segment_data['Event']
+ # !!! the parameters below are specific to the TobiiGlassesPro2 !!!
+ # Reference : https://www.biorxiv.org/content/10.1101/299925v1
+ tobii_accuracy = 1.42 # degree
+ tobii_precision = 0.34 # degree
+ tobii_camera_hfov = 82 # degree
+ tobii_visual_hfov = 160 # degree
+
# Video and data replay loop
try:
@@ -58,40 +67,77 @@ def main():
video_ts_ms = video_ts / 1e3
- # Write segment timing
- cv.putText(video_frame.matrix, f'Segment time: {int(video_ts_ms)} ms', (20, 40), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv.LINE_AA)
+ try:
+
+ # Get nearest head rotation before video timestamp and remove all head rotations before
+ _, nearest_head_rotation = tobii_ts_head_rotations.pop_first_until(video_ts)
+
+ # Calculate head movement considering only head yaw and pitch
+ head_movement = numpy.array(nearest_head_rotation.value)
+ head_movement_px = head_movement.astype(int)
+ head_movement_norm = numpy.linalg.norm(head_movement[0:2])
+
+ # Draw movement vector
+ cv.line(video_frame.matrix, (int(video_frame.width/2), int(video_frame.height/2)), (int(video_frame.width/2) + head_movement_px[1], int(video_frame.height/2) - head_movement_px[0]), (150, 150, 150), 3)
+
+ # Wait for head rotation
+ except ValueError:
+ pass
try:
- # Get closest gaze position before video timestamp and remove all gaze positions before
- closest_gaze_ts, closest_gaze_position = tobii_ts_gaze_positions.pop_first_until(video_ts)
+ # Get nearest gaze position before video timestamp and remove all gaze positions before
+ _, nearest_gaze_position = tobii_ts_gaze_positions.pop_first_until(video_ts)
+
+ # Ignore frame when gaze position is not valid
+ if nearest_gaze_position.validity == 0:
+
+ gaze_position_pixel = GazeFeatures.GazePosition( (int(nearest_gaze_position.value[0] * video_frame.width), int(nearest_gaze_position.value[1] * video_frame.height)) )
+
+ # Draw gaze position
+ cv.circle(video_frame.matrix, gaze_position_pixel, 2, (0, 255, 255), -1)
+
+ # Get nearest gaze position 3D before video timestamp and remove all gaze positions before
+ _, nearest_gaze_position_3d = tobii_ts_gaze_positions_3d.pop_first_until(video_ts)
- # Get closest pupil diameter before video timestamp and remove all pupil diameters before
- closest_pupil_ts, closest_pupil_diameter = tobii_ts_pupil_diameter.pop_first_until(video_ts)
+ # Ignore frame when gaze position 3D is not valid
+ if nearest_gaze_position_3d.validity == 0:
+
+ gaze_accuracy_mm = numpy.tan(numpy.deg2rad(tobii_accuracy)) * nearest_gaze_position_3d.value[2]
+ tobii_camera_hfov_mm = numpy.tan(numpy.deg2rad(tobii_camera_hfov / 2)) * nearest_gaze_position_3d.value[2]
- # Draw gaze position
- gaze_position = (int(closest_gaze_position.value[0] * video_frame.width), int(closest_gaze_position.value[1] * video_frame.height))
- pupil_diameter = int((10 - closest_pupil_diameter.value) / 2)
+ gaze_position_pixel.accuracy = round(video_frame.width * float(gaze_accuracy_mm) / float(tobii_camera_hfov_mm))
- cv.circle(video_frame.matrix, gaze_position, 10, (0, 255, 255), pupil_diameter)
+ # Draw gaze accuracy
+ cv.circle(video_frame.matrix, gaze_position_pixel, gaze_position_pixel.accuracy, (0, 255, 255), 1)
# Wait for gaze position
except ValueError:
- continue
+ pass
try:
- # Get closest event before video timestamp and remove all gaze positions before
- closest_event_ts, closest_event = tobii_ts_events.pop_first_until(video_ts)
+ # Get nearest event before video timestamp and remove all gaze positions before
+ nearest_event_ts, nearest_event = tobii_ts_events.pop_first_until(video_ts)
- print(closest_event_ts / 1e3, closest_event)
+ #print(nearest_event_ts / 1e3, nearest_event)
# Write events
- cv.putText(video_frame.matrix, str(closest_event), (20, 140), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv.LINE_AA)
+ cv.rectangle(video_frame.matrix, (0, 0), (550, 50), (63, 63, 63), -1)
+ cv.putText(video_frame.matrix, str(nearest_event), (20, 140), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv.LINE_AA)
+ # Wait for events
except ValueError:
pass
+ # Draw center
+ cv.line(video_frame.matrix, (int(video_frame.width/2) - 50, int(video_frame.height/2)), (int(video_frame.width/2) + 50, int(video_frame.height/2)), (255, 150, 150), 1)
+ cv.line(video_frame.matrix, (int(video_frame.width/2), int(video_frame.height/2) - 50), (int(video_frame.width/2), int(video_frame.height/2) + 50), (255, 150, 150), 1)
+
+ # Write segment timing
+ cv.rectangle(video_frame.matrix, (0, 0), (550, 50), (63, 63, 63), -1)
+ cv.putText(video_frame.matrix, f'Segment time: {int(video_ts_ms)} ms', (20, 40), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv.LINE_AA)
+
if args.window:
# Close window using 'Esc' key