aboutsummaryrefslogtreecommitdiff
path: root/docs/user_guide/gaze_analysis_pipeline/advanced_topics/scripting.md
blob: 264e866bb309bfb0e739a246967b5a442fc32407 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
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()
```