Log 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. ## 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. 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: ```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"] }, ... "layers": { "MyLayer": { "observers": { "my_layer_logger.AOIScanPathAnalysisLogger": { "path": "./aoi_scan_path_metrics.csv", "header": ["Timestamp (ms)", "NGram counts"] } }, ... } } } ``` !!! 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: ```python from argaze import DataFeatures from argaze.utils import UtilsFeatures class ScanPathAnalysisLogger(DataFeatures.PipelineStepObserver, UtilsFeatures.FileWriter): def on_look(self, timestamp, ar_frame, exception): """Log scan path metrics""" if ar_frame.is_analysis_available(): log = ( timestamp, ar_frame.analysis['argaze.GazeAnalysis.Basic'].path_duration, ar_frame.analysis['argaze.GazeAnalysis.Basic'].steps_number ) # Write to file self.write(log) ``` 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 | |... |... |... | Here is *my_layer_logger.py* file: ```python from argaze import DataFeatures from argaze.utils import UtilsFeatures class AOIScanPathAnalysisLogger(DataFeatures.PipelineStepObserver, UtilsFeatures.FileWriter): def on_look(self, timestamp, ar_layer, exception): """Log aoi scan path metrics.""" if ar_layer.is_analysis_available(): log = ( timestamp, ar_layer.analysis['argaze.GazeAnalysis.NGram'].ngrams_count ) # Write to file self.write(log) ``` 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| |:-------------|:-----------| |5687 |"{3: {}, 4: {}, 5: {}}"| |6208 |"{3: {('LeftPanel', 'GeoSector', 'CircularWidget'): 1}, 4: {}, 5: {}}"| |... |... | !!! note "" Learn to [script the pipeline](./advanced_topics/scripting.md) to know more about [ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) and [ArLayers](../../argaze.md/#argaze.ArFeatures.ArLayer) attributes. ### Export gaze analysis to video file As explained in [pipeline steps visualisation chapter](visualisation.md), it is possible to get [ArFrame.image](../../argaze.md/#argaze.ArFeatures.ArFrame.image) once timestamped gaze positions have been processed by [ArFrame.look](../../argaze.md/#argaze.ArFeatures.ArFrame.look) method. Here is the JSON ArFrame configuration file where [ArFrame](../../argaze.md/#argaze.ArFeatures.ArFrame) observers are extended with a new my_frame_logger.VideoRecorder instance: ```json { "name": "My FullHD screen", "size": [1920, 1080], "observers": { ... "my_frame_logger.VideoRecorder": { "path": "./video.mp4", "width": 1920, "height": 1080, "fps": 15 }, ... } ``` Here is *my_frame_logger.py* file extended with a new VideoRecorder class: ```python ... class VideoRecorder(DataFeatures.PipelineStepObserver, UtilsFeatures.VideoWriter): def on_look(self, timestamp, ar_frame, exception): """Record frame image into video file.""" self.write(ar_frame.image()) ``` Assuming that [ArFrame.image_parameters](../../argaze.md/#argaze.ArFeatures.ArFrame.image_parameters) are provided, ***video.mp4*** file would be created.