#!/usr/bin/env python """Implementation of K coefficient and K-modified coefficient. """ __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): """Implementation of the K coefficient algorithm as described in: **Krejtz K., Duchowski A., Krejtz I., Szarkowska A., & Kopacz A. (2016).** *Discerning ambient/focal attention with coefficient K.* ACM Transactions on Applied Perception (TAP, 1–20). [https://doi.org/10.1145/2896452](https://doi.org/10.1145/2896452) """ def __post_init__(self): pass def analyze(self, scan_path: GazeFeatures.ScanPathType) -> float: """Analyze scan path.""" assert(len(scan_path) > 1) durations = [] amplitudes = [] for scan_step in scan_path: durations.append(scan_step.duration) amplitudes.append(scan_step.last_saccade.amplitude) durations = numpy.array(durations) amplitudes = numpy.array(amplitudes) duration_mean = numpy.mean(durations) amplitude_mean = numpy.mean(amplitudes) duration_std = numpy.std(durations) amplitude_std = numpy.std(amplitudes) Ks = [] for scan_step in scan_path: Ks.append(((scan_step.duration - duration_mean) / duration_std) - ((scan_step.last_saccade.amplitude - amplitude_mean) / amplitude_std)) K = numpy.array(Ks).mean() return K @dataclass class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): """Implementation of the K-modified coefficient algorithm as described in: **Lounis, C. A., Hassoumi, A., Lefrancois, O., Peysakhovich, V., & Causse, M. (2020, June).** *Detecting ambient/focal visual attention in professional airline pilots with a modified Coefficient K: a full flight simulator study.* ACM Symposium on Eye Tracking Research and Applications (ETRA'20, 1-6). [https://doi.org/10.1145/3379157.3391412](https://doi.org/10.1145/3379157.3391412) """ def __post_init__(self): pass def analyze(self, aoi_scan_path: GazeFeatures.AOIScanPathType) -> float: """Analyze aoi scan path.""" assert(len(aoi_scan_path) > 1) durations = [] amplitudes = [] for aoi_scan_step in aoi_scan_path: durations.append(aoi_scan_step.duration) amplitudes.append(aoi_scan_step.last_saccade.amplitude) durations = numpy.array(durations) amplitudes = numpy.array(amplitudes) duration_mean = numpy.mean(durations) amplitude_mean = numpy.mean(amplitudes) duration_std = numpy.std(durations) amplitude_std = numpy.std(amplitudes) Ks = [] for aoi_scan_step in aoi_scan_path: Ks.append(((aoi_scan_step.duration - duration_mean) / duration_std) - ((aoi_scan_step.last_saccade.amplitude - amplitude_mean) / amplitude_std)) K = numpy.array(Ks).mean() return K