From e657f64183d0de95d69b18c363aecdd88544a339 Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Mon, 22 May 2023 14:39:16 +0200 Subject: Renaming file. Adding transition matrix density. --- src/argaze/GazeAnalysis/TransitionMatrix.py | 41 +++++++++++++++++++++ .../GazeAnalysis/TransitionProbabilityMatrix.py | 39 -------------------- src/argaze/GazeAnalysis/__init__.py | 2 +- src/argaze/utils/demo_gaze_features_run.py | 43 +++++++++++++--------- 4 files changed, 68 insertions(+), 57 deletions(-) create mode 100644 src/argaze/GazeAnalysis/TransitionMatrix.py delete mode 100644 src/argaze/GazeAnalysis/TransitionProbabilityMatrix.py diff --git a/src/argaze/GazeAnalysis/TransitionMatrix.py b/src/argaze/GazeAnalysis/TransitionMatrix.py new file mode 100644 index 0000000..d85bd0b --- /dev/null +++ b/src/argaze/GazeAnalysis/TransitionMatrix.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +""" """ + +__author__ = "Théo de la Hogue" +__credits__ = [] +__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" +__license__ = "BSD" + +from typing import TypeVar, Tuple, Any +from dataclasses import dataclass, field +import math + +from argaze import GazeFeatures + +import pandas + +@dataclass +class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): + """Implementation of transition matrix probabilities and density algorithm as described in ... + """ + + def __post_init__(self): + + pass + + def analyze(self, aoi_scan_path: GazeFeatures.AOIScanPathType) -> Any: + """Analyze aoi scan.""" + + assert(len(aoi_scan_path) > 1) + + sequence = [] + + for aoi_scan_step in aoi_scan_path: + + sequence.append(aoi_scan_step.aoi) + + transition_matrix_probabilities = pandas.crosstab(pandas.Series(sequence[1:], name='to'), pandas.Series(sequence[:-1], name='from'), normalize=1) + transition_matrix_density = (transition_matrix_probabilities == 0.).astype(int).sum(axis=1).sum() / transition_matrix_probabilities.size + + return transition_matrix_probabilities , transition_matrix_density diff --git a/src/argaze/GazeAnalysis/TransitionProbabilityMatrix.py b/src/argaze/GazeAnalysis/TransitionProbabilityMatrix.py deleted file mode 100644 index 62f0500..0000000 --- a/src/argaze/GazeAnalysis/TransitionProbabilityMatrix.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python - -""" """ - -__author__ = "Théo de la Hogue" -__credits__ = [] -__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)" -__license__ = "BSD" - -from typing import TypeVar, Tuple, Any -from dataclasses import dataclass, field -import math - -from argaze import GazeFeatures - -import pandas - -@dataclass -class AOIScanPathAnalyzer(GazeFeatures.AOIScanPathAnalyzer): - """Implementation of transition probability matrix algorithm as described in ... - """ - - def __post_init__(self): - - pass - - def analyze(self, aoi_scan_path: GazeFeatures.AOIScanPathType) -> Any: - """Analyze aoi scan.""" - - assert(len(aoi_scan_path) > 1) - - sequence = [] - - for aoi_scan_step in aoi_scan_path: - - sequence.append(aoi_scan_step.aoi) - - return pandas.crosstab(pandas.Series(sequence[1:], name='to'), pandas.Series(sequence[:-1], name='from'), normalize=1) - diff --git a/src/argaze/GazeAnalysis/__init__.py b/src/argaze/GazeAnalysis/__init__.py index faddbce..a980ed3 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', 'TransitionProbabilityMatrix', 'CoefficientK'] \ No newline at end of file +__all__ = ['DispersionThresholdIdentification', 'VelocityThresholdIdentification', 'TransitionMatrix', 'CoefficientK'] \ No newline at end of file diff --git a/src/argaze/utils/demo_gaze_features_run.py b/src/argaze/utils/demo_gaze_features_run.py index 67c7a52..5ab3ed0 100644 --- a/src/argaze/utils/demo_gaze_features_run.py +++ b/src/argaze/utils/demo_gaze_features_run.py @@ -72,14 +72,20 @@ def main(): 'I-DT': DispersionThresholdIdentification.GazeMovementIdentifier(args.deviation_max_threshold, args.duration_min_threshold), 'I-VT': VelocityThresholdIdentification.GazeMovementIdentifier(args.velocity_max_threshold, args.duration_min_threshold) } + fixation_color = { + 'I-DT': (0, 255, 255), + 'I-VT': (255, 0, 255) + } + current_fixation_color = (255, 255, 0) identification_mode = 'I-DT' raw_scan_path = GazeFeatures.ScanPath() aoi_scan_path = GazeFeatures.AOIScanPath() - tpm = TransitionProbabilityMatrix.AOIScanPathAnalyzer() - tpm_analysis = pandas.DataFrame() - enable_tpm_analysis = False + tm = TransitionMatrix.AOIScanPathAnalyzer() + tm_probabilities = pandas.DataFrame() + tm_density = 0. + enable_tm_analysis = False raw_cK_analyzer = CoefficientK.ScanPathAnalyzer() raw_cK_analysis = 0 @@ -103,7 +109,8 @@ def main(): nonlocal gaze_spread_buffer nonlocal heatmap_matrix nonlocal clear_sum_and_buffer - nonlocal tpm_analysis + nonlocal tm_probabilities + nonlocal tm_density nonlocal raw_cK_analysis nonlocal aoi_cK_analysis @@ -174,9 +181,9 @@ def main(): # Analyse aoi scan path if new_step and len(aoi_scan_path) > 1: - if enable_tpm_analysis: + if enable_tm_analysis: - tpm_analysis = tpm.analyze(aoi_scan_path) + tm_probabilities, tm_density = tm.analyze(aoi_scan_path) if enable_ck_analysis: @@ -225,7 +232,7 @@ def main(): enable_disable = 'disable' if enable_heatmap else 'enable' buffer_on_off = 'on' if enable_heatmap_buffer else 'off' buffer_enable_disable = 'disable' if enable_heatmap_buffer else 'enable' - cv2.putText(aoi_matrix, f'Heatmap: {on_off} (Press \'h\' key to {enable_disable}), Buffer: {buffer_on_off} (Press \'b\' key to {buffer_enable_disable})', (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA) + cv2.putText(aoi_matrix, f'Heatmap: {on_off} (Press \'h\' key to {enable_disable}), Buffer: {buffer_on_off} (Press \'b\' key to {buffer_enable_disable})', (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255) if enable_heatmap else (255, 255, 255), 1, cv2.LINE_AA) # Draw gaze spread heatmap if enable_heatmap: @@ -235,17 +242,17 @@ def main(): else: # Write identification mode - cv2.putText(aoi_matrix, f'Gaze movement identification mode: {identification_mode} (Press \'m\' key to switch)', (20, 80), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA) + cv2.putText(aoi_matrix, f'Gaze movement identification mode: {identification_mode} (Press \'m\' key to switch)', (20, 80), cv2.FONT_HERSHEY_SIMPLEX, 1, fixation_color[identification_mode], 1, cv2.LINE_AA) # Write TPM help - on_off = 'on' if enable_tpm_analysis else 'off' - display_hide = 'hide' if enable_tpm_analysis else 'display' - cv2.putText(aoi_matrix, f'Transition matrix probability: {on_off} (Press \'t\' key to {display_hide})', (20, 120), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA) + on_off = 'on' if enable_tm_analysis else 'off' + display_hide = 'hide' if enable_tm_analysis else 'display' + cv2.putText(aoi_matrix, f'Transition matrix: {on_off} (Press \'t\' key to {display_hide})', (20, 120), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255) if enable_tm_analysis else (255, 255, 255), 1, cv2.LINE_AA) # Write cK help on_off = 'on' if enable_ck_analysis else 'off' display_hide = 'hide' if enable_ck_analysis else 'display' - cv2.putText(aoi_matrix, f'coefficient K: {on_off} (Press \'k\' key to {display_hide})', (20, 160), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA) + cv2.putText(aoi_matrix, f'coefficient K: {on_off} (Press \'k\' key to {display_hide})', (20, 160), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255) if enable_ck_analysis else (255, 255, 255), 1, cv2.LINE_AA) # Check fixation identification if gaze_movement_identifier[identification_mode].current_fixation != None: @@ -256,7 +263,7 @@ def main(): aoi_scene_projection.draw_circlecast(aoi_matrix, current_fixation.focus, current_fixation.deviation_max, base_color=(0, 0, 0), matching_color=(255, 255, 255)) # Draw current fixation - current_fixation.draw(aoi_matrix, color=(255, 255, 0)) + current_fixation.draw(aoi_matrix, color=current_fixation_color) # Draw current fixation gaze positions current_fixation.draw_positions(aoi_matrix) @@ -278,7 +285,7 @@ def main(): current_saccade.draw_positions(aoi_matrix) # Draw last 10 steps of raw scan path - raw_scan_path.draw(aoi_matrix, fixation_color=(255, 0, 255), deepness=10) + raw_scan_path.draw(aoi_matrix, fixation_color=fixation_color[identification_mode], deepness=10) # Write last 5 steps of aoi scan path path = '' @@ -291,9 +298,11 @@ def main(): cv2.putText(aoi_matrix, path, (20, window_size[1]-40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA) # Draw transition probability matrix - if enable_tpm_analysis: + if enable_tm_analysis: - for from_aoi, column in tpm_analysis.items(): + cv2.putText(aoi_matrix, f'Transition matrix density: {tm_density:.2f}', (20, window_size[1]-160), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 1, cv2.LINE_AA) + + for from_aoi, column in tm_probabilities.items(): for to_aoi, probability in column.items(): @@ -367,7 +376,7 @@ def main(): # Enable TPM analysis with 't' key if key_pressed == 116: - enable_tpm_analysis = not enable_tpm_analysis + enable_tm_analysis = not enable_tm_analysis # Stop calibration by pressing 'Esc' key if cv2.waitKey(10) == 27: -- cgit v1.1