diff options
Diffstat (limited to 'src/argaze/ArFeatures.py')
-rw-r--r-- | src/argaze/ArFeatures.py | 145 |
1 files changed, 68 insertions, 77 deletions
diff --git a/src/argaze/ArFeatures.py b/src/argaze/ArFeatures.py index 4e1f5cb..3706aac 100644 --- a/src/argaze/ArFeatures.py +++ b/src/argaze/ArFeatures.py @@ -24,31 +24,72 @@ class ArEnvironment(): """Define an Augmented Reality environment based ArUco marker detection.""" name: str - """Environement name""" + """Environment name""" - aruco_detector: ArUcoDetector.ArUcoDetector = field(init=False, default_factory=ArUcoDetector.ArUcoDetector) - """ArUco detector""" - - def __init__(self, **kwargs): - - self.name = kwargs.pop('name') + working_directory: str + """Environment working directory""" - self.aruco_detector = ArUcoDetector.ArUcoDetector(**kwargs.pop('aruco_detector')) - - self.__scenes = {} - for name, scene_kwargs in kwargs.items(): + aruco_detector: ArUcoDetector.ArUcoDetector = field(default_factory=ArUcoDetector.ArUcoDetector) + """ArUco detector""" - self.__scenes[name] = ArScene(self, **scene_kwargs) + scenes: dict = field(default_factory=dict) + """All environment scenes""" @classmethod def from_json(self, json_filepath: str) -> ArSceneType: """Load ArEnvironment from .json file.""" + new_name = '' + new_working_directory = '.' + new_aruco_detector = None + new_scenes = {} + with open(json_filepath) as configuration_file: - self.__working_directory = os.path.dirname(json_filepath) + data = json.load(configuration_file) + + new_name = data.pop('name') + + new_working_directory = os.path.dirname(json_filepath) + + new_aruco_detector = ArUcoDetector.ArUcoDetector(**data.pop('aruco_detector')) + + for scene_name, scene_data in data.items(): + + new_aruco_scene = None + new_aoi_scene = None + + # Check aruco_scene value type + aruco_scene_value = scene_data.pop('aruco_scene') - return ArEnvironment(**json.load(configuration_file)) + # str: relative path to .obj file + if type(aruco_scene_value) == str: + + aruco_scene_value = os.path.join(new_working_directory, aruco_scene_value) + new_aruco_scene = ArUcoScene.ArUcoScene.from_obj(aruco_scene_value) + + # dict: + else: + + new_aruco_scene = ArUcoScene.ArUcoScene(new_aruco_detector.marker_size, new_aruco_detector.dictionary, aruco_scene_value) + + # Check aoi_scene value type + aoi_scene_value = scene_data.pop('aoi_scene') + + # str: relative path to .obj file + if type(aoi_scene_value) == str: + + obj_filepath = os.path.join(new_working_directory, aoi_scene_value) + new_aoi_scene = AOI3DScene.AOI3DScene.from_obj(obj_filepath) + + # dict: + else: + + new_aoi_scene = AOI3DScene.AOI3DScene(aoi_scene_value) + + new_scenes[scene_name] = ArScene(self, new_aruco_scene, new_aoi_scene, **scene_data) + + return ArEnvironment(new_name, new_working_directory, new_aruco_detector, new_scenes) def __str__(self) -> str: """String display""" @@ -60,17 +101,6 @@ class ArEnvironment(): return output - @property - def working_directory(self): - """Working directory path.""" - return self.__working_directory - - @property - def scenes(self) -> Tuple[str, ArSceneType]: - """Access to scenes dictionary.""" - - return self.__scenes - class PoseEstimationFailed(Exception): """Exception raised by ArScene project method when the pose can't be estimated due to unconsistencies.""" @@ -91,72 +121,33 @@ class SceneProjectionFailed(Exception): class ArScene(): """Define an Augmented Reality scene with ArUco markers and AOI scenes.""" - aruco_scene: ArUcoScene.ArUcoScene = field(init=False, default_factory=ArUcoScene.ArUcoScene) + ar_environment: ArEnvironment = field(default_factory=ArEnvironment) + """AR Environment to which the scene belongs.""" + + aruco_scene: ArUcoScene.ArUcoScene = field(default_factory=ArUcoScene.ArUcoScene) """ArUco markers 3D scene description used to estimate scene pose from detected markers: see `estimate_pose` function below.""" - aoi_scene: AOI3DScene.AOI3DScene = field(init=False, default_factory=AOI3DScene.AOI3DScene) + aoi_scene: AOI3DScene.AOI3DScene = field(default_factory=AOI3DScene.AOI3DScene) """AOI 3D scene description that will be projected onto estimated scene once its pose will be estimated : see `project` function below.""" - aruco_axis: dict + aruco_axis: dict = field(default_factory=dict) """Optional dictionary to define orthogonal axis where each axis is defined by list of 3 markers identifier (first is origin). \ This pose estimation strategy is used by `estimate_pose` function when at least 3 markers are detected.""" - aruco_aoi: dict + aruco_aoi: dict = field(default_factory=dict) """Optional dictionary of AOI defined by list of markers identifier and markers corners index tuples: see `build_aruco_aoi_scene` function below.""" - angle_tolerance: float + angle_tolerance: float = field(default=0.) """Optional angle error tolerance to validate marker pose in degree used into `estimate_pose` function.""" - distance_tolerance: float + distance_tolerance: float = field(default=0.) """Optional distance error tolerance to validate marker pose in centimeter used into `estimate_pose` function.""" - def __init__(self, ar_environment: ArEnvironment, **kwargs): - - self.__ar_environment = ar_environment - - # Check aruco_scene value type - aruco_scene_value = kwargs.pop('aruco_scene') - - # str: relative path to .obj file - if type(aruco_scene_value) == str: - - aruco_scene_value = os.path.join(self.__ar_environment.working_directory, aruco_scene_value) - - self.aruco_scene = ArUcoScene.ArUcoScene(aruco_scene_value, self.__ar_environment.aruco_detector.dictionary, self.__ar_environment.aruco_detector.marker_size) - - # Check aoi_scene value type - aoi_scene_value = kwargs.pop('aoi_scene') - - # str: relative path to .obj file - if type(aoi_scene_value) == str: - - obj_filepath = os.path.join(self.__ar_environment.working_directory, aoi_scene_value) - self.aoi_scene = AOI3DScene.AOI3DScene.from_obj(obj_filepath) - - # dict: all AOI - else: - self.aoi_scene = AOI3DScene.AOI3DScene(aoi_scene_value) - - # Init aruco axis - self.aruco_axis = {} - - # Init aruco aoi - self.aruco_aoi = {} - - # Update all attributes from arguments - self.__dict__.update(kwargs) + def __post_init__(self): # Preprocess orthogonal projection to speed up further aruco aoi processings self.__orthogonal_projection_cache = self.orthogonal_projection - @classmethod - def from_json(self, json_filepath: str) -> ArSceneType: - """Load ArScene from .json file.""" - - with open(json_filepath) as configuration_file: - - return ArScene(**json.load(configuration_file)) - def __str__(self) -> str: """String display""" @@ -270,7 +261,7 @@ class ArScene(): aoi_scene_copy = self.aoi_scene.copy() - aoi_scene_projection = aoi_scene_copy.project(tvec, rvec, self.__ar_environment.aruco_detector.camera.K) + aoi_scene_projection = aoi_scene_copy.project(tvec, rvec, self.ar_environment.aruco_detector.camera.K) # Warn user when the projected scene is empty if len(aoi_scene_projection) == 0: @@ -320,11 +311,11 @@ class ArScene(): def draw_axis(self, frame): """Draw scene axis into frame.""" - self.aruco_scene.draw_axis(frame, self.__ar_environment.aruco_detector.camera.K, self.__ar_environment.aruco_detector.camera.D) + self.aruco_scene.draw_axis(frame, self.ar_environment.aruco_detector.camera.K, self.ar_environment.aruco_detector.camera.D) def draw_places(self, frame): """Draw scene places into frame.""" - self.aruco_scene.draw_places(frame, self.__ar_environment.aruco_detector.camera.K, self.__ar_environment.aruco_detector.camera.D) + self.aruco_scene.draw_places(frame, self.ar_environment.aruco_detector.camera.K, self.ar_environment.aruco_detector.camera.D) |