aboutsummaryrefslogtreecommitdiff
path: root/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
diff options
context:
space:
mode:
authorThéo de la Hogue2024-02-29 16:40:52 +0100
committerThéo de la Hogue2024-02-29 16:40:52 +0100
commitaddbaaef4265688916f355d4cde24c96f0605cf9 (patch)
tree56913dc313697f794eef9691760baad9a4df2273 /src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
parent9c42e9f1ee8208e14dadcb73cf030a9baef236ed (diff)
parentac8cc60d27a57c892354214b04327878b511cc44 (diff)
downloadargaze-addbaaef4265688916f355d4cde24c96f0605cf9.zip
argaze-addbaaef4265688916f355d4cde24c96f0605cf9.tar.gz
argaze-addbaaef4265688916f355d4cde24c96f0605cf9.tar.bz2
argaze-addbaaef4265688916f355d4cde24c96f0605cf9.tar.xz
Merge branch 'fast_idt' into logging
Diffstat (limited to 'src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py')
-rw-r--r--src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py154
1 files changed, 67 insertions, 87 deletions
diff --git a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
index b7475b5..156f6f1 100644
--- a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
+++ b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
@@ -47,11 +47,13 @@ def build_gaze_fixation(size: int, center: tuple, deviation_max: float, min_time
else:
- gaze_position = GazeFeatures.UnvalidGazePosition()
+ 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
@@ -85,11 +87,13 @@ def build_gaze_saccade(size: int, center_A: tuple, center_B: tuple, min_time: fl
else:
- gaze_position = GazeFeatures.UnvalidGazePosition()
+ 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,14 +119,14 @@ 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.assertEqual(len(fixation), size)
self.assertLessEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, (size - 1) * min_time)
self.assertLessEqual(fixation.duration, (size - 1) * max_time)
self.assertLessEqual(fixation.finished, True)
-
+
def test_fixation_and_direct_saccade_identification(self):
"""Test DispersionThresholdIdentification fixation and saccade identification."""
@@ -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,45 +151,39 @@ 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.assertEqual(len(fixation), size)
self.assertLessEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, (size - 1) * min_time)
self.assertLessEqual(fixation.duration, (size - 1) * max_time)
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.assertEqual(len(saccade), 2)
self.assertGreaterEqual(saccade.duration, min_time)
self.assertLessEqual(saccade.duration, max_time)
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.assertEqual(len(fixation), size)
self.assertLessEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, (size - 1) * min_time)
self.assertLessEqual(fixation.duration, (size - 1) * max_time)
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,47 +211,41 @@ 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.assertEqual(len(fixation), size)
self.assertLessEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, (size - 1) * min_time)
self.assertLessEqual(fixation.duration, (size - 1) * max_time)
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.assertEqual(len(saccade), move + 2)
self.assertGreaterEqual(saccade.duration, (move + 1) * min_time)
self.assertLessEqual(saccade.duration, (move + 1) * max_time)
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.assertEqual(len(fixation), size)
self.assertLessEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, (size - 1) * min_time)
self.assertLessEqual(fixation.duration, (size - 1) * max_time)
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)
-
- def test_invalid_gaze_position(self):
- """Test DispersionThresholdIdentification fixation and saccade identification with invalid gaze position."""
+ self.assertEqual(saccade[-1].timestamp, fixation[0].timestamp)
+ self.assertEqual(saccade[-1].value, fixation[0].value)
+
+ def test_empty_gaze_position(self):
+ """Test DispersionThresholdIdentification fixation and saccade identification with empty gaze position."""
size = 15
center = (0, 0)
@@ -273,23 +265,23 @@ 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.assertEqual(len(fixation), 7)
self.assertLessEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, 6 * min_time)
self.assertLessEqual(fixation.duration, 6 * max_time)
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.assertEqual(len(fixation), 5)
self.assertLessEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, 4 * min_time)
self.assertLessEqual(fixation.duration, 4 * max_time)
self.assertLessEqual(fixation.finished, True)
-
+
def test_fixation_overlapping(self):
"""Test Fixation overlap function."""
@@ -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,14 +334,14 @@ 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.assertEqual(len(fixation), size * 2)
#self.assertGreaterEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, (2 * size - 1) * min_time)
self.assertLessEqual(fixation.duration, (2 * size - 1) * max_time)
self.assertLessEqual(fixation.finished, True)
-
+
def test_identification_browsing(self):
"""Test DispersionThresholdIdentification identification browsing."""
@@ -361,23 +353,20 @@ 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)
- # 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():
+ 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 == ts_gaze_positions[-1].timestamp))
if GazeFeatures.is_fixation(finished_gaze_movement):
- self.assertEqual(len(finished_gaze_movement.positions.keys()), size)
+ self.assertEqual(len(finished_gaze_movement), size)
self.assertLessEqual(finished_gaze_movement.deviation_max, deviation_max)
self.assertGreaterEqual(finished_gaze_movement.duration, (size-1) * min_time)
self.assertLessEqual(finished_gaze_movement.duration, (size-1) * max_time)
@@ -385,29 +374,20 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
elif GazeFeatures.is_saccade(finished_gaze_movement):
- self.assertEqual(len(finished_gaze_movement.positions.keys()), 2)
+ self.assertEqual(len(finished_gaze_movement), 2)
self.assertGreaterEqual(finished_gaze_movement.duration, min_time)
self.assertLessEqual(finished_gaze_movement.duration, max_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, for I-DT, 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 current_gaze_movement:
if GazeFeatures.is_fixation(current_gaze_movement):
- last_ts, _ = current_gaze_movement.positions.last
-
- self.assertEqual(last_ts, ts)
-
+ self.assertEqual(current_gaze_movement[-1].timestamp, gaze_position.timestamp)
+
def test_identification_generator(self):
"""Test DispersionThresholdIdentification identification using generator."""
@@ -419,17 +399,17 @@ 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)
- for ts, finished_gaze_movement in gaze_movement_identifier(ts_gaze_positions):
+ for finished_gaze_movement in gaze_movement_identifier(ts_gaze_positions):
if GazeFeatures.is_fixation(finished_gaze_movement):
- self.assertEqual(len(finished_gaze_movement.positions.keys()), size)
+ self.assertEqual(len(finished_gaze_movement), 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)
@@ -437,7 +417,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
elif GazeFeatures.is_saccade(finished_gaze_movement):
- self.assertEqual(len(finished_gaze_movement.positions.keys()), 2)
+ self.assertEqual(len(finished_gaze_movement), 2)
self.assertGreaterEqual(finished_gaze_movement.duration, 2 * min_time)
self.assertLessEqual(finished_gaze_movement.duration, 2 * max_time)
self.assertLessEqual(finished_gaze_movement.finished, True)