Gaze movement ============= ## Definition !!! note *"The act of classifying eye movements into distinct events is, on a general level, driven by a desire to isolate different intervals of the data stream strongly correlated with certain oculomotor or cognitive properties."* Citation from ["One algorithm to rule them all? An evaluation and discussion of ten eye movement event-detection algorithms"](https://link.springer.com/article/10.3758/s13428-016-0738-9) article. [GazeFeatures](/argaze/#argaze.GazeFeatures) defines abstract [GazeMovement](/argaze/#argaze.GazeFeatures.GazeMovement) class, then abstract [Fixation](/argaze/#argaze.GazeFeatures.Fixation) and [Saccade](/argaze/#argaze.GazeFeatures.Saccade) classes which inherit from [GazeMovement](/argaze/#argaze.GazeFeatures.GazeMovement). The **positions** [GazeMovement](/argaze/#argaze.GazeFeatures.GazeMovement) attribute contain all [GazePositions](/argaze/#argaze.GazeFeatures.GazePosition) belonging to itself. ![Fixation and Saccade](../../img/fixation_and_saccade.png) ## Identification [GazeFeatures](/argaze/#argaze.GazeFeatures) defines abstract [GazeMovementIdentifier](/argaze/#argaze.GazeFeatures.GazeMovementIdentifier) classe to let add various identification algorithms. Some gaze movement identification algorithms are available thanks to [GazeAnalysis](/argaze/#argaze.GazeAnalysis) submodule: * [Dispersion threshold identification (I-DT)](/argaze/#argaze.GazeAnalysis.DispersionThresholdIdentification) * [Velocity threshold identification (I-VT)](/argaze/#argaze.GazeAnalysis.VelocityThresholdIdentification) ### Identify method [GazeMovementIdentifier](/argaze/#argaze.GazeFeatures.GazeMovementIdentifier) **identify** method allows to fed its identification algorithm with successive gaze positions to output Fixation, Saccade or any kind of GazeMovement instances. Here is a sample of code based on I-DT algorithm to illustrate how to use it: ``` python from argaze import GazeFeatures from argaze.GazeAnalysis import DispersionThresholdIdentification # Create a gaze movement identifier based on dispersion algorithm with 50px max deviation 200 ms max duration thresholds gaze_movement_identifier = DispersionThresholdIdentification.GazeMovementIdentifier(50, 200) # Assuming that timestamped gaze positions are provided through live stream or later data reading ...: gaze_movement = gaze_movement_identifier.identify(timestamp, gaze_position) # Fixation identified if GazeFeatures.is_fixation(gaze_movement): # Access to first gaze position of identified fixation start_ts, start_position = gaze_movement.positions.first # Access to fixation duration print('duration: {gaze_movement.duration}') # Iterate over all gaze positions of identified fixation for ts, position in gaze_movement.positions.items(): # Do something with each fixation position ... # Saccade identified elif GazeFeatures.is_saccade(gaze_movement): # Access to first gaze position of identified saccade start_ts, start_position = gaze_movement.positions.first # Access to saccade amplitude print('amplitude: {gaze_movement.amplitude}') # Iterate over all gaze positions of identified saccade for ts, position in gaze_movement.positions.items(): # Do something with each saccade position ... # No gaze movement identified else: continue ``` ### Browse method [GazeMovementIdentifier](/argaze/#argaze.GazeFeatures.GazeMovementIdentifier) **browse** method allows to pass a TimeStampedGazePositions buffer to apply identification algorithm on all gaze positions inside. Identified gaze movements are returned through: * [TimeStampedGazeMovements](/argaze/#argaze.GazeFeatures.TimeStampedGazeMovements) instance where all fixations are stored by starting gaze position timestamp. * [TimeStampedGazeMovements](/argaze/#argaze.GazeFeatures.TimeStampedGazeMovements) instance where all saccades are stored by starting gaze position timestamp. * [TimeStampedGazeStatus](/argaze/#argaze.GazeFeatures.TimeStampedGazeStatus) instance where all gaze positions are linked to a fixation or saccade index. ``` python # Assuming that timestamped gaze positions are provided through data reading ts_fixations, ts_saccades, ts_status = gaze_movement_identifier.browse(ts_gaze_positions) ``` * ts_fixations would look like: |timestamp|positions |duration|dispersion|focus | |:--------|:-------------------------------------------------------------|:-------|:---------|:--------| |60034 |{"60034":[846,620], "60044":[837,641], "60054":[835,649], ...}|450 |40 |(840,660)| |60504 |{"60504":[838,667], "60514":[838,667], "60524":[837,669], ...}|100 |38 |(834,651)| |... |... |... |.. |... | * ts_saccades would look like: |timestamp|positions |duration| |:--------|:---------------------------------------|:-------| |60484 |{"60484":[836, 669], "60494":[837, 669]}|10 | |60594 |{"60594":[833, 613], "60614":[927, 601]}|20 | |... |... |... | * ts_status would look like: |timestamp|position |type |index| |:--------|:---------|:-------|:----| |60034 |(846, 620)|Fixation|1 | |60044 |(837, 641)|Fixation|1 | |... |... |... |. | |60464 |(836, 668)|Fixation|1 | |60474 |(836, 668)|Fixation|1 | |60484 |(836, 669)|Saccade |1 | |60494 |(837, 669)|Saccade |1 | |60504 |(838, 667)|Fixation|2 | |60514 |(838, 667)|Fixation|2 | |... |... |... |. | |60574 |(825, 629)|Fixation|2 | |60584 |(829, 615)|Fixation|2 | |60594 |(833, 613)|Saccade |2 | |60614 |(927, 601)|Saccade |2 | |60624 |(933, 599)|Fixation|3 | |60634 |(934, 603)|Fixation|3 | |... |... |... |. | !!! note [TimeStampedGazeMovements](/argaze/#argaze.GazeFeatures.TimeStampedGazeMovements), [TimeStampedGazeMovements](/argaze/#argaze.GazeFeatures.TimeStampedGazeMovements) and [TimeStampedGazeStatus](/argaze/#argaze.GazeFeatures.TimeStampedGazeStatus) classes inherit from [TimeStampedBuffer](/argaze/#argaze.DataStructures.TimeStampedBuffer) class. Read [timestamped data] page to understand all features it provides.