aboutsummaryrefslogtreecommitdiff
path: root/src/argaze/AreaOfInterest/AOIFeatures.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/argaze/AreaOfInterest/AOIFeatures.py')
-rw-r--r--src/argaze/AreaOfInterest/AOIFeatures.py66
1 files changed, 40 insertions, 26 deletions
diff --git a/src/argaze/AreaOfInterest/AOIFeatures.py b/src/argaze/AreaOfInterest/AOIFeatures.py
index 001affa..4495df5 100644
--- a/src/argaze/AreaOfInterest/AOIFeatures.py
+++ b/src/argaze/AreaOfInterest/AOIFeatures.py
@@ -40,21 +40,21 @@ class AreaOfInterest(numpy.ndarray):
return mpath.Path(self).contains_points([gaze_position])[0]
- def look_at(self, gaze_position):
- """Get where the area is looked using non orthogonal projection."""
+ def look_at(self, gaze_pixel):
+ """Get where the area is looked using perpespective transformation."""
if self.dimension() != 2:
raise RuntimeError(f'Bad area dimension ({self.dimension()})')
- clockwise_area = self.clockwise()
+ Src = self.clockwise()
+ Src_origin = Src[0]
+ Src = (Src - Src_origin).reshape((len(Src)), 2)
- O = clockwise_area[0] # Origin
- G = numpy.array(gaze_position) - O # Gaze point
+ Dst = numpy.array([[0., 0.], [1., 0.], [1., 1.], [0., 1.]]).astype(numpy.float32)
- M = numpy.array([clockwise_area[1] - O, clockwise_area[-1] - O]) # Basis projection matrix M = {U | V}
- Mt = numpy.transpose(M)
-
- Gp = numpy.dot(numpy.dot(numpy.linalg.inv(numpy.dot(Mt, M)), Mt), G)# Projected gaze point
+ P = cv.getPerspectiveTransform(Src, Dst)
+ G = gaze_pixel - Src_origin
+ Gp = numpy.dot(P, numpy.array([G[0], G[1], 1]))[:-1]
return numpy.around(Gp, 4).tolist()
@@ -64,40 +64,54 @@ class AreaOfInterest(numpy.ndarray):
if self.dimension() != 2:
raise RuntimeError(f'Bad area dimension ({self.dimension()})')
- clockwise_area = self.clockwise()
+ Src = numpy.array([[0., 0.], [1., 0.], [1., 1.], [0., 1.]]).astype(numpy.float32)
- O = clockwise_area[0] # Origin
- Gp = numpy.array(look_at) # Projected gaze point
- M = numpy.array([clockwise_area[1] - O, clockwise_area[-1] - O]) # Basis projection matrix M = {U | V}
- Mt = numpy.transpose(M)
+ Dst = self.clockwise()
+ Dst_origin = Dst[0]
+ Dst = (Dst - Dst_origin).reshape((len(Dst)), 2)
- Lp = O + numpy.dot(M, Gp) # Projected gaze pixel
+ P = cv.getPerspectiveTransform(Src, Dst)
+ L = look_at
+ Lp = Dst_origin + numpy.dot(P, numpy.array([L[0], L[1], 1]))[:-1]
return numpy.rint(Lp).astype(int).tolist()
def draw(self, frame, color):
# Draw form
- cv.line(frame, self[-1], self[0], color, 1)
- for A, B in zip(self, self[1:]):
+ pixels = numpy.rint(self).astype(int)
+ cv.line(frame, pixels[-1], pixels[0], color, 1)
+ for A, B in zip(pixels, pixels[1:]):
cv.line(frame, A, B, color, 1)
# Draw center
- cv.circle(frame, self.center().astype(int), 1, color, -1)
+ center_pixel = numpy.rint(self.center()).astype(int)
+ cv.circle(frame, center_pixel, 1, color, -1)
@dataclass
class AOIScene():
- """Define 2D/3D AOI scene."""
+ """Define 2D/3D AOI scene."""
- dimension: int = field(init=False, default=None)
- """Dimension of the AOIs in scene."""
+ dimension: int = field(init=False, repr=False, default=None)
+ """Dimension of the AOIs in scene."""
- areas: dict = field(init=False, default_factory=dict)
- """All aois in the scene."""
+ areas: dict = field(init=False, default_factory=dict)
+ """All aois in the scene."""
- def append(self, name, aoi: AreaOfInterest):
- """Add an aoi to the scene."""
- self.areas[name] = aoi
+ def __getitem__(self, key):
+ """Get an aoi from the scene."""
+ return numpy.array(self.areas[key]).astype(numpy.float32).view(AreaOfInterest)
+
+ def __setitem__(self, name, aoi: AreaOfInterest):
+ """Add an aoi to the scene."""
+ self.areas[name] = aoi.tolist()
+
+ def items(self):
+ for name, area in self.areas.items():
+ yield name, numpy.array(area).astype(numpy.float32).view(AreaOfInterest)
+
+ def keys(self):
+ return self.areas.keys()
class TimeStampedAOIScenes(DataStructures.TimeStampedBuffer):
"""Define timestamped buffer to store AOI scenes in time."""