From 10969554e3126c65d19e408b06b3169f35b81e41 Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Thu, 29 Feb 2024 01:00:40 +0100 Subject: First work to update DispersionThresholdIdentification. --- .../DispersionThresholdIdentification.py | 94 ++++++++++------------ 1 file changed, 43 insertions(+), 51 deletions(-) (limited to 'src/argaze.test') diff --git a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py index f0d286a..7e74c1d 100644 --- a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py +++ b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py @@ -49,9 +49,11 @@ def build_gaze_fixation(size: int, center: tuple, deviation_max: float, min_time gaze_position = GazeFeatures.GazePosition() + # Timestamp gaze position + gaze_position.timestamp = time.time() - start_time + start_ts + # Store gaze position - ts = time.time() - start_time + start_ts - ts_gaze_positions[ts] = gaze_position + ts_gaze_positions.append(gaze_position) return ts_gaze_positions @@ -87,9 +89,11 @@ def build_gaze_saccade(size: int, center_A: tuple, center_B: tuple, min_time: fl gaze_position = GazeFeatures.GazePosition() + # Timestamp gaze position + gaze_position.timestamp = time.time() - start_time + start_ts + # Store gaze position - ts = time.time() - start_time + start_ts - ts_gaze_positions[ts] = gaze_position + ts_gaze_positions.append(gaze_position) return ts_gaze_positions @@ -115,7 +119,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertEqual(len(ts_status), size) # Check fixation - ts, fixation = ts_fixations.pop_first() + fixation = ts_fixations.pop(0) self.assertEqual(len(fixation.positions.keys()), size) self.assertLessEqual(fixation.deviation_max, deviation_max) @@ -134,9 +138,9 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): 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_B = build_gaze_fixation(size, center_B, deviation_max, min_time, max_time, start_ts=ts_gaze_positions_A[-1].timestamp) - ts_gaze_positions = ts_gaze_positions_A.append(ts_gaze_positions_B) + ts_gaze_positions = ts_gaze_positions_A + ts_gaze_positions_B gaze_movement_identifier = DispersionThresholdIdentification.GazeMovementIdentifier(deviation_max_threshold=deviation_max, duration_min_threshold=max_time*2) ts_fixations, ts_saccades, ts_status = gaze_movement_identifier.browse(ts_gaze_positions) @@ -147,7 +151,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertEqual(len(ts_status), size * 2) # Check first fixation - ts, fixation = ts_fixations.pop_first() + fixation = ts_fixations.pop(0) self.assertEqual(len(fixation.positions.keys()), size) self.assertLessEqual(fixation.deviation_max, deviation_max) @@ -156,7 +160,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertLessEqual(fixation.finished, True) # Check first saccade - ts, saccade = ts_saccades.pop_first() + saccade = ts_saccades.pop(0) self.assertEqual(len(saccade.positions.keys()), 2) self.assertGreaterEqual(saccade.duration, min_time) @@ -164,14 +168,11 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertLessEqual(saccade.finished, True) # Check that last position of a movement is equal to first position of next movement - last_ts, last_position = fixation.positions.last - first_ts, first_position = saccade.positions.first - - self.assertEqual(last_ts, first_ts) - self.assertEqual(last_position.value, first_position.value) + self.assertEqual(fixation[-1].timestamp, saccade[0].timestamp) + self.assertEqual(fixation[-1].value, saccade[0].value) # Check second fixation - ts, fixation = ts_fixations.pop_first() + fixation = ts_fixations.pop(0) self.assertEqual(len(fixation.positions.keys()), size) self.assertLessEqual(fixation.deviation_max, deviation_max) @@ -180,11 +181,8 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertLessEqual(fixation.finished, True) # Check that last position of a movement is equal to first position of next movement - last_ts, last_position = saccade.positions.last - first_ts, first_position = fixation.positions.first - - self.assertEqual(last_ts, first_ts) - self.assertEqual(last_position.value, first_position.value) + self.assertEqual(saccade[-1].timestamp, fixation[0].timestamp) + self.assertEqual(saccade[-1].value, fixation[0].value) def test_fixation_and_short_saccade_identification(self): """Test DispersionThresholdIdentification fixation and saccade identification.""" @@ -199,10 +197,10 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): max_time = 0.1 ts_gaze_positions_A = build_gaze_fixation(size, center_A, deviation_max, min_time, max_time) - ts_move_positions = build_gaze_saccade(move, out_A, center_B, min_time, min_time, start_ts=ts_gaze_positions_A.last[0]) - ts_gaze_positions_B = build_gaze_fixation(size, center_B, deviation_max, min_time, max_time, start_ts=ts_move_positions.last[0]) + ts_move_positions = build_gaze_saccade(move, out_A, center_B, min_time, min_time, start_ts=ts_gaze_positions_A[-1].timestamp) + ts_gaze_positions_B = build_gaze_fixation(size, center_B, deviation_max, min_time, max_time, start_ts=ts_move_positions[-1].timestamp) - ts_gaze_positions = ts_gaze_positions_A.append(ts_move_positions).append(ts_gaze_positions_B) + ts_gaze_positions = ts_gaze_positions_A + ts_move_positions + ts_gaze_positions_B gaze_movement_identifier = DispersionThresholdIdentification.GazeMovementIdentifier(deviation_max_threshold=deviation_max, duration_min_threshold=max_time*2) ts_fixations, ts_saccades, ts_status = gaze_movement_identifier.browse(ts_gaze_positions) @@ -213,7 +211,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertEqual(len(ts_status), size * 2 + move) # Check first fixation - ts, fixation = ts_fixations.pop_first() + fixation = ts_fixations.pop(0) self.assertEqual(len(fixation.positions.keys()), size) self.assertLessEqual(fixation.deviation_max, deviation_max) @@ -222,7 +220,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertLessEqual(fixation.finished, True) # Check first saccade - ts, saccade = ts_saccades.pop_first() + saccade = ts_saccades.pop(0) self.assertEqual(len(saccade.positions.keys()), move + 2) self.assertGreaterEqual(saccade.duration, (move + 1) * min_time) @@ -230,14 +228,11 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertLessEqual(saccade.finished, True) # Check that last position of a movement is equal to first position of next movement - last_ts, last_position = fixation.positions.last - first_ts, first_position = saccade.positions.first - - self.assertEqual(last_ts, first_ts) - self.assertEqual(last_position.value, first_position.value) + self.assertEqual(fixation[-1].timestamp, saccade[0].timestamp) + self.assertEqual(fixation[-1].value, saccade[0].value) # Check second fixation - ts, fixation = ts_fixations.pop_first() + fixation = ts_fixations.pop(0) self.assertEqual(len(fixation.positions.keys()), size) self.assertLessEqual(fixation.deviation_max, deviation_max) @@ -246,11 +241,8 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertLessEqual(fixation.finished, True) # Check that last position of a movement is equal to first position of next movement - last_ts, last_position = saccade.positions.last - first_ts, first_position = fixation.positions.first - - self.assertEqual(last_ts, first_ts) - self.assertEqual(last_position.value, first_position.value) + self.assertEqual(saccade[-1].timestamp, fixation[0].timestamp) + self.assertEqual(saccade[-1].value, fixation[0].value) def test_invalid_gaze_position(self): """Test DispersionThresholdIdentification fixation and saccade identification with invalid gaze position.""" @@ -273,7 +265,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertEqual(len(ts_status), size-3) # Check first fixation - ts, fixation = ts_fixations.pop_first() + fixation = ts_fixations.pop(0) self.assertEqual(len(fixation.positions.keys()), 7) self.assertLessEqual(fixation.deviation_max, deviation_max) @@ -282,7 +274,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertLessEqual(fixation.finished, True) # Check second fixation - ts, fixation = ts_fixations.pop_first() + fixation = ts_fixations.pop(0) self.assertEqual(len(fixation.positions.keys()), 5) self.assertLessEqual(fixation.deviation_max, deviation_max) @@ -302,8 +294,8 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): 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_C = build_gaze_fixation(size, center_C, deviation_max, min_time, max_time, start_ts=ts_gaze_positions_B.last[0]) + ts_gaze_positions_B = build_gaze_fixation(size, center_B, deviation_max, min_time, max_time, start_ts=ts_gaze_positions_A[-1].timestamp) + ts_gaze_positions_C = build_gaze_fixation(size, center_C, deviation_max, min_time, max_time, start_ts=ts_gaze_positions_B[-1].timestamp) fixation_A = DispersionThresholdIdentification.Fixation(ts_gaze_positions_A) fixation_B = DispersionThresholdIdentification.Fixation(ts_gaze_positions_B) @@ -329,7 +321,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): 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_B = build_gaze_fixation(size, center_B, deviation_max, min_time, max_time, start_ts=ts_gaze_positions_A[-1].timestamp) ts_gaze_positions = ts_gaze_positions_A.append(ts_gaze_positions_B) @@ -342,7 +334,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): self.assertEqual(len(ts_status), size*2) # Check unique fixation - ts, fixation = ts_fixations.pop_first() + fixation = ts_fixations.pop(0) self.assertEqual(len(fixation.positions.keys()), size * 2) #self.assertGreaterEqual(fixation.deviation_max, deviation_max) @@ -361,19 +353,19 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): 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_B = build_gaze_fixation(size, center_B, deviation_max, min_time, max_time, start_ts=ts_gaze_positions_A[-1].timestamp) 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 + last_ts = ts_gaze_positions[-1].timestamp # Iterate on gaze positions - for ts, gaze_position in ts_gaze_positions.items(): + for gaze_position in ts_gaze_positions: - finished_gaze_movement = gaze_movement_identifier.identify(ts, gaze_position, terminate=(ts == last_ts)) + finished_gaze_movement = gaze_movement_identifier.identify(gaze_position.timestamp, gaze_position, terminate=(gaze_position.timestamp == last_ts)) if GazeFeatures.is_fixation(finished_gaze_movement): @@ -393,9 +385,9 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): # 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 + last_ts = finished_gaze_movement[-1].timestamp - self.assertNotEqual(last_ts, ts) + self.assertNotEqual(last_ts, gaze_position.timestamp) # Check that last gaze position date of current fixation is equal to given gaze position date # NOTE: This is not true for saccade as, for I-DT, there is a minimal time window while the gaze movement is unknown @@ -404,9 +396,9 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): if GazeFeatures.is_fixation(current_gaze_movement): - last_ts, _ = current_gaze_movement.positions.last + last_ts = current_gaze_movement[-1].timestamp - self.assertEqual(last_ts, ts) + self.assertEqual(last_ts, gaze_position.timestamp) def test_identification_generator(self): """Test DispersionThresholdIdentification identification using generator.""" @@ -419,7 +411,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase): 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_B = build_gaze_fixation(size, center_B, deviation_max, min_time, max_time, start_ts=ts_gaze_positions_A[-1].timestamp) ts_gaze_positions = ts_gaze_positions_A.append(ts_gaze_positions_B) -- cgit v1.1