aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/argaze/AreaOfInterest/AOI2DScene.py49
-rw-r--r--src/argaze/GazeFeatures.py57
2 files changed, 49 insertions, 57 deletions
diff --git a/src/argaze/AreaOfInterest/AOI2DScene.py b/src/argaze/AreaOfInterest/AOI2DScene.py
index b2dba39..73c977f 100644
--- a/src/argaze/AreaOfInterest/AOI2DScene.py
+++ b/src/argaze/AreaOfInterest/AOI2DScene.py
@@ -26,15 +26,20 @@ class AOI2DScene(AOIFeatures.AOIScene):
super().__init__(2, aois_2d)
- def draw(self, image: numpy.array, exclude=[], color=(0, 255, 255)):
- """Draw AOI polygons on image."""
+ def draw(self, image: numpy.array, draw_aoi: dict = None, exclude=[]):
+ """Draw AOI polygons on image.
+
+ Parameters:
+ draw_aoi: AOIFeatures.AOI.draw parameters (if None, no aoi is drawn)
+ """
for name, aoi in self.items():
if name in exclude:
continue
- aoi.draw(image, color)
+ if draw_aoi:
+ aoi.draw(image, **draw_aoi)
def raycast(self, pointer:tuple) -> Tuple[str, "AOIFeatures.AreaOfInterest", bool]:
"""Iterate over aoi to know which aoi is matching the given pointer position.
@@ -69,48 +74,20 @@ class AOI2DScene(AOIFeatures.AOIScene):
aoi.draw(image, color)
def circlecast(self, center:tuple, radius:float) -> Tuple[str, "AOIFeatures.AreaOfInterest", numpy.array, float, float]:
- """Iterate over areas to know which aoi is matching circle.
+ """Iterate over areas to know which aoi is matched circle.
Returns:
aoi name
aoi object
matching region points
- ratio of matching region area relatively to aoi area
- ratio of matching region area relatively to circle area
+ ratio of matched region area relatively to aoi area
+ ratio of matched region area relatively to circle area
"""
for name, aoi in self.items():
- matching_region, aoi_ratio, circle_ratio = aoi.circle_intersection(center, radius)
-
- yield name, aoi, matching_region, aoi_ratio, circle_ratio
-
- def draw_circlecast(self, image: numpy.array, center:tuple, radius:float, matching_aoi = [], 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):
-
- if name in exclude:
- continue
-
- color = base_color
-
- # Draw matching region
- if aoi_ratio > 0:
- matching_region.draw(image, color, 4)
-
- # Is aoi part of matching aoi?
- if name in matching_aoi:
-
- color = matching_color
-
- top_left_corner_pixel = numpy.rint(aoi.clockwise()[0]).astype(int)
- cv2.putText(image, name, top_left_corner_pixel, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA)
+ matched_region, aoi_ratio, circle_ratio = aoi.circle_intersection(center, radius)
- # Draw matching region
- matching_region.draw(image, matching_color, 4)
-
- # Draw form
- aoi.draw(image, color)
+ yield name, aoi, matched_region, aoi_ratio, circle_ratio
def reframe(self, aoi: AOIFeatures.AreaOfInterest, size: tuple) -> AOI2DSceneType:
"""
diff --git a/src/argaze/GazeFeatures.py b/src/argaze/GazeFeatures.py
index 2875c8e..3946e0c 100644
--- a/src/argaze/GazeFeatures.py
+++ b/src/argaze/GazeFeatures.py
@@ -91,7 +91,7 @@ class GazePosition():
else:
return distance < self.precision
- def draw(self, image: numpy.array, color=(0, 255, 255), draw_precision=True):
+ def draw(self, image: numpy.array, color=(255, 255, 255), draw_precision=True):
"""Draw gaze position point and precision circle."""
if self.valid:
@@ -276,8 +276,13 @@ class GazeMovement():
return self
- def draw_positions(self, image: numpy.array, color=(0, 55, 55)):
- """Draw gaze movement positions"""
+ def draw_positions(self, image: numpy.array, position_color: tuple = None, line_color: tuple = None):
+ """Draw gaze movement positions with line between each position.
+
+ Parameters:
+ position_color: color of position point
+ line_color: color of line between each position
+ """
gaze_positions = self.positions.copy()
@@ -286,13 +291,17 @@ class GazeMovement():
ts_start, start_gaze_position = gaze_positions.pop_first()
ts_next, next_gaze_position = gaze_positions.first
- # Draw start gaze
- start_gaze_position.draw(image, draw_precision=False)
+ # Draw position if required
+ if position_color:
- # Draw movement from start to next
- cv2.line(image, (int(start_gaze_position[0]), int(start_gaze_position[1])), (int(next_gaze_position[0]), int(next_gaze_position[1])), color, 1)
+ start_gaze_position.draw(image, position_color, draw_precision=False)
- def draw(self, image: numpy.array, color):
+ # Draw line between positions if required
+ if line_color:
+
+ cv2.line(image, (int(start_gaze_position[0]), int(start_gaze_position[1])), (int(next_gaze_position[0]), int(next_gaze_position[1])), line_color, 1)
+
+ def draw(self, image: numpy.array):
"""Draw gaze movement into image."""
raise NotImplementedError('draw() method not implemented')
@@ -662,19 +671,19 @@ class ScanPath(list):
self.__last_fixation = fixation
- def draw(self, image: numpy.array, fixation_color=(255, 255, 255), saccade_color=(255, 255, 255), deepness=0):
- """Draw scan path into image."""
-
- last_step = None
- for step in self[-deepness:]:
-
- if last_step != None:
+ def draw(self, image: numpy.array, draw_fixations: dict = None, draw_saccades: dict = None, deepness=0):
+ """Draw scan path into image.
- cv2.line(image, (int(last_step.first_fixation.focus[0]), int(last_step.first_fixation.focus[1])), (int(step.first_fixation.focus[0]), int(step.first_fixation.focus[1])), saccade_color, 2)
+ Parameters:
+ draw_fixations: Fixation.draw parameters (which depends of the loaded gaze movement identifier module, if None, no fixation is drawn)
+ draw_saccades: Saccade.draw parameters (which depends of the loaded gaze movement identifier module, if None, no saccade is drawn)
+ deepness: number of steps back to draw
+ """
- last_step.first_fixation.draw(image, fixation_color)
+ for step in self[-deepness:]:
- last_step = step
+ step.first_fixation.draw(image, **draw_fixations)
+ step.last_saccade.draw(image, **draw_saccades)
class ScanPathAnalyzer():
"""Abstract class to define what should provide a scan path analyzer."""
@@ -704,17 +713,23 @@ class ScanPathAnalyzer():
class AOIMatcher():
"""Abstract class to define what should provide an AOI matcher algorithm."""
- def match(self, aoi_scene: AOIFeatures.AOIScene, gaze_movement: GazeMovement, exclude=[]) -> str:
+ def match(self, aoi_scene: AOIFeatures.AOIScene, gaze_movement: GazeMovement, exclude=[]) -> Tuple[str, AOIFeatures.AreaOfInterest]:
"""Which AOI is looked in the scene?"""
raise NotImplementedError('match() method not implemented')
@property
- def looked_aoi(self) -> str:
- """Get most likely looked aoi name."""
+ def looked_aoi(self) -> AOIFeatures.AreaOfInterest:
+ """Get most likely looked aoi."""
raise NotImplementedError('looked_aoi getter not implemented')
+ @property
+ def looked_aoi_name(self) -> str:
+ """Get most likely looked aoi name."""
+
+ raise NotImplementedError('looked_aoi_name getter not implemented')
+
AOIScanStepType = TypeVar('AOIScanStep', bound="AOIScanStep")
# Type definition for type annotation convenience