diff options
author | Théo de la Hogue | 2024-01-23 14:54:30 +0100 |
---|---|---|
committer | Théo de la Hogue | 2024-01-23 14:54:30 +0100 |
commit | d904b99cc969c977f911d36cfeb2279544c528e5 (patch) | |
tree | ff23c5f0a3279581a86aafe98f86a2894d9f00f1 /src | |
parent | 8d0a2f7c4f3483aac55603a141e23e1dcc5b0dca (diff) | |
download | argaze-d904b99cc969c977f911d36cfeb2279544c528e5.zip argaze-d904b99cc969c977f911d36cfeb2279544c528e5.tar.gz argaze-d904b99cc969c977f911d36cfeb2279544c528e5.tar.bz2 argaze-d904b99cc969c977f911d36cfeb2279544c528e5.tar.xz |
Defing PipelineStepObject and PipelineStepMethod to assess time execution. Removing exception managment to let the user catch them into the script.
Diffstat (limited to 'src')
22 files changed, 122 insertions, 161 deletions
diff --git a/src/argaze/ArFeatures.py b/src/argaze/ArFeatures.py index f023d08..cb9658e 100644 --- a/src/argaze/ArFeatures.py +++ b/src/argaze/ArFeatures.py @@ -94,7 +94,7 @@ DEFAULT_ARLAYER_DRAW_PARAMETERS = { } @dataclass -class ArLayer(DataFeatures.SharedObject): +class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject): """ Defines a space where to make matching of gaze movements and AOI and inside which those matchings need to be analyzed. @@ -367,7 +367,7 @@ class ArLayer(DataFeatures.SharedObject): return self.__new_analysis_available - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def look(self, timestamp: int|float, gaze_movement: GazeFeatures.GazePosition = GazeFeatures.UnvalidGazePosition()) -> dict: """ Project timestamped gaze movement into layer. @@ -398,7 +398,7 @@ class ArLayer(DataFeatures.SharedObject): # Update looked aoi thanks to aoi matcher # Note: don't filter valid/unvalid and finished/unfinished fixation/saccade as we don't know how the aoi matcher works internally - self.__looked_aoi_name, _ , match_time, match_exception = self.aoi_matcher.match(self.aoi_scene, gaze_movement) + self.__looked_aoi_name, _ = self.aoi_matcher.match(self.aoi_scene, gaze_movement) # Valid and finished gaze movement has been identified if gaze_movement.valid and gaze_movement.finished: @@ -416,7 +416,7 @@ class ArLayer(DataFeatures.SharedObject): for aoi_scan_path_analyzer_module_path, aoi_scan_path_analyzer in self.aoi_scan_path_analyzers.items(): # Analyze aoi scan path - analyze_time, analyze_exception = aoi_scan_path_analyzer.analyze(self.aoi_scan_path) + aoi_scan_path_analyzer.analyze(self.aoi_scan_path) # Update new analysis available state self.__new_analysis_available = True @@ -490,7 +490,7 @@ DEFAULT_ARFRAME_IMAGE_PARAMETERS = { } @dataclass -class ArFrame(DataFeatures.SharedObject): +class ArFrame(DataFeatures.SharedObject, DataFeatures.PipelineStepObject): """ Defines a rectangular area where to project in timestamped gaze positions and inside which they need to be analyzed. @@ -822,7 +822,7 @@ class ArFrame(DataFeatures.SharedObject): return self.__new_analysis_available - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def look(self, timestamp: int|float, gaze_position: GazeFeatures.GazePosition = GazeFeatures.UnvalidGazePosition()): """ Project gaze position into frame. @@ -861,7 +861,7 @@ class ArFrame(DataFeatures.SharedObject): if self.gaze_movement_identifier is not None: # Identify finished gaze movement - self.__identified_gaze_movement, identify_time, identify_exception = self.gaze_movement_identifier.identify(timestamp, self.__calibrated_gaze_position) + self.__identified_gaze_movement = self.gaze_movement_identifier.identify(timestamp, self.__calibrated_gaze_position) # Valid and finished gaze movement has been identified if self.__identified_gaze_movement.valid and self.__identified_gaze_movement.finished: @@ -886,7 +886,7 @@ class ArFrame(DataFeatures.SharedObject): for scan_path_analyzer_module_path, scan_path_analyzer in self.scan_path_analyzers.items(): # Analyze aoi scan path - analyze_time, analyze_exception = scan_path_analyzer.analyze(self.scan_path) + scan_path_analyzer.analyze(self.scan_path) # Update new analysis available state self.__new_analysis_available = True @@ -903,13 +903,13 @@ class ArFrame(DataFeatures.SharedObject): scale = numpy.array([self.heatmap.size[0] / self.size[0], self.heatmap.size[1] / self.size[1]]) # Update heatmap image - update_time, update_exception = self.heatmap.update(self.__calibrated_gaze_position.value * scale) + self.heatmap.update(self.__calibrated_gaze_position.value * scale) # Look layers with valid identified gaze movement # Note: don't filter valid/unvalid finished/unfished gaze movement to allow layers to reset internally for layer_name, layer in self.layers.items(): - look_time, look_exception = layer.look(timestamp, self.__identified_gaze_movement) + layer.look(timestamp, self.__identified_gaze_movement) # Log look data for logger_name, logger in self.loggers.items(): @@ -1370,7 +1370,7 @@ class ArCamera(ArFrame): yield scene_frame - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def watch(self, timestamp: int|float, image: numpy.array): """Detect AR features from image and project scenes into camera frame. @@ -1393,7 +1393,10 @@ class ArCamera(ArFrame): """ # Project gaze position into camera frame - yield self, super().look(timestamp, gaze_position) + super().look(timestamp, gaze_position) + + # yield camera frame to process its results + yield self # Lock camera frame exploitation self.acquire() @@ -1415,8 +1418,11 @@ class ArCamera(ArFrame): # QUESTION: How to project gaze precision? inner_gaze_position = GazeFeatures.GazePosition((inner_x, inner_y)) + + scene_frame.look(timestamp, inner_gaze_position * scene_frame.size) - yield scene_frame, scene_frame.look(timestamp, inner_gaze_position * scene_frame.size) + # yield scene frame to process its results + yield scene_frame # Ignore missing aoi in camera frame layer projection except KeyError: diff --git a/src/argaze/ArUcoMarkers/ArUcoCamera.py b/src/argaze/ArUcoMarkers/ArUcoCamera.py index 86802a5..52979cc 100644 --- a/src/argaze/ArUcoMarkers/ArUcoCamera.py +++ b/src/argaze/ArUcoMarkers/ArUcoCamera.py @@ -142,15 +142,12 @@ class ArUcoCamera(ArFeatures.ArCamera): return ArUcoCamera.from_dict(aruco_camera_data, working_directory) - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def watch(self, timestamp: int|float, image: numpy.array) -> Tuple[float, float, dict]: """Detect environment aruco markers from image and project scenes into camera frame. !!! note This method timestamps camera frame and its layers. - - Returns: - detection time: aruco marker detection time in ms. """ # Use try block to always release the camera frame lock in finally block @@ -160,7 +157,7 @@ class ArUcoCamera(ArFeatures.ArCamera): self.acquire() # Detect aruco markers - detection_time = self.aruco_detector.detect_markers(image) + self.aruco_detector.detect_markers(image) # Fill camera frame background with image self.background = image @@ -212,9 +209,6 @@ class ArUcoCamera(ArFeatures.ArCamera): # Timestamp camera frame self.timestamp = timestamp - # Return detection time - return detection_time - def __image(self, draw_detected_markers: dict = None, draw_scenes: dict = None, draw_optic_parameters_grid: dict = None, **kwargs: dict) -> numpy.array: """Get frame image with ArUco detection visualisation. diff --git a/src/argaze/ArUcoMarkers/ArUcoDetector.py b/src/argaze/ArUcoMarkers/ArUcoDetector.py index e6a305f..6d12f3d 100644 --- a/src/argaze/ArUcoMarkers/ArUcoDetector.py +++ b/src/argaze/ArUcoMarkers/ArUcoDetector.py @@ -14,6 +14,7 @@ import os from collections import Counter import time +from argaze import DataFeatures from argaze.ArUcoMarkers import ArUcoMarkersDictionary, ArUcoMarker, ArUcoOpticCalibrator import numpy @@ -131,7 +132,7 @@ class DetectorParameters(): return self.__parameters @dataclass -class ArUcoDetector(): +class ArUcoDetector(DataFeatures.PipelineStepObject): """ArUco markers detector. Parameters: @@ -255,6 +256,7 @@ class ArUcoDetector(): return output + @DataFeatures.PipelineStepMethod def detect_markers(self, image: numpy.array) -> float: """Detect all ArUco markers into an image. diff --git a/src/argaze/AreaOfInterest/AOIFeatures.py b/src/argaze/AreaOfInterest/AOIFeatures.py index e20717c..6457e8f 100644 --- a/src/argaze/AreaOfInterest/AOIFeatures.py +++ b/src/argaze/AreaOfInterest/AOIFeatures.py @@ -554,7 +554,7 @@ HeatmapType = TypeVar('Heatmap', bound="Heatmap") # Type definition for type annotation convenience @dataclass -class Heatmap(): +class Heatmap(DataFeatures.PipelineStepObject): """Define image to draw heatmap.""" size: tuple = field(default=(1, 1)) @@ -599,7 +599,7 @@ class Heatmap(): self.__point_spread_buffer = [] self.__point_spread_buffer_size = self.buffer - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def update(self, point: tuple): """Update heatmap image.""" diff --git a/src/argaze/DataFeatures.py b/src/argaze/DataFeatures.py index 2636aaa..fec9f6d 100644 --- a/src/argaze/DataFeatures.py +++ b/src/argaze/DataFeatures.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -"""Timestamped data features.""" +"""Miscellaneous data features.""" __author__ = "Théo de la Hogue" __credits__ = [] @@ -104,76 +104,6 @@ class JsonEncoder(json.JSONEncoder): return public_dict -class SharedObject(): - """Enable multiple threads sharing.""" - - def __init__(self): - self._lock = threading.Lock() - self._timestamp = math.nan - self._token = None - - def acquire(self): - self._lock.acquire() - - def release(self): - self._lock.release() - - def locked(self) -> bool: - return self._lock.locked() - - @property - def timestamp(self) -> int|float: - """Get timestamp""" - - self._lock.acquire() - timestamp = self._timestamp - self._lock.release() - - return timestamp - - @timestamp.setter - def timestamp(self, timestamp: int|float): - """Set timestamp""" - - self._lock.acquire() - self._timestamp = timestamp - self._lock.release() - - def untimestamp(self): - """Reset timestamp""" - - self._lock.acquire() - self._timestamp = math.nan - self._lock.release() - - @property - def timestamped(self) -> bool: - """Is the object timestamped?""" - - self._lock.acquire() - timestamped = not math.isnan(self._timestamp) - self._lock.release() - - return timestamped - - @property - def token(self) -> any: - """Get token""" - - self._lock.acquire() - token = self._token - self._lock.release() - - return token - - @token.setter - def token(self, token: any): - """Set token""" - - self._lock.acquire() - self._token = token - self._lock.release() - class TimeStampedBuffer(collections.OrderedDict): """Ordered dictionary to handle timestamped data. ``` @@ -429,50 +359,86 @@ class DataDictionary(dict): __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ -def PipelineStep(method): - """Define a decorator to declare a method as a pipeline step.""" +class SharedObject(): + """Abstract class to enable multiple threads sharing and timestamp management.""" - def handler(*args, **kw) -> Tuple[Any, float, Exception]: - """Handle pipeline step - - Returns: - method_returns: what the handled method returns - execution_time: measure of method time execution in millisecond. - exception: any error catched during method execution. - """ + def __init__(self): + self._lock = threading.Lock() + self._timestamp = math.nan + self._execution_times = {} + self._exceptions = {} - # Initialize execution time assessment - start = time.perf_counter() - - try: + def acquire(self): + self._lock.acquire() - result = method(*args, **kw) - exception = None + def release(self): + self._lock.release() - except Exception as e: + def locked(self) -> bool: + return self._lock.locked() - result = None - exception = e - - # Measure execution time - execution_time = (time.perf_counter() - start) * 1e3 + @property + def timestamp(self) -> int|float: + """Get timestamp""" - # Edit result tuple - if type(result) is tuple: + self._lock.acquire() + timestamp = self._timestamp + self._lock.release() - result = result + (execution_time, exception) + return timestamp - elif result is not None: + @timestamp.setter + def timestamp(self, timestamp: int|float): + """Set timestamp""" - result = result, execution_time, exception + self._lock.acquire() + self._timestamp = timestamp + self._lock.release() - else: + def untimestamp(self): + """Reset timestamp""" + + self._lock.acquire() + self._timestamp = math.nan + self._lock.release() + + @property + def timestamped(self) -> bool: + """Is the object timestamped?""" + + self._lock.acquire() + timestamped = not math.isnan(self._timestamp) + self._lock.release() + + return timestamped + +class PipelineStepObject(): + """Abstract class to assess pipeline step methods execution time.""" + + execution_times: dict = {} + """Execution time for each mehtod in ms.""" + +def PipelineStepMethod(method): + """Define a decorator use into PipelineStepObject class to declare pipeline method.""" + + def wrapper(self, *args, **kw): + """Wrap pipeline step method to measure execution time.""" + + # Initialize execution time assessment + start = time.perf_counter() + + try: + + result = method(self, *args, **kw) + + finally: - result = execution_time, exception + # Measure execution time + self.execution_times[method.__name__] = (time.perf_counter() - start) * 1e3 return result - return handler + return wrapper # Import libraries that can be used in selector or formatter codes from argaze import GazeFeatures diff --git a/src/argaze/GazeAnalysis/Basic.py b/src/argaze/GazeAnalysis/Basic.py index 455ca2e..b75932a 100644 --- a/src/argaze/GazeAnalysis/Basic.py +++ b/src/argaze/GazeAnalysis/Basic.py @@ -26,7 +26,7 @@ class ScanPathAnalyzer(GazeFeatures.ScanPathAnalyzer): self.__steps_number = 0 self.__step_fixation_durations_average = 0 - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, scan_path: GazeFeatures.ScanPathType): self.__path_duration = scan_path.duration @@ -71,7 +71,7 @@ class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): self.__steps_number = 0 self.__step_fixation_durations_average = 0 - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, aoi_scan_path: GazeFeatures.ScanPathType): self.__path_duration = aoi_scan_path.duration diff --git a/src/argaze/GazeAnalysis/DeviationCircleCoverage.py b/src/argaze/GazeAnalysis/DeviationCircleCoverage.py index c86ebed..f890701 100644 --- a/src/argaze/GazeAnalysis/DeviationCircleCoverage.py +++ b/src/argaze/GazeAnalysis/DeviationCircleCoverage.py @@ -37,7 +37,7 @@ class AOIMatcher(GazeFeatures.AOIMatcher): self.__matched_gaze_movement = None self.__matched_region = None - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def match(self, aoi_scene, gaze_movement) -> Tuple[str, AOIFeatures.AreaOfInterest]: """Returns AOI with the maximal fixation's deviation circle coverage if above coverage threshold.""" diff --git a/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py index f3ee608..011c272 100644 --- a/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py +++ b/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py @@ -142,7 +142,7 @@ class GazeMovementIdentifier(GazeFeatures.GazeMovementIdentifier): self.__fixation_positions = GazeFeatures.TimeStampedGazePositions() self.__saccade_positions = GazeFeatures.TimeStampedGazePositions() - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def identify(self, ts, gaze_position, terminate=False) -> GazeMovementType: # Ignore non valid gaze position diff --git a/src/argaze/GazeAnalysis/Entropy.py b/src/argaze/GazeAnalysis/Entropy.py index a391092..a62dfe6 100644 --- a/src/argaze/GazeAnalysis/Entropy.py +++ b/src/argaze/GazeAnalysis/Entropy.py @@ -37,7 +37,7 @@ class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): self.__stationary_entropy = -1 self.__transition_entropy = -1 - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, aoi_scan_path: GazeFeatures.AOIScanPathType): assert(len(aoi_scan_path) > 1) diff --git a/src/argaze/GazeAnalysis/ExploreExploitRatio.py b/src/argaze/GazeAnalysis/ExploreExploitRatio.py index d4c0b6c..5516349 100644 --- a/src/argaze/GazeAnalysis/ExploreExploitRatio.py +++ b/src/argaze/GazeAnalysis/ExploreExploitRatio.py @@ -33,7 +33,7 @@ class ScanPathAnalyzer(GazeFeatures.ScanPathAnalyzer): self.__explore_exploit_ratio = 0. - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, scan_path: GazeFeatures.ScanPathType): assert(len(scan_path) > 1) diff --git a/src/argaze/GazeAnalysis/FocusPointInside.py b/src/argaze/GazeAnalysis/FocusPointInside.py index 62ce054..d559ac2 100644 --- a/src/argaze/GazeAnalysis/FocusPointInside.py +++ b/src/argaze/GazeAnalysis/FocusPointInside.py @@ -30,7 +30,7 @@ class AOIMatcher(GazeFeatures.AOIMatcher): self.__looked_aoi_data = (None, None) self.__matched_gaze_movement = None - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def match(self, aoi_scene, gaze_movement) -> Tuple[str, AOIFeatures.AreaOfInterest]: """Returns AOI containing fixation focus point.""" diff --git a/src/argaze/GazeAnalysis/KCoefficient.py b/src/argaze/GazeAnalysis/KCoefficient.py index c6dfa15..41338a3 100644 --- a/src/argaze/GazeAnalysis/KCoefficient.py +++ b/src/argaze/GazeAnalysis/KCoefficient.py @@ -30,7 +30,7 @@ class ScanPathAnalyzer(GazeFeatures.ScanPathAnalyzer): self.__K = 0 - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, scan_path: GazeFeatures.ScanPathType): assert(len(scan_path) > 1) @@ -87,7 +87,7 @@ class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): self.__K = 0 - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, aoi_scan_path: GazeFeatures.AOIScanPathType) -> float: assert(len(aoi_scan_path) > 1) diff --git a/src/argaze/GazeAnalysis/LempelZivComplexity.py b/src/argaze/GazeAnalysis/LempelZivComplexity.py index 67e5001..f6a49ab 100644 --- a/src/argaze/GazeAnalysis/LempelZivComplexity.py +++ b/src/argaze/GazeAnalysis/LempelZivComplexity.py @@ -31,7 +31,7 @@ class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): self.__lempel_ziv_complexity = 0 - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, aoi_scan_path: GazeFeatures.AOIScanPathType): assert(len(aoi_scan_path) > 1) diff --git a/src/argaze/GazeAnalysis/NGram.py b/src/argaze/GazeAnalysis/NGram.py index b1e5ab3..2526123 100644 --- a/src/argaze/GazeAnalysis/NGram.py +++ b/src/argaze/GazeAnalysis/NGram.py @@ -35,7 +35,7 @@ class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): self.__ngrams_count = {} - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, aoi_scan_path: GazeFeatures.AOIScanPathType): assert(len(aoi_scan_path) > 1) diff --git a/src/argaze/GazeAnalysis/NearestNeighborIndex.py b/src/argaze/GazeAnalysis/NearestNeighborIndex.py index 1dc692e..72df516 100644 --- a/src/argaze/GazeAnalysis/NearestNeighborIndex.py +++ b/src/argaze/GazeAnalysis/NearestNeighborIndex.py @@ -35,7 +35,7 @@ class ScanPathAnalyzer(GazeFeatures.ScanPathAnalyzer): self.__nearest_neighbor_index = 0 - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, scan_path: GazeFeatures.ScanPathType): assert(len(scan_path) > 1) diff --git a/src/argaze/GazeAnalysis/TransitionMatrix.py b/src/argaze/GazeAnalysis/TransitionMatrix.py index 313c945..d001947 100644 --- a/src/argaze/GazeAnalysis/TransitionMatrix.py +++ b/src/argaze/GazeAnalysis/TransitionMatrix.py @@ -33,7 +33,7 @@ class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): self.__transition_matrix_probabilities = pandas.DataFrame() self.__transition_matrix_density = 0. - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, aoi_scan_path: GazeFeatures.AOIScanPathType): assert(len(aoi_scan_path) > 1) diff --git a/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py b/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py index 1e486e1..2c3ecd1 100644 --- a/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py +++ b/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py @@ -142,7 +142,7 @@ class GazeMovementIdentifier(GazeFeatures.GazeMovementIdentifier): self.__fixation_positions = GazeFeatures.TimeStampedGazePositions() self.__saccade_positions = GazeFeatures.TimeStampedGazePositions() - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def identify(self, ts, gaze_position, terminate=False) -> GazeMovementType: # Ignore non valid gaze position diff --git a/src/argaze/GazeFeatures.py b/src/argaze/GazeFeatures.py index c9269e0..d70cdc6 100644 --- a/src/argaze/GazeFeatures.py +++ b/src/argaze/GazeFeatures.py @@ -520,10 +520,10 @@ class TimeStampedGazeStatus(DataFeatures.TimeStampedBuffer): super().__setitem__(key, value) -class GazeMovementIdentifier(): +class GazeMovementIdentifier(DataFeatures.PipelineStepObject): """Abstract class to define what should provide a gaze movement identifier.""" - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def identify(self, timestamp: int|float, gaze_position: GazePosition, terminate:bool=False) -> Tuple[GazeMovementType, GazeMovementType]: """Identify gaze movement from successive timestamped gaze positions. Each identified gaze movement should share its first/last gaze position with previous/next gaze movement. @@ -816,7 +816,7 @@ class ScanPath(list): step.last_saccade.draw(image, **draw_saccades) -class ScanPathAnalyzer(): +class ScanPathAnalyzer(DataFeatures.PipelineStepObject): """Abstract class to define what should provide a scan path analyzer.""" def __init__(self): @@ -836,14 +836,14 @@ class ScanPathAnalyzer(): return DataFeatures.DataDictionary(analysis) - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, scan_path: ScanPathType): """Analyze scan path.""" raise NotImplementedError('analyze() method not implemented') @dataclass -class AOIMatcher(): +class AOIMatcher(DataFeatures.PipelineStepObject): """Abstract class to define what should provide an AOI matcher algorithm.""" exclude: list[str] = field(default_factory = list) @@ -1154,7 +1154,7 @@ class AOIScanPath(list): return scan_fixations_count, aoi_fixations_count -class AOIScanPathAnalyzer(): +class AOIScanPathAnalyzer(DataFeatures.PipelineStepObject): """Abstract class to define what should provide a aoi scan path analyzer.""" def __init__(self): @@ -1174,7 +1174,7 @@ class AOIScanPathAnalyzer(): return DataFeatures.DataDictionary(analysis) - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, aoi_scan_path: AOIScanPathType): """Analyze aoi scan path.""" diff --git a/src/argaze/PupillAnalysis/WorkloadIndex.py b/src/argaze/PupillAnalysis/WorkloadIndex.py index 1429eaf..99f143b 100644 --- a/src/argaze/PupillAnalysis/WorkloadIndex.py +++ b/src/argaze/PupillAnalysis/WorkloadIndex.py @@ -33,7 +33,7 @@ class PupillDiameterAnalyzer(PupillFeatures.PupillDiameterAnalyzer): self.__variations_number = 0 self.__last_ts = 0 - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, ts, pupill_diameter) -> float: """Analyze workload index from successive timestamped pupill diameters.""" diff --git a/src/argaze/PupillFeatures.py b/src/argaze/PupillFeatures.py index 5eb70ce..8aa7827 100644 --- a/src/argaze/PupillFeatures.py +++ b/src/argaze/PupillFeatures.py @@ -79,10 +79,10 @@ class TimeStampedPupillDiameters(DataFeatures.TimeStampedBuffer): TimeStampedBufferType = TypeVar('TimeStampedBuffer', bound="TimeStampedBuffer") # Type definition for type annotation convenience -class PupillDiameterAnalyzer(): +class PupillDiameterAnalyzer(DataFeatures.PipelineStepObject): """Abstract class to define what should provide a pupill diameter analyser.""" - @DataFeatures.PipelineStep + @DataFeatures.PipelineStepMethod def analyze(self, ts, pupill_diameter) -> float: """Analyze pupill diameter from successive timestamped pupill diameters.""" diff --git a/src/argaze/utils/demo_aruco_markers_run.py b/src/argaze/utils/demo_aruco_markers_run.py index 67e2845..a0d044c 100644 --- a/src/argaze/utils/demo_aruco_markers_run.py +++ b/src/argaze/utils/demo_aruco_markers_run.py @@ -53,7 +53,7 @@ def main(): nonlocal gaze_positions_frequency nonlocal gaze_analysis_time - + # Assess gaze analysis lap_time, nb_laps, elapsed_time = call_chrono.lap() @@ -62,21 +62,14 @@ def main(): gaze_positions_frequency = nb_laps call_chrono.restart() - gaze_analysis_time = 0 - # Edit millisecond timestamp timestamp = int((time.time() - start_time) * 1e3) # Project gaze position into camera - for frame, look_data in aruco_camera.look(timestamp, GazeFeatures.GazePosition((x, y))): - - # Unpack look data - if look_data: + aruco_camera.look(timestamp, GazeFeatures.GazePosition((x, y))): - gaze_position, gaze_movement, scan_step_analysis, layer_analysis, execution_times, exception = look_data - - # Assess gaze analysis - gaze_analysis_time += execution_times['total'] + # Assess gaze analysis + gaze_analysis_time = aruco_camera.execution_times['look'] # Attach mouse callback to window cv2.setMouseCallback(aruco_camera.name, on_mouse_event) diff --git a/src/argaze/utils/demo_gaze_analysis_run.py b/src/argaze/utils/demo_gaze_analysis_run.py index 210e188..5f46596 100644 --- a/src/argaze/utils/demo_gaze_analysis_run.py +++ b/src/argaze/utils/demo_gaze_analysis_run.py @@ -50,7 +50,7 @@ def main(): timestamp = int((time.time() - start_time) * 1e3) # Project gaze position into frame - execution_time, exception = ar_frame.look(timestamp, GazeFeatures.GazePosition((x, y))) + ar_frame.look(timestamp, GazeFeatures.GazePosition((x, y))) # Attach mouse callback to window cv2.setMouseCallback(ar_frame.name, on_mouse_event) |