Script the pipeline =================== All gaze analysis pipeline objects are accessible from a Python script. This could be particularly useful for real-time gaze interaction applications. ## Load ArFrame configuration from dictionary An [ArFrame](../../../argaze.md/#argaze.ArFeatures.ArFrame) configuration can be loaded from a Python dictionary. ```python from argaze import ArFeatures, DataFeatures # Set working directory to enable relative file path loading DataFeatures.set_working_directory('path/to/folder') # Edit a dict with ArFrame configuration configuration = { "name": "My FullHD screen", "size": (1920, 1080), ... "gaze_movement_identifier": { ... }, "scan_path": { ... }, "scan_path_analyzers": { ... }, "heatmap": { ... }, "layers": { "MyLayer": { ... }, ... }, "image_parameters": { ... } } # Load ArFrame with ArFeatures.ArFrame(**configuration) as ar_frame: # Do something with ArFrame ... ``` ## Access to ArFrame and ArLayers attributes Then, once the configuration is loaded, it is possible to access to its attributes: [read ArFrame code reference](../../../argaze.md/#argaze.ArFeatures.ArFrame) to get a complete list of what is available. Thus, the [ArFrame.layers](../../../argaze.md/#argaze.ArFeatures.ArFrame) attribute allows to access each loaded layer and so, access to their attributes: [read ArLayer code reference](../../../argaze.md/#argaze.ArFeatures.ArLayer) to get a complete list of what is available. ```python from argaze import ArFeatures # Assuming the ArFrame is loaded ... # Iterate over each ArFrame layers for name, ar_layer in ar_frame.layers.items(): ... ``` ## Pipeline execution Timestamped [GazePositions](../../../argaze.md/#argaze.GazeFeatures.GazePosition) have to be passed one by one to the [ArFrame.look](../../../argaze.md/#argaze.ArFeatures.ArFrame.look) method to execute the whole instantiated pipeline. !!! warning "Mandatory" The [ArFrame.look](../../../argaze.md/#argaze.ArFeatures.ArFrame.look) method must be called from a *try* block to catch pipeline exceptions. ```python # Assuming that timestamped gaze positions are available ... try: # Look ArFrame at a timestamped gaze position ar_frame.look(timestamped_gaze_position) # Do something with pipeline exception except Exception as e: ... ``` Calling [ArFrame.look](../../../argaze.md/#argaze.ArFeatures.ArFrame.look) method leads to update many data into the pipeline. ```python # Assuming that timestamped gaze positions are available ... try: # Look ArFrame at a timestamped gaze position ar_frame.look(timestamped_gaze_position): # Do something with last gaze position ... ar_frame.last_gaze_position() # Check if a gaze movement has been identified if ar_frame.last_gaze_movement() and ar_frame.last_gaze_movement().is_finished(): # Do something with identified fixation if GazeFeatures.is_fixation(ar_frame.last_gaze_movement()): ... # Do something with identified saccade elif GazeFeatures.is_saccade(ar_frame.last_gaze_movement()): ... # Do something with scan path analysis if ar_frame.is_analysis_available(): for scan_path_analyzer_name, analyses in ar_frame.analysis().items(): for analysis_name, analysis in analyses.items(): ... # Do something with layers aoi scan path analysis for layer_name, ar_layer in ar_frame.layers.items(): # Do something with last looked aoi name ... ar_frame.last_looked_aoi_name() if ar_layer.is_analysis_available(): for aoi_scan_path_analyzer_name, analyses in ar_frame.analysis().items(): for analysis_name, analysis in analyses.items(): ... # Do something with pipeline exception except Exception as e: ... ``` Let's understand the meaning of each data. ### *ar_frame.last_gaze_position()* This is the last calibrated [GazePosition](../../../argaze.md/#argaze.GazeFeatures.GazePosition) returned by [GazePositionCalibrator](../../../argaze.md/#argaze.GazeFeatures.GazePositionCalibrator) if one is instanciated else, it is the last given [GazePosition](../../../argaze.md/#argaze.GazeFeatures.GazePosition). ### *ar_frame.last_gaze_movement()* Last [GazeMovement](../../../argaze.md/#argaze.GazeFeatures.GazeMovement) identified by [ArFrame.gaze_movement_identifier](../../../argaze.md/#argaze.ArFeatures.ArFrame) object from incoming consecutive timestamped gaze positions. If no gaze movement have been identified, it returns an empty [GazeMovement](../../../argaze.md/#argaze.GazeFeatures.GazeMovement). This could also be the current gaze movement if [ArFrame.filter_in_progress_identification](../../../argaze.md/#argaze.ArFeatures.ArFrame) attribute is false. In that case, the last gaze movement *finished* flag is false. Then, the last gaze movement type can be tested thanks to [GazeFeatures.is_fixation](../../../argaze.md/#argaze.GazeFeatures.is_fixation) and [GazeFeatures.is_saccade](../../../argaze.md/#argaze.GazeFeatures.is_saccade) functions. ### *ar_frame.is_analysis_available()* To know when new scan path analysis is available. ### *ar_frame.analysis().items()* This an iterator to access to all scan path analysis. Notice that each scan path analyzer returns a dictionary containing possibly several analyses. ### *ar_layer.last_looked_aoi_name()* The name of the last aoi matching done by [AoiMatcher](../../../argaze.md/#argaze.GazeFeatures.AoiMatcher) if one is instanciated else, it is a None value. ### *ar_layer.is_analysis_available()* To know when new aoi scan path analysis is available. ### *ar_layer.analysis().items()* This an iterator to access to all aoi scan path analysis. Notice that each aoi scan path analyzer returns a dictionary containing possibly several analyses. ## Setup ArFrame image parameters [ArFrame.image](../../../argaze.md/#argaze.ArFeatures.ArFrame.image) method parameters can be configured thanks to a Python dictionary. ```python # Assuming ArFrame is loaded ... # Edit a dict with ArFrame image parameters image_parameters = { "draw_scan_path": { ... }, "draw_layers": { "MyLayer": { ... } }, ... } # Pass image parameters to ArFrame ar_frame_image = ar_frame.image(**image_parameters) # Do something with ArFrame image ... ``` Then, [ArFrame.image](../../../argaze.md/#argaze.ArFeatures.ArFrame.image) method can be called in various situations. ### Live window display While timestamped gaze positions are processed by [ArFrame.look](../../../argaze.md/#argaze.ArFeatures.ArFrame.look) method, it is possible to display the [ArFrame](../../../argaze.md/#argaze.ArFeatures.ArFrame) image thanks to the [OpenCV package](https://pypi.org/project/opencv-python/). ```python import cv2 def main(): # Assuming ArFrame is loaded ... # Create a window to display ArFrame cv2.namedWindow(ar_frame.name, cv2.WINDOW_AUTOSIZE) # Assuming that timestamped gaze positions are being processed by ArFrame.look method ... # Update ArFrame image display cv2.imshow(ar_frame.name, ar_frame.image()) # Wait 10 ms cv2.waitKey(10) if __name__ == '__main__': main() ```