aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/argaze/ArFeatures.py118
-rw-r--r--src/argaze/ArUcoMarkers/ArUcoBoard.py4
-rw-r--r--src/argaze/ArUcoMarkers/ArUcoCamera.py2
-rw-r--r--src/argaze/ArUcoMarkers/ArUcoDetector.py8
-rw-r--r--src/argaze/ArUcoMarkers/ArUcoMarker.py2
-rw-r--r--src/argaze/ArUcoMarkers/ArUcoScene.py4
-rw-r--r--src/argaze/AreaOfInterest/AOI2DScene.py6
-rw-r--r--src/argaze/AreaOfInterest/AOIFeatures.py2
-rw-r--r--src/argaze/GazeAnalysis/DispersionThresholdIdentification.py4
-rw-r--r--src/argaze/GazeAnalysis/VelocityThresholdIdentification.py6
-rw-r--r--src/argaze/GazeFeatures.py10
-rw-r--r--src/argaze/PupilAnalysis/__init__.py1
-rw-r--r--src/argaze/utils/README.md10
-rw-r--r--src/argaze/utils/__init__.py3
14 files changed, 112 insertions, 68 deletions
diff --git a/src/argaze/ArFeatures.py b/src/argaze/ArFeatures.py
index e60f792..5899545 100644
--- a/src/argaze/ArFeatures.py
+++ b/src/argaze/ArFeatures.py
@@ -24,21 +24,20 @@ ArEnvironmentType = TypeVar('ArEnvironment', bound="ArEnvironment")
ArSceneType = TypeVar('ArScene', bound="ArScene")
# Type definition for type annotation convenience
-AOI2DSceneType = TypeVar('AOI2DScene', bound="AOI2DScene")
-# Type definition for type annotation convenience
-
@dataclass
class ArEnvironment():
- """Define an Augmented Reality environment based ArUco marker detection."""
+ """
+ Define Augmented Reality environment based on ArUco marker detection.
- name: str
- """Environment name"""
+ Parameters:
+ name: Environment name
+ aruco_detector: ArUco detector
+ scenes: All environment scenes
+ """
+ name: str
aruco_detector: ArUcoDetector.ArUcoDetector = field(default_factory=ArUcoDetector.ArUcoDetector)
- """ArUco detector"""
-
scenes: dict = field(default_factory=dict)
- """All environment scenes"""
def __post_init__(self):
@@ -48,7 +47,12 @@ class ArEnvironment():
@classmethod
def from_json(self, json_filepath: str) -> ArSceneType:
- """Load ArEnvironment from .json file."""
+ """
+ Load ArEnvironment from .json file.
+
+ Parameters:
+ json_filepath: path to json file
+ """
with open(json_filepath) as configuration_file:
@@ -117,7 +121,10 @@ class ArEnvironment():
return ArEnvironment(new_name, new_aruco_detector, new_scenes)
def __str__(self) -> str:
- """String display"""
+ """
+ Returns:
+ String representation
+ """
output = f'Name:\n{self.name}\n'
output += f'ArUcoDetector:\n{self.aruco_detector}\n'
@@ -135,7 +142,9 @@ class ArEnvironment():
json.dump(self, file, ensure_ascii=False, indent=4, cls=DataStructures.JsonEncoder)
class PoseEstimationFailed(Exception):
- """Exception raised by ArScene estimate_pose method when the pose can't be estimated due to unconsistencies."""
+ """
+ Exception raised by ArScene estimate_pose method when the pose can't be estimated due to unconsistencies.
+ """
def __init__(self, message, unconsistencies=None):
@@ -144,7 +153,9 @@ class PoseEstimationFailed(Exception):
self.unconsistencies = unconsistencies
class SceneProjectionFailed(Exception):
- """Exception raised by ArScene project method when the scene can't be projected."""
+ """
+ Exception raised by ArScene project method when the scene can't be projected.
+ """
def __init__(self, message):
@@ -152,26 +163,30 @@ class SceneProjectionFailed(Exception):
@dataclass
class ArScene():
- """Define an Augmented Reality scene with ArUco markers and AOI scenes."""
+ """
+ Define an Augmented Reality scene with ArUco markers and AOI scenes.
- 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."""
+ Parameters:
+ aruco_scene: ArUco markers 3D scene description used to estimate scene pose from detected markers: see [estimate_pose][argaze.ArFeatures.ArScene.estimate_pose] function below.
- 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."""
+ aoi_scene: AOI 3D scene description that will be projected onto estimated scene once its pose will be estimated : see [project][argaze.ArFeatures.ArScene.project] function below.
- 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_axis: 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][argaze.ArFeatures.ArScene.estimate_pose] function when at least 3 markers are detected.
- 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."""
+ aruco_aoi: Optional dictionary of AOI defined by list of markers identifier and markers corners index tuples: see [build_aruco_aoi_scene][argaze.ArFeatures.ArScene.build_aruco_aoi_scene] function below.
- angle_tolerance: float = field(default=0.)
- """Optional angle error tolerance to validate marker pose in degree used into `estimate_pose` function."""
+ angle_tolerance: Optional angle error tolerance to validate marker pose in degree used into [estimate_pose][argaze.ArFeatures.ArScene.estimate_pose] function.
+
+ distance_tolerance: Optional distance error tolerance to validate marker pose in centimeter used into [estimate_pose][argaze.ArFeatures.ArScene.estimate_pose] function.
+ """
+ aruco_scene: ArUcoScene.ArUcoScene = field(default_factory=ArUcoScene.ArUcoScene)
+ aoi_scene: AOI3DScene.AOI3DScene = field(default_factory=AOI3DScene.AOI3DScene)
+ aruco_axis: dict = field(default_factory=dict)
+ aruco_aoi: dict = field(default_factory=dict)
+ angle_tolerance: float = field(default=0.)
distance_tolerance: float = field(default=0.)
- """Optional distance error tolerance to validate marker pose in centimeter used into `estimate_pose` function."""
def __post_init__(self):
@@ -182,7 +197,10 @@ class ArScene():
self.__orthogonal_projection_cache = self.orthogonal_projection
def __str__(self) -> str:
- """String display"""
+ """
+ Returns:
+ String representation
+ """
output = f'ArEnvironment:\n{self._environment.name}\n'
output += f'ArUcoScene:\n{self.aruco_scene}\n'
@@ -191,8 +209,13 @@ class ArScene():
return output
@property
- def orthogonal_projection(self) -> AOI2DSceneType:
- """Orthogonal projection of whole AOI scene."""
+ def orthogonal_projection(self) -> AOI2DScene.AOI2DScene:
+ """
+ Orthogonal projection of whole AOI scene.
+
+ Returns:
+ projected AOI 2D scene
+ """
scene_size = self.aoi_scene.size
@@ -268,13 +291,13 @@ class ArScene():
return tvec, rmat, 'estimate_pose_from_markers', consistent_markers
- def project(self, tvec: numpy.array, rvec: numpy.array, visual_hfov=0) -> AOI2DSceneType:
+ def project(self, tvec: numpy.array, rvec: numpy.array, visual_hfov: float = 0.) -> AOI2DScene.AOI2DScene:
"""Project AOI scene according estimated pose and optional horizontal field of view clipping angle.
- * **Arguments:**
- - translation vector
- - rotation vector
- - horizontal field of view clipping angle
+ Parameters:
+ tvec: translation vector
+ rvec: rotation vector
+ visual_hfov: horizontal field of view clipping angle
"""
# Clip AOI out of the visual horizontal field of view (optional)
@@ -305,8 +328,13 @@ class ArScene():
return aoi_scene_projection
- def build_aruco_aoi_scene(self, detected_markers) -> AOI2DSceneType:
- """Build AOI scene from ArUco markers into frame as defined in aruco_aoi dictionary."""
+ def build_aruco_aoi_scene(self, detected_markers) -> AOI2DScene.AOI2DScene:
+ """
+ Build AOI scene from ArUco markers into frame as defined in aruco_aoi dictionary.
+
+ Returns:
+ built AOI 2D scene
+ """
# AOI projection fails when no marker is detected
if len(detected_markers) == 0:
@@ -343,13 +371,23 @@ class ArScene():
return AOI2DScene.AOI2DScene(aruco_aoi_scene)
- def draw_axis(self, frame):
- """Draw scene axis into frame."""
+ def draw_axis(self, frame: numpy.array):
+ """
+ Draw scene axis into frame.
+
+ Parameters:
+ frame: where to draw
+ """
self.aruco_scene.draw_axis(frame, self._environment.aruco_detector.camera.K, self._environment.aruco_detector.camera.D)
- def draw_places(self, frame):
- """Draw scene places into frame."""
+ def draw_places(self, frame: numpy.array):
+ """
+ Draw scene places into frame.
+
+ Parameters:
+ frame: where to draw
+ """
self.aruco_scene.draw_places(frame, self._environment.aruco_detector.camera.K, self._environment.aruco_detector.camera.D)
diff --git a/src/argaze/ArUcoMarkers/ArUcoBoard.py b/src/argaze/ArUcoMarkers/ArUcoBoard.py
index 7cab792..e9a2b2d 100644
--- a/src/argaze/ArUcoMarkers/ArUcoBoard.py
+++ b/src/argaze/ArUcoMarkers/ArUcoBoard.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-""" """
+"""Calibration chess board with ArUco markers inside."""
__author__ = "Théo de la Hogue"
__credits__ = []
@@ -17,7 +17,7 @@ import cv2.aruco as aruco
@dataclass
class ArUcoBoard():
- """Calibration chess board with ArUco markers inside."""
+ """ """
columns: int = field(default=0)
"""Number of columns."""
diff --git a/src/argaze/ArUcoMarkers/ArUcoCamera.py b/src/argaze/ArUcoMarkers/ArUcoCamera.py
index 4f943e2..205c591 100644
--- a/src/argaze/ArUcoMarkers/ArUcoCamera.py
+++ b/src/argaze/ArUcoMarkers/ArUcoCamera.py
@@ -63,7 +63,7 @@ class CalibrationData():
return output
- def draw(self, frame, width:float, height:float, z:float, color=(0, 0, 255)):
+ def draw(self, frame: numpy.array, width:float, height:float, z:float, color=(0, 0, 255)):
"""Draw grid to display K and D"""
# Edit 3D grid
diff --git a/src/argaze/ArUcoMarkers/ArUcoDetector.py b/src/argaze/ArUcoMarkers/ArUcoDetector.py
index 2f424f6..4392e7a 100644
--- a/src/argaze/ArUcoMarkers/ArUcoDetector.py
+++ b/src/argaze/ArUcoMarkers/ArUcoDetector.py
@@ -175,7 +175,7 @@ class ArUcoDetector():
return output
- def detect_markers(self, frame):
+ def detect_markers(self, frame: numpy.array):
"""Detect all ArUco markers into a frame.
.. danger:: DON'T MIRROR FRAME
@@ -262,14 +262,14 @@ class ArUcoDetector():
return len(list(self.__detected_markers.keys()))
- def draw_detected_markers(self, frame):
+ def draw_detected_markers(self, frame: numpy.array):
"""Draw traked markers."""
for marker_id, marker in self.__detected_markers.items():
marker.draw(frame, self.camera.K, self.camera.D)
- def detect_board(self, frame, board, expected_markers_number):
+ def detect_board(self, frame: numpy.array, board, expected_markers_number):
"""Detect ArUco markers board in frame setting up the number of detected markers needed to agree detection.
.. danger:: DON'T MIRROR FRAME
@@ -293,7 +293,7 @@ class ArUcoDetector():
self.__board_corners = []
self.__board_corners_ids = []
- def draw_board(self, frame):
+ def draw_board(self, frame: numpy.array):
"""Draw detected board corners in frame."""
if self.__board != None:
diff --git a/src/argaze/ArUcoMarkers/ArUcoMarker.py b/src/argaze/ArUcoMarkers/ArUcoMarker.py
index 1e02ad3..8b59166 100644
--- a/src/argaze/ArUcoMarkers/ArUcoMarker.py
+++ b/src/argaze/ArUcoMarkers/ArUcoMarker.py
@@ -59,7 +59,7 @@ class ArUcoMarker():
return numpy.repeat(matrix, 3).reshape(dimension, dimension, 3)
- def draw(self, frame, K, D):
+ def draw(self, frame: numpy.array, K, D):
"""Draw marker in frame."""
# Draw marker axis if pose has been estimated
diff --git a/src/argaze/ArUcoMarkers/ArUcoScene.py b/src/argaze/ArUcoMarkers/ArUcoScene.py
index d069df0..672e31c 100644
--- a/src/argaze/ArUcoMarkers/ArUcoScene.py
+++ b/src/argaze/ArUcoMarkers/ArUcoScene.py
@@ -612,7 +612,7 @@ class ArUcoScene():
self._rotation = rmat
- def draw_axis(self, frame, K, D, consistency=2):
+ def draw_axis(self, frame: numpy.array, K, D, consistency=2):
"""Draw scene axis according a consistency score."""
l = self.marker_size / 2
@@ -637,7 +637,7 @@ class ArUcoScene():
except cv.error:
pass
- def draw_places(self, frame, K, D, consistency=2):
+ def draw_places(self, frame: numpy.array, K, D, consistency=2):
"""Draw scene places and their axis according a consistency score."""
l = self.marker_size / 2
diff --git a/src/argaze/AreaOfInterest/AOI2DScene.py b/src/argaze/AreaOfInterest/AOI2DScene.py
index 73988b7..8301e23 100644
--- a/src/argaze/AreaOfInterest/AOI2DScene.py
+++ b/src/argaze/AreaOfInterest/AOI2DScene.py
@@ -23,7 +23,7 @@ class AOI2DScene(AOIFeatures.AOIScene):
super().__init__(2, aois_2d)
- def draw(self, frame, exclude=[], color=(0, 255, 255)):
+ def draw(self, frame: numpy.array, exclude=[], color=(0, 255, 255)):
"""Draw AOI polygons on frame."""
for name, aoi in self.items():
@@ -47,7 +47,7 @@ class AOI2DScene(AOIFeatures.AOIScene):
yield name, aoi, matching
- def draw_raycast(self, frame, pointer:tuple, exclude=[], base_color=(0, 0, 255), matching_color=(0, 255, 0)):
+ def draw_raycast(self, frame: numpy.array, pointer:tuple, exclude=[], base_color=(0, 0, 255), matching_color=(0, 255, 0)):
"""Draw AOIs with their matching status."""
for name, aoi, matching in self.raycast(pointer):
@@ -81,7 +81,7 @@ class AOI2DScene(AOIFeatures.AOIScene):
yield name, aoi, matching_region, aoi_ratio, circle_ratio
- def draw_circlecast(self, frame, center:tuple, radius:float, exclude=[], base_color=(0, 0, 255), matching_color=(0, 255, 0)):
+ def draw_circlecast(self, frame: numpy.array, center:tuple, radius:float, exclude=[], base_color=(0, 0, 255), matching_color=(0, 255, 0)):
"""Draw AOIs with their matching status and matching region."""
for name, aoi, matching_region, aoi_ratio, circle_ratio in self.circlecast(center, radius):
diff --git a/src/argaze/AreaOfInterest/AOIFeatures.py b/src/argaze/AreaOfInterest/AOIFeatures.py
index 438e15f..4a109a8 100644
--- a/src/argaze/AreaOfInterest/AOIFeatures.py
+++ b/src/argaze/AreaOfInterest/AOIFeatures.py
@@ -184,7 +184,7 @@ class AreaOfInterest(numpy.ndarray):
return empty_array, 0., 0.
- def draw(self, frame, color, border_size=1):
+ def draw(self, frame: numpy.array, color, border_size=1):
"""Draw 2D AOI into frame.
!!! warning
Available for 2D AOI only."""
diff --git a/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py
index 9d5a8d7..c79b4fc 100644
--- a/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py
+++ b/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py
@@ -78,7 +78,7 @@ class Fixation(GazeFeatures.Fixation):
return self
- def draw(self, frame, color=(127, 127, 127), border_color=(255, 255, 255)):
+ def draw(self, frame: numpy.array, color=(127, 127, 127), border_color=(255, 255, 255)):
"""Draw fixation into frame."""
cv2.circle(frame, (int(self.focus[0]), int(self.focus[1])), int(self.deviation_max), color, -1)
@@ -91,7 +91,7 @@ class Saccade(GazeFeatures.Saccade):
def __post_init__(self):
super().__post_init__()
- def draw(self, frame, color=(255, 255, 255)):
+ def draw(self, frame: numpy.array, color=(255, 255, 255)):
"""Draw saccade into frame."""
_, start_position = self.positions.first
diff --git a/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py b/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py
index 4883eb0..32af1c5 100644
--- a/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py
+++ b/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py
@@ -78,7 +78,7 @@ class Fixation(GazeFeatures.Fixation):
return self
- def draw(self, frame, color=(127, 127, 127), border_color=(255, 255, 255)):
+ def draw(self, frame: numpy.array, color=(127, 127, 127), border_color=(255, 255, 255)):
"""Draw fixation into frame."""
cv2.circle(frame, (int(self.focus[0]), int(self.focus[1])), int(self.deviation_max), color, -1)
@@ -91,7 +91,7 @@ class Saccade(GazeFeatures.Saccade):
def __post_init__(self):
super().__post_init__()
- def draw(self, frame, color=(255, 255, 255)):
+ def draw(self, frame: numpy.array, color=(255, 255, 255)):
"""Draw saccade into frame."""
_, start_position = self.positions.first
@@ -106,7 +106,7 @@ class GazeMovementIdentifier(GazeFeatures.GazeMovementIdentifier):
Dario D. Salvucci and Joseph H. Goldberg. 2000. Identifying fixations and
saccades in eye-tracking protocols. In Proceedings of the 2000 symposium
on Eye tracking research & applications (ETRA '00). ACM, New York, NY, USA,
- 71-78. [DOI=http://dx.doi.org/10.1145/355017.355028](DOI=http://dx.doi.org/10.1145/355017.355028)
+ 71-78. [http://dx.doi.org/10.1145/355017.355028](http://dx.doi.org/10.1145/355017.355028)
"""
velocity_max_threshold: int|float
diff --git a/src/argaze/GazeFeatures.py b/src/argaze/GazeFeatures.py
index 6209dc9..0e40238 100644
--- a/src/argaze/GazeFeatures.py
+++ b/src/argaze/GazeFeatures.py
@@ -88,7 +88,7 @@ class GazePosition():
else:
return distance < self.precision
- def draw(self, frame, color=(0, 255, 255), draw_precision=True):
+ def draw(self, frame: numpy.array, color=(0, 255, 255), draw_precision=True):
"""Draw gaze position point and precision circle."""
if self.valid:
@@ -190,7 +190,7 @@ class GazeMovement():
return output
- def draw_positions(self, frame, color=(0, 55, 55)):
+ def draw_positions(self, frame: numpy.array, color=(0, 55, 55)):
"""Draw gaze movement positions"""
gaze_positions = self.positions.copy()
@@ -224,7 +224,7 @@ class Fixation(GazeMovement):
raise NotImplementedError('merge() method not implemented')
- def draw(self, frame, color):
+ def draw(self, frame: numpy.array, color):
"""Draw fixation into frame."""
raise NotImplementedError('draw() method not implemented')
@@ -241,7 +241,7 @@ class Saccade(GazeMovement):
super().__post_init__()
- def draw(self, frame, color):
+ def draw(self, frame: numpy.array, color):
"""Draw saccade into frame."""
raise NotImplementedError('draw() method not implemented')
@@ -442,7 +442,7 @@ class ScanPath(list):
self.__last_fixation = fixation
- def draw(self, frame, fixation_color=(255, 255, 255), saccade_color=(255, 255, 255), deepness=0):
+ def draw(self, frame: numpy.array, fixation_color=(255, 255, 255), saccade_color=(255, 255, 255), deepness=0):
"""Draw scan path into frame."""
last_step = None
diff --git a/src/argaze/PupilAnalysis/__init__.py b/src/argaze/PupilAnalysis/__init__.py
index 41698b5..c563968 100644
--- a/src/argaze/PupilAnalysis/__init__.py
+++ b/src/argaze/PupilAnalysis/__init__.py
@@ -1,5 +1,4 @@
"""
Class interface to work with various pupil analysis algorithms.
"""
-__docformat__ = "restructuredtext"
__all__ = ['WorkloadIndex'] \ No newline at end of file
diff --git a/src/argaze/utils/README.md b/src/argaze/utils/README.md
index d5af45a..3c7d616 100644
--- a/src/argaze/utils/README.md
+++ b/src/argaze/utils/README.md
@@ -6,6 +6,8 @@ Collection of command-line high level features scripts.
!!! note
*Use -h option to get command arguments documentation.*
+<!--ready-start-->
+
# ArUco Markers factory
Export all markers from *DICT_APRILTAG_16h5* dictionary as 5 cm pictures with 300 dpi resolution into an *./src/argaze/utils/_export/markers* folder:
@@ -31,6 +33,10 @@ python ./src/argaze/utils/camera_calibrate.py 7 5 5 3 DICT_APRILTAG_16h5 -d DEVI
!!! note
Use **A3_DICT_APRILTAG_16h5_3cm_35cmx25cm.pdf** file located in *./src/argaze/ArUcoMarkers/utils/* folder ready to be printed on A3 paper sheet.
+<!--ready-end-->
+
+<!--demo-start-->
+
# AR environment demonstration
Load AR environment from **setup.json** file, detect ArUco markers into camera device (-d DEVICE) frames and estimate envirnoment pose.
@@ -60,4 +66,6 @@ A picture is saved by pressing escape key.
```
python ./src/argaze/utils/demo_heatmap_run.py
-``` \ No newline at end of file
+```
+
+<!--demo-end--> \ No newline at end of file
diff --git a/src/argaze/utils/__init__.py b/src/argaze/utils/__init__.py
index 8134e6f..94deb78 100644
--- a/src/argaze/utils/__init__.py
+++ b/src/argaze/utils/__init__.py
@@ -1,5 +1,4 @@
"""
-{!./src/argaze/utils/README.md!}
+Collection of command-line high level features scripts.
"""
-__docformat__ = "restructuredtext"
__all__ = ['MiscFeatures'] \ No newline at end of file