Skip to content
Draft
384 changes: 384 additions & 0 deletions fixation_correction_test.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,384 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"# Fixation correction\n",
"\n",
"The code tests algorithms from `preprocessing.events.fixation_correction.py`\n"
]
},
{
"cell_type": "markdown",
"id": "1",
"metadata": {},
"source": [
"### Preparation - the same as in the `preprocessing.ipynb`"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2",
"metadata": {},
"outputs": [],
"source": [
"# from preprocessing.data_collection.multipleye_data_collection import prepare_language_folder\n",
"from preprocessing.data_collection.multipleye_data_collection import (\n",
" MultipleyeDataCollection,\n",
")\n",
"from pathlib import Path\n",
"\n",
"import preprocessing\n",
"\n",
"# the config will be loaded into general constants module, so we can access all settings at the same place\n",
"from preprocessing import constants\n",
"from preprocessing.scripts.prepare_language_folder import prepare_language_folder\n",
"\n",
"\n",
"import polars as pl"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3",
"metadata": {},
"outputs": [],
"source": [
"# get the data collection name from the config and create the path to the data folder\n",
"this_repo = Path().resolve()\n",
"data_collection_name = constants.DATA_COLLECTION_NAME\n",
"data_folder_path = this_repo / \"data\" / data_collection_name"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4",
"metadata": {},
"outputs": [],
"source": [
"# run the preparation function to prepare the language folder structure\n",
"prepare_language_folder(data_collection_name)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5",
"metadata": {},
"outputs": [],
"source": [
"multipleye = MultipleyeDataCollection.create_from_data_folder(\n",
" data_folder_path,\n",
" include_pilots=constants.INCLUDE_PILOTS,\n",
" excluded_sessions=constants.EXCLUDE_SESSIONS,\n",
" included_sessions=constants.INCLUDE_SESSIONS,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6",
"metadata": {},
"outputs": [],
"source": [
"multipleye.convert_edf_to_asc()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7",
"metadata": {},
"outputs": [],
"source": [
"multipleye.prepare_session_level_information()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8",
"metadata": {},
"outputs": [],
"source": [
"# print an overview on the data collection and the sessions\n",
"multipleye"
]
},
{
"cell_type": "markdown",
"id": "9",
"metadata": {},
"source": [
"### Choosing the first session to work with"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10",
"metadata": {},
"outputs": [],
"source": [
"# pick only one session as an example to work with in the next steps\n",
"sessions = [s for s in multipleye]\n",
"sess = sessions[0]\n",
"idf = sess.session_identifier"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "11",
"metadata": {},
"outputs": [],
"source": [
"# get the path to the .asc file for the session\n",
"asc = sess.asc_path"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "12",
"metadata": {},
"outputs": [],
"source": [
"# load gaze data\n",
"gaze = preprocessing.load_gaze_data(\n",
" asc_file=asc,\n",
" lab_config=sess.lab_config,\n",
" session_idf=idf,\n",
" trial_cols=constants.TRIAL_COLS,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "13",
"metadata": {},
"outputs": [],
"source": [
"preprocessing.preprocess_gaze(gaze)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "14",
"metadata": {},
"outputs": [],
"source": [
"preprocessing.detect_fixations(\n",
" gaze,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "15",
"metadata": {},
"outputs": [],
"source": [
"sess.stimuli"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "16",
"metadata": {},
"outputs": [],
"source": [
"gaze"
]
},
{
"cell_type": "markdown",
"id": "17",
"metadata": {},
"source": [
"## And now it begins with the fixation correction!"
]
},
{
"cell_type": "markdown",
"id": "18",
"metadata": {},
"source": [
"### Add \"fixation_correction_{algorithm}\" events to all stimuli and pages"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "19",
"metadata": {},
"outputs": [],
"source": [
"import importlib\n",
"\n",
"import preprocessing.events.fixation_correction as fc\n",
"\n",
"importlib.reload(fc)\n",
"\n",
"algorithm = \"chain\"\n",
"\n",
"### Necessary to remove previous corrections if you want to add new ones,\n",
"# otherwise you will get an error because the function is designed\n",
"# to not allow multiple corrections with the same algorithm in the events\n",
"# dataframe to avoid confusion and mistakes.\n",
"fc.remove_previous_fixation_corrections(gaze.events, algorithm)\n",
"\n",
"### Add the corrected fixations to the events dataframe\n",
"fc.add_corrected_fixations(sess, gaze.events, algorithm, verbose=1)"
]
},
{
"cell_type": "markdown",
"id": "20",
"metadata": {},
"source": [
"## Visualisation for testing purposes - requires opencv-python and matplotlib!"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "21",
"metadata": {},
"outputs": [],
"source": [
"#!pip install opencv-python\n",
"#!pip install matplotlib"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "22",
"metadata": {},
"outputs": [],
"source": [
"import cv2\n",
"import matplotlib.pyplot as plt\n",
"\n",
"\n",
"def compare_events(stimulus, page, events, event1, event2):\n",
" stimulus_name = f\"{stimulus.name}_{stimulus.id}\"\n",
" page_name = f\"page_{page.number}\"\n",
"\n",
" fix1 = events.frame.filter(\n",
" (pl.col(\"page\") == f\"{page_name}\")\n",
" & (pl.col(\"stimulus\") == f\"{stimulus_name}\")\n",
" & (pl.col(\"name\") == f\"{event1}\")\n",
" )\n",
"\n",
" fix2 = events.frame.filter(\n",
" (pl.col(\"page\") == f\"{page_name}\")\n",
" & (pl.col(\"stimulus\") == f\"{stimulus_name}\")\n",
" & (pl.col(\"name\") == f\"{event2}\")\n",
" )\n",
"\n",
" if len(fix1) != len(fix2):\n",
" raise ValueError(\n",
" f\"Number of events in {event1} and {event2} do not match, cannot compare them. Number of events in {event1}: {len(fix1)}, number of events in {event2}: {len(fix2)}\"\n",
" )\n",
"\n",
" image = cv2.imread(stimulus.pages[page.number - 1].aoi_image_path)\n",
" org_coords = []\n",
" previous_coords = [-1, -1]\n",
" for i, p in enumerate(fix1.iter_rows(named=True)):\n",
" x = int(p[\"location\"][0])\n",
" y = int(p[\"location\"][1])\n",
" cv2.circle(image, (x, y), radius=5, color=(255, 0, 0), thickness=-1)\n",
" if previous_coords != [-1, -1]:\n",
" cv2.line(\n",
" image,\n",
" (x, y),\n",
" (previous_coords[0], previous_coords[1]),\n",
" (255, 0, 0),\n",
" thickness=1,\n",
" )\n",
" previous_coords = [x, y]\n",
" org_coords.append((x, y))\n",
"\n",
" previous_coords = [-1, -1]\n",
" for i, p in enumerate(fix2.iter_rows(named=True)):\n",
" x = int(p[\"location\"][0])\n",
" y = int(p[\"location\"][1])\n",
" cv2.circle(image, (x, y), radius=5, color=(0, 0, 255), thickness=-1)\n",
" if previous_coords != [-1, -1]:\n",
" cv2.line(\n",
" image,\n",
" (x, y),\n",
" (previous_coords[0], previous_coords[1]),\n",
" (0, 0, 255),\n",
" thickness=1,\n",
" )\n",
" previous_coords = [x, y]\n",
" cv2.line(\n",
" image, (x, y), (org_coords[i][0], org_coords[i][1]), (0, 0, 0), thickness=1\n",
" )\n",
"\n",
" show_image(\n",
" image,\n",
" title=f\"{stimulus.name}_{stimulus.id} page: {page_name} 'fixation' vs. 'fixation_corrected_{algorithm}'\",\n",
" )\n",
"\n",
"\n",
"def show_image(image, title=\"image\"):\n",
" # print(image.shape)\n",
" plt.figure(figsize=(10, 10))\n",
" plt.imshow(image)\n",
" plt.title(title)\n",
" plt.axis(\"off\")\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "23",
"metadata": {},
"outputs": [],
"source": [
"algorithm = \"chain\"\n",
"for stimulus in sess.stimuli:\n",
" for page in stimulus.pages:\n",
" compare_events(\n",
" stimulus, page, gaze.events, \"fixation\", f\"fixation_corrected_{algorithm}\"\n",
" )"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "pymov2",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
2 changes: 2 additions & 0 deletions preprocessing/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .signals.preprocess import preprocess_gaze
from .events.properties import compute_event_properties
from .events.detect import detect_fixations, detect_saccades
from .events.fixation_correction import correct_fixations
from .mapping.aoi import map_fixations_to_aois
from .io.save import (
save_raw_data,
Expand Down Expand Up @@ -50,4 +51,5 @@
"parse_answers_from_logfile",
"collect_session_answers",
"run_preflight_check",
"correct_fixations",
]
Loading
Loading