diff options
-rw-r--r-- | src/argaze/utils/export_tobii_segment_movements.py | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/src/argaze/utils/export_tobii_segment_movements.py b/src/argaze/utils/export_tobii_segment_movements.py new file mode 100644 index 0000000..624d758 --- /dev/null +++ b/src/argaze/utils/export_tobii_segment_movements.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +import argparse +import bisect +import os + +from argaze import GazeFeatures +from argaze.TobiiGlassesPro2 import TobiiEntities +from argaze.utils import MiscFeatures + +def main(): + """ + Analyse Tobii segment fixations + """ + + # Manage arguments + parser = argparse.ArgumentParser(description=main.__doc__.split('-')[0]) + parser.add_argument('-s', '--segment_path', metavar='SEGMENT_PATH', type=str, default=None, help='path to a tobii segment folder') + 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('-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)') + args = parser.parse_args() + + if args.segment_path != None: + + # Manage destination path + if args.output != None: + + if not os.path.exists(os.path.dirname(args.output)): + + os.makedirs(os.path.dirname(args.output)) + print(f'{os.path.dirname(args.output)} folder created') + + fixations_filepath = f'{args.output}/fixations.csv' + saccades_filepath = f'{args.output}/saccades.csv' + + else: + + fixations_filepath = f'{args.segment_path}/fixations.csv' + saccades_filepath = f'{args.segment_path}/saccades.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()}') + + # Load a tobii segment data + tobii_segment_data = tobii_segment.load_data() + print(f'Data keys: {tobii_segment_data.keys()}') + + # Access to timestamped gaze position data buffer + tobii_ts_gaze_positions = tobii_segment_data.gidx_l_gp + print(f'{len(tobii_ts_gaze_positions)} gaze positions loaded') + + # Format tobii gaze data into generic gaze data and store them using millisecond unit timestamp + generic_ts_gaze_positions = GazeFeatures.TimeStampedGazePositions() + + for ts, tobii_data in tobii_ts_gaze_positions.items(): + generic_data = GazeFeatures.GazePosition(tobii_data.gp[0] * tobii_segment_video.get_width(), tobii_data.gp[1] * tobii_segment_video.get_height()) + generic_ts_gaze_positions[ts/1000] = generic_data + + print(f'Dispersion threshold: {args.dispersion_threshold}') + print(f'Duration threshold: {args.duration_threshold}') + + # Start movement identification + movement_identifier = GazeFeatures.DispersionBasedMovementIdentifier(generic_ts_gaze_positions, args.dispersion_threshold, args.duration_threshold) + fixations = GazeFeatures.TimeStampedFixations() + saccades = GazeFeatures.TimeStampedSaccades() + + # Initialise progress bar + MiscFeatures.printProgressBar(0, int(tobii_segment_video.get_duration()/1000), prefix = 'Progress:', suffix = 'Complete', length = 100) + + for ts, item in movement_identifier: + + if isinstance(item, GazeFeatures.Fixation): + fixations[ts] = item + + elif isinstance(item, GazeFeatures.Saccade): + saccades[ts] = item + + else: + continue + + # Update Progress Bar + progress = ts - int(args.time_range[0] * 1000) + MiscFeatures.printProgressBar(progress, int(tobii_segment_video.get_duration()/1000), prefix = 'Progress:', suffix = 'Complete', length = 100) + + print(f'\n{len(fixations)} fixations and {len(saccades)} saccades found') + + # Export fixations analysis + fixations.export_as_csv(fixations_filepath) + print(f'Fixations saved into {fixations_filepath}') + + # Export saccades analysis + saccades.export_as_csv(saccades_filepath) + print(f'Saccades saved into {saccades_filepath}') + +if __name__ == '__main__': + + main()
\ No newline at end of file |