aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThéo de la Hogue2023-06-15 16:59:25 +0200
committerThéo de la Hogue2023-06-15 16:59:25 +0200
commita097f0495ff95a410455a134082bb7a059a8da74 (patch)
tree47ebc73ff8d16e04810114c3230bfe97d5f8a14d
parenta08cd2838e5c506f1af6f966f0681eae60a1573e (diff)
downloadargaze-a097f0495ff95a410455a134082bb7a059a8da74.zip
argaze-a097f0495ff95a410455a134082bb7a059a8da74.tar.gz
argaze-a097f0495ff95a410455a134082bb7a059a8da74.tar.bz2
argaze-a097f0495ff95a410455a134082bb7a059a8da74.tar.xz
Adding and testing GazeMovementIdentifier generator.
-rw-r--r--src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py36
-rw-r--r--src/argaze/GazeFeatures.py27
2 files changed, 63 insertions, 0 deletions
diff --git a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
index 712308a..eba80c8 100644
--- a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
+++ b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
@@ -311,6 +311,42 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
self.assertGreaterEqual(fixation.duration, 2 * size * min_time)
self.assertLessEqual(fixation.duration, 2 * size * max_time)
+ def test_identification_generator(self):
+ """Test DispersionThresholdIdentification identification using generator."""
+
+ size = 10
+ center_A = (0, 0)
+ center_B = (50, 50)
+ deviation_max = 10
+ min_time = 0.01
+ max_time = 0.1
+
+ ts_gaze_positions_A = build_gaze_fixation(size, center_A, deviation_max, min_time, max_time)
+ ts_gaze_positions_B = build_gaze_fixation(size, center_B, deviation_max, min_time, max_time, start_ts=ts_gaze_positions_A.last[0])
+
+ ts_gaze_positions = ts_gaze_positions_A.append(ts_gaze_positions_B)
+
+ gaze_movement_identifier = DispersionThresholdIdentification.GazeMovementIdentifier(deviation_max_threshold=deviation_max, duration_min_threshold=max_time*2)
+
+ fixation_count = 0
+
+ for ts, gaze_movement in gaze_movement_identifier(ts_gaze_positions):
+
+ if GazeFeatures.is_fixation(gaze_movement):
+
+ self.assertEqual(len(gaze_movement.positions.keys()), size-fixation_count)
+ self.assertLessEqual(gaze_movement.deviation_max, deviation_max)
+ self.assertGreaterEqual(gaze_movement.duration, size * min_time)
+ self.assertLessEqual(gaze_movement.duration, size * max_time)
+
+ fixation_count += 1
+
+ elif GazeFeatures.is_saccade(gaze_movement):
+
+ self.assertEqual(len(gaze_movement.positions.keys()), 1)
+ self.assertGreaterEqual(gaze_movement.duration, 0.)
+ self.assertLessEqual(gaze_movement.duration, 0.)
+
if __name__ == '__main__':
unittest.main() \ No newline at end of file
diff --git a/src/argaze/GazeFeatures.py b/src/argaze/GazeFeatures.py
index 870cd3c..cf1f7b8 100644
--- a/src/argaze/GazeFeatures.py
+++ b/src/argaze/GazeFeatures.py
@@ -405,6 +405,33 @@ class GazeMovementIdentifier():
return ts_fixations, ts_saccades, ts_status
+ def __call__(self, ts_gaze_positions: TimeStampedGazePositions) -> Tuple[int|float, GazeMovementType]:
+ """GazeMovement generator.
+
+ Parameters:
+ ts_gaze_positions: timestamped gaze positions to process.
+
+ Returns:
+ timestamp
+ gaze movement
+ """
+
+ assert(type(ts_gaze_positions) == TimeStampedGazePositions)
+
+ # Get last ts to terminate identification on last gaze position
+ last_ts, _ = ts_gaze_positions.last
+
+ # Iterate on gaze positions
+ for ts, gaze_position in ts_gaze_positions.items():
+
+ gaze_movement = self.identify(ts, gaze_position, terminate=(ts == last_ts))
+
+ if gaze_movement:
+
+ start_ts, start_position = gaze_movement.positions.first
+
+ yield start_ts, gaze_movement
+
ScanStepType = TypeVar('ScanStep', bound="ScanStep")
# Type definition for type annotation convenience