aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThéo de la Hogue2024-03-22 10:45:32 +0100
committerThéo de la Hogue2024-03-22 10:45:32 +0100
commit03a2be14d6778dca8552ca28dd89e562756ccc17 (patch)
tree2af5b953caaa5e8915c40e8edc3d98fae7c422a4
parent392f94a7757d95bf514d0562b0a54c25f72d5e1e (diff)
downloadargaze-03a2be14d6778dca8552ca28dd89e562756ccc17.zip
argaze-03a2be14d6778dca8552ca28dd89e562756ccc17.tar.gz
argaze-03a2be14d6778dca8552ca28dd89e562756ccc17.tar.bz2
argaze-03a2be14d6778dca8552ca28dd89e562756ccc17.tar.xz
More work on serialization.
-rw-r--r--src/argaze/ArFeatures.py38
-rw-r--r--src/argaze/ArUcoMarkers/ArUcoCamera.py6
-rw-r--r--src/argaze/ArUcoMarkers/ArUcoScene.py27
-rw-r--r--src/argaze/DataFeatures.py97
-rw-r--r--src/argaze/utils/demo_aruco_markers_run.py5
-rw-r--r--src/argaze/utils/demo_gaze_analysis_run.py4
6 files changed, 120 insertions, 57 deletions
diff --git a/src/argaze/ArFeatures.py b/src/argaze/ArFeatures.py
index 9892b6a..24ba8e3 100644
--- a/src/argaze/ArFeatures.py
+++ b/src/argaze/ArFeatures.py
@@ -104,7 +104,7 @@ class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
# Init parent classes
DataFeatures.SharedObject.__init__(self)
- DataFeatures.PipelineStepObject.__init__(self, **kwargs)
+ DataFeatures.PipelineStepObject.__init__(self)
# Init private attributes
self.__aoi_scene = None
@@ -164,6 +164,9 @@ class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
self.__aoi_scene = AOI3DScene.AOI3DScene(new_aoi_scene)
+ # Update expected AOI of AOI scan path
+ self.__update_expected_aoi()
+
# Edit parent
if self.__aoi_scene is not None:
@@ -196,14 +199,8 @@ class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
self.__aoi_scan_path = aoi_scan_path
- # Edit aoi_scan_path's expected aoi list by removing aoi with name equals to layer name
- expected_aoi = list(self.__aoi_scene.keys())
-
- if self.name in expected_aoi:
-
- expected_aoi.remove(self.name)
-
- self.__aoi_scan_path.expected_aoi = expected_aoi
+ # Update expected AOI of AOI scan path
+ self.__update_expected_aoi()
# Edit parent
if self.__aoi_scan_path is not None:
@@ -270,6 +267,7 @@ class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
return self.__draw_parameters
@draw_parameters.setter
+ @DataFeatures.PipelineStepAttributeSetter
def draw_parameters(self, draw_parameters: dict):
self.__draw_parameters = draw_parameters
@@ -304,6 +302,19 @@ class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
"draw_parameters": self.__draw_parameters
}
+ def __update_expected_aoi(self):
+ """Edit aoi_scan_path's expected aoi list by removing aoi with name equals to layer name."""
+
+ if self.__aoi_scene is not None and self.__aoi_scan_path is not None:
+
+ expected_aoi = list(self.__aoi_scene.keys())
+
+ if self.name in expected_aoi:
+
+ expected_aoi.remove(self.name)
+
+ self.__aoi_scan_path.expected_aoi = expected_aoi
+
@DataFeatures.PipelineStepMethod
def look(self, gaze_movement: GazeFeatures.GazePosition = GazeFeatures.GazePosition()):
"""
@@ -328,7 +339,7 @@ class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
# Reset aoi scan path analyzed state
self.__aoi_scan_path_analyzed = False
- if self.__aoi_matcher is not None:
+ if self.__aoi_matcher is not None and self.__aoi_scene is not None:
# Update looked aoi thanks to aoi matcher
# Note: don't filter valid/unvalid and finished/unfinished fixation/saccade as we don't know how the aoi matcher works internally
@@ -381,7 +392,7 @@ class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
with self._lock:
# Draw aoi if required
- if draw_aoi_scene is not None:
+ if draw_aoi_scene is not None and self.__aoi_scene is not None:
self.__aoi_scene.draw(image, **draw_aoi_scene)
@@ -621,6 +632,7 @@ class ArFrame(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
return self.__layers
@layers.setter
+ @DataFeatures.PipelineStepAttributeSetter
def layers(self, layers: dict):
self.__layers = {}
@@ -640,6 +652,7 @@ class ArFrame(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
return self.__image_parameters
@image_parameters.setter
+ @DataFeatures.PipelineStepAttributeSetter
def image_parameters(self, image_parameters: dict):
self.__image_parameters = image_parameters
@@ -891,6 +904,7 @@ class ArScene(DataFeatures.PipelineStepObject):
return self.__layers
@layers.setter
+ @DataFeatures.PipelineStepAttributeSetter
def layers(self, layers:dict):
self.__layers = {}
@@ -911,6 +925,7 @@ class ArScene(DataFeatures.PipelineStepObject):
return self.__frames
@frames.setter
+ @DataFeatures.PipelineStepAttributeSetter
def frames(self, frames: dict):
self.__frames = {}
@@ -1104,6 +1119,7 @@ class ArCamera(ArFrame):
return self.__scenes
@scenes.setter
+ @DataFeatures.PipelineStepAttributeSetter
def scenes(self, scenes: dict):
self.__scenes = {}
diff --git a/src/argaze/ArUcoMarkers/ArUcoCamera.py b/src/argaze/ArUcoMarkers/ArUcoCamera.py
index 9fc6117..5c495dc 100644
--- a/src/argaze/ArUcoMarkers/ArUcoCamera.py
+++ b/src/argaze/ArUcoMarkers/ArUcoCamera.py
@@ -48,7 +48,7 @@ class ArUcoCamera(ArFeatures.ArCamera):
"""Initialize ArUcoCamera"""
# Init parent class
- super().__init__(**kwargs)
+ super().__init__()
# Init private attribute
self.__aruco_detector = None
@@ -86,6 +86,7 @@ class ArUcoCamera(ArFeatures.ArCamera):
self.__aruco_detector.parent = self
@ArFeatures.ArCamera.scenes.setter
+ @DataFeatures.PipelineStepAttributeSetter
def scenes(self, scenes: dict):
self.__scenes = {}
@@ -127,6 +128,9 @@ class ArUcoCamera(ArFeatures.ArCamera):
# Clear former layers projection into camera frame
for layer_name, layer in self.layers.items():
+
+ # DEBUG
+ print('ArUcoCamera.watch', layer_name, layer.aoi_scene)
layer.aoi_scene.clear()
diff --git a/src/argaze/ArUcoMarkers/ArUcoScene.py b/src/argaze/ArUcoMarkers/ArUcoScene.py
index 999fd6f..078c9e2 100644
--- a/src/argaze/ArUcoMarkers/ArUcoScene.py
+++ b/src/argaze/ArUcoMarkers/ArUcoScene.py
@@ -47,31 +47,10 @@ class ArUcoScene(ArFeatures.ArScene):
return self.__aruco_markers_group
@aruco_markers_group.setter
- def aruco_markers_group(self, aruco_markers_group_value: ArUcoMarkersGroup.ArUcoMarkersGroup):
+ @DataFeatures.PipelineStepAttributeSetter
+ def aruco_markers_group(self, aruco_markers_group: ArUcoMarkersGroup.ArUcoMarkersGroup):
- if isinstance(aruco_markers_group_value, ArUcoMarkersGroup.ArUcoMarkersGroup):
-
- new_aruco_markers_group = aruco_markers_group_value
-
- # str: relative path to file
- elif type(aruco_markers_group_value) == str:
-
- filepath = os.path.join(self.working_directory, aruco_markers_group_value)
- file_format = filepath.split('.')[-1]
-
- # OBJ file format for 3D dimension only
- if file_format == 'obj':
-
- new_aruco_markers_group = ArUcoMarkersGroup.ArUcoMarkersGroup.from_obj(filepath)
-
- elif file_format == 'json':
-
- with open(filepath) as file:
-
- new_aruco_markers_group = ArUcoMarkersGroup.ArUcoMarkersGroup(**json.load(file))
-
- # Update value
- self.__aruco_markers_group = new_aruco_markers_group
+ self.__aruco_markers_group = aruco_markers_group
# Edit parent
if self.__aruco_markers_group is not None:
diff --git a/src/argaze/DataFeatures.py b/src/argaze/DataFeatures.py
index ff88693..b6105e4 100644
--- a/src/argaze/DataFeatures.py
+++ b/src/argaze/DataFeatures.py
@@ -19,6 +19,7 @@ __license__ = "GPLv3"
from typing import Self
import os
import sys
+import logging
import traceback
import importlib
import collections
@@ -464,13 +465,18 @@ def PipelineStepInit(method):
def PipelineStepAttributeSetter(method):
"""Define a decorator use into PipelineStepObject class to declare pipeline step attribute setter."""
- def wrapper(self, new_value):
+ def wrapper(self, new_value, unwrap: bool = False):
"""Wrap pipeline step attribute setter to load attribute from file.
Parameters:
new_value: value used to set attribute.
+ unwrap: call wrapped method directly.
"""
+ if unwrap:
+
+ return method(self, new_value)
+
# Get new value type
new_value_type = type(new_value)
@@ -483,9 +489,13 @@ def PipelineStepAttributeSetter(method):
raise(ValueError(f'Missing annotations in {method.__name__}: {method.__annotations__}'))
+ logging.debug('@PipelineStepAttributeSetter %s.%s.setter(%s) with %s', type(self).__name__, method.__name__, expected_value_type.__name__, new_value_type.__name__)
+
# Define function to load dict values
def load_dict(data: dict) -> any:
+ logging.debug('\t> load %s from %s', expected_value_type.__name__, new_value_type.__name__)
+
# Check if json keys are PipelineStepObject class and store them in a list
new_objects_list = []
@@ -506,6 +516,8 @@ def PipelineStepAttributeSetter(method):
raise(e)
+ logging.debug('\t+ create %s object from key using value as argument', key)
+
new_objects_list.append( new_class(**value) )
# Only one object have been loaded: pass the object if it is a subclass of expected type
@@ -519,32 +531,57 @@ def PipelineStepAttributeSetter(method):
return new_objects_list
# Otherwise, data are parameters of the expected class
+ logging.debug('\t+ create %s object using %s as argument', expected_value_type.__name__, new_value_type.__name__)
+
return expected_value_type(**data)
# String not expected: load value from file
if new_value_type == str and new_value_type != expected_value_type:
- filepath = os.path.join(self.working_directory, new_value)
- file_format = filepath.split('.')[-1]
-
- # Load image from JPG and PNG formats
- if file_format == 'jpg' or file_format == 'png':
+ split_point = new_value.split('.')
+
+ # String have a dot inside: file path with format
+ if len(split_point) > 1:
+
+ file_format = split_point[-1]
+
+ logging.debug('\t> %s is a path to a %s file', new_value, file_format.upper())
+
+ filepath = os.path.join(self.working_directory, new_value)
+
+ # Load image from JPG and PNG formats
+ if file_format == 'jpg' or file_format == 'png':
+
+ return method(self, cv2.imread(filepath))
+
+ # Load image from OBJ formats
+ elif file_format == 'obj':
+
+ return method(self, expected_value_type.from_obj(filepath))
- return method(self, cv2.imread(filepath))
+ # Load object from JSON file
+ elif file_format == 'json':
- # Load object from JSON file
- elif file_format == 'json':
+ with open(filepath) as file:
- with open(filepath) as file:
+ return method(self, load_dict(json.load(file)))
- return method(self, load_dict(json.load(file)))
+ # No point inside string: identifier name
+ else:
+
+ logging.debug('\t> %s is an identifier', new_value)
+ logging.debug('\t+ create %s object using string as argument', expected_value_type.__name__)
+
+ return method(self, expected_value_type(new_value))
- # Always load value from dict
- if new_value_type == dict:
+ # Dict not expected: load value from dict
+ if new_value_type == dict and expected_value_type != dict:
return method(self, load_dict(new_value))
# Otherwise, pass new value to setter method
+ logging.debug('\t> use %s value as passed', new_value_type.__name__)
+
method(self, new_value)
return wrapper
@@ -558,6 +595,8 @@ class PipelineStepObject():
def __init__(self, **kwargs):
"""Initialize PipelineStepObject."""
+ logging.debug('PipelineStepObject.__init__ %s %s', type(self).__name__, kwargs['name'] if 'name' in kwargs else '')
+
# Init private attribute
self.__name = None
self.__working_directory = None
@@ -600,6 +639,8 @@ class PipelineStepObject():
for key, value in object_data.items():
+ logging.debug('PipelineStepObject.update_attributes %s.%s with %s value', type(self).__name__, key, type(value).__name__)
+
setattr(self, key, value)
@property
@@ -677,6 +718,8 @@ class PipelineStepObject():
patch_filepath: path to json patch file to modify any configuration entries
"""
+ logging.debug('%s.from_json', cls.__name__)
+
# Load configuration from JSON file
with open(configuration_filepath) as configuration_file:
@@ -747,10 +790,10 @@ class PipelineStepObject():
output = f'{Fore.GREEN}{Style.BRIGHT}{self.__class__.__module__}.{self.__class__.__name__}{Style.RESET_ALL}\n'
if self.__name is not None:
- output += f'{tabs}\t{Style.BRIGHT}name{Style.RESET_ALL}: {self.__name}\n'
+ output += f'{tabs}\t{Style.BRIGHT}name: {Fore.MAGENTA}{self.__name}{Style.RESET_ALL}\n'
if self.__parent is not None:
- output += f'{tabs}\t{Style.BRIGHT}parent{Style.RESET_ALL}: {self.__parent.name}\n'
+ output += f'{tabs}\t{Style.BRIGHT}parent{Style.RESET_ALL}: {Fore.MAGENTA}{self.__parent.name}{Style.RESET_ALL}\n'
if len(self.__observers):
output += f'{tabs}\t{Style.BRIGHT}observers{Style.RESET_ALL}:\n'
@@ -767,7 +810,15 @@ class PipelineStepObject():
for k, v in value.items():
- output += f'{tabs}\t - {Fore.RED}{k}{Style.RESET_ALL}: {v}\n'
+ output += f'{tabs}\t - {Fore.MAGENTA}{k}{Style.RESET_ALL}: {v}\n'
+
+ if type(value) == list:
+
+ output += '\n'
+
+ for v in value:
+
+ output += f'{tabs}\t - {v}\n'
elif type(value) == numpy.ndarray:
@@ -779,7 +830,13 @@ class PipelineStepObject():
else:
- output += f'{value}'
+ try:
+
+ output += f'{value}'
+
+ except TypeError as e:
+
+ output += f'{Fore.RED}{Style.BRIGHT}!!! {type(self).__name__}.{name}: {e}{Style.RESET_ALL}\n\n'
if output[-1] != '\n':
@@ -933,15 +990,13 @@ class PipelineInputProvider(PipelineStepObject):
@PipelineStepInit
def __init__(self, **kwargs):
- # DEBUG
- print('PipelineInputProvider.__init__')
+ logging.debug('PipelineInputProvider.__init__')
super().__init__()
def attach(self, method):
- # DEBUG
- print('PipelineInputProvider.attach', method)
+ logging.debug('PipelineInputProvider.attach', method)
def __enter__(self):
"""
diff --git a/src/argaze/utils/demo_aruco_markers_run.py b/src/argaze/utils/demo_aruco_markers_run.py
index f5bc756..cdd9184 100644
--- a/src/argaze/utils/demo_aruco_markers_run.py
+++ b/src/argaze/utils/demo_aruco_markers_run.py
@@ -19,6 +19,7 @@ __copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)"
__license__ = "GPLv3"
import argparse
+import logging
import contextlib
import os
import time
@@ -36,8 +37,12 @@ parser = argparse.ArgumentParser(description=__doc__.split('-')[0])
parser.add_argument('configuration', metavar='CONFIGURATION', type=str, help='configuration filepath')
parser.add_argument('-s', '--source', metavar='SOURCE', type=str, default='0', help='video capture source (a number to select camera device or a filepath to load a movie)')
parser.add_argument('-v', '--verbose', action='store_true', default=False, help='enable verbose mode to print information in console')
+
args = parser.parse_args()
+# Manage logging
+logging.basicConfig(format = '%(levelname)s: %(message)s', level = logging.DEBUG if args.verbose else logging.INFO)
+
def main():
# Load ArUcoCamera
diff --git a/src/argaze/utils/demo_gaze_analysis_run.py b/src/argaze/utils/demo_gaze_analysis_run.py
index b5d9a20..16644ce 100644
--- a/src/argaze/utils/demo_gaze_analysis_run.py
+++ b/src/argaze/utils/demo_gaze_analysis_run.py
@@ -19,6 +19,7 @@ __copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)"
__license__ = "GPLv3"
import argparse
+import logging
import contextlib
import os
import time
@@ -37,6 +38,9 @@ parser.add_argument('configuration', metavar='CONFIGURATION', type=str, help='co
parser.add_argument('-v', '--verbose', action='store_true', default=False, help='enable verbose mode to print information in console')
args = parser.parse_args()
+# Manage logging
+logging.basicConfig(format = '%(levelname)s: %(message)s', level = logging.DEBUG if args.verbose else logging.INFO)
+
def main():
# Load ArFrame