aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py79
-rw-r--r--src/argaze/GazeAnalysis/DispersionThresholdIdentification.py13
2 files changed, 52 insertions, 40 deletions
diff --git a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
index dc4284c..712308a 100644
--- a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
+++ b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
@@ -16,13 +16,15 @@ from argaze.GazeAnalysis import DispersionThresholdIdentification
import numpy
-def build_gaze_fixation(size: int, center: tuple, deviation_max: float, start_time: float, min_time: float, max_time: float, validity: list = []):
+def build_gaze_fixation(size: int, center: tuple, deviation_max: float, min_time: float, max_time: float, start_ts: float = 0., validity: list = []):
""" Generate N TimeStampedGazePsoitions dispersed around a center point for testing purpose.
Timestamps are current time after random sleep (second).
GazePositions are random values.
"""
ts_gaze_positions = GazeFeatures.TimeStampedGazePositions()
+ start_time = time.time()
+
for i in range(0, size):
# Check position validity
@@ -43,7 +45,7 @@ def build_gaze_fixation(size: int, center: tuple, deviation_max: float, start_ti
gaze_position = GazeFeatures.UnvalidGazePosition()
# Store gaze position
- ts = time.time() - start_time
+ ts = time.time() - start_time + start_ts
ts_gaze_positions[ts] = gaze_position
# Sleep a random time
@@ -52,13 +54,15 @@ def build_gaze_fixation(size: int, center: tuple, deviation_max: float, start_ti
return ts_gaze_positions
-def build_gaze_saccade(size: int, center_A: tuple, center_B: tuple, start_time: float, min_time: float, max_time: float, validity: list = []):
+def build_gaze_saccade(size: int, center_A: tuple, center_B: tuple, min_time: float, max_time: float, start_ts: float = 0., validity: list = []):
""" Generate N TimeStampedGazePsoitions between 2 center points for testing purpose.
Timestamps are current time after random sleep (second).
GazePositions are random values.
"""
ts_gaze_positions = GazeFeatures.TimeStampedGazePositions()
+ start_time = time.time()
+
for i in range(0, size):
# Check position validity
@@ -79,7 +83,7 @@ def build_gaze_saccade(size: int, center_A: tuple, center_B: tuple, start_time:
gaze_position = GazeFeatures.UnvalidGazePosition()
# Store gaze position
- ts = time.time() - start_time
+ ts = time.time() - start_time + start_ts
ts_gaze_positions[ts] = gaze_position
# Sleep a random time
@@ -97,11 +101,10 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
size = 10
center = (0, 0)
deviation_max = 10
- start_time = time.time()
min_time = 0.01
max_time = 0.1
- ts_gaze_positions = build_gaze_fixation(size, center, deviation_max, start_time, min_time, max_time)
+ ts_gaze_positions = build_gaze_fixation(size, center, deviation_max, min_time, max_time)
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)
@@ -125,12 +128,11 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
center_A = (0, 0)
center_B = (50, 50)
deviation_max = 10
- start_time = time.time()
min_time = 0.01
max_time = 0.1
- ts_gaze_positions_A = build_gaze_fixation(size, center_A, deviation_max, start_time, min_time, max_time)
- ts_gaze_positions_B = build_gaze_fixation(size, center_B, deviation_max, start_time, min_time, max_time)
+ 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)
@@ -164,7 +166,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
self.assertLessEqual(fixation.deviation_max, deviation_max)
self.assertGreaterEqual(fixation.duration, size * min_time)
self.assertLessEqual(fixation.duration, size * max_time)
- '''
+
def test_fixation_and_short_saccade_identification(self):
"""Test DispersionThresholdIdentification fixation and saccade identification."""
@@ -174,15 +176,12 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
out_A = (20, 20)
center_B = (50, 50)
deviation_max = 10
- start_time = time.time()
min_time = 0.01
max_time = 0.1
- ts_gaze_positions_A = build_gaze_fixation(size, center_A, deviation_max, start_time, min_time, max_time)
- ts_move_positions = build_gaze_saccade(move, out_A, center_B, start_time, min_time, min_time)
- ts_gaze_positions_B = build_gaze_fixation(size, center_B, deviation_max, start_time, min_time, max_time)
-
- print(ts_move_positions)
+ 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_gaze_positions = ts_gaze_positions_A.append(ts_move_positions).append(ts_gaze_positions_B)
@@ -197,7 +196,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
# Check first 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)
@@ -205,48 +204,54 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
# Check first saccade
ts, saccade = ts_saccades.pop_first()
- self.assertEqual(len(saccade.positions.keys()), move + 2)
+ self.assertEqual(len(saccade.positions.keys()), move)
self.assertGreaterEqual(saccade.duration, min_time)
self.assertLessEqual(saccade.duration, max_time)
# 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)
- '''
+
def test_invalid_gaze_position(self):
"""Test DispersionThresholdIdentification fixation and saccade identification with invalid gaze position."""
size = 15
center = (0, 0)
deviation_max = 10
- start_time = time.time()
- min_time = 0.01
+ min_time = 0.05
max_time = 0.1
- validity = [True, True, True, True, False, True, True, True, False, False, True, True, True, True, True]
+ validity = [True, True, True, True, True, True, True, False, False, False, True, True, True, True, True]
- ts_gaze_positions = build_gaze_fixation(size, center, deviation_max, start_time, min_time, max_time, validity)
+ ts_gaze_positions = build_gaze_fixation(size, center, deviation_max, min_time, max_time, validity=validity)
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)
# Check result size
- self.assertEqual(len(ts_fixations), 1)
+ self.assertEqual(len(ts_fixations), 2)
self.assertEqual(len(ts_saccades), 0)
self.assertEqual(len(ts_status), size-3)
- # Check fixation
+ # Check first fixation
ts, fixation = ts_fixations.pop_first()
- self.assertEqual(len(fixation.positions.keys()), size-3)
+ self.assertEqual(len(fixation.positions.keys()), 7)
self.assertLessEqual(fixation.deviation_max, deviation_max)
- self.assertGreaterEqual(fixation.duration, size * min_time)
- self.assertLessEqual(fixation.duration, size * max_time)
+ self.assertGreaterEqual(fixation.duration, 7 * min_time)
+ self.assertLessEqual(fixation.duration, 7 * max_time)
+
+ # Check seconde fixation
+ ts, fixation = ts_fixations.pop_first()
+
+ self.assertEqual(len(fixation.positions.keys()), 5)
+ self.assertLessEqual(fixation.deviation_max, deviation_max)
+ self.assertGreaterEqual(fixation.duration, 5 * min_time)
+ self.assertLessEqual(fixation.duration, 5 * max_time)
- @unittest.skip("Fixation overlapping is broken.")
def test_fixation_overlapping(self):
"""Test Fixation overlap function."""
@@ -255,13 +260,12 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
center_B = (50, 50)
center_C = (55, 55)
deviation_max = 10
- start_time = time.time()
min_time = 0.01
max_time = 0.1
- ts_gaze_positions_A = build_gaze_fixation(size, center_A, deviation_max, start_time, min_time, max_time)
- ts_gaze_positions_B = build_gaze_fixation(size, center_B, deviation_max, start_time, min_time, max_time)
- ts_gaze_positions_C = build_gaze_fixation(size, center_C, deviation_max, start_time, min_time, max_time)
+ 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])
fixation_A = DispersionThresholdIdentification.Fixation(ts_gaze_positions_A)
fixation_B = DispersionThresholdIdentification.Fixation(ts_gaze_positions_B)
@@ -275,7 +279,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
self.assertTrue(fixation_B.overlap(fixation_C))
self.assertTrue(fixation_C.overlap(fixation_B))
- @unittest.skip("Fixation overlapping identification is broken.")
+ @unittest.skip("Fixation overlapping is not supported anymore.")
def test_fixation_overlapping_identification(self):
"""Test DispersionThresholdIdentification identification when fixations overlap."""
@@ -283,12 +287,11 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
center_A = (-5, 0)
center_B = (5, 0)
deviation_max = 15
- start_time = time.time()
min_time = 0.01
max_time = 0.1
- ts_gaze_positions_A = build_gaze_fixation(size, center_A, deviation_max, start_time, min_time, max_time)
- ts_gaze_positions_B = build_gaze_fixation(size, center_B, deviation_max, start_time, min_time, max_time)
+ 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)
diff --git a/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py
index c6353d4..9d5a8d7 100644
--- a/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py
+++ b/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py
@@ -57,7 +57,7 @@ class Fixation(GazeFeatures.Fixation):
def point_deviation(self, gaze_position) -> float:
"""Get distance of a point from the fixation's centroïd."""
- return numpy.sqrt((self.centroid[0] - gaze_position.value[0])**2 + (self.centroid[1] - gaze_position.value[1])**2)
+ return numpy.sqrt((self.focus[0] - gaze_position.value[0])**2 + (self.focus[1] - gaze_position.value[1])**2)
def overlap(self, fixation) -> bool:
"""Does a gaze position from another fixation having a deviation to this fixation centroïd smaller than maximal deviation?"""
@@ -65,7 +65,7 @@ class Fixation(GazeFeatures.Fixation):
points = fixation.positions.values()
points_x, points_y = [p[0] for p in points], [p[1] for p in points]
points_array = numpy.column_stack([points_x, points_y])
- centroid_array = numpy.array([self.centroid[0], self.centroid[1]])
+ centroid_array = numpy.array([self.focus[0], self.focus[1]])
deviations_array = numpy.sqrt(numpy.sum((points_array - centroid_array)**2, axis=1))
return min(deviations_array) <= self.deviation_max
@@ -133,11 +133,20 @@ class GazeMovementIdentifier(GazeFeatures.GazeMovementIdentifier):
if (ts - ts_last) > self.duration_min_threshold:
+ # Get last movement
+ last_movement = self.current_saccade if len(self.__fixation_positions) == 0 else self.current_fixation
+
# Clear all former gaze positions
self.__valid_positions = GazeFeatures.TimeStampedGazePositions()
self.__fixation_positions = GazeFeatures.TimeStampedGazePositions()
self.__saccade_positions = GazeFeatures.TimeStampedGazePositions()
+ # Store valid gaze position
+ self.__valid_positions[ts] = gaze_position
+
+ # Return last valid movement if exist
+ return last_movement
+
# Store gaze positions until a minimal duration
self.__valid_positions[ts] = gaze_position