From cb3883ccdae38609517bdac3b01947f6200b91ab Mon Sep 17 00:00:00 2001
From: Théo de la Hogue
Date: Wed, 3 Apr 2024 14:44:02 +0200
Subject: Renaming log notion into record notion.
---
docs/user_guide/gaze_analysis_pipeline/logging.md | 86 ++++++++--------
src/argaze/__main__.py | 7 +-
src/argaze/utils/demo/gaze_analysis_pipeline.json | 16 +--
src/argaze/utils/demo/loggers.py | 116 ----------------------
src/argaze/utils/demo/recorders.py | 115 +++++++++++++++++++++
5 files changed, 174 insertions(+), 166 deletions(-)
delete mode 100644 src/argaze/utils/demo/loggers.py
create mode 100644 src/argaze/utils/demo/recorders.py
diff --git a/docs/user_guide/gaze_analysis_pipeline/logging.md b/docs/user_guide/gaze_analysis_pipeline/logging.md
index 6ed497b..6ef3a85 100644
--- a/docs/user_guide/gaze_analysis_pipeline/logging.md
+++ b/docs/user_guide/gaze_analysis_pipeline/logging.md
@@ -1,30 +1,28 @@
-Log gaze analysis
+Record gaze analysis
=================
-[ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) and [ArLayer](../../argaze.md/#argaze.ArFeatures.ArLayer) analysis can be logged by registering observers to their **look** method.
+[ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) and [ArLayer](../../argaze.md/#argaze.ArFeatures.ArLayer) analysis can be recorded by registering observers to their **look** method.
## Export gaze analysis to CSV file
-[ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) and [ArLayer](../../argaze.md/#argaze.ArFeatures.ArLayer) have an observers attribute to enable pipeline execution logging.
+[ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) and [ArLayer](../../argaze.md/#argaze.ArFeatures.ArLayer) have an *observers* attribute to enable pipeline execution recording.
-Here is an extract from the JSON ArFrame configuration file where logging is enabled for the [ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) and for one [ArLayer](../../argaze.md/#argaze.ArFeatures.ArLayer) by pointing to dedicated Python files:
+Here is an extract from the JSON ArFrame configuration file where recording is enabled for the [ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) and for one [ArLayer](../../argaze.md/#argaze.ArFeatures.ArLayer) by loaded classes from Python files:
```json
{
"name": "My FullHD screen",
"size": [1920, 1080],
"observers": {
- "my_frame_logger.ScanPathAnalysisLogger": {
- "path": "./scan_path_metrics.csv",
- "header": ["Timestamp (ms)", "Duration (ms)", "Steps number"]
+ "my_recorders.ScanPathAnalysisRecorder": {
+ "path": "./scan_path_metrics.csv"
},
...
"layers": {
"MyLayer": {
"observers": {
- "my_layer_logger.AOIScanPathAnalysisLogger": {
- "path": "./aoi_scan_path_metrics.csv",
- "header": ["Timestamp (ms)", "NGram counts"]
+ "my_recorders.AOIScanPathAnalysisRecorder": {
+ "path": "./aoi_scan_path_metrics.csv"
}
},
...
@@ -36,67 +34,73 @@ Here is an extract from the JSON ArFrame configuration file where logging is ena
!!! note
[ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) and its [ArLayers](../../argaze.md/#argaze.ArFeatures.ArLayer) automatically notify **look** method observers after each call.
-Here is *my_frame_logger.py* file:
+Here is *my_recorders.py* file:
```python
-from argaze import DataFeatures
-from argaze.GazeAnalysis import Basic
from argaze.utils import UtilsFeatures
-class ScanPathAnalysisLogger(DataFeatures.PipelineStepObserver, UtilsFeatures.FileWriter):
+class ScanPathAnalysisRecorder(UtilsFeatures.FileWriter):
+
+ def __init__(self, **kwargs):
+
+ # Init FileWriter
+ super().__init__(**kwargs)
+
+ # Edit hearder line
+ self.header = "Timestamp (ms)", "Duration (ms)", "Steps number"
def on_look(self, timestamp, ar_frame, exception):
- """Log scan path metrics"""
+ """Record scan path metrics"""
if ar_frame.is_analysis_available():
analysis = ar_frame.analysis()
- log = (
+ data = (
timestamp,
- analysis[Basic.ScanPathAnalyzer].path_duration,
- analysis[Basic.ScanPathAnalyzer].steps_number
+ analysis['argaze.GazeAnalysis.Basic.ScanPathAnalyzer'].path_duration,
+ analysis['argaze.GazeAnalysis.Basic.ScanPathAnalyzer'].steps_number
)
# Write to file
- self.write(log)
-```
+ self.write(data)
-Assuming that [ArGaze.GazeAnalysis.Basic](../../argaze.md/#argaze.GazeAnalysis.Basic) scan path analysis module is enabled for 'My FullHD screen' ArFrame, a ***scan_path_metrics.csv*** file would be created:
+class AOIScanPathAnalysisRecorder(UtilsFeatures.FileWriter):
-|Timestamp (ms)|Duration (ms)|Steps number|
-|:-------------|:------------|:-----------|
-|3460 |1750 |2 |
-|4291 |2623 |3 |
-|4769 |3107 |4 |
-|6077 |4411 |5 |
-|6433 |4760 |6 |
-|7719 |6050 |7 |
-|... |... |... |
+ def __init__(self, **kwargs):
-Here is *my_layer_logger.py* file:
+ # Init FileWriter
+ super().__init__(**kwargs)
-```python
-from argaze import DataFeatures
-from argaze.GazeAnalysis import NGram
-from argaze.utils import UtilsFeatures
-
-class AOIScanPathAnalysisLogger(DataFeatures.PipelineStepObserver, UtilsFeatures.FileWriter):
+ # Edit header line
+ self.header = "Timestamp (ms)", "NGram counts"
def on_look(self, timestamp, ar_layer, exception):
- """Log aoi scan path metrics."""
+ """Record aoi scan path metrics."""
if ar_layer.is_analysis_available():
- log = (
+ data = (
timestamp,
- ar_layer.analysis[NGram.AOIScanPathAnalyzer].ngrams_count
+ ar_layer.analysis['argaze.GazeAnalysis.NGram.AOIScanPathAnalyzer'].ngrams_count
)
# Write to file
- self.write(log)
+ self.write(data)
```
+Assuming that [ArGaze.GazeAnalysis.Basic](../../argaze.md/#argaze.GazeAnalysis.Basic) scan path analysis module is enabled for 'My FullHD screen' ArFrame, a ***scan_path_metrics.csv*** file would be created:
+
+|Timestamp (ms)|Duration (ms)|Steps number|
+|:-------------|:------------|:-----------|
+|3460 |1750 |2 |
+|4291 |2623 |3 |
+|4769 |3107 |4 |
+|6077 |4411 |5 |
+|6433 |4760 |6 |
+|7719 |6050 |7 |
+|... |... |... |
+
Assuming that [ArGaze.GazeAnalysis.NGram](../../argaze.md/#argaze.GazeAnalysis.NGram) AOI scan path analysis module is enabled for 'MyLayer' ArLayer, a ***aoi_scan_path_metrics.csv*** file would be created:
|Timestamp (ms)|NGram counts|
diff --git a/src/argaze/__main__.py b/src/argaze/__main__.py
index 0682583..0184d69 100644
--- a/src/argaze/__main__.py
+++ b/src/argaze/__main__.py
@@ -21,7 +21,7 @@ import logging
import contextlib
from .DataFeatures import from_json
-from .ArFeatures import ArCamera
+from .ArFeatures import ArCamera, ArContext
import cv2
@@ -38,6 +38,11 @@ logging.basicConfig(format = '%(levelname)s: %(message)s', level = logging.DEBUG
# Load context from JSON file
with from_json(args.context_file) as context:
+ # Loaded object must be a subclass of ArContext
+ if not issubclass(type(context), ArContext):
+
+ raise TypeError('Loaded object is not a subclass of ArContext')
+
if args.verbose:
print(context)
diff --git a/src/argaze/utils/demo/gaze_analysis_pipeline.json b/src/argaze/utils/demo/gaze_analysis_pipeline.json
index 0d1062b..291b3e1 100644
--- a/src/argaze/utils/demo/gaze_analysis_pipeline.json
+++ b/src/argaze/utils/demo/gaze_analysis_pipeline.json
@@ -49,8 +49,8 @@
"argaze.GazeAnalysis.Entropy.AOIScanPathAnalyzer":{}
},
"observers": {
- "loggers.AOIScanPathAnalysisLogger": {
- "path": "_export/logs/aoi_scan_path_metrics.csv"
+ "recorders.AOIScanPathAnalysisRecorder": {
+ "path": "_export/records/aoi_scan_path_metrics.csv"
}
}
}
@@ -115,14 +115,14 @@
}
},
"observers": {
- "loggers.FixationLogger": {
- "path": "_export/logs/fixations.csv"
+ "recorders.FixationRecorder": {
+ "path": "_export/records/fixations.csv"
},
- "loggers.ScanPathAnalysisLogger": {
- "path": "_export/logs/scan_path_metrics.csv"
+ "recorders.ScanPathAnalysisRecorder": {
+ "path": "_export/records/scan_path_metrics.csv"
},
- "loggers.VideoRecorder": {
- "path": "_export/logs/video.mp4",
+ "recorders.VideoRecorder": {
+ "path": "_export/records/video.mp4",
"width": 1920,
"height": 1080,
"fps": 15
diff --git a/src/argaze/utils/demo/loggers.py b/src/argaze/utils/demo/loggers.py
deleted file mode 100644
index 25b55d3..0000000
--- a/src/argaze/utils/demo/loggers.py
+++ /dev/null
@@ -1,116 +0,0 @@
-"""
-
-This program is free software: you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later
-version.
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-You should have received a copy of the GNU General Public License along with
-this program. If not, see .
-"""
-
-__author__ = "Théo de la Hogue"
-__credits__ = []
-__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)"
-__license__ = "GPLv3"
-
-import logging
-
-from argaze import DataFeatures, GazeFeatures
-from argaze.GazeAnalysis import *
-from argaze.utils import UtilsFeatures
-
-class FixationLogger(UtilsFeatures.FileWriter):
-
- def __init__(self, **kwargs):
-
- super().__init__(**kwargs)
-
- self.header = "Timestamp (ms)", "Focus (px)", "Duration (ms)", "AOI"
-
- logging.info('%s writes into %s', type(self).__name__, self.path)
-
- def on_look(self, timestamp, frame, exception):
- """Log frame fixations."""
-
- # Log fixations
- if GazeFeatures.is_fixation(frame.last_gaze_movement()) and frame.last_gaze_movement().is_finished():
-
- log = (
- timestamp,
- frame.last_gaze_movement().focus,
- frame.last_gaze_movement().duration,
- frame.layers['demo_layer'].last_looked_aoi_name()
- )
-
- self.write(log)
-
-class ScanPathAnalysisLogger(UtilsFeatures.FileWriter):
-
- def __init__(self, **kwargs):
-
- super().__init__(**kwargs)
-
- self.header = "Timestamp (ms)", "Duration (ms)", "Step", "K", "NNI", "XXR"
-
- logging.info('%s writes into %s', type(self).__name__, self.path)
-
- def on_look(self, timestamp, frame, exception):
- """Log frame scan path metrics."""
-
- if frame.is_analysis_available():
-
- analysis = frame.analysis()
-
- log = (
- timestamp,
- analysis[Basic.ScanPathAnalyzer].path_duration,
- analysis[Basic.ScanPathAnalyzer].steps_number,
- analysis[KCoefficient.ScanPathAnalyzer].K,
- analysis[NearestNeighborIndex.ScanPathAnalyzer].nearest_neighbor_index,
- analysis[ExploreExploitRatio.ScanPathAnalyzer].explore_exploit_ratio
- )
-
- self.write(log)
-
-class VideoRecorder(UtilsFeatures.VideoWriter):
-
- def __init__(self, **kwargs):
-
- super().__init__(**kwargs)
-
- logging.info('%s writes into %s', type(self).__name__, self.path)
-
- def on_look(self, timestamp, frame, exception):
- """Write frame image."""
-
- self.write(frame.image())
-
-class AOIScanPathAnalysisLogger(UtilsFeatures.FileWriter):
-
- def __init__(self, **kwargs):
-
- super().__init__(**kwargs)
-
- self.header = "Timestamp (ms)", "Duration (ms)", "Step", "K", "LZC"
-
- logging.info('%s writes into %s', type(self).__name__, self.path)
-
- def on_look(self, timestamp, layer, exception):
- """Log layer aoi scan path metrics"""
-
- if layer.is_analysis_available():
-
- analysis = layer.analysis()
-
- log = (
- timestamp,
- analysis[Basic.AOIScanPathAnalyzer].path_duration,
- analysis[Basic.AOIScanPathAnalyzer].steps_number,
- analysis[KCoefficient.AOIScanPathAnalyzer].K,
- analysis[LempelZivComplexity.AOIScanPathAnalyzer].lempel_ziv_complexity
- )
-
- self.write(log)
diff --git a/src/argaze/utils/demo/recorders.py b/src/argaze/utils/demo/recorders.py
new file mode 100644
index 0000000..75bca70
--- /dev/null
+++ b/src/argaze/utils/demo/recorders.py
@@ -0,0 +1,115 @@
+"""
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+"""
+
+__author__ = "Théo de la Hogue"
+__credits__ = []
+__copyright__ = "Copyright 2023, Ecole Nationale de l'Aviation Civile (ENAC)"
+__license__ = "GPLv3"
+
+import logging
+
+from argaze import DataFeatures, GazeFeatures
+from argaze.utils import UtilsFeatures
+
+class FixationRecorder(UtilsFeatures.FileWriter):
+
+ def __init__(self, **kwargs):
+
+ super().__init__(**kwargs)
+
+ self.header = "Timestamp (ms)", "Focus (px)", "Duration (ms)", "AOI"
+
+ logging.info('%s writes into %s', type(self).__name__, self.path)
+
+ def on_look(self, timestamp, frame, exception):
+ """Log frame fixations."""
+
+ # Log fixations
+ if GazeFeatures.is_fixation(frame.last_gaze_movement()) and frame.last_gaze_movement().is_finished():
+
+ log = (
+ timestamp,
+ frame.last_gaze_movement().focus,
+ frame.last_gaze_movement().duration,
+ frame.layers['demo_layer'].last_looked_aoi_name()
+ )
+
+ self.write(log)
+
+class ScanPathAnalysisRecorder(UtilsFeatures.FileWriter):
+
+ def __init__(self, **kwargs):
+
+ super().__init__(**kwargs)
+
+ self.header = "Timestamp (ms)", "Duration (ms)", "Step", "K", "NNI", "XXR"
+
+ logging.info('%s writes into %s', type(self).__name__, self.path)
+
+ def on_look(self, timestamp, frame, exception):
+ """Log frame scan path metrics."""
+
+ if frame.is_analysis_available():
+
+ analysis = frame.analysis()
+
+ log = (
+ timestamp,
+ analysis['argaze.GazeAnalysis.Basic.ScanPathAnalyzer'].path_duration,
+ analysis['argaze.GazeAnalysis.Basic.ScanPathAnalyzer'].steps_number,
+ analysis['argaze.GazeAnalysis.KCoefficient.ScanPathAnalyzer'].K,
+ analysis['argaze.GazeAnalysis.NearestNeighborIndex.ScanPathAnalyzer'].nearest_neighbor_index,
+ analysis['argaze.GazeAnalysis.ExploreExploitRatio.ScanPathAnalyzer'].explore_exploit_ratio
+ )
+
+ self.write(log)
+
+class VideoRecorder(UtilsFeatures.VideoWriter):
+
+ def __init__(self, **kwargs):
+
+ super().__init__(**kwargs)
+
+ logging.info('%s writes into %s', type(self).__name__, self.path)
+
+ def on_look(self, timestamp, frame, exception):
+ """Write frame image."""
+
+ self.write(frame.image())
+
+class AOIScanPathAnalysisRecorder(UtilsFeatures.FileWriter):
+
+ def __init__(self, **kwargs):
+
+ super().__init__(**kwargs)
+
+ self.header = "Timestamp (ms)", "Duration (ms)", "Step", "K", "LZC"
+
+ logging.info('%s writes into %s', type(self).__name__, self.path)
+
+ def on_look(self, timestamp, layer, exception):
+ """Log layer aoi scan path metrics"""
+
+ if layer.is_analysis_available():
+
+ analysis = layer.analysis()
+
+ log = (
+ timestamp,
+ analysis['argaze.GazeAnalysis.Basic.AOIScanPathAnalyzer'].path_duration,
+ analysis['argaze.GazeAnalysis.Basic.AOIScanPathAnalyzer'].steps_number,
+ analysis['argaze.GazeAnalysis.KCoefficient.AOIScanPathAnalyzer'].K,
+ analysis['argaze.GazeAnalysis.LempelZivComplexity.AOIScanPathAnalyzer'].lempel_ziv_complexity
+ )
+
+ self.write(log)
--
cgit v1.1