aboutsummaryrefslogtreecommitdiff
path: root/src/argaze.test/GazeAnalysis
diff options
context:
space:
mode:
authorThéo de la Hogue2023-07-10 14:08:30 +0200
committerThéo de la Hogue2023-07-10 14:08:30 +0200
commit49d6cf63d02b80d10c96cfe2edc6fd197558c899 (patch)
treeac958b51f0531f4a74b8f056e8c1218636e97266 /src/argaze.test/GazeAnalysis
parent17d247f0652afcc68b9477bce718e91c850d5499 (diff)
downloadargaze-49d6cf63d02b80d10c96cfe2edc6fd197558c899.zip
argaze-49d6cf63d02b80d10c96cfe2edc6fd197558c899.tar.gz
argaze-49d6cf63d02b80d10c96cfe2edc6fd197558c899.tar.bz2
argaze-49d6cf63d02b80d10c96cfe2edc6fd197558c899.tar.xz
Improving and testing gaze movement identification. Now each movement have to share its last position with the next movement.
Diffstat (limited to 'src/argaze.test/GazeAnalysis')
-rw-r--r--src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py122
1 files changed, 88 insertions, 34 deletions
diff --git a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
index f1d02d6..09eb58b 100644
--- a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
+++ b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
@@ -27,6 +27,10 @@ def build_gaze_fixation(size: int, center: tuple, deviation_max: float, min_time
for i in range(0, size):
+ # Sleep a random time
+ sleep_time = random.random() * (max_time - min_time) + min_time
+ time.sleep(sleep_time)
+
# Check position validity
valid = True
if len(validity) > i:
@@ -48,10 +52,6 @@ def build_gaze_fixation(size: int, center: tuple, deviation_max: float, min_time
ts = time.time() - start_time + start_ts
ts_gaze_positions[ts] = gaze_position
- # Sleep a random time
- sleep_time = random.random() * (max_time - min_time) + min_time
- time.sleep(sleep_time)
-
return ts_gaze_positions
def build_gaze_saccade(size: int, center_A: tuple, center_B: tuple, min_time: float, max_time: float, start_ts: float = 0., validity: list = []):
@@ -65,6 +65,10 @@ def build_gaze_saccade(size: int, center_A: tuple, center_B: tuple, min_time: fl
for i in range(0, size):
+ # Sleep a random time
+ sleep_time = random.random() * (max_time - min_time) + min_time
+ time.sleep(sleep_time)
+
# Check position validity
valid = True
if len(validity) > i:
@@ -86,10 +90,6 @@ def build_gaze_saccade(size: int, center_A: tuple, center_B: tuple, min_time: fl
ts = time.time() - start_time + start_ts
ts_gaze_positions[ts] = gaze_position
- # Sleep a random time
- sleep_time = random.random() * (max_time - min_time) + min_time
- time.sleep(sleep_time)
-
return ts_gaze_positions
class TestDispersionThresholdIdentificationClass(unittest.TestCase):
@@ -121,7 +121,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
self.assertGreaterEqual(fixation.duration, size * min_time)
self.assertLessEqual(fixation.duration, size * max_time)
self.assertLessEqual(fixation.finished, True)
-
+
def test_fixation_and_direct_saccade_identification(self):
"""Test DispersionThresholdIdentification fixation and saccade identification."""
@@ -157,20 +157,20 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
# Check first saccade
ts, saccade = ts_saccades.pop_first()
- self.assertEqual(len(saccade.positions.keys()), 1)
- self.assertGreaterEqual(saccade.duration, 0.)
- self.assertLessEqual(saccade.duration, 0.)
+ self.assertEqual(len(saccade.positions.keys()), 2)
+ self.assertGreaterEqual(saccade.duration, min_time)
+ self.assertLessEqual(saccade.duration, max_time + min_time)
self.assertLessEqual(saccade.finished, True)
# Check second fixation
ts, fixation = ts_fixations.pop_first()
- self.assertEqual(len(fixation.positions.keys()), size-1)
+ self.assertEqual(len(fixation.positions.keys()), size)
self.assertLessEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, size * min_time)
self.assertLessEqual(fixation.duration, size * max_time)
self.assertLessEqual(fixation.finished, True)
-
+
def test_fixation_and_short_saccade_identification(self):
"""Test DispersionThresholdIdentification fixation and saccade identification."""
@@ -209,9 +209,9 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
# Check first saccade
ts, saccade = ts_saccades.pop_first()
- self.assertEqual(len(saccade.positions.keys()), move)
+ self.assertEqual(len(saccade.positions.keys()), move + 2)
self.assertGreaterEqual(saccade.duration, min_time)
- self.assertLessEqual(saccade.duration, max_time)
+ self.assertLessEqual(saccade.duration, max_time + 2 * min_time)
self.assertLessEqual(saccade.finished, True)
# Check second fixation
@@ -222,7 +222,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
self.assertGreaterEqual(fixation.duration, size * min_time)
self.assertLessEqual(fixation.duration, size * max_time)
self.assertLessEqual(fixation.finished, True)
-
+
def test_invalid_gaze_position(self):
"""Test DispersionThresholdIdentification fixation and saccade identification with invalid gaze position."""
@@ -260,7 +260,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
self.assertGreaterEqual(fixation.duration, 5 * min_time)
self.assertLessEqual(fixation.duration, 5 * max_time)
self.assertLessEqual(fixation.finished, True)
-
+
def test_fixation_overlapping(self):
"""Test Fixation overlap function."""
@@ -321,6 +321,64 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
self.assertLessEqual(fixation.duration, 2 * size * max_time)
self.assertLessEqual(fixation.finished, True)
+ def test_identification_browsing(self):
+ """Test DispersionThresholdIdentification identification browsing."""
+
+ 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)
+
+ # 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():
+
+ finished_gaze_movement = gaze_movement_identifier.identify(ts, gaze_position, terminate=(ts == last_ts))
+
+ if GazeFeatures.is_fixation(finished_gaze_movement):
+
+ self.assertEqual(len(finished_gaze_movement.positions.keys()), size)
+ self.assertLessEqual(finished_gaze_movement.deviation_max, deviation_max)
+ self.assertGreaterEqual(finished_gaze_movement.duration, size * min_time)
+ self.assertLessEqual(finished_gaze_movement.duration, size * max_time)
+ self.assertLessEqual(finished_gaze_movement.finished, True)
+
+ elif GazeFeatures.is_saccade(finished_gaze_movement):
+
+ self.assertEqual(len(finished_gaze_movement.positions.keys()), 2)
+ self.assertGreaterEqual(finished_gaze_movement.duration, min_time)
+ self.assertLessEqual(finished_gaze_movement.duration, max_time + min_time)
+ self.assertLessEqual(finished_gaze_movement.finished, True)
+
+ # Check that last gaze position date is not equal to given gaze position date
+ if finished_gaze_movement.valid:
+
+ last_ts, _ = finished_gaze_movement.positions.last
+
+ self.assertNotEqual(last_ts, ts)
+
+ # Check that last gaze position date of current fixation is equal to given gaze position date
+ # NOTE: This is not true for saccade as there is a minimal time window while the gaze movement is unknown
+ current_gaze_movement = gaze_movement_identifier.current_gaze_movement
+ if current_gaze_movement.valid:
+
+ if GazeFeatures.is_fixation(current_gaze_movement):
+
+ last_ts, _ = current_gaze_movement.positions.last
+
+ self.assertEqual(last_ts, ts)
+
def test_identification_generator(self):
"""Test DispersionThresholdIdentification identification using generator."""
@@ -337,27 +395,23 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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):
+ for ts, finished_gaze_movement in gaze_movement_identifier(ts_gaze_positions):
- 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)
- self.assertLessEqual(gaze_movement.finished, True)
+ if GazeFeatures.is_fixation(finished_gaze_movement):
- fixation_count += 1
+ self.assertEqual(len(finished_gaze_movement.positions.keys()), size)
+ self.assertLessEqual(finished_gaze_movement.deviation_max, deviation_max)
+ self.assertGreaterEqual(finished_gaze_movement.duration, size * min_time)
+ self.assertLessEqual(finished_gaze_movement.duration, size * max_time)
+ self.assertLessEqual(finished_gaze_movement.finished, True)
- elif GazeFeatures.is_saccade(gaze_movement):
+ elif GazeFeatures.is_saccade(finished_gaze_movement):
- self.assertEqual(len(gaze_movement.positions.keys()), 1)
- self.assertGreaterEqual(gaze_movement.duration, 0.)
- self.assertLessEqual(gaze_movement.duration, 0.)
- self.assertLessEqual(gaze_movement.finished, True)
+ self.assertEqual(len(finished_gaze_movement.positions.keys()), 2)
+ self.assertGreaterEqual(finished_gaze_movement.duration, min_time)
+ self.assertLessEqual(finished_gaze_movement.duration, max_time + min_time)
+ self.assertLessEqual(finished_gaze_movement.finished, True)
if __name__ == '__main__':