From 346c9d18d4d6e90bf871adbd56eecf3c613f1164 Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Wed, 11 May 2022 10:48:10 +0200 Subject: Improving export scripts arguments and console outputs. --- src/argaze/ArUcoMarkers/ArUcoTracker.py | 27 ++++--- .../export_tobii_segment_aruco_visual_scan.py | 89 ++++++++++++++++------ src/argaze/utils/export_tobii_segment_movements.py | 36 +++++---- 3 files changed, 105 insertions(+), 47 deletions(-) diff --git a/src/argaze/ArUcoMarkers/ArUcoTracker.py b/src/argaze/ArUcoMarkers/ArUcoTracker.py index 19f4643..363a5bb 100644 --- a/src/argaze/ArUcoMarkers/ArUcoTracker.py +++ b/src/argaze/ArUcoMarkers/ArUcoTracker.py @@ -58,7 +58,8 @@ class ArUcoTracker(): # setup ArUco detection parameters self.__detector_parameters = aruco.DetectorParameters_create() - self.__detector_parameters.cornerRefinementMethod = aruco.CORNER_REFINE_CONTOUR + self.__detector_parameters.cornerRefinementMethod = aruco.CORNER_REFINE_CONTOUR # to get a better pose estimation + self.__detector_parameters_loaded = {} # define tracked markers data self.__markers_corners = [] @@ -74,29 +75,29 @@ class ArUcoTracker(): self.__board_corners_ids = [] def load_configuration_file(self, configuration_filepath): - """Load aruco detection parameters from .json file. - """ - print(f'ArUcoTracker configuration for {self.__aruco_dict.get_markers_format()} markers detection:') + """Load aruco detection parameters from .json file.""" + with open(configuration_filepath) as configuration_file: - configuration = json.load(configuration_file) + self.__detector_parameters_loaded = json.load(configuration_file) - for key, value in configuration.items(): + for key, value in self.__detector_parameters_loaded.items(): try: setattr(self.__detector_parameters, key, value) - print(f'\t{key}: {getattr(self.__detector_parameters, key)}') except AttributeError as error: print(error) - def print_configuration(self): + def print_configuration(self, print_all=False): """Print aruco detection parameters.""" - print(f'ArUcoTracker configuration for {self.__aruco_dict.get_markers_format()} markers detection:') - for parameters in ArUcoTrackerParameters: - print(f'\t{parameters}: {getattr(self.__detector_parameters, parameters)}') + for parameter in ArUcoTrackerParameters: + if parameter in self.__detector_parameters_loaded.keys(): + print(f'\t*{parameter}: {getattr(self.__detector_parameters, parameter)}') + elif print_all: + print(f'\t{parameter}: {getattr(self.__detector_parameters, parameter)}') def track(self, frame, estimate_pose = True): """Track ArUco markers in frame.""" @@ -162,6 +163,10 @@ class ArUcoTracker(): cv.drawChessboardCorners(frame, ((self.__board.get_size()[0] - 1 ), (self.__board.get_size()[1] - 1)), self.__board_corners, True) + def get_markers_dictionay(self): + """Get tracked aruco markers dictionary.""" + return self.__aruco_dict + def get_markers_number(self): """Get tracked markers number.""" return len(self.__markers_corners) diff --git a/src/argaze/utils/export_tobii_segment_aruco_visual_scan.py b/src/argaze/utils/export_tobii_segment_aruco_visual_scan.py index 52103fe..7a270f3 100644 --- a/src/argaze/utils/export_tobii_segment_aruco_visual_scan.py +++ b/src/argaze/utils/export_tobii_segment_aruco_visual_scan.py @@ -25,16 +25,19 @@ def main(): 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('-c', '--camera_calibration', metavar='CAM_CALIB', type=str, default='tobii_camera.json', help='json camera calibration filepath') - parser.add_argument('-a', '--aoi_scene', metavar='AOI_SCENE', type=str, default='aoi3D_scene.obj', help='obj aoi scene filepath') + parser.add_argument('-c', '--camera_calibration', metavar='CAM_CALIB', type=str, default=None, help='json camera calibration filepath') + parser.add_argument('-p', '--aruco_tracker_configuration', metavar='TRACK_CONFIG', type=str, default=None, help='json aruco tracker configuration filepath') + parser.add_argument('-a', '--aoi_scene', metavar='AOI_SCENE', type=str, default=None, help='obj aoi scene filepath') parser.add_argument('-d', '--dictionary', metavar='DICT', type=str, default='DICT_ARUCO_ORIGINAL', help='aruco marker dictionnary (DICT_4X4_50, DICT_4X4_100, DICT_4X4_250, DICT_4X4_1000, DICT_5X5_50, DICT_5X5_100, DICT_5X5_250, DICT_5X5_1000, DICT_6X6_50, DICT_6X6_100, DICT_6X6_250, DICT_6X6_1000, DICT_7X7_50, DICT_7X7_100, DICT_7X7_250, DICT_7X7_1000, DICT_ARUCO_ORIGINAL,DICT_APRILTAG_16h5, DICT_APRILTAG_25h9, DICT_APRILTAG_36h10, DICT_APRILTAG_36h11)') parser.add_argument('-m', '--marker_size', metavar='MARKER_SIZE', type=float, default=6, help='aruco marker size (cm)') parser.add_argument('-i', '--markers_id', metavar='MARKERS_ID', nargs='*', type=int, default=[], help='markers id to track') parser.add_argument('-o', '--output', metavar='OUT', type=str, default=None, help='destination folder path (segment folder by default)') + 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: + # Manage markers id to track empty_marker_set = len(args.markers_id) == 0 if empty_marker_set: print(f'Track any Aruco markers from the {args.dictionary} dictionary') @@ -42,6 +45,7 @@ def main(): print(f'Track Aruco markers {args.markers_id} from the {args.dictionary} dictionary') # Manage destination path + destination_path = '.' if args.output != None: if not os.path.exists(os.path.dirname(args.output)): @@ -49,26 +53,29 @@ def main(): os.makedirs(os.path.dirname(args.output)) print(f'{os.path.dirname(args.output)} folder created') - vs_data_filepath = f'{args.output}/visual_scan.csv' - vs_visu_filepath = f'{args.output}/visual_scan.jpg' - vs_video_filepath = f'{args.output}/visual_scan.mp4' + destination_path = args.output else: - vs_data_filepath = f'{args.segment_path}/visual_scan.csv' - vs_visu_filepath = f'{args.segment_path}/visual_scan.jpg' - vs_video_filepath = f'{args.segment_path}/visual_scan.mp4' + destination_path = args.segment_path + + vs_data_filepath = f'{destination_path}/visual_scan.csv' + vs_visu_filepath = f'{destination_path}/visual_scan.jpg' + vs_video_filepath = f'{destination_path}/visual_scan.mp4' # Load a tobii segment tobii_segment = TobiiEntities.TobiiSegment(args.segment_path, int(args.time_range[0] * 1000000), int(args.time_range[1] * 1000000) if args.time_range[1] != None else None) # Load a tobii segment video tobii_segment_video = tobii_segment.load_video() - print(f'Video duration: {tobii_segment_video.get_duration()/1000000}, width: {tobii_segment_video.get_width()}, height: {tobii_segment_video.get_height()}') + print(f'Video properties:\n\tduration: {tobii_segment_video.get_duration()/1000000} s\n\twidth: {tobii_segment_video.get_width()} px\n\theight: {tobii_segment_video.get_height()} px') # Load a tobii segment data tobii_segment_data = tobii_segment.load_data() - print(f'Data keys: {tobii_segment_data.keys()}') + + print(f'Data keys:') + for name in tobii_segment_data.keys(): + print(f'\t{name}') # Access to timestamped gaze positions data buffer tobii_ts_gaze_positions = tobii_segment_data.gidx_l_gp @@ -83,15 +90,42 @@ def main(): # Create aruco camera aruco_camera = ArUcoCamera.ArUcoCamera() - aruco_camera.load_calibration_file(args.camera_calibration) + + # Load calibration file + if args.camera_calibration != None: + + aruco_camera.load_calibration_file(args.camera_calibration) + + else: + + raise ValueError('.json camera calibration filepath required. Use -c option.') # Create aruco tracker aruco_tracker = ArUcoTracker.ArUcoTracker(args.dictionary, args.marker_size, aruco_camera) + # Load specific configuration file + if args.aruco_tracker_configuration != None: + + aruco_tracker.load_configuration_file(args.aruco_tracker_configuration) + + print(f'ArUcoTracker configuration for {aruco_tracker.get_markers_dictionay().get_markers_format()} markers detection:') + aruco_tracker.print_configuration() + # Create AOIs 3D scene aoi3D_scene = AOI3DScene.AOI3DScene() - aoi3D_scene.load(args.aoi_scene) - print(f'AOIs names: {aoi3D_scene.keys()}') + + # Load AOI 3D scene file + if args.aoi_scene != None: + + aoi3D_scene.load(args.aoi_scene) + + print(f'AOI names:') + for name in aoi3D_scene.keys(): + print(f'\t{name}') + + else: + + raise ValueError('.json AOI scene filepath required. Use -a option.') # Create timestamped buffer to store AOIs scene in time ts_aois_scenes = AOIFeatures.TimeStampedAOIScenes() @@ -105,6 +139,8 @@ def main(): visu_ratio = visu_height visu_frame = numpy.full((visu_height, visu_width, 3), 255, dtype=numpy.uint8) + cv.putText(visu_frame, f'Segment time range: {int(args.time_range[0] * 1000)} - {int(args.time_range[1] * 1000)} ms', (20, 40), cv.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 1, cv.LINE_AA) + # Project 3D scene on the reference frame # TODO : center projection on a reference AOI ref_aoi = 'Scene_Plan' @@ -130,6 +166,11 @@ def main(): # Iterate on video frames for video_ts, video_frame in tobii_segment_video.frames(): + video_ts_ms = video_ts / 1000 + + # 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 closest gaze position before video timestamp and remove all gaze positions before @@ -140,7 +181,7 @@ def main(): cv.circle(video_frame.matrix, video_gaze_pixel, 4, (0, 255, 255), -1) # Store gaze position at this time in millisecond - ts_gaze_positions[round(video_ts/1000)] = video_gaze_pixel + ts_gaze_positions[round(video_ts_ms)] = video_gaze_pixel # Wait for gaze position except ValueError: @@ -173,7 +214,7 @@ def main(): aoi2D_video_scene.draw(video_frame.matrix, video_gaze_pixel) # Store 2D scene at this time in millisecond - ts_aois_scenes[round(video_ts/1000)] = aoi2D_video_scene + ts_aois_scenes[round(video_ts_ms)] = aoi2D_video_scene # Draw gaze path look_at = aoi2D_video_scene[ref_aoi].look_at(video_gaze_pixel) @@ -181,21 +222,23 @@ def main(): visu_gaze_pixel = aoi2D_visu_scene[ref_aoi].looked_pixel(look_at) cv.circle(visu_frame, visu_gaze_pixel, 4, (0, 0, 255), -1) - # Close window using 'Esc' key - if cv.waitKey(1) == 27: - break + if args.window: + + # Close window using 'Esc' key + if cv.waitKey(1) == 27: + break - # Display video - cv.imshow(f'Segment {tobii_segment.get_id()} video', video_frame.matrix) + # Display video + cv.imshow(f'Segment {tobii_segment.get_id()} ArUco AOI', video_frame.matrix) - # Display visual scan frame - cv.imshow(f'Segment {tobii_segment.get_id()} visual scan', visu_frame) + # Display visual scan frame + cv.imshow(f'Segment {tobii_segment.get_id()} visual scan', visu_frame) # Write video output_video.write(video_frame.matrix) # Update Progress Bar - progress = video_ts - int(args.time_range[0] * 1000000) + progress = video_ts_ms - int(args.time_range[0] * 1000) MiscFeatures.printProgressBar(progress, tobii_segment_video.get_duration(), prefix = 'Progress:', suffix = 'Complete', length = 100) # Exit on 'ctrl+C' interruption diff --git a/src/argaze/utils/export_tobii_segment_movements.py b/src/argaze/utils/export_tobii_segment_movements.py index 8ae074a..0b0d867 100644 --- a/src/argaze/utils/export_tobii_segment_movements.py +++ b/src/argaze/utils/export_tobii_segment_movements.py @@ -21,11 +21,13 @@ def main(): parser.add_argument('-d', '--dispersion_threshold', metavar='DISPERSION_THRESHOLD', type=int, default=10, help='dispersion threshold in pixel') parser.add_argument('-t', '--duration_threshold', metavar='DURATION_THRESHOLD', type=int, default=100, help='duration threshold in millisecond') parser.add_argument('-o', '--output', metavar='OUT', type=str, default=None, help='destination folder path (segment folder by default)') + 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: # Manage destination path + destination_path = '.' if args.output != None: if not os.path.exists(os.path.dirname(args.output)): @@ -33,28 +35,30 @@ def main(): os.makedirs(os.path.dirname(args.output)) print(f'{os.path.dirname(args.output)} folder created') - gaze_video_filepath = f'{args.output}/movements.mp4' - fixations_filepath = f'{args.output}/movements_fixations.csv' - saccades_filepath = f'{args.output}/movements_saccades.csv' - movements_filepath = f'{args.output}/movements.csv' + destination_path = args.output else: - gaze_video_filepath = f'{args.segment_path}/movements.mp4' - fixations_filepath = f'{args.segment_path}/movements_fixations.csv' - saccades_filepath = f'{args.segment_path}/movements_saccades.csv' - movements_filepath = f'{args.segment_path}/movements.csv' + destination_path = args.segment_path + + gaze_video_filepath = f'{destination_path}/movements.mp4' + fixations_filepath = f'{destination_path}/movements_fixations.csv' + saccades_filepath = f'{destination_path}/movements_saccades.csv' + movements_filepath = f'{destination_path}/movements.csv' # Load a tobii segment tobii_segment = TobiiEntities.TobiiSegment(args.segment_path, int(args.time_range[0] * 1000000), int(args.time_range[1] * 1000000) if args.time_range[1] != None else None) # Load a tobii segment video tobii_segment_video = tobii_segment.load_video() - print(f'Video duration: {tobii_segment_video.get_duration()/1000000}, width: {tobii_segment_video.get_width()}, height: {tobii_segment_video.get_height()}') + print(f'Video properties:\n\tduration: {tobii_segment_video.get_duration()/1000000} s\n\twidth: {tobii_segment_video.get_width()} px\n\theight: {tobii_segment_video.get_height()} px') # Load a tobii segment data tobii_segment_data = tobii_segment.load_data() - print(f'Data keys: {tobii_segment_data.keys()}') + + print(f'Data keys:') + for name in tobii_segment_data.keys(): + print(f'\t{name}') # Access to timestamped gaze position data buffer tobii_ts_gaze_positions = tobii_segment_data.gidx_l_gp @@ -131,7 +135,6 @@ def main(): current_saccade_ts, current_saccade = saccades.pop_first() - # Iterate on video frames for video_ts, video_frame in tobii_segment_video.frames(): @@ -179,6 +182,15 @@ def main(): except ValueError: pass + if args.window: + + # Close window using 'Esc' key + if cv.waitKey(1) == 27: + break + + # Display video + cv.imshow(f'Segment {tobii_segment.get_id()} movements', video_frame.matrix) + # Write video output_video.write(video_frame.matrix) @@ -194,8 +206,6 @@ def main(): output_video.close() print(f'\nVideo with movements saved into {gaze_video_filepath}') - - if __name__ == '__main__': main() \ No newline at end of file -- cgit v1.1