aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/argaze/TobiiGlassesPro2/TobiiEntities.py281
-rw-r--r--src/argaze/TobiiGlassesPro2/__init__.py2
-rw-r--r--src/argaze/utils/README.md18
-rw-r--r--src/argaze/utils/explore_tobii_sdcard.py60
4 files changed, 360 insertions, 1 deletions
diff --git a/src/argaze/TobiiGlassesPro2/TobiiEntities.py b/src/argaze/TobiiGlassesPro2/TobiiEntities.py
new file mode 100644
index 0000000..e243a5a
--- /dev/null
+++ b/src/argaze/TobiiGlassesPro2/TobiiEntities.py
@@ -0,0 +1,281 @@
+#!/usr/bin/env python
+
+import datetime
+import json
+import gzip
+import os
+import cv2 as cv
+
+TOBII_DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S+%f'
+
+TOBII_PROJECTS_DIRNAME = "projects"
+TOBII_PROJECT_FILENAME = "project.json"
+
+TOBII_PARTICIPANTS_DIRNAME = "participants"
+TOBII_PARTICIPANT_FILENAME = "participant.json"
+
+TOBII_RECORDINGS_DIRNAME = "recordings"
+TOBII_RECORD_FILENAME = "recording.json"
+
+TOBII_SEGMENTS_DIRNAME = "segments"
+TOBII_SEGMENT_FILENAME = "segment.json"
+TOBII_SEGMENT_VIDEO_FILENAME = "fullstream.mp4"
+TOBII_SEGMENT_LIVEDATA_FILENAME = "livedata.json.gz"
+
+class TobiiSegmentVideo:
+ """Handle Tobii Glasses Pro 2 segment video file."""
+
+ def __init__(self, segment_video_path):
+ """Load segment video from segment directory."""
+
+ self.__segment_video_path = segment_video_path
+
+ cap = cv.VideoCapture(self.__segment_video_path)
+
+ self.__width = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
+ self.__height = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
+ self.__fps = int(cap.get(cv.CAP_PROP_FPS))
+
+ def get_path(self):
+ return self.__segment_video_path
+
+ def get_height(self):
+ return self.__height
+
+ def get_width(self):
+ return self.__width
+
+ def get_fps(self):
+ return self.__fps
+
+class TobiiSegment:
+ """Handle Tobii Glasses Pro 2 segment info, data and video."""
+
+ def __init__(self, segment_path):
+ """Load segment info from segment directory."""
+
+ self.__segment_id = os.path.basename(segment_path)
+ self.__segment_path = segment_path
+
+ with open(os.path.join(self.__segment_path, TOBII_SEGMENT_FILENAME)) as f:
+ try:
+ item = json.load(f)
+ except:
+ raise RuntimeError(f'JSON fails to load {self.__segment_path}/{TOBII_SEGMENT_FILENAME}')
+
+ self.__length_us = int(item["seg_length_us"])
+ self.__calibrated = bool(item["seg_calibrated"])
+ self.__time_start = datetime.datetime.strptime(item["seg_t_start"], TOBII_DATETIME_FORMAT)
+ self.__time_stop = datetime.datetime.strptime(item["seg_t_stop"], TOBII_DATETIME_FORMAT)
+
+ def get_path(self):
+ return self.__segment_path
+
+ def get_id(self):
+ return self.__segment_id
+
+ def get_length_us(self):
+ return self.__length_us
+
+ def get_live_data_filepath(self):
+ return os.path.join(self.__segment_path, self.__segment_id)
+
+ def get_start_date_time(self):
+ return self.__time_start
+
+ def get_stop_date_time(self):
+ return self.__time_stop
+
+ def is_calibrated(self):
+ return self.__calibrated
+
+ def get_video(self):
+ return TobiiSegmentVideo(os.path.join(self.__segment_path, TOBII_SEGMENT_VIDEO_FILENAME))
+
+class TobiiRecording:
+ """Handle Tobii Glasses Pro 2 recording info and segments."""
+
+ def __init__(self, recording_path):
+ """Load recording info from recording directory."""
+
+ self.__recording_id = os.path.basename(recording_path)
+ self.__recording_path = recording_path
+ self.__project_path = os.path.dirname(os.path.dirname(os.path.abspath(self.__recording_path)))
+
+ with open(os.path.join(self.__recording_path, TOBII_RECORD_FILENAME)) as f:
+ try:
+ item = json.load(f)
+ except:
+ raise RuntimeError(f'JSON fails to load {self.__recording_path}/{TOBII_RECORD_FILENAME}')
+
+ self.__recording_created = datetime.datetime.strptime(item["rec_created"], TOBII_DATETIME_FORMAT)
+ self.__recording_name = item["rec_info"]["Name"]
+ self.__recording_length = int(item["rec_length"])
+ self.__recording_segments = int(item["rec_segments"])
+ self.__recording_et_samples = int(item["rec_et_samples"])
+ self.__recording_et_valid_samples = int(item["rec_et_valid_samples"])
+ self.__project = TobiiProject(self.__project_path)
+ self.__participant = TobiiParticipant(self.__recording_path)
+
+ def get_attributes(self):
+ """Get recording attributes dictionnary."""
+
+ attr = []
+ names = []
+ names.append("Recording name"); attr.append(self.__recording_name)
+ names.append("Project name"); attr.append(self.__project.getName())
+ names.append("Creation Date"); attr.append(str(self.__recording_created))
+ names.append("Duration (s)"); attr.append(str(self.__recording_length))
+ names.append("Participant name"); attr.append(self.__participant.getName())
+ names.append("Segments"); attr.append(str(self.__recording_segments))
+ names.append("Et samples"); attr.append(str(self.__recording_et_samples))
+ names.append("Et valid samples"); attr.append(str(self.__recording_et_valid_samples))
+ return (names, attr)
+
+ def get_path(self):
+ return self.__recording_path
+
+ def get_creation_date(self):
+ return self.__recording_created
+
+ def get_et_samples(self):
+ return self.__recording_et_samples
+
+ def get_et_valid_samples(self):
+ return self.__recording_et_valid_samples
+
+ def get_id(self):
+ return self.__recording_id
+
+ def get_length(self):
+ return self.__recording_length
+
+ def get_name(self):
+ return self.__recording_name
+
+ def get_participant(self):
+ return self.__participant
+
+ def get_recording_directory(self):
+ return self.__recording_dir
+
+ def get_segment(self, segment_id):
+ if segment_id > 0:
+ return self.__segments[segment_id-1]
+ raise ValueError('Cannot get segment less or equal to zero')
+
+ def get_all_segments(self):
+
+ all_segments = []
+ segments_path = os.path.join(self.__recording_path, TOBII_SEGMENTS_DIRNAME)
+ for item in os.listdir (segments_path):
+ segment_path = os.path.join(segments_path, item)
+ if os.path.isdir(segment_path):
+ all_segments.append(TobiiSegment(segment_path))
+
+ return all_segments
+
+class TobiiParticipant:
+ """Handle Tobii Glasses Pro 2 participant data."""
+
+ def __init__(self, participant_path):
+ """Load participant data from path"""
+
+ self.__participant_id = os.path.basename(participant_path)
+ self.__participant_path = participant_path
+
+ with open(os.path.join(self.__participant_path, TOBII_PARTICIPANT_FILENAME)) as f:
+ try:
+ item = json.load(f)
+ except:
+ raise RuntimeError(f'JSON fails to load {source_dir}/{TOBII_PARTICIPANT_FILENAME}')
+
+ self.__participant_name = item["pa_info"]["Name"]
+
+ def get_path(self):
+ return self.__participant_path
+
+ def get_id(self):
+ return self.__participant_id
+
+ def get_name(self):
+ return self.__participant_name
+
+class TobiiProject:
+ """Handle Tobii Glasses Pro 2 project data."""
+
+ def __init__(self, project_path):
+ """Load project data from projects directory and project id."""
+
+ self.__project_id = os.path.basename(project_path)
+ self.__project_path = project_path
+
+ with open(os.path.join(self.__project_path, TOBII_PROJECT_FILENAME)) as f:
+ try:
+ item = json.load(f)
+ except:
+ raise RuntimeError(f'JSON fails to load {self.__project_path}/{TOBII_PROJECT_FILENAME}')
+
+ self.__project_created = datetime.datetime.strptime(item["pr_created"], TOBII_DATETIME_FORMAT)
+
+ try:
+ self.__project_name = item["pr_info"]["Name"]
+ except:
+ self.__project_name = None
+
+ def get_path(self):
+ return self.__project_path
+
+ def get_creation_date(self):
+ return self.__project_created
+
+ def get_id(self):
+ return self.__project_id
+
+ def get_name(self):
+ return self.__project_name
+
+ def get_all_participants(self):
+
+ all_participants = []
+ participants_path = os.path.join(self.__project_path, TOBII_PARTICIPANTS_DIRNAME)
+ for item in os.listdir(participants_path):
+ participant_path = os.path.join(participants_path, item)
+ if os.path.isdir(participant_path):
+ all_participants.append(TobiiParticipant(participant_path))
+
+ return all_participants
+
+ def get_all_recordings(self):
+
+ all_recordings = []
+ recordings_path = os.path.join(self.__project_path, TOBII_RECORDINGS_DIRNAME)
+ for item in os.listdir(recordings_path):
+ recording_path = os.path.join(recordings_path, item)
+ if os.path.isdir(recording_path):
+ all_recordings.append(TobiiRecording(recording_path))
+
+ return all_recordings
+
+class TobiiDrive:
+ """Handle Tobii Glasses Pro 2 drive data."""
+
+ def __init__(self, drive_path):
+ """Load drive data from drive directory path."""
+
+ self.__drive_path = drive_path
+
+ def get_path(self):
+ return self.__drive_path
+
+ def get_all_projects(self):
+
+ all_projects = []
+ projects_path = os.path.join(self.__drive_path, TOBII_PROJECTS_DIRNAME)
+ for item in os.listdir(projects_path):
+ project_path = os.path.join(projects_path, item)
+ if os.path.isdir(project_path):
+ all_projects.append(TobiiProject(project_path))
+
+ return all_projects
+
diff --git a/src/argaze/TobiiGlassesPro2/__init__.py b/src/argaze/TobiiGlassesPro2/__init__.py
index 7d712c6..3c92c1f 100644
--- a/src/argaze/TobiiGlassesPro2/__init__.py
+++ b/src/argaze/TobiiGlassesPro2/__init__.py
@@ -2,4 +2,4 @@
.. include:: README.md
"""
__docformat__ = "restructuredtext"
-__all__ = ['TobiiController', 'TobiiData', 'TobiiVideo'] \ No newline at end of file
+__all__ = ['TobiiEntities','TobiiController', 'TobiiData', 'TobiiVideo'] \ No newline at end of file
diff --git a/src/argaze/utils/README.md b/src/argaze/utils/README.md
index c4ed010..c215916 100644
--- a/src/argaze/utils/README.md
+++ b/src/argaze/utils/README.md
@@ -36,6 +36,24 @@ python ./src/argaze/utils/display_tobii_gaze.py -t IP_ADDRESS
python ./src/argaze/utils/record_tobii_session.py -t IP_ADDRESS -p Test -i 1
```
+- Explore Tobii Glasses Pro 2 interface's SD Card (replace DRIVE_PATH, PROJECT_PATH, RECORDING_PATH, SEGMENT_PATH)
+
+```
+python ./src/argaze/utils/explore_tobii_sdcard.py -d DRIVE_PATH
+```
+
+```
+python ./src/argaze/utils/explore_tobii_sdcard.py -p PROJECT_PATH
+```
+
+```
+python ./src/argaze/utils/explore_tobii_sdcard.py -r RECORDING_PATH
+```
+
+```
+python ./src/argaze/utils/explore_tobii_sdcard.py -s SEGMENT_PATH
+```
+
- Track any 6cm ArUco marker into calibrated Tobii camera video stream (replace IP_ADDRESS). Load an roi scene (replace ROI_SCENE) .obj file, position it virtually like the detected ArUco markers and project the scene into camera frame. Then, detect if Tobii gaze point is inside any ROI. Export all collected datas into an export folder.
```
diff --git a/src/argaze/utils/explore_tobii_sdcard.py b/src/argaze/utils/explore_tobii_sdcard.py
new file mode 100644
index 0000000..7fd9032
--- /dev/null
+++ b/src/argaze/utils/explore_tobii_sdcard.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+import argparse
+
+from argaze.TobiiGlassesPro2 import TobiiEntities
+
+def main():
+ """
+ Explore Tobii Glasses Pro 2 interface's SD Card
+ """
+
+ # manage arguments
+ parser = argparse.ArgumentParser(description=main.__doc__.split('-')[0])
+ parser.add_argument('-d', '--drive_path', metavar='DRIVE_PATH', type=str, default=None, help='drive path')
+ parser.add_argument('-p', '--project_path', metavar='PROJECT_PATH', type=str, default=None, help='project path')
+ parser.add_argument('-r', '--recording_path', metavar='RECORDING_PATH', type=str, default=None, help='recording path')
+ parser.add_argument('-s', '--segment_path', metavar='SEGMENT_PATH', type=str, default=None, help='segment path')
+ args = parser.parse_args()
+
+ if args.drive_path != None:
+
+ # Load all projects from a tobii drive
+ tobii_drive = TobiiEntities.TobiiDrive(args.drive_path)
+
+ for project in tobii_drive.get_all_projects():
+ print(f'Project id: {project.get_id()}, name: {project.get_name()}')
+
+ elif args.project_path != None:
+
+ # Load one tobii project
+ tobii_project = TobiiEntities.TobiiProject(args.project_path)
+
+ for participant in tobii_project.get_all_participants():
+ print(f'Participant id: {participant.get_id()}, name: {participant.get_name()}')
+
+ for recording in tobii_project.get_all_recordings():
+ print(f'Recording id: {recording.get_id()}, name: {recording.get_name()}')
+
+ elif args.recording_path != None:
+
+ # Load a tobii segment
+ tobii_recording = TobiiEntities.TobiiRecording(args.recording_path)
+
+ for segment in tobii_recording.get_all_segments():
+ print(f'Segment id: {segment.get_id()}')
+
+ elif args.segment_path != None:
+
+ # Load a tobii segment
+ tobii_segment = TobiiEntities.TobiiSegment(args.segment_path)
+
+ tobii_segment_video = tobii_segment.get_video()
+ print(f'Video width: {tobii_segment_video.get_width()}, height: {tobii_segment_video.get_height()}, fps: {tobii_segment_video.get_fps()}')
+
+ #tobii_segment_data = tobii_segment.get_data()
+ #print(f'Data width: {tobii_segment_data.get_width()}, height: {tobii_segment_data.get_height()}, fps: {tobii_segment_data.get_fps()}')
+
+if __name__ == '__main__':
+
+ main() \ No newline at end of file