diff options
Diffstat (limited to 'src/argaze/AreaOfInterest/AOIFeatures.py')
-rw-r--r-- | src/argaze/AreaOfInterest/AOIFeatures.py | 66 |
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.""" |