From b41077e071877cd95a2ae25a60ab6f34f3c31529 Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Wed, 31 May 2023 18:39:16 +0200 Subject: Adding new ExploitExploreRatio analyser. --- .../GazeAnalysis/ExploitExploreRatio.py | 40 ++++++++++++++ src/argaze/GazeAnalysis/ExploitExploreRatio.py | 63 ++++++++++++++++++++++ src/argaze/GazeAnalysis/__init__.py | 2 +- 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/argaze.test/GazeAnalysis/ExploitExploreRatio.py create mode 100644 src/argaze/GazeAnalysis/ExploitExploreRatio.py (limited to 'src') diff --git a/src/argaze.test/GazeAnalysis/ExploitExploreRatio.py b/src/argaze.test/GazeAnalysis/ExploitExploreRatio.py new file mode 100644 index 0000000..febfec7 --- /dev/null +++ b/src/argaze.test/GazeAnalysis/ExploitExploreRatio.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +""" """ + +__author__ = "Théo de la Hogue" +__credits__ = [] +__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" +__license__ = "BSD" + +import unittest + +from argaze import GazeFeatures +from argaze.GazeAnalysis import ExploitExploreRatio +from argaze.utils import MiscFeatures + +GazeFeaturesTest = MiscFeatures.importFromTestPackage('GazeFeatures') + +class TestScanPathAnalyzer(unittest.TestCase): + """Test ScanPathAnalyzer class.""" + + def test_analyze(self): + """Test analyze method.""" + + xxr_analyzer = ExploitExploreRatio.ScanPathAnalyzer() + + scan_path = GazeFeaturesTest.build_scan_path(10) + + # Check scan path + self.assertEqual(len(scan_path), 10) + + xxr = xxr_analyzer.analyze(scan_path) + + print(xxr) + + # Check exploit explore ratio + self.assertGreaterEqual(xxr, 0) + +if __name__ == '__main__': + + unittest.main() \ No newline at end of file diff --git a/src/argaze/GazeAnalysis/ExploitExploreRatio.py b/src/argaze/GazeAnalysis/ExploitExploreRatio.py new file mode 100644 index 0000000..eba8ec2 --- /dev/null +++ b/src/argaze/GazeAnalysis/ExploitExploreRatio.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +"""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) +""" + +__author__ = "Théo de la Hogue" +__credits__ = [] +__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" +__license__ = "BSD" + +from dataclasses import dataclass + +from argaze import GazeFeatures + +import numpy + +@dataclass +class ScanPathAnalyzer(GazeFeatures.ScanPathAnalyzer): + + def __post_init__(self): + + pass + + def analyze(self, scan_path: GazeFeatures.ScanPathType, long_fixation_duration_threshold: float = 0.) -> float: + """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 > long_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() + + print(short_fixations_duration, long_fixations_duration, saccades_duration) + + assert(saccades_duration + short_fixations_duration > 0) + + return long_fixations_duration / (saccades_duration + short_fixations_duration) diff --git a/src/argaze/GazeAnalysis/__init__.py b/src/argaze/GazeAnalysis/__init__.py index 21753c1..5f8b102 100644 --- a/src/argaze/GazeAnalysis/__init__.py +++ b/src/argaze/GazeAnalysis/__init__.py @@ -2,4 +2,4 @@ .. include:: README.md """ __docformat__ = "restructuredtext" -__all__ = ['DispersionThresholdIdentification', 'VelocityThresholdIdentification', 'TransitionMatrix', 'KCoefficient', 'LempelZivComplexity', 'NGram', 'Entropy', 'NearestNeighborIndex'] \ No newline at end of file +__all__ = ['DispersionThresholdIdentification', 'VelocityThresholdIdentification', 'TransitionMatrix', 'KCoefficient', 'LempelZivComplexity', 'NGram', 'Entropy', 'NearestNeighborIndex', 'ExploitExploreRatio'] \ No newline at end of file -- cgit v1.1