Visualizing Fixation Patterns#

The FixationViewer#

Overview

The FixationViewer provides three visualizations:

  • A playback of scanpath

  • A fixation density map (i.e., heatmap)

  • An areas-of-interest map

It also shows some basic fixation measures, such as fixation count and duration.

FixationViewer should come in handy when you want to get a first impression of eye movement patterns during a particular trial.

To demonstrate how FixationViewer works, let’s first generate some fake data:

import numpy as np
import pandas as pd
import pupeyes as pe

# generate some fake fixation data
screen_dims = (1600, 1200) # width, height
pics = ['street1.jpg', 'street2.jpg']
x = np.random.randint(0, screen_dims[0], size=100)
y = np.random.randint(0, screen_dims[1], size=100)
duration = np.random.randint(100, 1000, size=100)
stimuli = ['street1.jpg'] * 50 + ['street2.jpg'] * 50
trial_id = [1] * 50 + [2] * 50

fixation_data = pd.DataFrame({'x': x, 'y': y, 'duration': duration, 'stimuli': stimuli, 'trial_id': trial_id})
fixation_data
x y duration stimuli trial_id
0 260 771 441 street1.jpg 1
1 1000 522 667 street1.jpg 1
2 1576 519 123 street1.jpg 1
3 377 345 595 street1.jpg 1
4 825 629 843 street1.jpg 1
... ... ... ... ... ...
95 594 471 967 street2.jpg 2
96 416 1139 742 street2.jpg 2
97 1329 708 658 street2.jpg 2
98 1572 755 243 street2.jpg 2
99 748 1082 406 street2.jpg 2

100 rows × 5 columns

Note

Note that these columns are not Eyelink-specific. You may export a data file like this from Tobii Pro Lab and it will work with the fixation analyses functions in PupEyes.

The fake data consists of 2 trials, with each trial including 50 fixations on the associated stimulus.

Next, we will define some AOIs. I already did this using the AOIDrawer tool. So I will just load those .json files and format the AOIs in a way that’s supported by FixationViewer.

import json

# load AOIs
with open('assets/street1_aois.json', 'r') as f:
    aois1 = json.load(f)

with open('assets/street2_aois.json', 'r') as f:
    aois2 = json.load(f)

# nested dictionary
aois = {
    'street1.jpg': aois1, # key is the stimulus name in the data
    'street2.jpg': aois2
}

The final step is to initialize FixationViewer by passing the correct arguments.

from pupeyes import FixationViewer

# path that stores my stimulus pictures
pic_path = 'assets/'

# Create visualizer
viz = FixationViewer(
    data=fixation_data,  # your fixation dataframe
    screen_dims = (1600, 1200),
    stimuli_path = pic_path,
    col_mapping={
        'trial_id': 'trial_id',  # List of columns that identify a trial
        'x': 'x',  # x coordinate column name
        'y': 'y',  # y coordinate column name
        'duration': 'duration',  # duration column name
        'stimuli': 'stimuli'  # uncomment if you have stimuli images
    }
)

# Set AOIs if needed
viz.set_aois(aois)

# viz.run()
No timestamp column specified.

Below is an animation of FixationViewer as it runs on my local machine.

_images/fixation_viewer.gif

Note

The timestamp and duration columns are optional. Some measures will not be displayed, however.

Error

Known Issues Currently, autoscaling and resetting axes do not correctly reset axes for the scanpath plot and the AOI plot.

Static Drawing Tools#

PupEyes also provides several static drawing options using matplotlib.

Scanpaths#

from pupeyes import draw_scanpath

plot_data = fixation_data.query('trial_id==1')
path = 'assets/street1.jpg'

draw_scanpath(x=plot_data['x'], y=plot_data['y'], screen_dims=(1600, 1200), durations=plot_data['duration'], background_img=path)
Original size: (6231, 4154) Resized size: (1600, 1200)
(<Figure size 640x480 with 2 Axes>, <Axes: title={'center': 'Scanpath'}>)
_images/98f72db045ced549842288511461fe0e9549de88889a43a178475de567ce9fd1.png

Drawing Heatmap#

from pupeyes import draw_heatmap

draw_heatmap(x=plot_data['x'], y=plot_data['y'], screen_dims=(1600, 1200), durations=plot_data['duration'], background_img=path)
Original size: (6231, 4154) Resized size: (1600, 1200)
(<Figure size 640x480 with 2 Axes>,
 <Axes: title={'center': 'Fixation Density Heatmap'}>)
_images/3fa446653a4e58bcc127a9466c7a555f4f5e4d55a4b2927f4193a0e2befe6eca.png

Draw AOIs#

from pupeyes import draw_aois

draw_aois(aois=aois1, x=plot_data['x'], y=plot_data['y'], screen_dims=(1600, 1200), background_img=path)
Original size: (6231, 4154) Resized size: (1600, 1200)
(<Figure size 640x480 with 1 Axes>,
 <Axes: title={'center': 'Areas of Interest (AOIs)'}>)
_images/0ccb887c79dda62381d1480b3b69bb931a74def14b36af60d04e4844fe150868.png