From d7107ed868229b9665ee7432dcdc1da90c97c75a Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Tue, 10 Oct 2023 16:25:56 +0200 Subject: Replacing ExploitExplore by ExploreExploit. --- .../configuration_and_execution.md | 4 +- .../pipeline_modules/scan_path_analyzers.md | 6 +- .../GazeAnalysis/ExploitExploreRatio.py | 6 +- src/argaze/GazeAnalysis/ExploitExploreRatio.py | 75 ---------------------- src/argaze/GazeAnalysis/ExploreExploitRatio.py | 70 ++++++++++++++++++++ src/argaze/GazeAnalysis/__init__.py | 2 +- .../utils/demo_data/demo_gaze_analysis_setup.json | 2 +- src/argaze/utils/demo_gaze_analysis_run.py | 6 +- 8 files changed, 83 insertions(+), 88 deletions(-) delete mode 100644 src/argaze/GazeAnalysis/ExploitExploreRatio.py create mode 100644 src/argaze/GazeAnalysis/ExploreExploitRatio.py 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 3b21cbd..c40039c 100644 --- a/docs/user_guide/gaze_analysis_pipeline/configuration_and_execution.md +++ b/docs/user_guide/gaze_analysis_pipeline/configuration_and_execution.md @@ -26,7 +26,7 @@ Here is a simple JSON [ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) conf }, "scan_path_analyzers": { "Basic": {}, - "ExploitExploreRatio": { + "ExploreExploitRatio": { "short_fixation_duration_threshold": 0 } } @@ -90,7 +90,7 @@ Finally, the last [ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) pipeline Each analysis algorithm can be selected by instantiating a particular [ScanPathAnalyzer from GazeAnalysis submodule](pipeline_modules/scan_path_analyzers.md) or [from another python package](advanced_topics/module_loading.md). -In the example file, the choosen analysis algorithms are the [Basic](../../argaze.md/#argaze.GazeAnalysis.Basic) module and the [ExploitExploreRatio](../../argaze.md/#argaze.GazeAnalysis.ExploitExploreRatio) module which has one specific *short_fixation_duration_threshold* attribute. +In the example file, the choosen analysis algorithms are the [Basic](../../argaze.md/#argaze.GazeAnalysis.Basic) module and the [ExploreExploitRatio](../../argaze.md/#argaze.GazeAnalysis.ExploreExploitRatio) module which has one specific *short_fixation_duration_threshold* attribute. ## Pipeline execution diff --git a/docs/user_guide/gaze_analysis_pipeline/pipeline_modules/scan_path_analyzers.md b/docs/user_guide/gaze_analysis_pipeline/pipeline_modules/scan_path_analyzers.md index afba844..f9f757a 100644 --- a/docs/user_guide/gaze_analysis_pipeline/pipeline_modules/scan_path_analyzers.md +++ b/docs/user_guide/gaze_analysis_pipeline/pipeline_modules/scan_path_analyzers.md @@ -13,15 +13,15 @@ Here are JSON samples to include a chosen module inside [ArFrame configuration]( [See in code reference](../../../argaze.md/#argaze.GazeAnalysis.Basic.ScanPathAnalyzer) -## Exploit/Explore ratio +## Explore/Exploit ratio ```json -"ExploitExploreRatio": { +"ExploreExploitRatio": { "short_fixation_duration_threshold": 0 } ``` -[See in code reference](../../../argaze.md/#argaze.GazeAnalysis.ExploitExploreRatio.ScanPathAnalyzer) +[See in code reference](../../../argaze.md/#argaze.GazeAnalysis.ExploreExploitRatio.ScanPathAnalyzer) ## K coefficient diff --git a/src/argaze.test/GazeAnalysis/ExploitExploreRatio.py b/src/argaze.test/GazeAnalysis/ExploitExploreRatio.py index 0e6b74a..7b323d4 100644 --- a/src/argaze.test/GazeAnalysis/ExploitExploreRatio.py +++ b/src/argaze.test/GazeAnalysis/ExploitExploreRatio.py @@ -10,7 +10,7 @@ __license__ = "BSD" import unittest from argaze import GazeFeatures -from argaze.GazeAnalysis import ExploitExploreRatio +from argaze.GazeAnalysis import ExploreExploitRatio from argaze.utils import UtilsFeatures GazeFeaturesTest = UtilsFeatures.importFromTestPackage('GazeFeatures') @@ -21,7 +21,7 @@ class TestScanPathAnalyzer(unittest.TestCase): def test_analyze(self): """Test analyze method.""" - xxr_analyzer = ExploitExploreRatio.ScanPathAnalyzer() + xxr_analyzer = ExploreExploitRatio.ScanPathAnalyzer() scan_path = GazeFeaturesTest.build_scan_path(10) @@ -31,7 +31,7 @@ class TestScanPathAnalyzer(unittest.TestCase): xxr_analyzer.analyze(scan_path) # Check exploit explore ratio: it should greater than 1 because of build_scan_path - self.assertGreaterEqual(xxr_analyzer.exploit_explore_ratio, 1.) + self.assertGreaterEqual(xxr_analyzer.explore_exploit_ratio, 1.) if __name__ == '__main__': diff --git a/src/argaze/GazeAnalysis/ExploitExploreRatio.py b/src/argaze/GazeAnalysis/ExploitExploreRatio.py deleted file mode 100644 index f35561f..0000000 --- a/src/argaze/GazeAnalysis/ExploitExploreRatio.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python - -"""Exploit/Explore ratio module. -""" - -__author__ = "Théo de la Hogue" -__credits__ = [] -__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" -__license__ = "BSD" - -from dataclasses import dataclass, field - -from argaze import GazeFeatures - -import numpy - -@dataclass -class ScanPathAnalyzer(GazeFeatures.ScanPathAnalyzer): - """Implementation of exploit vs explore ratio algorithm as described in: - - **Goldberg J. H., Kotval X. P. (1999).** - *Computer interface evaluation using eye movements: methods and constructs.* - International Journal of Industrial Ergonomics (631–645). - [https://doi.org/10.1016/S0169-8141(98)00068-7](https://doi.org/10.1016/S0169-8141\\(98\\)00068-7) - - **Dehais F., Peysakhovich V., Scannella S., Fongue J., Gateau T. (2015).** - *Automation surprise in aviation: Real-time solutions.* - Proceedings of the 33rd annual ACM conference on Human Factors in Computing Systems (2525–2534). - [https://doi.org/10.1145/2702123.2702521](https://doi.org/10.1145/2702123.2702521) - """ - - short_fixation_duration_threshold: float = field(default=0.) - """Time below which a fixation is considered to be short and so as exploratory.""" - - def __post_init__(self): - - super().__init__() - - self.__exploit_explore_ratio = 0. - - def analyze(self, scan_path: GazeFeatures.ScanPathType): - """Analyze scan path.""" - - assert(len(scan_path) > 1) - - short_fixations_durations = [] - long_fixations_durations = [] - saccades_durations = [] - - for scan_step in scan_path: - - if scan_step.first_fixation.duration > self.short_fixation_duration_threshold: - - long_fixations_durations.append(scan_step.first_fixation.duration) - - else: - - short_fixations_durations.append(scan_step.first_fixation.duration) - - saccades_durations.append(scan_step.last_saccade.duration) - - short_fixations_duration = numpy.array(short_fixations_durations).sum() - long_fixations_duration = numpy.array(long_fixations_durations).sum() - saccades_duration = numpy.array(saccades_durations).sum() - - assert(saccades_duration + short_fixations_duration > 0) - - self.__exploit_explore_ratio = long_fixations_duration / (saccades_duration + short_fixations_duration) - - @property - def exploit_explore_ratio(self) -> float: - """Exploit/Explore ratio.""" - - return self.__exploit_explore_ratio - \ No newline at end of file diff --git a/src/argaze/GazeAnalysis/ExploreExploitRatio.py b/src/argaze/GazeAnalysis/ExploreExploitRatio.py new file mode 100644 index 0000000..b4550e7 --- /dev/null +++ b/src/argaze/GazeAnalysis/ExploreExploitRatio.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +"""Explore/Explore ratio module. +""" + +__author__ = "Théo de la Hogue" +__credits__ = [] +__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" +__license__ = "BSD" + +from dataclasses import dataclass, field + +from argaze import GazeFeatures + +import numpy + +@dataclass +class ScanPathAnalyzer(GazeFeatures.ScanPathAnalyzer): + """Implementation of explore vs exploit ratio algorithm as described in: + + **Dehais F., Peysakhovich V., Scannella S., Fongue J., Gateau T. (2015).** + *Automation surprise in aviation: Real-time solutions.* + Proceedings of the 33rd annual ACM conference on Human Factors in Computing Systems (2525–2534). + [https://doi.org/10.1145/2702123.2702521](https://doi.org/10.1145/2702123.2702521) + """ + + short_fixation_duration_threshold: float = field(default=0.) + """Time below which a fixation is considered to be short and so as exploratory.""" + + def __post_init__(self): + + super().__init__() + + self.__explore_exploit_ratio = 0. + + def analyze(self, scan_path: GazeFeatures.ScanPathType): + """Analyze scan path.""" + + assert(len(scan_path) > 1) + + short_fixations_durations = [] + long_fixations_durations = [] + saccades_durations = [] + + for scan_step in scan_path: + + if scan_step.first_fixation.duration > self.short_fixation_duration_threshold: + + long_fixations_durations.append(scan_step.first_fixation.duration) + + else: + + short_fixations_durations.append(scan_step.first_fixation.duration) + + saccades_durations.append(scan_step.last_saccade.duration) + + short_fixations_duration = numpy.array(short_fixations_durations).sum() + long_fixations_duration = numpy.array(long_fixations_durations).sum() + saccades_duration = numpy.array(saccades_durations).sum() + + assert(long_fixations_duration > 0) + + self.__explore_exploit_ratio = (saccades_duration + short_fixations_duration) / long_fixations_duration + + @property + def explore_exploit_ratio(self) -> float: + """Explore/Exploit ratio.""" + + return self.__explore_exploit_ratio + \ No newline at end of file diff --git a/src/argaze/GazeAnalysis/__init__.py b/src/argaze/GazeAnalysis/__init__.py index 164de74..62e0823 100644 --- a/src/argaze/GazeAnalysis/__init__.py +++ b/src/argaze/GazeAnalysis/__init__.py @@ -1,4 +1,4 @@ """ Various gaze movement identification, AOI matching and scan path analysis algorithms. """ -__all__ = ['Basic', 'DispersionThresholdIdentification', 'VelocityThresholdIdentification', 'TransitionMatrix', 'KCoefficient', 'LempelZivComplexity', 'NGram', 'Entropy', 'NearestNeighborIndex', 'ExploitExploreRatio'] \ No newline at end of file +__all__ = ['Basic', 'DispersionThresholdIdentification', 'VelocityThresholdIdentification', 'TransitionMatrix', 'KCoefficient', 'LempelZivComplexity', 'NGram', 'Entropy', 'NearestNeighborIndex', 'ExploreExploitRatio'] \ No newline at end of file diff --git a/src/argaze/utils/demo_data/demo_gaze_analysis_setup.json b/src/argaze/utils/demo_data/demo_gaze_analysis_setup.json index fe5d197..f921662 100644 --- a/src/argaze/utils/demo_data/demo_gaze_analysis_setup.json +++ b/src/argaze/utils/demo_data/demo_gaze_analysis_setup.json @@ -18,7 +18,7 @@ "NearestNeighborIndex": { "size": [1920, 1149] }, - "ExploitExploreRatio": { + "ExploreExploitRatio": { "short_fixation_duration_threshold": 0 } }, diff --git a/src/argaze/utils/demo_gaze_analysis_run.py b/src/argaze/utils/demo_gaze_analysis_run.py index 789657b..9856d90 100644 --- a/src/argaze/utils/demo_gaze_analysis_run.py +++ b/src/argaze/utils/demo_gaze_analysis_run.py @@ -206,12 +206,12 @@ def main(): except KeyError: pass - # Display Exploit/Explore ratio analysis if loaded + # Display Explore/Exploit ratio analysis if loaded try: - xxr_analyser = ar_frame.scan_path_analyzers["argaze.GazeAnalysis.ExploitExploreRatio"] + xxr_analyser = ar_frame.scan_path_analyzers["argaze.GazeAnalysis.ExploreExploitRatio"] - cv2.putText(frame_image, f'Exploit explore ratio: {xxr_analyser.exploit_explore_ratio:.3f}', (20, ar_frame.size[1]-360), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA) + cv2.putText(frame_image, f'Explore/Exploit ratio: {xxr_analyser.explore_exploit_ratio:.3f}', (20, ar_frame.size[1]-360), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA) except KeyError: -- cgit v1.1