aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThéo de la Hogue2024-03-11 16:29:09 +0100
committerThéo de la Hogue2024-03-11 16:29:09 +0100
commitf56b287d251afd65e74757c85ff5c78517fa1595 (patch)
tree22cb8405857caa3202775f8728120cc5ba0e8689
parent9943b930ec42556bd60e8bb33d56d1ab4a9638b3 (diff)
downloadargaze-f56b287d251afd65e74757c85ff5c78517fa1595.zip
argaze-f56b287d251afd65e74757c85ff5c78517fa1595.tar.gz
argaze-f56b287d251afd65e74757c85ff5c78517fa1595.tar.bz2
argaze-f56b287d251afd65e74757c85ff5c78517fa1595.tar.xz
Harmonizing name of methods returning a boolean status.
-rw-r--r--docs/user_guide/aruco_markers_pipeline/configuration_and_execution.md2
-rw-r--r--docs/user_guide/gaze_analysis_pipeline/advanced_topics/gaze_position_calibration.md2
-rw-r--r--docs/user_guide/gaze_analysis_pipeline/advanced_topics/scripting.md12
-rw-r--r--docs/user_guide/gaze_analysis_pipeline/configuration_and_execution.md2
-rw-r--r--docs/user_guide/gaze_analysis_pipeline/logging.md4
-rw-r--r--docs/user_guide/gaze_analysis_pipeline/timestamped_gaze_positions_edition.md10
-rw-r--r--src/argaze.test/AreaOfInterest/AOIFeatures.py10
-rw-r--r--src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py28
-rw-r--r--src/argaze.test/GazeAnalysis/VelocityThresholdIdentification.py18
-rw-r--r--src/argaze.test/GazeFeatures.py12
-rw-r--r--src/argaze/ArFeatures.py28
-rw-r--r--src/argaze/AreaOfInterest/AOIFeatures.py10
-rw-r--r--src/argaze/DataFeatures.py4
-rw-r--r--src/argaze/GazeAnalysis/DispersionThresholdIdentification.py2
-rw-r--r--src/argaze/GazeAnalysis/LinearRegression.py55
-rw-r--r--src/argaze/GazeAnalysis/VelocityThresholdIdentification.py2
-rw-r--r--src/argaze/GazeFeatures.py42
-rw-r--r--src/argaze/utils/demo_data/demo_frame_logger.py4
-rw-r--r--src/argaze/utils/demo_data/demo_layer_logger.py2
19 files changed, 109 insertions, 140 deletions
diff --git a/docs/user_guide/aruco_markers_pipeline/configuration_and_execution.md b/docs/user_guide/aruco_markers_pipeline/configuration_and_execution.md
index 0349a91..885db50 100644
--- a/docs/user_guide/aruco_markers_pipeline/configuration_and_execution.md
+++ b/docs/user_guide/aruco_markers_pipeline/configuration_and_execution.md
@@ -138,7 +138,7 @@ Particularly, timestamped gaze positions can be passed one by one to [ArUcoCamer
try:
# Look ArUcoCamera frame at a timestamped gaze position
- aruco_camera.look(timestamp, gaze_position)
+ aruco_camera.look(timestamped_gaze_position)
# Do something with pipeline exception
except Exception as e:
diff --git a/docs/user_guide/gaze_analysis_pipeline/advanced_topics/gaze_position_calibration.md b/docs/user_guide/gaze_analysis_pipeline/advanced_topics/gaze_position_calibration.md
index 9e0ec4c..4d80c05 100644
--- a/docs/user_guide/gaze_analysis_pipeline/advanced_topics/gaze_position_calibration.md
+++ b/docs/user_guide/gaze_analysis_pipeline/advanced_topics/gaze_position_calibration.md
@@ -37,7 +37,7 @@ Here is an extract from the JSON ArFrame configuration file where a [Linear Regr
...
# If calibration process started
- if ar_frame.gaze_position_calibrator.calibrating:
+ if ar_frame.gaze_position_calibrator.is_calibrating():
# Store calibration data
ar_frame.gaze_position_calibrator.store(timestamp, observed_gaze_position, expected_gaze_position)
diff --git a/docs/user_guide/gaze_analysis_pipeline/advanced_topics/scripting.md b/docs/user_guide/gaze_analysis_pipeline/advanced_topics/scripting.md
index 11e561e..4935847 100644
--- a/docs/user_guide/gaze_analysis_pipeline/advanced_topics/scripting.md
+++ b/docs/user_guide/gaze_analysis_pipeline/advanced_topics/scripting.md
@@ -80,7 +80,7 @@ Calling [ArFrame.look](../../../argaze.md/#argaze.ArFeatures.ArFrame.look) metho
... ar_frame.last_gaze_position
# Check if a gaze movement has been identified
- if ar_frame.last_gaze_movement and ar_frame.last_gaze_movement.finished:
+ if ar_frame.last_gaze_movement and ar_frame.last_gaze_movement.is_finished():
# Do something with identified fixation
if GazeFeatures.is_fixation(ar_frame.last_gaze_movement):
@@ -93,7 +93,7 @@ Calling [ArFrame.look](../../../argaze.md/#argaze.ArFeatures.ArFrame.look) metho
...
# Do something with scan path analysis
- if ar_frame.analysis_available:
+ if ar_frame.is_analysis_available():
for scan_path_analyzer_name, scan_path_analysis in ar_frame.analysis():
@@ -105,7 +105,7 @@ Calling [ArFrame.look](../../../argaze.md/#argaze.ArFeatures.ArFrame.look) metho
# Do something with last looked aoi name
... ar_frame.last_looked_aoi_name
- if ar_layer.analysis_available:
+ if ar_layer.is_analysis_available():
for aoi_scan_path_analyzer_name, aoi_scan_path_analysis in ar_layer.analysis():
@@ -132,9 +132,9 @@ In that case, the last gaze movement *finished* flag is false.
Then, the last gaze movement type can be tested thanks to [GazeFeatures.is_fixation](../../../argaze.md/#argaze.GazeFeatures.is_fixation) and [GazeFeatures.is_saccade](../../../argaze.md/#argaze.GazeFeatures.is_saccade) functions.
-### *ar_frame.analysis_available*
+### *ar_frame.is_analysis_available()*
-This flag allows to know when new scan path analysis are available.
+This method allows to know when new scan path analysis are available.
### *ar_frame.analysis()*
@@ -144,7 +144,7 @@ This an iterator to access to all scan path analysis.
The name of the last aoi matching done by [AoiMatcher](../../../argaze.md/#argaze.GazeFeatures.AoiMatcher) if one is instanciated else, it is a None value.
-### *ar_layer.analysis_available*
+### *ar_layer.is_analysis_available()*
This flag allows to know when new aoi scan path analysis are available.
diff --git a/docs/user_guide/gaze_analysis_pipeline/configuration_and_execution.md b/docs/user_guide/gaze_analysis_pipeline/configuration_and_execution.md
index 633b736..e7a5c17 100644
--- a/docs/user_guide/gaze_analysis_pipeline/configuration_and_execution.md
+++ b/docs/user_guide/gaze_analysis_pipeline/configuration_and_execution.md
@@ -110,7 +110,7 @@ Timestamped gaze positions have to be passed one by one to [ArFrame.look](../../
try:
# Look ArFrame at a timestamped gaze position
- ar_frame.look(gaze_position)
+ ar_frame.look(timestamped_gaze_position)
# Do something with pipeline exception
except Exception as e:
diff --git a/docs/user_guide/gaze_analysis_pipeline/logging.md b/docs/user_guide/gaze_analysis_pipeline/logging.md
index 48808cc..001c213 100644
--- a/docs/user_guide/gaze_analysis_pipeline/logging.md
+++ b/docs/user_guide/gaze_analysis_pipeline/logging.md
@@ -38,7 +38,7 @@ class ScanPathAnalysisLogger(DataFeatures.PipelineStepObserver, UtilsFeatures.Fi
def on_look(self, timestamp, ar_frame, exception):
"""Log scan path metrics"""
- if ar_frame.analysis_available:
+ if ar_frame.is_analysis_available():
log = (
timestamp,
@@ -78,7 +78,7 @@ class AOIScanPathAnalysisLogger(DataFeatures.PipelineStepObserver, UtilsFeatures
def on_look(self, timestamp, ar_layer, exception):
"""Log aoi scan path metrics."""
- if ar_layer.analysis_available:
+ if ar_layer.is_analysis_available():
log = (
timestamp,
diff --git a/docs/user_guide/gaze_analysis_pipeline/timestamped_gaze_positions_edition.md b/docs/user_guide/gaze_analysis_pipeline/timestamped_gaze_positions_edition.md
index 4c53258..5c60f8c 100644
--- a/docs/user_guide/gaze_analysis_pipeline/timestamped_gaze_positions_edition.md
+++ b/docs/user_guide/gaze_analysis_pipeline/timestamped_gaze_positions_edition.md
@@ -20,7 +20,7 @@ dataframe = pandas.read_csv('gaze_positions.csv', delimiter=",", low_memory=Fals
ts_gaze_positions = GazeFeatures.TimeStampedGazePositions.from_dataframe(dataframe, timestamp = 'Recording timestamp [ms]', x = 'Gaze point X [px]', y = 'Gaze point Y [px]')
# Iterate over timestamped gaze positions
-for gaze_position in ts_gaze_positions:
+for timestamped_gaze_position in ts_gaze_positions:
# Do something with each timestamped gaze position
...
@@ -37,8 +37,8 @@ from argaze import GazeFeatures
# Assuming to be inside the function where timestamp_µs, gaze_x and gaze_y values are catched
...
- # Define a basic gaze position converting microsecond timestamp into second timestamp
- gaze_position = GazeFeatures.GazePosition((gaze_x, gaze_y), timestamp=timestamp_µs * 1e-6)
+ # Define a timestamped gaze position converting microsecond timestamp into second timestamp
+ timestamped_gaze_position = GazeFeatures.GazePosition((gaze_x, gaze_y), timestamp=timestamp_µs * 1e-6)
# Do something with each timestamped gaze position
...
@@ -55,8 +55,8 @@ start_time = time.time()
# Assuming to be inside the function where only gaze_x and gaze_y values are catched (no timestamp)
...
- # Define a basic gaze position with millisecond timestamp
- gaze_position = GazeFeatures.GazePosition((gaze_x, gaze_y), timestamp=int((time.time() - start_time) * 1e3))
+ # Define a timestamped gaze position with millisecond timestamp
+ timestamped_gaze_position = GazeFeatures.GazePosition((gaze_x, gaze_y), timestamp=int((time.time() - start_time) * 1e3))
# Do something with each timestamped gaze position
...
diff --git a/src/argaze.test/AreaOfInterest/AOIFeatures.py b/src/argaze.test/AreaOfInterest/AOIFeatures.py
index cb8fb52..baebadf 100644
--- a/src/argaze.test/AreaOfInterest/AOIFeatures.py
+++ b/src/argaze.test/AreaOfInterest/AOIFeatures.py
@@ -23,12 +23,12 @@ class TestAreaOfInterestClass(unittest.TestCase):
# Check that 0D AreaOfInterest creation is considered as empty
aoi_0D = AOIFeatures.AreaOfInterest()
- self.assertTrue(aoi_0D.empty)
+ self.assertTrue(aoi_0D.is_empty())
# Check 1 point 1D AreaOfInterest creation
aoi_1D = AOIFeatures.AreaOfInterest([[0]])
- self.assertFalse(aoi_1D.empty)
+ self.assertFalse(aoi_1D.is_empty())
self.assertEqual(aoi_1D.dimension, 1)
self.assertEqual(aoi_1D.points_number, 1)
self.assertIsNone(numpy.testing.assert_array_equal(aoi_1D.bounds, [[0], [0]]))
@@ -41,7 +41,7 @@ class TestAreaOfInterestClass(unittest.TestCase):
# Check 2 points 1D AreaOfInterest creation
aoi_1D = AOIFeatures.AreaOfInterest([[0], [1]])
- self.assertFalse(aoi_1D.empty)
+ self.assertFalse(aoi_1D.is_empty())
self.assertEqual(aoi_1D.dimension, 1)
self.assertEqual(aoi_1D.points_number, 2)
self.assertIsNone(numpy.testing.assert_array_equal(aoi_1D.bounds, [[0], [1]]))
@@ -54,7 +54,7 @@ class TestAreaOfInterestClass(unittest.TestCase):
# Check 4 points 2D AreaOfInterest creation
aoi_2D = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 0], [1, 1]])
- self.assertFalse(aoi_2D.empty)
+ self.assertFalse(aoi_2D.is_empty())
self.assertEqual(aoi_2D.dimension, 2)
self.assertEqual(aoi_2D.points_number, 4)
self.assertIsNone(numpy.testing.assert_array_equal(aoi_2D.bounds, [[0, 0], [1, 1]]))
@@ -66,7 +66,7 @@ class TestAreaOfInterestClass(unittest.TestCase):
# Check 8 points 3D AreaOfInterest creation
aoi_3D = AOIFeatures.AreaOfInterest([[0, 0, 0], [0, 1, 0], [1, 0, 0], [1, 1, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
- self.assertFalse(aoi_3D.empty)
+ self.assertFalse(aoi_3D.is_empty())
self.assertEqual(aoi_3D.dimension, 3)
self.assertEqual(aoi_3D.points_number, 8)
self.assertIsNone(numpy.testing.assert_array_equal(aoi_3D.bounds, [[0, 0, 0], [1, 1, 1]]))
diff --git a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
index 156f6f1..0bb8ed7 100644
--- a/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
+++ b/src/argaze.test/GazeAnalysis/DispersionThresholdIdentification.py
@@ -125,7 +125,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(fixation.is_finished(), True)
def test_fixation_and_direct_saccade_identification(self):
"""Test DispersionThresholdIdentification fixation and saccade identification."""
@@ -157,7 +157,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check first saccade
saccade = ts_saccades.pop(0)
@@ -165,7 +165,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
self.assertEqual(len(saccade), 2)
self.assertGreaterEqual(saccade.duration, min_time)
self.assertLessEqual(saccade.duration, max_time)
- self.assertLessEqual(saccade.finished, True)
+ self.assertLessEqual(saccade.is_finished(), True)
# Check that last position of a movement is equal to first position of next movement
self.assertEqual(fixation[-1].timestamp, saccade[0].timestamp)
@@ -178,7 +178,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check that last position of a movement is equal to first position of next movement
self.assertEqual(saccade[-1].timestamp, fixation[0].timestamp)
@@ -217,7 +217,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check first saccade
saccade = ts_saccades.pop(0)
@@ -225,7 +225,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(saccade.is_finished(), True)
# Check that last position of a movement is equal to first position of next movement
self.assertEqual(fixation[-1].timestamp, saccade[0].timestamp)
@@ -238,7 +238,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check that last position of a movement is equal to first position of next movement
self.assertEqual(saccade[-1].timestamp, fixation[0].timestamp)
@@ -271,7 +271,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check second fixation
fixation = ts_fixations.pop(0)
@@ -280,7 +280,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(fixation.is_finished(), True)
def test_fixation_overlapping(self):
"""Test Fixation overlap function."""
@@ -340,7 +340,7 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
#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)
+ self.assertLessEqual(fixation.is_finished(), True)
def test_identification_browsing(self):
"""Test DispersionThresholdIdentification identification browsing."""
@@ -370,14 +370,14 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
- self.assertLessEqual(finished_gaze_movement.finished, True)
+ self.assertLessEqual(finished_gaze_movement.is_finished(), True)
elif GazeFeatures.is_saccade(finished_gaze_movement):
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)
+ self.assertLessEqual(finished_gaze_movement.is_finished(), True)
# 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
@@ -413,14 +413,14 @@ class TestDispersionThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(finished_gaze_movement.is_finished(), True)
elif GazeFeatures.is_saccade(finished_gaze_movement):
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)
+ self.assertLessEqual(finished_gaze_movement.is_finished(), True)
if __name__ == '__main__':
diff --git a/src/argaze.test/GazeAnalysis/VelocityThresholdIdentification.py b/src/argaze.test/GazeAnalysis/VelocityThresholdIdentification.py
index 262cfc0..1c7f7e3 100644
--- a/src/argaze.test/GazeAnalysis/VelocityThresholdIdentification.py
+++ b/src/argaze.test/GazeAnalysis/VelocityThresholdIdentification.py
@@ -125,7 +125,7 @@ class TestVelocityThresholdIdentificationClass(unittest.TestCase):
self.assertEqual(len(fixation), size - 1)
self.assertGreaterEqual(fixation.duration, (size - 2) * min_time)
self.assertLessEqual(fixation.duration, (size - 2) * max_time)
- self.assertLessEqual(fixation.finished, True)
+ self.assertLessEqual(fixation.is_finished(), True)
def test_fixation_and_direct_saccade_identification(self):
"""Test VelocityThresholdIdentification fixation and saccade identification."""
@@ -157,7 +157,7 @@ class TestVelocityThresholdIdentificationClass(unittest.TestCase):
self.assertEqual(len(fixation), size - 1)
self.assertGreaterEqual(fixation.duration, (size - 2) * min_time)
self.assertLessEqual(fixation.duration, (size - 2) * max_time)
- self.assertLessEqual(fixation.finished, True)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check first saccade
saccade = ts_saccades.pop(0)
@@ -165,7 +165,7 @@ class TestVelocityThresholdIdentificationClass(unittest.TestCase):
self.assertEqual(len(saccade), 2)
self.assertGreaterEqual(saccade.duration, min_time)
self.assertLessEqual(saccade.duration, max_time)
- self.assertLessEqual(saccade.finished, True)
+ self.assertLessEqual(saccade.is_finished(), True)
# Check that last position of a movement is equal to first position of next movement
self.assertEqual(fixation[-1].timestamp, saccade[0].timestamp)
@@ -177,7 +177,7 @@ class TestVelocityThresholdIdentificationClass(unittest.TestCase):
self.assertEqual(len(fixation), size)
self.assertGreaterEqual(fixation.duration, (size - 1) * min_time)
self.assertLessEqual(fixation.duration, (size - 1) * max_time)
- self.assertLessEqual(fixation.finished, True)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check that last position of a movement is equal to first position of next movement
self.assertEqual(saccade[-1].timestamp, fixation[0].timestamp)
@@ -216,7 +216,7 @@ class TestVelocityThresholdIdentificationClass(unittest.TestCase):
self.assertEqual(len(fixation), size - 1) # BUG: NOT ALWAYS TRUE !!!
self.assertGreaterEqual(fixation.duration, (size - 2) * min_time)
self.assertLessEqual(fixation.duration, (size - 2) * max_time)
- self.assertLessEqual(fixation.finished, True)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check first saccade
saccade = ts_saccades.pop(0)
@@ -224,7 +224,7 @@ class TestVelocityThresholdIdentificationClass(unittest.TestCase):
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)
+ self.assertLessEqual(saccade.is_finished(), True)
# Check that last position of a movement is equal to first position of next movement
self.assertEqual(fixation[-1].timestamp, saccade[0].timestamp)
@@ -236,7 +236,7 @@ class TestVelocityThresholdIdentificationClass(unittest.TestCase):
self.assertEqual(len(fixation), size)
self.assertGreaterEqual(fixation.duration, (size - 1) * min_time)
self.assertLessEqual(fixation.duration, (size - 1) * max_time)
- self.assertLessEqual(fixation.finished, True)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check that last position of a movement is equal to first position of next movement
self.assertEqual(saccade[-1], fixation[0])
@@ -269,7 +269,7 @@ class TestVelocityThresholdIdentificationClass(unittest.TestCase):
self.assertEqual(len(fixation), 6)
self.assertGreaterEqual(fixation.duration, 5 * min_time)
self.assertLessEqual(fixation.duration, 5 * max_time)
- self.assertLessEqual(fixation.finished, True)
+ self.assertLessEqual(fixation.is_finished(), True)
# Check second fixation
fixation = ts_fixations.pop(0)
@@ -277,7 +277,7 @@ class TestVelocityThresholdIdentificationClass(unittest.TestCase):
self.assertEqual(len(fixation), 4)
self.assertGreaterEqual(fixation.duration, 3 * min_time)
self.assertLessEqual(fixation.duration, 3 * max_time)
- self.assertLessEqual(fixation.finished, True)
+ self.assertLessEqual(fixation.is_finished(), True)
def test_identification_browsing(self):
"""Test VelocityThresholdIdentification identification browsing."""
diff --git a/src/argaze.test/GazeFeatures.py b/src/argaze.test/GazeFeatures.py
index 035c76a..2d052ab 100644
--- a/src/argaze.test/GazeFeatures.py
+++ b/src/argaze.test/GazeFeatures.py
@@ -321,7 +321,7 @@ class TestGazeMovementClass(unittest.TestCase):
self.assertEqual(abstract_gaze_movement.duration, 0)
self.assertEqual(abstract_gaze_movement.amplitude, 0)
self.assertEqual(bool(abstract_gaze_movement), False)
- self.assertEqual(abstract_gaze_movement.finished, False)
+ self.assertEqual(abstract_gaze_movement.is_finished(), False)
def test_finish(self):
"""Test GazeMovement finishing."""
@@ -331,15 +331,15 @@ class TestGazeMovementClass(unittest.TestCase):
abstract_gaze_movement_ref = abstract_gaze_movement
# Check abstract GazeMovement and its reference
- self.assertEqual(abstract_gaze_movement.finished, False)
- self.assertEqual(abstract_gaze_movement_ref.finished, False)
+ self.assertEqual(abstract_gaze_movement.is_finished(), False)
+ self.assertEqual(abstract_gaze_movement_ref.is_finished(), False)
# Set gaze movement as finished
abstract_gaze_movement.finish()
# Check abstract GazeMovement and its reference
- self.assertEqual(abstract_gaze_movement.finished, True)
- self.assertEqual(abstract_gaze_movement_ref.finished, True)
+ self.assertEqual(abstract_gaze_movement.is_finished(), True)
+ self.assertEqual(abstract_gaze_movement_ref.is_finished(), True)
def test_message(self):
"""Test GazeMovement creation with message only."""
@@ -351,7 +351,7 @@ class TestGazeMovementClass(unittest.TestCase):
self.assertEqual(gaze_movement.duration, 0)
self.assertEqual(gaze_movement.amplitude, 0)
self.assertEqual(bool(gaze_movement), False)
- self.assertEqual(gaze_movement.finished, False)
+ self.assertEqual(gaze_movement.is_finished(), False)
self.assertEqual(gaze_movement.message, 'test')
class TestScanStepClass(unittest.TestCase):
diff --git a/src/argaze/ArFeatures.py b/src/argaze/ArFeatures.py
index fb22afa..b5d1012 100644
--- a/src/argaze/ArFeatures.py
+++ b/src/argaze/ArFeatures.py
@@ -195,12 +195,11 @@ class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
return self.__draw_parameters
@property
- def last_looked_aoi_name(self) -> bool:
+ def last_looked_aoi_name(self) -> str:
"""Get last looked aoi name."""
return self.__looked_aoi_name
- @property
- def analysis_available(self) -> bool:
+ def is_analysis_available(self) -> bool:
"""Are aoi scan path analysis ready?"""
return self.__aoi_scan_path_analyzed
@@ -414,7 +413,7 @@ class ArLayer(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
self.__looked_aoi_name, _ = self.__aoi_matcher.match(self.__aoi_scene, gaze_movement, timestamp=gaze_movement.timestamp)
# Valid and finished gaze movement has been identified
- if gaze_movement and gaze_movement.finished:
+ if gaze_movement and gaze_movement.is_finished():
if GazeFeatures.is_fixation(gaze_movement):
@@ -624,8 +623,7 @@ class ArFrame(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
"""Get last identified gaze movement"""
return self.__identified_gaze_movement
- @property
- def analysis_available(self) -> bool:
+ def is_analysis_available(self) -> bool:
"""Are scan path analysis ready?"""
return self.__scan_path_analyzed
@@ -700,12 +698,24 @@ class ArFrame(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
# JSON file format
if file_format == 'json':
- new_gaze_position_calibrator = GazeFeatures.GazePositionCalibrator.from_json(filepath)
+ with open(filepath) as file:
+
+ gaze_movement_calibrator_value = json.load(file)
# dict:
else:
- new_gaze_position_calibrator = GazeFeatures.GazePositionCalibrator.from_dict(gaze_position_calibrator_value)
+ gaze_movement_calibrator_value = frame_data.pop('gaze_movement_identifier')
+
+ # Create gaze position calibrator
+ gaze_position_calibrator_module_path, gaze_position_calibrator_parameters = gaze_movement_calibrator_value.popitem()
+
+ # Prepend argaze.GazeAnalysis path when a single name is provided
+ if len(gaze_position_calibrator_module_path.split('.')) == 1:
+ gaze_position_calibrator_module_path = f'argaze.GazeAnalysis.{gaze_position_calibrator_module_path}'
+
+ gaze_position_calibrator_module = importlib.import_module(gaze_position_calibrator_module_path)
+ new_gaze_position_calibrator = gaze_position_calibrator_module.GazePositionCalibrator(**gaze_position_calibrator_parameters)
except KeyError:
@@ -911,7 +921,7 @@ class ArFrame(DataFeatures.SharedObject, DataFeatures.PipelineStepObject):
self.__identified_gaze_movement = self.__gaze_movement_identifier.identify(self.__calibrated_gaze_position)
# Valid and finished gaze movement has been identified
- if self.__identified_gaze_movement and self.__identified_gaze_movement.finished:
+ if self.__identified_gaze_movement and self.__identified_gaze_movement.is_finished():
if GazeFeatures.is_fixation(self.__identified_gaze_movement):
diff --git a/src/argaze/AreaOfInterest/AOIFeatures.py b/src/argaze/AreaOfInterest/AOIFeatures.py
index c7e5193..616f088 100644
--- a/src/argaze/AreaOfInterest/AOIFeatures.py
+++ b/src/argaze/AreaOfInterest/AOIFeatures.py
@@ -93,25 +93,20 @@ class AreaOfInterest(numpy.ndarray):
@property
def dimension(self) -> int:
"""Number of axis coding area points positions."""
-
return self.shape[1]
@property
def points_number(self) -> int:
"""Number of points defining the area."""
-
return self.shape[0]
- @property
- def empty(self) -> bool:
+ def is_empty(self) -> bool:
"""Is AOI empty ?"""
-
return self.shape[0] == 0
@property
def bounds(self) -> numpy.array:
"""Get area's bounds."""
-
min_bounds = numpy.min(self, axis=0)
max_bounds = numpy.max(self, axis=0)
@@ -120,13 +115,11 @@ class AreaOfInterest(numpy.ndarray):
@property
def center(self) -> numpy.array:
"""Center of mass."""
-
return self.mean(axis=0)
@property
def size(self) -> numpy.array:
"""Get scene size."""
-
min_bounds, max_bounds = self.bounds
return max_bounds - min_bounds
@@ -134,7 +127,6 @@ class AreaOfInterest(numpy.ndarray):
@property
def area(self) -> float:
"""Area of the polygon defined by aoi's points."""
-
return Polygon(self).area
@property
diff --git a/src/argaze/DataFeatures.py b/src/argaze/DataFeatures.py
index 6d471e4..60b7c71 100644
--- a/src/argaze/DataFeatures.py
+++ b/src/argaze/DataFeatures.py
@@ -444,7 +444,7 @@ class PipelineStepObject():
# Init private attribute
self.__name = name
- self.__working_directory = None
+ self.__working_directory = working_directory
self.__observers = observers if observers is not None else {}
self.__execution_times = {}
self.__properties = {}
@@ -640,7 +640,7 @@ class PipelineStepObject():
# Open file
with open(self.__json_filepath, 'w', encoding='utf-8') as object_file:
- json.dump({DataFeatures.module_path(self):DataFeatures.JsonEncoder().default(self)}, object_file, ensure_ascii=False, indent=4)
+ json.dump({module_path(self):as_dict(self)}, object_file, ensure_ascii=False, indent=4)
# QUESTION: maybe we need two saving mode?
#json.dump(self, object_file, ensure_ascii=False, indent=4, cls=DataFeatures.JsonEncoder)
diff --git a/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py b/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py
index 84d14e7..745b62c 100644
--- a/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py
+++ b/src/argaze/GazeAnalysis/DispersionThresholdIdentification.py
@@ -49,7 +49,7 @@ class Fixation(GazeFeatures.Fixation):
"""Get fixation's maximal deviation."""
return self.__deviation_max
- def overlap(self, fixation: FixationType) -> bool:
+ def is_overlapping(self, fixation: FixationType) -> bool:
"""Does a gaze position from another fixation having a deviation to this fixation centroïd smaller than maximal deviation?"""
positions_array = numpy.asarray(fixation.values())
diff --git a/src/argaze/GazeAnalysis/LinearRegression.py b/src/argaze/GazeAnalysis/LinearRegression.py
index 717e8a3..d788f6f 100644
--- a/src/argaze/GazeAnalysis/LinearRegression.py
+++ b/src/argaze/GazeAnalysis/LinearRegression.py
@@ -9,9 +9,8 @@ __copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)"
__license__ = "BSD"
from typing import TypeVar, Tuple
-from dataclasses import dataclass, field
-from argaze import GazeFeatures
+from argaze import DataFeatures, GazeFeatures
from sklearn.linear_model import LinearRegression
import numpy
@@ -20,7 +19,6 @@ import cv2
GazePositionType = TypeVar('GazePositionType', bound="GazePositionType")
# Type definition for type annotation convenience
-@dataclass
class GazePositionCalibrator(GazeFeatures.GazePositionCalibrator):
"""Implementation of linear regression algorithm as described in:
@@ -28,29 +26,41 @@ class GazePositionCalibrator(GazeFeatures.GazePositionCalibrator):
*Time- and space-efficient eye tracker calibration.*
Proceedings of the 11th ACM Symposium on Eye Tracking Research & Applications (ETRA'19, 1-8).
[https://dl.acm.org/doi/pdf/10.1145/3314111.3319818](https://dl.acm.org/doi/pdf/10.1145/3314111.3319818)
- """
- coefficients: numpy.array = field(default_factory=lambda : numpy.array([[1., 0.], [0., 1.]]))
- """Linear regression coefficients"""
+ Parameters:
+ coefficients: linear regression coefficients.
+ intercept: linear regression intercept value.
+ """
- intercept: numpy.array = field(default_factory=lambda : numpy.array([0., 0.]))
- """Linear regression intercept value"""
+ def __init__(self, coefficients: list = [[1., 0.], [0., 1.]], intercept: list = [0., 0.]):
- def __post_init__(self):
+ super().__init__()
self.__linear_regression = LinearRegression()
- self.__linear_regression.coef_ = numpy.array(self.coefficients)
- self.__linear_regression.intercept_ = numpy.array(self.intercept)
+ self.__linear_regression.coef_ = numpy.array(coefficients)
+ self.__linear_regression.intercept_ = numpy.array(intercept)
- def store(self, timestamp: int|float, observed_gaze_position: GazeFeatures.GazePosition, expected_gaze_position: GazeFeatures.GazePosition):
- """Store observed and expected gaze positions."""
+ @property
+ def coefficients(self) -> list:
+ """Get linear regression coefficients."""
+ return self.__linear_regression.coef_.tolist()
+
+ @property
+ def intercept(self):
+ """Get linear regression intercept value."""
+ return self.__linear_regression.intercept_.tolist()
+
+ def is_calibrating(self) -> bool:
+ """Is the calibration running?"""
+ return self.__linear_regression is None
+ def store(self, observed_gaze_position: GazeFeatures.GazePosition, expected_gaze_position: GazeFeatures.GazePosition):
+ """Store observed and expected gaze positions."""
self.__observed_positions.append(observed_gaze_position)
self.__expected_positions.append(expected_gaze_position)
def reset(self):
"""Reset observed and expected gaze positions."""
-
self.__observed_positions = []
self.__expected_positions = []
self.__linear_regression = None
@@ -61,24 +71,16 @@ class GazePositionCalibrator(GazeFeatures.GazePositionCalibrator):
Returns:
score: the score of linear regression
"""
-
self.__linear_regression = LinearRegression().fit(self.__observed_positions, self.__expected_positions)
- # Update frozen coefficients attribute
- object.__setattr__(self, 'coefficients', self.__linear_regression.coef_)
-
- # Update frozen intercept attribute
- object.__setattr__(self, 'intercept', self.__linear_regression.intercept_)
-
# Return calibrated gaze position
return self.__linear_regression.score(self.__observed_positions, self.__expected_positions)
def apply(self, gaze_position: GazeFeatures.GazePosition) -> GazePositionType:
"""Apply calibration onto observed gaze position."""
-
if not self.calibrating:
- return GazeFeatures.GazePosition(self.__linear_regression.predict(numpy.array([gaze_position]))[0], precision=gaze_position.precision)
+ return GazeFeatures.GazePosition(self.__linear_regression.predict(numpy.array([gaze_position]))[0], precision=gaze_position.precision, timestamp=gaze_position.timestamp)
else:
@@ -86,7 +88,6 @@ class GazePositionCalibrator(GazeFeatures.GazePositionCalibrator):
def draw(self, image: numpy.array, size: tuple, resolution: tuple, line_color: tuple = (0, 0, 0), thickness: int = 1):
"""Draw calibration field."""
-
width, height = size
if width * height > 0:
@@ -104,9 +105,3 @@ class GazePositionCalibrator(GazeFeatures.GazePositionCalibrator):
end = self.apply(GazeFeatures.GazePosition(start)).value
cv2.line(image, (int(start[0]), int(start[1])), (int(end[0]), int(end[1])), line_color, thickness)
-
- @property
- def calibrating(self) -> bool:
- """Is the calibration running?"""
-
- return self.__linear_regression is None \ No newline at end of file
diff --git a/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py b/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py
index a54cee1..bfe04fa 100644
--- a/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py
+++ b/src/argaze/GazeAnalysis/VelocityThresholdIdentification.py
@@ -48,7 +48,7 @@ class Fixation(GazeFeatures.Fixation):
"""Get fixation's maximal deviation."""
return self.__deviation_max
- def overlap(self, fixation: FixationType) -> bool:
+ def is_overlapping(self, fixation: FixationType) -> bool:
"""Does a gaze position from another fixation having a deviation to this fixation centroïd smaller than maximal deviation?"""
positions_array = numpy.asarray(fixation.values())
diff --git a/src/argaze/GazeFeatures.py b/src/argaze/GazeFeatures.py
index fb56935..cdd29a3 100644
--- a/src/argaze/GazeFeatures.py
+++ b/src/argaze/GazeFeatures.py
@@ -288,43 +288,17 @@ class GazePositionCalibrationFailed(Exception):
GazePositionCalibratorType = TypeVar('GazePositionCalibrator', bound="GazePositionCalibrator")
# Type definition for type annotation convenience
-@dataclass
-class GazePositionCalibrator():
+class GazePositionCalibrator(DataFeatures.PipelineStepObject):
"""Abstract class to define what should provide a gaze position calibrator algorithm."""
- @classmethod
- def from_dict(cls, calibrator_data: dict) -> GazePositionCalibratorType:
- """Load gaze position calibrator from dictionary.
-
- Parameters:
- calibrator_data: dictionary with class name and attributes to load
- """
- gaze_position_calibrator_module_path, gaze_position_calibrator_parameters = calibrator_data.popitem()
-
- # Prepend argaze.GazeAnalysis path when a single name is provided
- if len(gaze_position_calibrator_module_path.split('.')) == 1:
- gaze_position_calibrator_module_path = f'argaze.GazeAnalysis.{gaze_position_calibrator_module_path}'
-
- gaze_position_calibrator_module = importlib.import_module(gaze_position_calibrator_module_path)
- return gaze_position_calibrator_module.GazePositionCalibrator(**gaze_position_calibrator_parameters)
-
- @classmethod
- def from_json(self, json_filepath: str) -> GazePositionCalibratorType:
- """Load calibrator from .json file."""
-
- # Remember file path to ease rewriting
- self.__json_filepath = json_filepath
-
- # Open file
- with open(self.__json_filepath) as calibration_file:
+ def __init__(self):
- return GazePositionCalibrator.from_dict(json.load(calibration_file))
+ super().__init__()
- def store(self, timestamp: int|float, observed_gaze_position: GazePosition, expected_gaze_position: GazePosition):
+ def store(self, observed_gaze_position: GazePosition, expected_gaze_position: GazePosition):
"""Store observed and expected gaze positions.
Parameters:
- timestamp: time of observed gaze position
observed_gaze_position: where gaze position actually is
expected_gaze_position: where gaze position should be
"""
@@ -366,8 +340,7 @@ class GazePositionCalibrator():
raise NotImplementedError('draw() method not implemented')
- @property
- def calibrating(self) -> bool:
+ def is_calibrating(self) -> bool:
"""Is the calibration running?"""
raise NotImplementedError('ready getter not implemented')
@@ -415,8 +388,7 @@ class GazeMovement(TimeStampedGazePositions, DataFeatures.TimestampedObject):
"""Block gaze movement timestamp setting."""
raise('GazeMovement timestamp is first positon timestamp.')
- @property
- def finished(self) -> bool:
+ def is_finished(self) -> bool:
"""Is the movement finished?"""
return self.__finished
@@ -446,7 +418,7 @@ class GazeMovement(TimeStampedGazePositions, DataFeatures.TimestampedObject):
if self:
- output = f'{type(self)}:\n\tduration={self.duration}\n\tsize={len(self)}\n\tfinished={self.finished}'
+ output = f'{type(self)}:\n\tduration={self.duration}\n\tsize={len(self)}\n\tfinished={self.is_finished()}'
for position in self:
diff --git a/src/argaze/utils/demo_data/demo_frame_logger.py b/src/argaze/utils/demo_data/demo_frame_logger.py
index 57bc149..2bb4bdc 100644
--- a/src/argaze/utils/demo_data/demo_frame_logger.py
+++ b/src/argaze/utils/demo_data/demo_frame_logger.py
@@ -16,7 +16,7 @@ class FixationLogger(DataFeatures.PipelineStepObserver, UtilsFeatures.FileWriter
"""Log fixations."""
# Log fixations
- if GazeFeatures.is_fixation(frame.last_gaze_movement) and frame.last_gaze_movement.finished:
+ if GazeFeatures.is_fixation(frame.last_gaze_movement) and frame.last_gaze_movement.is_finished():
log = (
timestamp,
@@ -32,7 +32,7 @@ class ScanPathAnalysisLogger(DataFeatures.PipelineStepObserver, UtilsFeatures.Fi
def on_look(self, timestamp, frame, exception):
"""Log scan path metrics."""
- if frame.analysis_available:
+ if frame.is_analysis_available():
log = (
timestamp,
diff --git a/src/argaze/utils/demo_data/demo_layer_logger.py b/src/argaze/utils/demo_data/demo_layer_logger.py
index 47cf577..d702ac1 100644
--- a/src/argaze/utils/demo_data/demo_layer_logger.py
+++ b/src/argaze/utils/demo_data/demo_layer_logger.py
@@ -15,7 +15,7 @@ class AOIScanPathAnalysisLogger(DataFeatures.PipelineStepObserver, UtilsFeatures
def on_look(self, timestamp, layer, exception):
"""Log aoi scan path metrics"""
- if layer.analysis_available:
+ if layer.is_analysis_available():
log = (
timestamp,