Webcam (WebGazer): browser recording to BIDS derivatives¶
This notebook runs the complete Pyxations workflow on a jsPsych/WebGazer
export. Install the optional detector and animation dependencies with
pip install "pyxations[remodnav,video]" before running it.
In [1]:
Copied!
import base64
from pathlib import Path
from IPython.display import HTML
repo = next(
path
for path in (Path.cwd(), *Path.cwd().parents)
if (path / "pyproject.toml").is_file()
)
import base64
from pathlib import Path
from IPython.display import HTML
repo = next(
path
for path in (Path.cwd(), *Path.cwd().parents)
if (path / "pyproject.toml").is_file()
)
In [2]:
Copied!
import pyxations as pyx
import pyxations as pyx
Dataset to BIDS¶
The example contains a WebGazer CSV export. Pyxations interprets the first underscore-separated filename token as the source subject ID and the next token as the session label.
Where "arx" is the subject id and "first" is the session name, both separated by an underscore.
In [3]:
Copied!
output_folder = repo / "generated"
files_folder_path = repo / "examples" / "webgazer_antisaccade"
bids_dataset_folder = pyx.dataset_to_bids(
output_folder,
files_folder_path,
"antisacadas_dataset",
format_name="webgazer",
overwrite=True,
)
output_folder = repo / "generated"
files_folder_path = repo / "examples" / "webgazer_antisaccade"
bids_dataset_folder = pyx.dataset_to_bids(
output_folder,
files_folder_path,
"antisacadas_dataset",
format_name="webgazer",
overwrite=True,
)
In [4]:
Copied!
print(bids_dataset_folder)
print(bids_dataset_folder)
/home/runner/work/pyxations/pyxations/generated/antisacadas_dataset
Compute derivatives¶
In [5]:
Copied!
dataset_type = "webgazer"
detection_algorithm = "remodnav"
pyx.compute_derivatives_for_dataset(
bids_dataset_folder,
dataset_type,
detection_algorithm,
overwrite=True,
screen_height=768,
screen_width=1024,
)
dataset_type = "webgazer"
detection_algorithm = "remodnav"
pyx.compute_derivatives_for_dataset(
bids_dataset_folder,
dataset_type,
detection_algorithm,
overwrite=True,
screen_height=768,
screen_width=1024,
)
/home/runner/work/pyxations/pyxations/pyxations/bids_formatting.py:188: UserWarning: This recording is sampled at 5.9 Hz, below the 50 Hz that velocity-based detection needs to resolve a saccade, so the events remodnav reports will be unreliable. Consider analysing the gaze samples directly instead of the detected events. _warn_if_rate_is_too_low_to_detect(raw.sampling_frequency, detection_algorithm) At the provided sampling rate of 5.9171597633136095, the timeframe for the parameter 'min-intersaccade-duration' would be lower than a single sample (0.2 samples). Consider increasing the parameter value to prevent errors.
At the provided sampling rate of 5.9171597633136095, the timeframe for the parameter 'min-saccade-duration' would be lower than a single sample (0.2 samples). Consider increasing the parameter value to prevent errors.
At the provided sampling rate of 5.9171597633136095, the timeframe for the parameter 'max-pso-duration' would be lower than a single sample (0.6 samples). Consider increasing the parameter value to prevent errors.
At the provided sampling rate of 5.9171597633136095, the timeframe for the parameter 'min-fixation-duration' would be lower than a single sample (0.3 samples). Consider increasing the parameter value to prevent errors.
Out[5]:
PosixPath('/home/runner/work/pyxations/pyxations/generated/antisacadas_dataset_derivatives')
Animating the gaze trace¶
SampleVisualization animates the sample stream directly, without
relying on detected events. The animation below covers the first 300
samples, about one minute of this recording, since one frame is rendered
per sample.
In [6]:
Copied!
experiment = pyx.Experiment(bids_dataset_folder)
experiment.load_data(detection_algorithm)
samples = experiment.get_session("0001", "antisacadas").samples().head(300)
animation_path = repo / "generated" / "webgazer_trace.gif"
pyx.SampleVisualization(samples, screen_width=1024, screen_height=768).animate(
display=False, out_file=animation_path
)
# Embed as a data URI: the HTML export renders html outputs but falls back
# to a text repr for image/gif ones.
encoded = base64.b64encode(animation_path.read_bytes()).decode()
HTML(f'<img src="data:image/gif;base64,{encoded}" alt="gaze trace">')
experiment = pyx.Experiment(bids_dataset_folder)
experiment.load_data(detection_algorithm)
samples = experiment.get_session("0001", "antisacadas").samples().head(300)
animation_path = repo / "generated" / "webgazer_trace.gif"
pyx.SampleVisualization(samples, screen_width=1024, screen_height=768).animate(
display=False, out_file=animation_path
)
# Embed as a data URI: the HTML export renders html outputs but falls back
# to a text repr for image/gif ones.
encoded = base64.b64encode(animation_path.read_bytes()).decode()
HTML(f'<img src="data:image/gif;base64,{encoded}" alt="gaze trace">')
Out[6]: