aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThéo de la Hogue2024-04-23 14:29:40 +0200
committerThéo de la Hogue2024-04-23 14:29:40 +0200
commit354fb9b6e763f0834a917fc01b0f63eccd382c42 (patch)
tree36294901b67c11778ca216f162b0faf1096d4591
parenta836169186bcdb878cc3d1120e2d04366a1dcffe (diff)
downloadargaze-354fb9b6e763f0834a917fc01b0f63eccd382c42.zip
argaze-354fb9b6e763f0834a917fc01b0f63eccd382c42.tar.gz
argaze-354fb9b6e763f0834a917fc01b0f63eccd382c42.tar.bz2
argaze-354fb9b6e763f0834a917fc01b0f63eccd382c42.tar.xz
Making LiveStream as LiveProcessingContext.
-rw-r--r--src/argaze/utils/contexts/TobiiProGlasses2.py220
1 files changed, 101 insertions, 119 deletions
diff --git a/src/argaze/utils/contexts/TobiiProGlasses2.py b/src/argaze/utils/contexts/TobiiProGlasses2.py
index 4491c34..b7db70b 100644
--- a/src/argaze/utils/contexts/TobiiProGlasses2.py
+++ b/src/argaze/utils/contexts/TobiiProGlasses2.py
@@ -71,12 +71,6 @@ TOBII_SEGMENT_INFO_FILENAME = "segment.json"
TOBII_SEGMENT_VIDEO_FILENAME = "fullstream.mp4"
TOBII_SEGMENT_DATA_FILENAME = "livedata.json.gz"
-# Define default Tobii image_parameters values
-DEFAULT_TOBII_IMAGE_PARAMETERS = {
- "draw_something": False
-}
-
-
# Define extra classes to support Tobii data parsing
@dataclass
class DirSig():
@@ -335,12 +329,12 @@ class TobiiJsonDataParser():
return MarkerPosition(data['marker3d'], data['marker2d'])
-class LiveStream(ArFeatures.ArContext):
+class LiveStream(ArFeatures.LiveProcessingContext):
@DataFeatures.PipelineStepInit
def __init__(self, **kwargs):
- # Init ArContext class
+ # Init LiveProcessingContext class
super().__init__()
# Init private attributes
@@ -353,13 +347,15 @@ class LiveStream(ArFeatures.ArContext):
self.__participant_name = None
self.__participant_id = None
+ self.__calibration_status = None
+ self.__calibration_id = None
+
+ self.__recording_id = None
+
self.__configuration = {}
self.__parser = TobiiJsonDataParser()
- # Init protected attributes
- self._image_parameters = {**ArFeatures.DEFAULT_ARCONTEXT_IMAGE_PARAMETERS, **DEFAULT_TOBII_IMAGE_PARAMETERS}
-
@property
def address(self) -> str:
"""Network address where to find the device."""
@@ -603,23 +599,6 @@ class LiveStream(ArFeatures.ArContext):
# Stop video streaming
threading.Thread.join(self.__video_thread)
- @DataFeatures.PipelineStepImage
- def image(self, draw_something: bool = None, **kwargs: dict) -> numpy.array:
- """Get Tobii visualization.
-
- Parameters:
- draw_something: example
- kwargs: ArContext.image parameters
- """
-
- # Get context image
- image = super().image(**kwargs)
-
- if draw_something:
- cv2.putText(image, 'SOMETHING', (512, 512), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA)
-
- return image
-
def __make_socket(self):
"""Create a socket to enable network communication."""
@@ -892,135 +871,107 @@ class LiveStream(ArFeatures.ArContext):
return datetime.datetime.now().replace(microsecond=0).strftime(timeformat)
- # CALIBRATION
-
- def calibrate(self, project_name, participant_name):
+ def calibrate(self):
"""Handle whole Tobii glasses calibration process."""
- # Start calibration
- self.calibration_start(project_name, participant_name)
-
- # While calibrating...
- status = self.calibration_status()
-
- while status == 'calibrating':
- time.sleep(1)
- status = self.calibration_status()
-
- if status == 'uncalibrated' or status == 'stale' or status == 'failed':
- raise Exception(f'Calibration {status}')
-
- # CALIBRATION
-
- def calibration_start(self, project_name, participant_name):
- """Start calibration process for project and participant."""
-
- project_id = self.__get_project_id(project_name)
- participant_id = self.get_participant_id(participant_name)
-
- # Init calibration id
- self.__calibration_id = None
+ self.__calibration_status = None
# Calibration have to be done for a project and a participant
- if project_id is None or participant_id is None:
- raise Exception(f'Setup project and participant before')
+ if self.__project_id is None or self.__participant_id is None:
+ raise Exception(f'Set project and participant names')
+
+ # Request calibration id
data = {
- 'ca_project': project_id,
+ 'ca_project': self.__project_id,
'ca_type': 'default',
- 'ca_participant': participant_id,
+ 'ca_participant': self.__participant_id,
'ca_created': self.__get_current_datetime()
}
- # Request calibration
- json_data = self.__post_request('/api/calibrations', data)
- # noinspection PyAttributeOutsideInit
- self.__calibration_id = json_data['ca_id']
+ self.__calibration_id = self.__post_request('/api/calibrations', data)['ca_id']
# Start calibration
self.__post_request('/api/calibrations/' + self.__calibration_id + '/start')
- def calibration_status(self) -> str:
- """Ask for calibration status: calibrating, calibrated, stale, uncalibrated or failed."""
+ # Check calibration status
+ self.__calibration_status_thread = threading.Thread(target=self.__check_calibration_status)
+ self.__calibration_status_thread.start()
- if self.__calibration_id is not None:
+ def __check_calibration_status(self):
- status = self.__wait_for_status('/api/calibrations/' + self.__calibration_id + '/status', 'ca_state', ['calibrating', 'calibrated', 'stale', 'uncalibrated', 'failed'])
+ while True:
- # Forget calibration id
- if status != 'calibrating':
- # noinspection PyAttributeOutsideInit
- self.__calibration_id = None
+ # Wait one second
+ time.sleep(1)
- return status
+ # Check new calibration status
+ self.__calibration_status = self.__wait_for_status('/api/calibrations/' + self.__calibration_id + '/status', 'ca_state', ['calibrating', 'calibrated', 'stale', 'uncalibrated', 'failed'])
- else:
+ # Exit once it is not calibrating
+ if self.__calibration_status != 'calibrating':
- raise Exception(f'Start calibration before')
+ break
- # RECORDING FEATURES
+ # Forget calibration id
+ self.__calibration_id = None
- def __wait_for_recording_status(self, recording_id, status_array=['init', 'starting', 'recording', 'pausing', 'paused', 'stopping', 'stopped', 'done', 'stale', 'failed']):
+ def get_calibration_status(self) -> str:
- return self.__wait_for_status('/api/recordings/' + recording_id + '/status', 'rec_state', status_array)
+ return self.__calibration_status
- def create_recording(self, participant_name, recording_name='', recording_notes='') -> str:
- """Create a new recording.
+ # RECORDING FEATURES
- Returns:
- recording id
- """
+ def create_recording(self, name: str = '', notes: str = ''):
+ """Create a new recording on the Tobii interface's SD Card."""
- participant_id = self.get_participant_id(participant_name)
+ # Reset recording id
+ self.__recording_id = None
- if participant_id is None:
- raise NameError(f'{participant_name} participant doesn\'t exist')
+ # Recording have to be done for a participant
+ if self.__participant_id is None:
+
+ raise Exception(f'Set participant name')
+ # Request record id
data = {
- 'rec_participant': participant_id,
+ 'rec_participant': self.__participant_id,
'rec_info': {
- 'EagleId': str(uuid.uuid5(uuid.NAMESPACE_DNS, participant_name)),
- 'Name': recording_name,
- 'Notes': recording_notes
+ 'EagleId': str(uuid.uuid5(uuid.NAMESPACE_DNS, self.participant)),
+ 'Name': name,
+ 'Notes': notes
},
'rec_created': self.__get_current_datetime()
}
- json_data = self.__post_request('/api/recordings', data)
-
- return json_data['rec_id']
-
- def start_recording(self, recording_id) -> bool:
- """Start recording on the Tobii interface's SD Card."""
+ self.__recording_id = self.__post_request('/api/recordings', data)['rec_id']
- self.__post_request('/api/recordings/' + recording_id + '/start')
- return self.__wait_for_recording_status(recording_id, ['recording']) == 'recording'
-
- def stop_recording(self, recording_id) -> bool:
- """Stop recording on the Tobii interface's SD Card."""
+ def __wait_for_recording_status(self, recording_id, status_array=['init', 'starting', 'recording', 'pausing', 'paused', 'stopping', 'stopped', 'done', 'stale', 'failed']):
+
+ return self.__wait_for_status('/api/recordings/' + recording_id + '/status', 'rec_state', status_array)
- self.__post_request('/api/recordings/' + recording_id + '/stop')
- return self.__wait_for_recording_status(recording_id, ['done']) == "done"
+ def start_recording(self) -> bool:
+ """Start recording."""
- def pause_recording(self, recording_id) -> bool:
- """Pause recording on the Tobii interface's SD Card."""
+ self.__post_request('/api/recordings/' + self.__recording_id + '/start')
+ return self.__wait_for_recording_status(self.__recording_id, ['recording']) == 'recording'
- self.__post_request('/api/recordings/' + recording_id + '/pause')
- return self.__wait_for_recording_status(recording_id, ['paused']) == "paused"
+ def stop_recording(self) -> bool:
+ """Stop recording."""
- def __get_recording_status(self):
- return self.get_status()['sys_recording']
+ self.__post_request('/api/recordings/' + self.__recording_id + '/stop')
+ return self.__wait_for_recording_status(self.__recording_id, ['done']) == "done"
- def get_current_recording_id(self) -> str:
- """Get current recording id."""
+ def pause_recording(self) -> bool:
+ """Pause recording."""
- return self.__get_recording_status()['rec_id']
+ self.__post_request('/api/recordings/' + self.__recording_id + '/pause')
+ return self.__wait_for_recording_status(self.__recording_id, ['paused']) == "paused"
- @property
- def recording(self) -> bool:
+ def is_recording(self) -> bool:
"""Is it recording?"""
- rec_status = self.__get_recording_status()
+ rec_status = self.get_status()['sys_recording']
if rec_status != {}:
if rec_status['rec_state'] == "recording":
@@ -1035,15 +986,15 @@ class LiveStream(ArFeatures.ArContext):
# EVENTS AND EXPERIMENTAL VARIABLES
- def __post_recording_data(self, event_type: str, event_tag=''):
+ def __post_data(self, event_type: str, event_tag=''):
data = {'type': event_type, 'tag': event_tag}
self.__post_request('/api/events', data, wait_for_response=False)
def send_event(self, event_type: str, event_value=None):
- self.__post_recording_data('JsonEvent', "{'event_type': '%s','event_value': '%s'}" % (event_type, event_value))
+ self.__post_data('JsonEvent', "{'event_type': '%s','event_value': '%s'}" % (event_type, event_value))
def send_variable(self, variable_name: str, variable_value=None):
- self.__post_recording_data(str(variable_name), str(variable_value))
+ self.__post_data(str(variable_name), str(variable_value))
# MISC
@@ -1123,6 +1074,40 @@ class LiveStream(ArFeatures.ArContext):
data = {'sys_sc_fps': 50}
json_data = self.__post_request('/api/system/conf/', data)
+ @DataFeatures.PipelineStepImage
+ def image(self, **kwargs):
+ """
+ Get pipeline image with live processing information.
+ """
+ logging.debug('LiveProcessingContext.image %s', self.name)
+
+ image = super().image(**kwargs)
+ height, width, _ = image.shape
+
+ # Display calibration status
+ calibration_panel = ((int(width/2), 0), (width, 50))
+
+ if self.__calibration_status is None:
+
+ cv2.rectangle(image, calibration_panel[0], calibration_panel[1], (0, 0, 0), -1)
+ cv2.putText(image, 'Calibration required', (calibration_panel[0][0]+20, calibration_panel[0][1]+40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA)
+
+ elif self.__calibration_status == 'calibrating':
+
+ cv2.rectangle(image, calibration_panel[0], calibration_panel[1], (127, 127, 127), -1)
+ cv2.putText(image, f'Calibrating...', (calibration_panel[0][0]+20, calibration_panel[0][1]+40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA)
+
+ elif self.__calibration_status != 'calibrated':
+
+ cv2.rectangle(image, calibration_panel[0], calibration_panel[1], (0, 0, 127), -1)
+ cv2.putText(image, f'Calibration {calibration_status}', (calibration_panel[0][0]+20, calibration_panel[0][1]+40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA)
+
+ else:
+
+ cv2.rectangle(image, calibration_panel[0], calibration_panel[1], (0, 127, 0), -1)
+ cv2.putText(image, f'Calibration succeeded', (calibration_panel[0][0]+20, calibration_panel[0][1]+40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA)
+
+ return image
class PostProcessing(ArFeatures.PostProcessingContext):
@@ -1176,9 +1161,6 @@ class PostProcessing(ArFeatures.PostProcessingContext):
# Initialize inconsistent timestamp monitoring
self.__last_data_ts = None
- # Init protected attributes
- self._image_parameters = {**ArFeatures.DEFAULT_ARCONTEXT_IMAGE_PARAMETERS, **DEFAULT_TOBII_IMAGE_PARAMETERS}
-
@property
def segment(self) -> str:
"""Path to segment folder."""