Detecting Gaze Events#

What you will learn in this tutorial:#

  • how to detect saccades using the microsaccades algorithm

  • how to detect fixations using the I-DT and I-VT algorithms

Preparations#

We import pymovements as the alias pm for convenience.

[1]:
import polars as pl

import pymovements as pm
/home/docs/checkouts/readthedocs.org/user_builds/pymovements/envs/v0.15.0/lib/python3.9/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

Let’s start by downloading our ToyDataset and loading in its data:

[2]:
dataset = pm.Dataset('ToyDataset', path='data/ToyDataset')
dataset.download()
dataset.load()
Downloading http://github.com/aeye-lab/pymovements-toy-dataset/zipball/6cb5d663317bf418cec0c9abe1dde5085a8a8ebd/ to data/ToyDataset/downloads/pymovements-toy-dataset.zip
pymovements-toy-dataset.zip: 100%|██████████| 3.06M/3.06M [00:00<00:00, 24.1MB/s]
Checking integrity of pymovements-toy-dataset.zip
Extracting pymovements-toy-dataset.zip to data/ToyDataset/raw
100%|██████████| 20/20 [00:00<00:00, 194.03it/s]
[2]:
<pymovements.dataset.dataset.Dataset at 0x7faad7146730>

Now let’s do some basic preprocessing:

[3]:
dataset.pix2deg()
dataset.pos2vel('smooth')

dataset.gaze[0].frame.head()
100%|██████████| 20/20 [00:00<00:00, 718.87it/s]
100%|██████████| 20/20 [00:00<00:00, 666.48it/s]
[3]:
shape: (5, 9)
text_idpage_idtimex_right_pixy_right_pixy_right_posx_right_posx_right_vely_right_vel
i64i64f64f64f64f64f64f64f64
011.988145e6206.8152.4-12.005591-7.5280751.221164-3.589697
011.988146e6206.9152.1-12.01277-7.5256332.442343-7.179203
011.988147e6207.0151.8-12.019949-7.523191.628238-5.184827
011.988148e6207.1151.7-12.022342-7.5207480.407059-4.386968
011.988149e6207.0151.5-12.027128-7.523190.407069-3.190445

Detecting Events#

pymovements provides a range of event detection methods for several types of gaze events.

See the reference for pymovements.events to get an overview of all the supported methods.

For this tutorial we will use the microsaccades algorithm for detecting saccades and the I-DT and I-VT algorithms for detecting fixations.

We start out by detecting saccades.

[4]:
dataset.detect_events('microsaccades', minimum_duration=12)
20it [00:00, 71.09it/s]
[4]:
<pymovements.dataset.dataset.Dataset at 0x7faad7146730>

The detected events are added as rows with the name saccade to the event dataframe:

[5]:
dataset.events[0].frame.head()
[5]:
shape: (5, 6)
nameonsetoffsetdurationtext_idpage_id
stri64i64i64i64i64
"saccade"198832319883371401
"saccade"198854719885672001
"saccade"198873719887602301
"saccade"198876519887771201
"saccade"198901419890311701

Next we will detect fixations using the I-DT and I-VT algorithms.

To be able to differentiate between the detected events, we specify custom event names for each algorithm:

[6]:
dataset.detect_events('idt', dispersion_threshold=2.7, name='fixation.idt')
dataset.detect_events('ivt', velocity_threshold=20, name='fixation.ivt')
20it [00:09,  2.18it/s]
20it [00:00, 359.24it/s]
[6]:
<pymovements.dataset.dataset.Dataset at 0x7faad7146730>

This has added new rows with the fixation events to the event dataframe.

[7]:
dataset.events[0].frame.filter(pl.col('name') == 'fixation.idt').head()
[7]:
shape: (5, 6)
nameonsetoffsetdurationtext_idpage_id
stri64i64i64i64i64
"fixation.idt"1988145198856341801
"fixation.idt"1988564198875018601
"fixation.idt"1988751198917842701
"fixation.idt"1989179198943625701
"fixation.idt"1989437198960016301
[8]:
dataset.events[0].frame.filter(pl.col('name') == 'fixation.ivt').head()
[8]:
shape: (5, 6)
nameonsetoffsetdurationtext_idpage_id
stri64i64i64i64i64
"fixation.ivt"1988145198832217701
"fixation.ivt"1988351198854619501
"fixation.ivt"1988592198873614401
"fixation.ivt"1988788198901322501
"fixation.ivt"1989060198917011001

What you have learned in this tutorial:#

  • detecting saccades by using the microsaccades algorithm

  • detecting fixations by using the I-DT and I-VT algorithms