From 2e0f8f4200da7bba4667e4bc098a3c5d964ae40e Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Wed, 20 Apr 2022 20:40:50 +0200 Subject: Using @dataclass decoration to define base library data structures --- src/argaze/AreaOfInterest/AOI2DScene.py | 18 +----- src/argaze/AreaOfInterest/AOI3DScene.py | 41 ++++--------- src/argaze/AreaOfInterest/AOIFeatures.py | 68 ++++++++++------------ src/argaze/GazeFeatures.py | 34 +++++------ src/argaze/TobiiGlassesPro2/TobiiVideo.py | 10 ++-- .../export_tobii_segment_aruco_visual_scan.py | 2 +- src/argaze/utils/live_tobii_aruco_aois.py | 2 +- 7 files changed, 70 insertions(+), 105 deletions(-) diff --git a/src/argaze/AreaOfInterest/AOI2DScene.py b/src/argaze/AreaOfInterest/AOI2DScene.py index 929cc30..ee172f8 100644 --- a/src/argaze/AreaOfInterest/AOI2DScene.py +++ b/src/argaze/AreaOfInterest/AOI2DScene.py @@ -8,16 +8,7 @@ import cv2 as cv import matplotlib.path as mpath class AOI2DScene(AOIFeatures.AOIScene): - """Define AOI 2D scene as: - ``` - { - 'dimension': 2, - 'name 1': AOI 1, - 'name 2': AOI 2, - ... - } - ``` - """ + """Define AOI 2D scene.""" def __init__(self, **aois_2d): @@ -29,9 +20,7 @@ class AOI2DScene(AOIFeatures.AOIScene): def look_at(self, gaze_position: GazeFeatures.GazePosition): """Store gaze position as a pointer inside looked AOIs.""" - for name in self.areas(): - - aoi2D = self[name] + for name, aoi2D in self.areas.items(): if mpath.Path(aoi2D.vertices).contains_points([(gaze_position.x, gaze_position.y)])[0]: @@ -45,9 +34,8 @@ class AOI2DScene(AOIFeatures.AOIScene): def draw(self, frame): """Draw AOI polygons on frame.""" - for name in self.areas(): + for name, aoi2D in self.areas.items(): - aoi2D = self[name] inside = aoi2D.pointer != None color = (0, 255, 0) if inside else (0, 0, 255) diff --git a/src/argaze/AreaOfInterest/AOI3DScene.py b/src/argaze/AreaOfInterest/AOI3DScene.py index 2930983..d126f45 100644 --- a/src/argaze/AreaOfInterest/AOI3DScene.py +++ b/src/argaze/AreaOfInterest/AOI3DScene.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +from dataclasses import dataclass, field import math import re @@ -9,35 +10,18 @@ from argaze.AreaOfInterest import AOIFeatures, AOI2DScene import numpy import cv2 as cv +@dataclass class AOI3DScene(AOIFeatures.AOIScene): - """Define AOI 3D scene as: - ``` - { - 'dimension': 3, - 'rotation': (x, y, z) tuples, - 'translation': (x, y, z) tuples, - 'name 1': AOI 1, - 'name 2': AOI 2, - ... - } - ``` - """ - - def __init__(self, **aois_3d): + """Define AOI 3D scene.""" - # append rotation and translation matrix - aois_3d['rotation'] = numpy.asarray([0., 0., 0.]) - aois_3d['translation'] = numpy.asarray([0., 0., 0.]) + rotation: list = field(init=False, default=numpy.asarray([0., 0., 0.])) + translation: list = field(init=False, default=numpy.asarray([0., 0., 0.])) - super().__init__(**aois_3d) + def __post_init__(self, **aois): # set dimension member self.dimension = 3 - def areas(self): - """Get areas names""" - return self.keys()[3::] - def load(self, obj_filepath: str): """Load AOI3D scene from .obj file.""" @@ -103,7 +87,8 @@ class AOI3DScene(AOIFeatures.AOIScene): # retreive all aoi3D vertices for name, face in faces.items(): - self.append(name, AOIFeatures.AreaOfInterest(**{'vertices': [ vertices[i-1] for i in face ]})) + aoi3D = AOIFeatures.AreaOfInterest(vertices=[ vertices[i-1] for i in face ]) + self.append(name, aoi3D) except IOError: raise IOError(f'File not found: {obj_filepath}') @@ -112,11 +97,9 @@ class AOI3DScene(AOIFeatures.AOIScene): """Project 3D scene onto 2D scene according optical parameters. **Returns:** AOI2DScene""" - aoi2D_scene = {} - - for name in self.areas(): + aoi2D_scene = AOI2DScene.AOI2DScene() - aoi3D = self[name] + for name, aoi3D in self.areas.items(): vertices_3D = numpy.array(aoi3D.vertices).astype('float32') @@ -125,6 +108,6 @@ class AOI3DScene(AOIFeatures.AOIScene): aoi2D = AOIFeatures.AreaOfInterest(vertices_2D) - aoi2D_scene[name] = aoi2D + aoi2D_scene.append(name, aoi2D) - return AOI2DScene.AOI2DScene(**aoi2D_scene) + return aoi2D_scene diff --git a/src/argaze/AreaOfInterest/AOIFeatures.py b/src/argaze/AreaOfInterest/AOIFeatures.py index 4788100..b78a104 100644 --- a/src/argaze/AreaOfInterest/AOIFeatures.py +++ b/src/argaze/AreaOfInterest/AOIFeatures.py @@ -1,43 +1,39 @@ #!/usr/bin/env python +from dataclasses import dataclass, field + from argaze import DataStructures -class AreaOfInterest(DataStructures.DictObject): - """Define 2D/3D Area Of Interest - ``` - { - 'vertices': array of (x, y(,z)) tuples for each vertices of the area - 'pointer': None or (x, y(,z)) tuple to set where the area is looked - } - ``` - """ - - def __init__(self, vertices, pointer = None): - - super().__init__(type(self).__name__, **{'dimension': len(vertices[0]),'vertices': vertices, 'pointer': pointer}) - -class AOIScene(DataStructures.DictObject): - """Define AOI scene as: - ``` - { - 'dimension': 2 or 3, - 'name 1': AOI 1, - 'name 2': AOI 2, - ... - } - ``` - """ - - def __init__(self, **aois): - - # append dimension member - aois['dimension'] = None - - super().__init__(type(self).__name__, **aois) - - def areas(self): - """Get areas names.""" - return self.keys()[:-1] +@dataclass +class AreaOfInterest(): + """Define 2D/3D Area Of Interest.""" + + dimension: int = field(init=False, default=None) + """number of the coordinates to code vertice or pointer positions.""" + + vertices: list(tuple()) + """for each vertices of the area.""" + + pointer: tuple = None + """to set where the area is looked.""" + + def __post_init__(self): + + self.dimension = len(self.vertices[0]) + +@dataclass +class AOIScene(): + """Define 2D/3D AOI scene.""" + + dimension: int = field(init=False, default=None) + """dimension of the AOIs in 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 class TimeStampedAOIScenes(DataStructures.TimeStampedBuffer): """Define timestamped buffer to store AOI scenes in time.""" diff --git a/src/argaze/GazeFeatures.py b/src/argaze/GazeFeatures.py index dfbfc2d..a382ddc 100644 --- a/src/argaze/GazeFeatures.py +++ b/src/argaze/GazeFeatures.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +from dataclasses import dataclass import math from argaze import DataStructures @@ -9,16 +10,12 @@ import numpy FIXATION_MAX_DURATION = 1000 +@dataclass class GazePosition(DataStructures.DictObject): """Define gaze position.""" - def __init__(self, x, y): - - super().__init__(type(self).__name__, **{'x': x, 'y': y}) - - def __iter__(self): - yield self.x - yield self.y + x: float + y: float class TimeStampedGazePositions(DataStructures.TimeStampedBuffer): """Define timestamped buffer to store gaze positions.""" @@ -30,12 +27,13 @@ class TimeStampedGazePositions(DataStructures.TimeStampedBuffer): super().__setitem__(key, value) +@dataclass class Fixation(DataStructures.DictObject): """Define fixation""" - def __init__(self, duration, dispersion, cx, cy): - - super().__init__(type(self).__name__, **{'duration': duration, 'dispersion': dispersion, 'centroid': [cx, cy]}) + duration: float + dispersion: float + centroid: tuple((float, float)) class TimeStampedFixations(DataStructures.TimeStampedBuffer): """Define timestamped buffer to store fixations.""" @@ -177,7 +175,7 @@ class DispersionBasedFixationIdentifier(FixationIdentifier): if duration > 0: # return timestamp and fixation - return ts_list[0], Fixation(duration, dispersion, cx, cy) + return ts_list[0], Fixation(duration, dispersion, (cx, cy)) return -1, None @@ -193,12 +191,13 @@ class DispersionBasedFixationIdentifier(FixationIdentifier): return -1, None +@dataclass class VisualScanStep(DataStructures.DictObject): """Define a visual scan step as a duration, the name of the area of interest and all its frames during the step.""" - def __init__(self, duration, aoi, frames = []): - - super().__init__(type(self).__name__, **{'duration': duration, 'aoi': aoi, 'frames': frames}) + duration: float + aoi: str + frames: DataStructures.TimeStampedBuffer class TimeStampedVisualScanSteps(DataStructures.TimeStampedBuffer): """Define timestamped buffer to store visual scan steps.""" @@ -230,9 +229,7 @@ class VisualScanGenerator(): if step == None: continue - if step.get_type() == 'VisualScanStep': - - visual_scan_steps[ts] = step + visual_scan_steps[ts] = step return visual_scan_steps @@ -260,9 +257,8 @@ class PointerBasedVisualScan(VisualScanGenerator): #if not aoi_scene_current.looked: # raise ValueError('TimeStampedAOIScenes must be looked using look_at method.') - for name in aoi_scene_current.areas(): + for name, aoi in aoi_scene_current.areas.items(): - aoi = aoi_scene_current[name] aoi_looked = aoi.pointer != None if aoi_looked: diff --git a/src/argaze/TobiiGlassesPro2/TobiiVideo.py b/src/argaze/TobiiGlassesPro2/TobiiVideo.py index e6ec064..f3c8c78 100644 --- a/src/argaze/TobiiGlassesPro2/TobiiVideo.py +++ b/src/argaze/TobiiGlassesPro2/TobiiVideo.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +from dataclasses import dataclass, field import threading import uuid import time @@ -12,12 +13,13 @@ import cv2 as cv import av import numpy -class TobiiVideoFrame(DataStructures.DictObject): +@dataclass +class TobiiVideoFrame(): """Define tobii video frame""" - def __init__(self, matrix, width, height): - - super().__init__(type(self).__name__, **{'matrix': matrix, 'width': width, 'height': height}) + matrix: list + width: int + height: int class TobiiVideoSegment(): """Handle Tobii Glasses Pro 2 segment video file.""" diff --git a/src/argaze/utils/export_tobii_segment_aruco_visual_scan.py b/src/argaze/utils/export_tobii_segment_aruco_visual_scan.py index 290bdb7..312e10d 100644 --- a/src/argaze/utils/export_tobii_segment_aruco_visual_scan.py +++ b/src/argaze/utils/export_tobii_segment_aruco_visual_scan.py @@ -87,7 +87,7 @@ def main(): # Create AOIs 3D scene aoi3D_scene = AOI3DScene.AOI3DScene() aoi3D_scene.load(args.aoi_scene) - print(f'AOIs names: {aoi3D_scene.areas()}') + print(f'AOIs names: {aoi3D_scene.areas.keys()}') # Create timestamped buffer to store AOIs scene in time ts_aois_scenes = AOIFeatures.TimeStampedAOIScenes() diff --git a/src/argaze/utils/live_tobii_aruco_aois.py b/src/argaze/utils/live_tobii_aruco_aois.py index 1bc79eb..7642aaf 100644 --- a/src/argaze/utils/live_tobii_aruco_aois.py +++ b/src/argaze/utils/live_tobii_aruco_aois.py @@ -57,7 +57,7 @@ def main(): # Create AOIs 3D scene aoi3D_scene = AOI3DScene.AOI3DScene() aoi3D_scene.load(args.aoi_scene) - print(f'AOIs names: {aoi3D_scene.areas()}') + print(f'AOIs names: {aoi3D_scene.areas.keys()}') # Start streaming tobii_controller.start_streaming() -- cgit v1.1