diff options
-rw-r--r-- | docs/user_guide/eye_tracking_context/context_modules/pupil_labs_invisible.md | 32 | ||||
-rw-r--r-- | docs/user_guide/eye_tracking_context/context_modules/pupil_labs_neon.md (renamed from docs/user_guide/eye_tracking_context/context_modules/pupil_labs.md) | 10 | ||||
-rw-r--r-- | docs/user_guide/eye_tracking_context/context_modules/tobii_pro_glasses_3.md | 32 | ||||
-rw-r--r-- | mkdocs.yml | 4 | ||||
-rw-r--r-- | src/argaze/utils/contexts/PupilLabsNeon.py | 140 | ||||
-rw-r--r-- | src/argaze/utils/demo/aruco_markers_pipeline.json | 8 | ||||
-rw-r--r-- | src/argaze/utils/demo/gaze_analysis_pipeline.json | 4 | ||||
-rw-r--r-- | src/argaze/utils/demo/pupillabs_neon_live_stream_context.json | 6 |
8 files changed, 228 insertions, 8 deletions
diff --git a/docs/user_guide/eye_tracking_context/context_modules/pupil_labs_invisible.md b/docs/user_guide/eye_tracking_context/context_modules/pupil_labs_invisible.md new file mode 100644 index 0000000..1f4a94f --- /dev/null +++ b/docs/user_guide/eye_tracking_context/context_modules/pupil_labs_invisible.md @@ -0,0 +1,32 @@ +Pupil Labs Invisible +========== + +ArGaze provides a ready-made context to work with Pupil Labs Invisible device. + +To select a desired context, the JSON samples have to be edited and saved inside an [ArContext configuration](../configuration_and_execution.md) file. +Notice that the *pipeline* entry is mandatory. + +```json +{ + JSON sample + "pipeline": ... +} +``` + +Read more about [ArContext base class in code reference](../../../argaze.md/#argaze.ArFeatures.ArContext). + +## Live Stream + +::: argaze.utils.contexts.PupilLabsInvisible.LiveStream + +### JSON sample + +```json +{ + "argaze.utils.contexts.PupilLabsInvisible.LiveStream": { + "name": "Pupil Labs Invisible live stream", + "project": "my_experiment", + "pipeline": ... + } +} +``` diff --git a/docs/user_guide/eye_tracking_context/context_modules/pupil_labs.md b/docs/user_guide/eye_tracking_context/context_modules/pupil_labs_neon.md index d2ec336..535f5d5 100644 --- a/docs/user_guide/eye_tracking_context/context_modules/pupil_labs.md +++ b/docs/user_guide/eye_tracking_context/context_modules/pupil_labs_neon.md @@ -1,7 +1,7 @@ -Pupil Labs +Pupil Labs Neon ========== -ArGaze provides a ready-made context to work with Pupil Labs devices. +ArGaze provides a ready-made context to work with Pupil Labs Neon device. To select a desired context, the JSON samples have to be edited and saved inside an [ArContext configuration](../configuration_and_execution.md) file. Notice that the *pipeline* entry is mandatory. @@ -17,14 +17,14 @@ Read more about [ArContext base class in code reference](../../../argaze.md/#arg ## Live Stream -::: argaze.utils.contexts.PupilLabs.LiveStream +::: argaze.utils.contexts.PupilLabsNeon.LiveStream ### JSON sample ```json { - "argaze.utils.contexts.PupilLabs.LiveStream": { - "name": "Pupil Labs live stream", + "argaze.utils.contexts.PupilLabsNeon.LiveStream": { + "name": "Pupil Labs Neon live stream", "project": "my_experiment", "pipeline": ... } diff --git a/docs/user_guide/eye_tracking_context/context_modules/tobii_pro_glasses_3.md b/docs/user_guide/eye_tracking_context/context_modules/tobii_pro_glasses_3.md new file mode 100644 index 0000000..3d37fcc --- /dev/null +++ b/docs/user_guide/eye_tracking_context/context_modules/tobii_pro_glasses_3.md @@ -0,0 +1,32 @@ +Tobii Pro Glasses 3 +=================== + +ArGaze provides a ready-made context to work with Tobii Pro Glasses 3 devices. + +To select a desired context, the JSON samples have to be edited and saved inside an [ArContext configuration](../configuration_and_execution.md) file. +Notice that the *pipeline* entry is mandatory. + +```json +{ + JSON sample + "pipeline": ... +} +``` + +Read more about [ArContext base class in code reference](../../../argaze.md/#argaze.ArFeatures.ArContext). + +## Live Stream + +::: argaze.utils.contexts.TobiiProGlasses3.LiveStream + +### JSON sample + +```json +{ + "argaze.utils.contexts.TobiiProGlasses3.LiveStream": { + "name": "Tobii Pro Glasses 3 live stream", + "pipeline": ... + } +} +``` + @@ -9,7 +9,9 @@ nav: - user_guide/eye_tracking_context/configuration_and_execution.md - Context Modules: - user_guide/eye_tracking_context/context_modules/tobii_pro_glasses_2.md - - user_guide/eye_tracking_context/context_modules/pupil_labs.md + - user_guide/eye_tracking_context/context_modules/tobii_pro_glasses_3.md + - user_guide/eye_tracking_context/context_modules/pupil_labs_invisible.md + - user_guide/eye_tracking_context/context_modules/pupil_labs_neon.md - user_guide/eye_tracking_context/context_modules/opencv.md - user_guide/eye_tracking_context/context_modules/random.md - Advanced Topics: diff --git a/src/argaze/utils/contexts/PupilLabsNeon.py b/src/argaze/utils/contexts/PupilLabsNeon.py new file mode 100644 index 0000000..e7d1f47 --- /dev/null +++ b/src/argaze/utils/contexts/PupilLabsNeon.py @@ -0,0 +1,140 @@ +"""Handle network connection to Pupil Labs devices. Tested with Pupil Neon. + Based on Pupil Labs' Realtime Python API.""" + +""" +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +You should have received a copy of the GNU General Public License along with +this program. If not, see <https://www.gnu.org/licenses/>. +""" + +__author__ = "Damien Mouratille" +__credits__ = [] +__copyright__ = "Copyright 2024, Ecole Nationale de l'Aviation Civile (ENAC)" +__license__ = "GPLv3" + +import sys +import logging +import time + +import threading +from dataclasses import dataclass + +from argaze import ArFeatures, DataFeatures, GazeFeatures +from argaze.utils import UtilsFeatures + +import numpy +import cv2 + +from pupil_labs.realtime_api.simple import discover_one_device + + +class LiveStream(ArFeatures.DataCaptureContext): + + @DataFeatures.PipelineStepInit + def __init__(self, **kwargs): + + # Init DataCaptureContext class + super().__init__() + + def __enter__(self): + + logging.info('Pupil-Labs Neon connexion starts...') + + # Init timestamp + self.__start_time = time.time() + + # Look for devices. Returns as soon as it has found the first device. + self.__device = discover_one_device(max_search_duration_seconds=10) + + if self.__device is None: + logging.info('No device found. Exit!') + raise SystemExit(-1) + else: + logging.info('Device found. Stream loading.') + + # Open gaze stream + self.__gaze_thread = threading.Thread(target=self.__stream_gaze) + + logging.debug('> starting gaze thread...') + + self.__gaze_thread.start() + + # Open video stream + self.__video_thread = threading.Thread(target=self.__stream_video) + + logging.debug('> starting video thread...') + + self.__video_thread.start() + + return self + + def __stream_gaze(self): + """Stream gaze.""" + + logging.debug('Stream gaze from Pupil Neon') + + while self.is_running(): + + try: + while True: + gaze = self.__device.receive_gaze_datum() + + gaze_timestamp = int((gaze.timestamp_unix_seconds - self.__start_time) * 1e3) + + logging.debug('Gaze received at %i timestamp', gaze_timestamp) + + # When gaze position is valid + if gaze.worn is True: + + self._process_gaze_position( + timestamp=gaze_timestamp, + x=int(gaze.x), + y=int(gaze.y)) + else: + # Process empty gaze position + logging.debug('Not worn at %i timestamp', gaze_timestamp) + + self._process_gaze_position(timestamp=gaze_timestamp) + + except KeyboardInterrupt: + pass + + def __stream_video(self): + """Stream video.""" + + logging.debug('Stream video from Pupil Neon') + + while self.is_running(): + + try: + while True: + scene_frame, frame_datetime = self.__device.receive_scene_video_frame() + + scene_timestamp = int((frame_datetime - self.__start_time) * 1e3) + + logging.debug('Video received at %i timestamp', scene_timestamp) + + self._process_camera_image( + timestamp=scene_timestamp, + image=scene_frame) + + except KeyboardInterrupt: + pass + + @DataFeatures.PipelineStepExit + def __exit__(self, exception_type, exception_value, exception_traceback): + + logging.debug('Pupil-Labs context stops...') + + # Close data stream + self.stop() + + # Stop streaming + threading.Thread.join(self.__gaze_thread) + threading.Thread.join(self.__video_thread) diff --git a/src/argaze/utils/demo/aruco_markers_pipeline.json b/src/argaze/utils/demo/aruco_markers_pipeline.json index 8221cec..e8d85eb 100644 --- a/src/argaze/utils/demo/aruco_markers_pipeline.json +++ b/src/argaze/utils/demo/aruco_markers_pipeline.json @@ -56,7 +56,11 @@ }, "frames": { "GrayRectangle": { +<<<<<<< HEAD "size": [1088, 1080], +======= + "size": [1600, 1200], +>>>>>>> dev/G3andNeon "background": "frame_background.jpg", "gaze_movement_identifier": { "argaze.GazeAnalysis.DispersionThresholdIdentification.GazeMovementIdentifier": { @@ -71,7 +75,11 @@ "argaze.GazeAnalysis.Basic.ScanPathAnalyzer": {}, "argaze.GazeAnalysis.KCoefficient.ScanPathAnalyzer": {}, "argaze.GazeAnalysis.NearestNeighborIndex.ScanPathAnalyzer": { +<<<<<<< HEAD "size": [1088, 1080] +======= + "size": [1600, 1200] +>>>>>>> dev/G3andNeon }, "argaze.GazeAnalysis.ExploreExploitRatio.ScanPathAnalyzer": { "short_fixation_duration_threshold": 0 diff --git a/src/argaze/utils/demo/gaze_analysis_pipeline.json b/src/argaze/utils/demo/gaze_analysis_pipeline.json index 6e23321..e0f99a8 100644 --- a/src/argaze/utils/demo/gaze_analysis_pipeline.json +++ b/src/argaze/utils/demo/gaze_analysis_pipeline.json @@ -126,8 +126,8 @@ }, "recorders.FrameImageRecorder": { "path": "_export/records/video.mp4", - "width": 1920, - "height": 1080, + "width": 1600, + "height": 1200, "fps": 15 } } diff --git a/src/argaze/utils/demo/pupillabs_neon_live_stream_context.json b/src/argaze/utils/demo/pupillabs_neon_live_stream_context.json new file mode 100644 index 0000000..a87c30e --- /dev/null +++ b/src/argaze/utils/demo/pupillabs_neon_live_stream_context.json @@ -0,0 +1,6 @@ +{ + "argaze.utils.contexts.PupilLabsNeon.LiveStream" : { + "name": "PupilLabs Neon", + "pipeline": "aruco_markers_pipeline.json" + } +}
\ No newline at end of file |