From 08fb70d368753aafc262d02f1f88758998b6b4d0 Mon Sep 17 00:00:00 2001 From: penzkofer Date: Wed, 12 Mar 2025 18:20:56 +0100 Subject: [PATCH] add subgoal generation pipeline --- Preprocess_AtariHEAD.ipynb | 200 +++++++ RAMStateLabeling.ipynb | 262 +++++++++ README.md | 40 ++ SubgoalsFromGaze.ipynb | 262 +++++++++ TrajectoryMatching.ipynb | 1108 ++++++++++++++++++++++++++++++++++++ dataset_utils.py | 432 ++++++++++++++ 6 files changed, 2304 insertions(+) create mode 100644 Preprocess_AtariHEAD.ipynb create mode 100644 RAMStateLabeling.ipynb create mode 100644 SubgoalsFromGaze.ipynb create mode 100644 TrajectoryMatching.ipynb create mode 100644 dataset_utils.py diff --git a/Preprocess_AtariHEAD.ipynb b/Preprocess_AtariHEAD.ipynb new file mode 100644 index 0000000..b7ce2b7 --- /dev/null +++ b/Preprocess_AtariHEAD.ipynb @@ -0,0 +1,200 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "d094257c", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import numpy as np \n", + "import cv2 \n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt \n", + "import subprocess\n", + "import warnings\n", + "\n", + "import dataset_utils as utils" + ] + }, + { + "cell_type": "markdown", + "id": "dbabd791", + "metadata": {}, + "source": [ + "\n", + "### Read all Montezuma's Revenge trials" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e42d862", + "metadata": {}, + "outputs": [], + "source": [ + "DATA_PATH = 'montezuma_revenge/'\n", + "\n", + "df = pd.read_csv(os.path.join(DATA_PATH, 'meta_data.csv'))\n", + "df = df.loc[df.GameName.str.contains('montezuma_revenge')]\n", + "df.sort_values(by=['trial_id'], inplace=True, ascending=True)\n", + "df.reset_index(drop=True, inplace=True)\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "e42160ea", + "metadata": {}, + "source": [ + "#### Get folder names of each trial" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9175b8f", + "metadata": {}, + "outputs": [], + "source": [ + "file_lst = [os.path.join(root, name) for root, dirs, files in os.walk(DATA_PATH) for name in files if 'tar.bz2' in name]\n", + "\n", + "folder_lst = [f.split('.')[0].split('/')[-1] for f in file_lst]\n", + "folder_lst.sort(key=lambda x: int(str(x).split('_')[0]), reverse=False)\n", + "\n", + "df['trial_folder'] = folder_lst\n", + "\n", + "df.to_pickle(os.path.join(DATA_PATH, \"all_trials_summary.pkl\")) \n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "28f38343", + "metadata": {}, + "source": [ + "#### Unpack all folders" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c83443d7", + "metadata": {}, + "outputs": [], + "source": [ + "for i in df.trial_id:\n", + " file = [f for f in file_lst if str(i) + '_' in f]\n", + " print(i, *file)\n", + " cmd = f'tar -jxf {file[0]} --directory {DATA_PATH}'\n", + " subprocess.call(cmd, shell=True)" + ] + }, + { + "cell_type": "markdown", + "id": "b65c1efb", + "metadata": {}, + "source": [ + "## Genarate Dataframe with all Trials" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24ec2a88", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "%%time\n", + "def write_unique_id(frame_id, episode_id, trial_id):\n", + " if not pd.isna(episode_id) and not pd.isna(frame_id):\n", + " unique_id = str(trial_id)+ '_' + '_'.join(frame_id.split('_')[:2]) + '_E{:01d}'.format(int(episode_id))\n", + " elif not pd.isna(frame_id):\n", + " unique_id = str(trial_id)+ '_' + '_'.join(frame_id.split('_')[:2]) + '_E0'\n", + " else: \n", + " unique_id = None\n", + " return unique_id\n", + "\n", + "\n", + "path = os.path.join(DATA_PATH, df.iloc[0].trial_folder)\n", + "print(path) \n", + " \n", + "# Read Annotations\n", + "trial_df = utils.txt_to_dataframe(path + '.txt') \n", + "\n", + "# Write unique ID\n", + "trial_df['ID'] = trial_df.apply(lambda x: write_unique_id(x['frame_id'], x['episode_id'], df.iloc[0].trial_id), axis=1)\n", + "\n", + "# Write image paths\n", + "trial_df['img_path'] = trial_df.apply(lambda x: os.path.join(path, str(x['frame_id']) + '.png'), axis=1)\n", + "\n", + "# Reorder columns\n", + "cols = ['ID'] + [c for c in trial_df.columns.tolist() if not c=='ID'] \n", + "trial_df = trial_df[cols]\n", + "\n", + "# Cut frames without annotations\n", + "trial_df = trial_df[trial_df.ID.notnull()] \n", + "\n", + "print(f'Episodes: {trial_df.ID.unique()}\\n')\n", + "\n", + "full_df = trial_df.copy()\n", + "\n", + "for idx in df.index[1:]:\n", + " row = df.iloc[idx]\n", + " if row.GameName == 'montezuma_revenge':\n", + " path = os.path.join(DATA_PATH, row.trial_folder)\n", + " elif row.GameName == 'montezuma_revenge_highscore':\n", + " path = os.path.join(DATA_PATH, 'highscore', row.trial_folder)\n", + " else: \n", + " path = ''\n", + " warnings.warn(f\"GameName of row {idx} not recognised! Returning empty path.\")\n", + " print(f'Reading {path}')\n", + " \n", + " # Read Annotations\n", + " trial_df = utils.txt_to_dataframe(path + '.txt') \n", + " \n", + " # Write unique ID \n", + " trial_df['ID'] = trial_df.apply(lambda x: write_unique_id(x['frame_id'], x['episode_id'], row.trial_id), axis=1)\n", + " \n", + " # Write image paths\n", + " trial_df['img_path'] = trial_df.apply(lambda x: os.path.join(path, str(x['frame_id']) + '.png'), axis=1)\n", + "\n", + " # Cut frames without annotations\n", + " trial_df = trial_df[trial_df.ID.notnull()] \n", + "\n", + " print(f'Episodes: {trial_df.ID.unique()}\\n')\n", + " full_df = pd.concat([full_df, trial_df], join='inner', ignore_index=True)\n", + "\n", + "outpath = os.path.join(DATA_PATH, \"all_trials.pkl\")\n", + "print(f'Saving dataframe to {outpath}\\n')\n", + "\n", + "full_df.to_pickle(outpath)\n", + "full_df.head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/RAMStateLabeling.ipynb b/RAMStateLabeling.ipynb new file mode 100644 index 0000000..ce6db15 --- /dev/null +++ b/RAMStateLabeling.ipynb @@ -0,0 +1,262 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e5e28b09", + "metadata": {}, + "source": [ + "# Get RAM state of Montezuma's Revenge" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9660bf17", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import random\n", + "import cv2\n", + "\n", + "import numpy as np\n", + "import pandas as pd \n", + "import matplotlib.pyplot as plt \n", + "import gym\n", + "\n", + "from atariari.benchmark.wrapper import AtariARIWrapper\n", + "from utils import visualize_sample\n", + "\n", + "\n", + "DATA_PATH = 'montezuma_revenge'\n", + "\n", + "df = pd.read_pickle(os.path.join(DATA_PATH, \"all_trials.pkl\"))\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "4417d418", + "metadata": {}, + "source": [ + "## Use AtariARI Wrapper to extract RAM state \n", + "```\n", + "labels: {'room_number': 15,\n", + "'player_x': 46,\n", + "'player_y': 235,\n", + "'player_direction': 76,\n", + "'enemy_skull_x': 58,\n", + "'enemy_skull_y': 240,\n", + "'key_monster_x': 132,\n", + "'key_monster_y': 254,\n", + "'level': 0,\n", + "'num_lives': 1,\n", + "'items_in_inventory_count': 0,\n", + "'room_state': 10,\n", + "'score_0': 1,\n", + "'score_1': 8,\n", + "'score_2': 0}```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19450d3e", + "metadata": {}, + "outputs": [], + "source": [ + "env = AtariARIWrapper(gym.make('MontezumaRevenge-v4', \n", + " frameskip=1, \n", + " render_mode='rgb_array', \n", + " repeat_action_probability=0.0))\n", + "\n", + "#env.unwrapped.ale.getRAM()\n", + "obs = env.reset(seed=42)\n", + "obs, reward, done, info = env.step(1)" + ] + }, + { + "cell_type": "markdown", + "id": "cc13394b", + "metadata": {}, + "source": [ + "## Visualize AtariHEAD data and RAM state labels \n", + "> offset of player and skull locations was discovered manually " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3882fa3f", + "metadata": {}, + "outputs": [], + "source": [ + "from IPython import display\n", + "obs = env.reset()\n", + "\n", + "screen = plt.imshow(env.render(mode='rgb_array'), aspect='auto')\n", + "plt.axis('off')\n", + "\n", + "all_images = []\n", + "agent_locations = []\n", + "skull_locations = []\n", + "room_ids = []\n", + "\n", + "for i, action in enumerate(df.loc[df.ID == '285_RZ_5619207_E00'].action.values): \n", + "\n", + " n_state, reward, done, info = env.step(action)\n", + " img = info['rgb']\n", + " room_ids.append(info['labels']['room_number'])\n", + " \n", + " # agent \n", + " mean_x, mean_y = info['labels']['player_x'], 320 - info['labels']['player_y']\n", + " agent_locations.append([mean_x, mean_y])\n", + " \n", + " x1, x2, y1, y2 = mean_x - 5 , mean_x + 10, mean_y - 15, mean_y + 10\n", + " img = cv2.rectangle(img, (x1, y1), (x2, y2), (0,255,0), 2)\n", + " \n", + " # skull\n", + " mean_x, mean_y = info['labels']['enemy_skull_x'] + 35, info['labels']['enemy_skull_y'] - 65\n", + " skull_locations.append([mean_x, mean_y])\n", + " x1, x2, y1, y2 = mean_x - 5, mean_x + 5, mean_y - 10, mean_y + 5\n", + " img = cv2.rectangle(img, (x1, y1), (x2, y2), (255,0,0), 2)\n", + " \n", + " img = cv2.putText(img=img, text='Room ID: ' + str(info['labels']['room_number']) + ' index: ' + str(i), org=(5, 205), fontFace=cv2.FONT_HERSHEY_SIMPLEX, \n", + " fontScale=0.3, color=(255, 255, 255),thickness=1)\n", + " \n", + " screen.set_data(img) # just update the data\n", + " display.display(plt.gcf())\n", + " display.clear_output(wait=True)" + ] + }, + { + "cell_type": "markdown", + "id": "1031fee6", + "metadata": {}, + "source": [ + "### Number of actions with correct labeling for environment with random seed = 42 \n", + "> Discovered manually through above visualization \n", + "[-1: all actions valid, 0: no actions valid]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "868493b1", + "metadata": {}, + "outputs": [], + "source": [ + "test = {'284_RZ_5540489_E00': 11900, '285_RZ_5619207_E00': 2940, '285_RZ_5619207_E01': -1,\n", + " '287_RZ_7172481_E00': 0, '291_RZ_7364933_E00': 12000, '324_RZ_452975_E00':0,\n", + " '333_RZ_900705_E00': 3000, '340_RZ_1323550_E00': 5950, '359_RZ_1993616_E00': 9000,\n", + " '365_RZ_2079996_E00': 9000, '371_RZ_2173469_E00': -1, '385_RZ_2344725_E00': 3500,\n", + " '398_RZ_2530473_E00': 1200, '402_RZ_2603283_E00': -1, '416_RZ_2788252_E00': -1,\n", + " '429_RZ_2945490_E00': 4500, '436_RZ_3131841_E00': 10500, '459_RZ_3291266_E00': 5400,\n", + " '469_RZ_3390904_E00': 14500, '480_RZ_3470098_E00': 8000, '493_RZ_3557734_E00': 10500, \n", + " '523_RZ_4091327_E00': 0, '536_RZ_4420664_E00': 0, '548_RZ_4509746_E00': 0,\n", + " '561_RZ_4598680_E00': 0, '573_RZ_4680777_E00': 0, '584_RZ_4772014_E00': 0,\n", + " '588_RZ_5032278_E00': 0}\n", + "\n", + "num_frames = 0 \n", + "num_labeled_frames = 0\n", + "counter = 0 \n", + "for episode in test.keys():\n", + " counter += 1\n", + " num_frames += len(df.loc[df.ID == episode])\n", + " num_samples = test.get(episode)\n", + " if num_samples == -1:\n", + " num_labeled_frames += len(df.loc[df.ID == episode])\n", + " else: \n", + " num_labeled_frames += num_samples\n", + " \n", + "print(f'Overall percantage {num_labeled_frames / num_frames:%} for 21 episodes')" + ] + }, + { + "cell_type": "markdown", + "id": "7f3c0026", + "metadata": {}, + "source": [ + "## Label Atari-HEAD data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe0dcd7a", + "metadata": {}, + "outputs": [], + "source": [ + "%%time \n", + "df['level'] = None \n", + "df['room_id'] = None\n", + "df['player_location'] = None\n", + "df['skull_location'] = None \n", + "\n", + "for episode in df.ID.unique():\n", + " \n", + " obs = env.reset()\n", + " room_ids = []\n", + " agent_locations = []\n", + " skull_locations = []\n", + " level = []\n", + " \n", + " num_valid_actions = test.get(episode)\n", + "\n", + " for action in df.loc[df.ID == episode].action.values[:num_valid_actions]: \n", + " \n", + " n_state, reward, done, info = env.step(action)\n", + " room_ids.append(info['labels']['room_number'])\n", + " level.append(info['labels']['level'])\n", + " \n", + " # agent \n", + " mean_x, mean_y = info['labels']['player_x'], 320 - info['labels']['player_y']\n", + " agent_locations.append([mean_x, mean_y])\n", + " \n", + " # skull\n", + " mean_x, mean_y = info['labels']['enemy_skull_x'] + 35, info['labels']['enemy_skull_y'] - 65\n", + " skull_locations.append([mean_x, mean_y])\n", + " \n", + " index = df.loc[df.ID == episode].index[:num_valid_actions]\n", + " df.loc[index, 'level'] = level\n", + " df.loc[index, 'room_id'] = room_ids\n", + " df.loc[index, 'player_location'] = agent_locations\n", + " df.loc[index, 'skull_location'] = skull_locations\n", + "\n", + "print(f'Percentage of labeled data {len(df[df.room_id.notnull()]) / len(df):%}')\n", + "print()\n", + "df.to_pickle(os.path.join(DATA_PATH, \"all_trials_labeled.pkl\")) \n", + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96ff9136", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "msc_env", + "language": "python", + "name": "msc_env" + }, + "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.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/README.md b/README.md index 7a245ab..4d509fc 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,42 @@ # Int-HRL +This is the official repository for [Int-HRL: Towards Intention-based Hierarchical Reinforcement Learning](https://perceptualui.org/publications/penzkofer23_ala/)
+ +Int-HRL uses eye gaze from human demonstration data on the Atari game Montezuma's Revenge to extract human player's intentions and converts them to sub-goals for Hierarchical Reinforcement Learning (HRL). For further details take a look at the corresponding paper. + +## Dataset +Atari-HEAD: Atari Human Eye-Tracking and Demonstration Dataset available at [https://zenodo.org/record/3451402#.Y5chr-zMK3J](https://zenodo.org/record/3451402#.Y5chr-zMK3J)
+![Atari-HEAD Montezuma's Revenge](supplementary/291_RZ_7364933_May-08-20-23-25.gif) + +To pre-process the Atari-HEAD data run [Preprocess_AtariHEAD.ipynb](Preprocess_AtariHEAD.ipynb), yielding the `all_trials.pkl` file needed for the following steps. + +## Sub-goal Extraction Pipeline + +1. [RAM State Labeling](RAMStateLabeling.ipynb): annotate Atari-HEAD data with room id and level information, as well as agent and skull location +2. [Subgoals From Gaze](SubgoalsFromGaze.ipynb): run sub-goal proposal extraction by generating saliency maps +3. [Alignment with Trajectory](TrajectoryMatching.ipynb): run expert trajectory to get order of subgoals + +## Intention-based Hierarchical RL Agent +under construction + +## Citation +Please consider citing these paper if you use Int-HRL or parts of this repository in your research: +``` +@article{penzkofer24_ncaa, + author = {Penzkofer, Anna and Schaefer, Simon and Strohm, Florian and Bâce, Mihai and Leutenegger, Stefan and Bulling, Andreas}, + title = {Int-HRL: Towards Intention-based Hierarchical Reinforcement Learning}, + journal = {Neural Computing and Applications (NCAA)}, + year = {2024}, + pages = {1--7}, + doi = {10.1007/s00521-024-10596-2}, + volume = {36} +} +@inproceedings{penzkofer23_ala, + author = {Penzkofer, Anna and Schaefer, Simon and Strohm, Florian and Bâce, Mihai and Leutenegger, Stefan and Bulling, Andreas}, + title = {Int-HRL: Towards Intention-based Hierarchical Reinforcement Learning}, + booktitle = {Proc. Adaptive and Learning Agents Workshop (ALA)}, + year = {2023}, + doi = {10.48550/arXiv.2306.11483}, + pages = {1--7} +} +``` diff --git a/SubgoalsFromGaze.ipynb b/SubgoalsFromGaze.ipynb new file mode 100644 index 0000000..9e24a17 --- /dev/null +++ b/SubgoalsFromGaze.ipynb @@ -0,0 +1,262 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b694995f", + "metadata": {}, + "source": [ + "# Extract Subgoals from Gaze\n", + "\n", + "### For each episode\n", + "- Generate saliency map of first room and threshold\n", + "- Draw bounding box around each _salient_ pixel \n", + "- Perform Non-Maximum Supression (NMS) on generated bounding boxes with iou threshold \n", + "- Merge resulting boxes if there are still overlaps \n", + "\n", + "### For room 1\n", + "- Perform NMS on the filtered and merged bounding box proposals from all episodes\n", + "- Merge again " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1245d4c", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import random\n", + "\n", + "import cv2\n", + "import torch\n", + "import numpy as np\n", + "import pandas as pd \n", + "import matplotlib.pyplot as plt \n", + "from scipy.ndimage import gaussian_filter\n", + "from torchvision.ops import masks_to_boxes\n", + "\n", + "from dataset_utils import visualize_sample, apply_nms, merge_boxes, get_subgoal_proposals, SIGMA\n", + "\n", + "DATA_PATH = 'montezuma_revenge'\n", + "\n", + "df = pd.read_pickle(os.path.join(DATA_PATH, \"all_trials_labeled.pkl\"))\n", + "\n", + "init_screen = cv2.imread(df.iloc[0].img_path)\n", + "init_screen = cv2.cvtColor(init_screen, cv2.COLOR_BGR2RGB)\n", + "\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "27bf55d2", + "metadata": {}, + "source": [ + "### Generate example saliency map " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b13e060", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# Get gaze from one run \n", + "episode = '284_RZ_5540489_E00'\n", + "gaze = df.loc[df.ID == episode].loc[df.room_id == 1].loc[df.level==0].gaze_positions\n", + "\n", + "flat_list = []\n", + "for gaze_points in gaze:\n", + " if gaze_points is not None: \n", + " for item in gaze_points:\n", + " flat_list.append(item)\n", + "\n", + "saliency_map = np.zeros(init_screen.shape[:2])\n", + "threshold = 0.35\n", + "\n", + "# Add gaze coordinates to saliency map\n", + "for cords in flat_list:\n", + " try: \n", + " saliency_map[int(cords[1])][int(cords[0])] += 1\n", + " except:\n", + " # Not all gaze points are on image \n", + " continue\n", + "\n", + "# Construct fixation map \n", + "fix_map = saliency_map >= 1.0 \n", + "\n", + "# Construct empirical saliency map\n", + "saliency_map = gaussian_filter(saliency_map, sigma=SIGMA, mode='nearest')\n", + "\n", + "# Normalize saliency map into range [0, 1]\n", + "if not saliency_map.max() == 0:\n", + " saliency_map /= saliency_map.max()\n", + "\n", + "gray = cv2.cvtColor(init_screen, cv2.COLOR_BGR2GRAY) \n", + "fov_image = np.multiply(saliency_map, gray) # element-wise product\n", + "\n", + "visualize_sample(init_screen, saliency_map)" + ] + }, + { + "cell_type": "markdown", + "id": "334fc3b8", + "metadata": {}, + "source": [ + "### Find good saliency threshold " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8043f4bf", + "metadata": {}, + "outputs": [], + "source": [ + "plt.hist(saliency_map.flatten())\n", + "plt.show()\n", + "\n", + "mask = saliency_map > 0.4\n", + "masked_saliency = saliency_map.copy()\n", + "masked_saliency[~mask] = 0 \n", + "print('Threshold: 0.4')\n", + "visualize_sample(init_screen, masked_saliency)\n", + "\n", + "mask = saliency_map > 0.35\n", + "masked_saliency = saliency_map.copy()\n", + "masked_saliency[~mask] = 0 \n", + "print('Threshold: 0.35')\n", + "visualize_sample(init_screen, masked_saliency)\n", + "\n", + "mask = saliency_map > 0.2\n", + "masked_saliency = saliency_map.copy()\n", + "masked_saliency[~mask] = 0 \n", + "print('Threshold: 0.2')\n", + "visualize_sample(init_screen, masked_saliency)" + ] + }, + { + "cell_type": "markdown", + "id": "9f4a356f", + "metadata": {}, + "source": [ + "# Extract subgoals from all saliency maps" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6aad7e32", + "metadata": {}, + "outputs": [], + "source": [ + "%%time \n", + "\n", + "SALIENCY_THRESH = 0.3\n", + "VISUALIZE = False \n", + "ROOM = 1\n", + "\n", + "init_screen = cv2.imread(df.loc[df.room_id == ROOM].iloc[10].img_path)\n", + "init_screen = cv2.cvtColor(init_screen, cv2.COLOR_BGR2RGB)\n", + "\n", + "subgoal_proposals = get_subgoal_proposals(df, threshold=SALIENCY_THRESH, visualize=VISUALIZE, room=ROOM)\n", + "\n", + "proposal_df = pd.DataFrame.from_dict(subgoal_proposals, orient='index', columns=['bboxes', 'merged_bboxes'])\n", + "proposal_df.to_pickle(os.path.join(DATA_PATH, f\"room1_subgoal_proposals{int(SALIENCY_THRESH * 100)}.pkl\")) \n", + "\n", + "proposal_df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cbe23a6a", + "metadata": {}, + "outputs": [], + "source": [ + "img = init_screen.copy()\n", + "for proposals in proposal_df.merged_bboxes:\n", + " \n", + " for box in proposals:\n", + " img = cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (255,0,0), 1)\n", + "\n", + "fig = plt.figure(figsize=(8,8))\n", + "plt.imshow(img)\n", + "plt.axis('off')\n", + "plt.savefig(f'visualizations/room{ROOM}_all_subgoal_proposals{int(SALIENCY_THRESH * 100)}.png', bbox_inches='tight')\n", + "plt.show()\n", + "\n", + "all_proposals = np.concatenate(proposal_df.merged_bboxes.to_numpy())\n", + "print(all_proposals.shape)\n", + "\n", + "# Non-max suppression \n", + "keep = apply_nms(all_proposals, thresh_iou=0.01)\n", + "\n", + "print('Bounding boxes after non-maximum suppression')\n", + "img = init_screen.copy()\n", + "for box in keep:\n", + " img = cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (255,0,0), 1)\n", + "\n", + "fig = plt.figure(figsize=(8,8))\n", + "plt.imshow(img)\n", + "plt.axis('off')\n", + "plt.show()\n", + "\n", + "merged = merge_boxes(keep)\n", + "print('Bounding boxes after merging')\n", + "img = init_screen.copy()\n", + "for box in merged:\n", + " img = cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (255,0,0), 1)\n", + "\n", + "fig = plt.figure(figsize=(8,8))\n", + "plt.imshow(img)\n", + "plt.axis('off')\n", + "plt.savefig(f'visualizations/room{ROOM}_final_subgoals{int(SALIENCY_THRESH * 100)}.png', bbox_inches='tight')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdf9db11", + "metadata": {}, + "outputs": [], + "source": [ + "np.savetxt('subgoals.txt', merged, fmt='%i', delimiter=',')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad587996", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "msc_env", + "language": "python", + "name": "msc_env" + }, + "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.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/TrajectoryMatching.ipynb b/TrajectoryMatching.ipynb new file mode 100644 index 0000000..b45caee --- /dev/null +++ b/TrajectoryMatching.ipynb @@ -0,0 +1,1108 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "909118c1", + "metadata": {}, + "source": [ + "# Label Subgoals\n", + "- Label each frame with current subgoal \n", + "- Extract order of subgoals visited \n", + "- Only include visited subgoals as true subgoals \n", + "\n", + "Result: `subgoal_order = [8, 6, 1, 0, 2, 7, 9]`\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b382d001", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import random\n", + "\n", + "import cv2\n", + "import numpy as np\n", + "import pandas as pd \n", + "import matplotlib.pyplot as plt \n", + "\n", + "\n", + "SIGMA = (210 / 44.6, 160 / 28.5)\n", + "\n", + "GPU_DEVICE = 3\n", + "\n", + "os.environ['CUDA_VISIBLE_DEVICES']=str(GPU_DEVICE)\n", + "\n", + "DATA_PATH = '/datasets/public/anna/montezuma_revenge'" + ] + }, + { + "cell_type": "markdown", + "id": "f7c3ec86", + "metadata": {}, + "source": [ + "### Load labeled data and extracted subgoals " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "21935cab", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 11.2 s, sys: 3.94 s, total: 15.2 s\n", + "Wall time: 15.2 s\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
IDframe_idepisode_idscoreduration(ms)unclipped_rewardactiongaze_positionsimg_pathlevel...player_locationskull_locationnum_gaze_positionsgaze_duration_ratioangular_gaze_displacementgaze_velocitymax_gaze_velocityavg_gaze_velocitytime_stampscurrent_subgoal
0284_RZ_5540489_E00RZ_5540489_100281700[[80.4, 103.5], [80.34, 103.4], [80.34, 103.3].../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][92, 175]2614.00.927938[8.986148924666498, 0.00034557292731263054, 0....[96840.02112006705, 3.724096925170927, 2.97928...266.0795.051218[0.0, 0.9279375221867234, 1.8558750443734469, ...8
1284_RZ_5540489_E00RZ_5540489_2005000[[113.66, 98.28], [113.65, 98.42], [113.65, 98.../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][92, 175]50.01.000000[8.986165402283532, 0.00044873289690316374, 0....[89861.65402283533, 4.487328969031638, 0.0, 3....114.40104.255400[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ...8
2284_RZ_5540489_E00RZ_5540489_3005100[[87.26, 100.72], [86.4, 100.8], [85.61, 100.8.../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][92, 175]51.01.000000[8.986150521057317, 0.0029062899985003087, 0.0...[89861.50521057317, 29.06289998500309, 21.2663...102.5090.330686[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ...8
3284_RZ_5540489_E00RZ_5540489_4005100[[78.41, 101.5], [78.41, 101.5], [78.41, 101.6.../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][92, 175]51.01.000000[8.986146988240504, 0.00041474113941382504, 0....[89861.46988240504, 4.14741139413825, 7.982843...102.8890.392941[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ...8
4284_RZ_5540489_E00RZ_5540489_5005500[[78.41, 102.85], [78.42, 102.95], [78.41, 102.../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][91, 175]55.01.000000[8.986147775662209, 7.822521255579121e-05, 0.0...[89861.47775662209, 0.7822521255579121, 2.8601...103.9290.895364[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ...8
\n", + "

5 rows × 21 columns

\n", + "
" + ], + "text/plain": [ + " ID frame_id episode_id score duration(ms) \\\n", + "0 284_RZ_5540489_E00 RZ_5540489_1 0 0 2817 \n", + "1 284_RZ_5540489_E00 RZ_5540489_2 0 0 50 \n", + "2 284_RZ_5540489_E00 RZ_5540489_3 0 0 51 \n", + "3 284_RZ_5540489_E00 RZ_5540489_4 0 0 51 \n", + "4 284_RZ_5540489_E00 RZ_5540489_5 0 0 55 \n", + "\n", + " unclipped_reward action gaze_positions \\\n", + "0 0 0 [[80.4, 103.5], [80.34, 103.4], [80.34, 103.3]... \n", + "1 0 0 [[113.66, 98.28], [113.65, 98.42], [113.65, 98... \n", + "2 0 0 [[87.26, 100.72], [86.4, 100.8], [85.61, 100.8... \n", + "3 0 0 [[78.41, 101.5], [78.41, 101.5], [78.41, 101.6... \n", + "4 0 0 [[78.41, 102.85], [78.42, 102.95], [78.41, 102... \n", + "\n", + " img_path level ... \\\n", + "0 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "1 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "2 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "3 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "4 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "\n", + " player_location skull_location num_gaze_positions gaze_duration_ratio \\\n", + "0 [77, 85] [92, 175] 2614.0 0.927938 \n", + "1 [77, 85] [92, 175] 50.0 1.000000 \n", + "2 [77, 85] [92, 175] 51.0 1.000000 \n", + "3 [77, 85] [92, 175] 51.0 1.000000 \n", + "4 [77, 85] [91, 175] 55.0 1.000000 \n", + "\n", + " angular_gaze_displacement \\\n", + "0 [8.986148924666498, 0.00034557292731263054, 0.... \n", + "1 [8.986165402283532, 0.00044873289690316374, 0.... \n", + "2 [8.986150521057317, 0.0029062899985003087, 0.0... \n", + "3 [8.986146988240504, 0.00041474113941382504, 0.... \n", + "4 [8.986147775662209, 7.822521255579121e-05, 0.0... \n", + "\n", + " gaze_velocity max_gaze_velocity \\\n", + "0 [96840.02112006705, 3.724096925170927, 2.97928... 266.07 \n", + "1 [89861.65402283533, 4.487328969031638, 0.0, 3.... 114.40 \n", + "2 [89861.50521057317, 29.06289998500309, 21.2663... 102.50 \n", + "3 [89861.46988240504, 4.14741139413825, 7.982843... 102.88 \n", + "4 [89861.47775662209, 0.7822521255579121, 2.8601... 103.92 \n", + "\n", + " avg_gaze_velocity time_stamps \\\n", + "0 95.051218 [0.0, 0.9279375221867234, 1.8558750443734469, ... \n", + "1 104.255400 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ... \n", + "2 90.330686 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ... \n", + "3 90.392941 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ... \n", + "4 90.895364 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ... \n", + "\n", + " current_subgoal \n", + "0 8 \n", + "1 8 \n", + "2 8 \n", + "3 8 \n", + "4 8 \n", + "\n", + "[5 rows x 21 columns]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time \n", + "df = pd.read_pickle(os.path.join(DATA_PATH, \"all_trials_labeled.pkl\"))\n", + "\n", + "\n", + "init_screen = cv2.imread(df.iloc[0].img_path)\n", + "init_screen = cv2.cvtColor(init_screen, cv2.COLOR_BGR2RGB)\n", + "SUBGOALS = np.loadtxt('subgoals.txt', dtype=int, delimiter=',')\n", + "\n", + "dim = (init_screen.shape[1] * 2, init_screen.shape[0] * 2)\n", + "img = cv2.resize(init_screen.copy(), dim, interpolation = cv2.INTER_AREA)\n", + "\n", + "\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "284918a0", + "metadata": {}, + "source": [ + "## Visualize extracted order" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e42effb0", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "subgoal_order = np.array([8, 6, 1, 0, 2, 7, 2, 0, 1, 6, 8, 9])\n", + "\n", + "for i, box in enumerate(SUBGOALS):\n", + " \n", + " #img = cv2.putText(img=img, text=str(i), org=(int(box[0]), int(box[1])), fontFace=cv2.FONT_HERSHEY_SIMPLEX, \n", + " #fontScale=0.3, color=(255, 255, 255),thickness=1)\n", + " \n", + " if i in subgoal_order:\n", + " box = box.copy() * 2\n", + " img = cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (255,0,0), 2)\n", + " idx = np.where(subgoal_order == i)[0]\n", + " img = cv2.putText(img=img, text='/'.join([f'{n}.' for n in idx]), org=(int(box[0]), int(box[1]) - 5), fontFace=cv2.FONT_HERSHEY_SIMPLEX, \n", + " fontScale=0.4, color=(0, 255, 255), thickness=2)\n", + "\n", + " # text='[' + ','.join([str(n) for n in idx]) + ']'\n", + " \n", + "fig = plt.figure(figsize=(8,8))\n", + "plt.imshow(img)\n", + "plt.axis('off')\n", + "plt.savefig('visualizations/subgoals_labeled.png', bbox_inches='tight')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5af4f01c", + "metadata": {}, + "outputs": [], + "source": [ + "def agent_in_subgoal(subgoals, agent_x, agent_y): \n", + "\n", + " test_min_x = subgoals[:, 0] < agent_x \n", + " test_max_x = subgoals[:, 2] > agent_x\n", + "\n", + " test_min_y = subgoals[:, 1] < agent_y\n", + " test_max_y = subgoals[:, 3] > agent_y\n", + "\n", + " return np.any(test_min_x & test_max_x & test_min_y & test_max_y), np.where(test_min_x & test_max_x & test_min_y & test_max_y)[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "7eb6ceb1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Percentage on subgoals 56.721905%\n", + "Percentage not on screen 1.566032%\n", + "Percentage on skull 8.218883%\n", + "Percentage on agent 32.611321%\n", + "Percentage on agent vicinity 68.234675%\n", + "CPU times: user 1min 7s, sys: 1.19 s, total: 1min 8s\n", + "Wall time: 1min 11s\n" + ] + } + ], + "source": [ + "%%time\n", + "screen = [0,0, 160, 210]\n", + "\n", + "counter = 0\n", + "\n", + "subgoal_count = 0 \n", + "out_screen = 0 \n", + "\n", + "areas_count = {}\n", + "for k in range(3):\n", + " areas_count[k] = 0 \n", + " \n", + "\n", + "for episode in df.ID.unique():\n", + " \n", + " valid_actions_idx = df.loc[df.ID == episode].loc[df.room_id == 1].loc[df.level==0].index\n", + " \n", + " if len(valid_actions_idx) > 0: \n", + " \n", + " temp_df = df.iloc[valid_actions_idx]\n", + " \n", + " for i, frame in temp_df.iterrows():\n", + " if not frame.gaze_positions is None: \n", + " \n", + " mean_x, mean_y = frame.skull_location\n", + " \n", + " # x1, x2, y1, y2 \n", + " skull_area = [mean_x - 5, mean_y - 15, mean_x + 10, mean_y + 10]\n", + " \n", + " mean_x, mean_y = frame.player_location\n", + " agent_area = [mean_x - 5, mean_y - 15, mean_x + 10, mean_y + 10]\n", + " agent_vicinity = [mean_x - 10, mean_y - 30, mean_x + 20, mean_y + 20]\n", + " \n", + " for gaze_points in frame.gaze_positions:\n", + " counter += 1\n", + " \n", + " if agent_in_subgoal(SUBGOALS, gaze_points[0], gaze_points[1])[0]: \n", + " subgoal_count += 1\n", + " \n", + " if not agent_in_subgoal(np.expand_dims(screen, axis=0), gaze_points[0], gaze_points[1])[0]:\n", + " out_screen += 1\n", + " \n", + " check, idx = agent_in_subgoal(np.stack([skull_area, agent_area, agent_vicinity]), \n", + " gaze_points[0], gaze_points[1])\n", + " if check: \n", + " for k in idx: \n", + " areas_count[k] += 1\n", + " \n", + "print(f'Percentage on subgoals {subgoal_count / counter:%}')\n", + "print(f'Percentage not on screen {out_screen / counter:%}')\n", + "print(f'Percentage on skull {areas_count[0] / counter:%}')\n", + "print(f'Percentage on agent {areas_count[1] / counter:%}')\n", + "print(f'Percentage on agent vicinity {areas_count[2] / counter:%}')" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "a7d3b2cb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(10829, 691493)" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "out_screen, counter" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "a845983b", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "img = init_screen.copy()\n", + "img = cv2.rectangle(img, (int(agent_area[0]), int(agent_area[1])), (int(agent_area[2]), int(agent_area[3])), (255,0,0), 2)\n", + "img = cv2.rectangle(img, (0,0), (160, 210), (255,0,0), 2)\n", + " \n", + " \n", + "fig = plt.figure(figsize=(8,8))\n", + "plt.imshow(img)\n", + "plt.axis('off')\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8cec6e9c", + "metadata": {}, + "source": [ + "### Run Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "7e7cb363", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "A.L.E: Arcade Learning Environment (version 0.7.5+db37282)\n", + "[Powered by Stella]\n" + ] + } + ], + "source": [ + "import gym\n", + "from atariari.benchmark.wrapper import AtariARIWrapper\n", + "\n", + "\n", + "env = AtariARIWrapper(gym.make('MontezumaRevenge-v4', \n", + " frameskip=1, \n", + " render_mode='rgb_array', \n", + " repeat_action_probability=0.0))\n", + "\n", + "#env.unwrapped.ale.getRAM()\n", + "obs = env.reset(seed=42)\n", + "obs, reward, done, info = env.step(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "236a44a5", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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uvjJT34yKqN7KqmoqMAPfvADzJ07FzfccAPefPNNfPWrX8Xf/M3f4OjRoyPOft966y288MIL6OjowAcffIBAIIB58+Zh5syZ+OxnP4vS0lIAlmGcMWPGMKOhqiruuOMO3HnnnThx4sSQGXD/c/H73/8eN910E+rq6nDPPfcgnU7j2WefHXim/H4/br75ZtTU1ODBBx9Ed3c3rrrqKlx55ZUALD3/9Kc/HZXRFaGqKu666y7cfvvtuO+++3DFFVcMO6a0tBSrVq1CS0sLDh48iJaWlgG9btiwAR6PB9dff/2AC4gxhjPOOANr1qzB0aNH0draildffRWHDh3C9ddfj9/97nf4xS9+gdtvvz2vkwUif5wSo3veeedh5syZAzMav9+Puro67Ny5E9dddx0aGhqsk2sa6urqUFFRga6uLuzYsQNf//rX8fvf/35A1vr167F582Y89thj+NKXvoQrrrgCTz75JF5++WVcc8016O3tHXLuWbNm4de//vXA7O6dd94BAOi6jg0bNuA//uM/Bo796le/iocffhgvvvgi/vEf/xGGMTREadWqVfj1r3+NSCSCa665BgcOHDgV6hqRdDqN9evXY/369QU7Zz4wDAOPPvoonnzyyWFtt912G1555ZWB2d3ZZ5+N5557DsePHx/yXJw86xfxv//7v/jxj3888O9PfvKT+NrXvjbw7127duFTn/oUli5dihdeeGFg6d/e3j5s1r9hwwZs2LAh52vvhzGGGTNmoLS0FIlEArt27Rrmq+/u7saePXtQWlqK733ve9i7dy9++MMfoqysDHfddRemT5+O2tpadHd3A7D0euzYMRw4cADXXnstrr76avz85z/Hn//8Z1x33XW45pprUF5eDoVKvk94TonRfeGFF4a4F1avXo3HHnsMXV1dqK2tBeccra2tCAaD2LBhAxYsWIBHHnkE69evPyUfkiY75eXlCAaD6O7uRigUGu/uTAg+85nP4PLLLx/4dyAQEH6kzJaysjIUFxcjFAoNGLux4PF48Oijj+Laa6/FnXfeiS9+8YvDjnn55Zexc+dOxGIxtLe3Y9WqVQNj5Gtf+xo0TcMDDzwAn8+HZDKJeDyOhx9+GF6vF+3t7YjH47j//vvxwgsv4Gc/+9mAIb799tvR3t5Oft0JTEFCxrZu3YqVK1cO+HTT6TSuu+467NmzBzfccAOCwSB+8Ytf4Omnn8aXv/xlvPjiiwO/5ZyjubkZ4XAYgPWVv6WlBbFYDFVVVfD7/UPOVVZWhp6eHmiaNiGXVy6XC9XV1XA6h25yKCsrQ29vL1paWgbcGMXFxfD7/Vi/fj0+97nP4etf/zoeeuihrM+l6zo6OjrgdDoRCAQwbdrQ8kuMMZimiebm5mErhkKQSqXQ2tqKnp4elJeXD7ykKysrh+nnwzz99NP4/ve/P/Dv6667Dt/+9rcH/u10OlFSUoKSkhJ0dXUN+ItDoRACgQCqq6vR09ODZDKJ4uJi+Hw+3H///fj7v//7Ufu3+2GMobS0FMFgEMlkEs3NzUPcaCdz/fXX40c/+hFefvll3H333Th+/DiuvvpqLF26FM899xwSicTAGAGAYDCIp556CpdccsnAGHnqqafw1FNP4Z577sEHH3yAxx57DAsXLiRf7gQnr0Y3mUwiFAohFhs5h4Cu6+jp6UEmkxmylOecIxqNIh6P48EHH8SDDz440Patb30L06dPH/j3888/j+effx6XXXYZ/vKXvyAQCAw5R0NDA2655Rbs3r0bgOXaiEajCIVCw4xwMplEd3e3cGD09zcajQ5zPYxWF5lMBj09PVi0aNGwD1oA0NbWhs997nN46623AFjG+ZFHHsGnP/1pAJaxGK1P+cSJE7jiiitQXl6Op59+Guedd96QdtM08Y1vfGOIfrPFNE1EIhGEQiHoug7OOWKx2Ih67ieVSiEUCiEajYJzjjfeeAPLli3DsmXL8Oyzzw77QBYKhYZ8F+CcIx6PIxQK4dZbb8Wttw7d9ROLxRCJRMA5x4oVK/DMM8+gs7MTN9xwA44dOwYAA7p44okn8PnPfx5/+ctf8NBDD+HGG28cOGf/R7fRUlxcjCeffBKrVq3CnXfeib/9278VHtuvi9WrV2Pr1q3D2ltbW7MeI/39Fj3HxMSCdqQRBEHkGdmONPK6EwRBFBAyugRBEAWEjC5BEEQBkfp0CYIgiPxCM12CIIgCQkaXIAiigJDRJQiCKCBkdAmCIAoIGV2CIIgCQkaXIAiigPz/37s2v1gMtS8AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from IPython import display\n", + "obs = env.reset()\n", + "\n", + "#fig = plt.figure(figsize=(10, 8))\n", + "screen = plt.imshow(env.render(mode='rgb_array'), aspect='auto')\n", + "plt.axis('off')\n", + "\n", + "all_images = []\n", + "agent_locations = []\n", + "skull_locations = []\n", + "room_ids = []\n", + "\n", + "subgoal_anno = []\n", + "\n", + "\n", + "for i, action in enumerate(df.loc[df.ID == '285_RZ_5619207_E00'].action.values[:800]): \n", + " #print(action)\n", + " n_state, reward, done, info = env.step(action)\n", + " img = info['rgb']\n", + " room_ids.append(info['labels']['room_number'])\n", + " \n", + " # agent \n", + " mean_x, mean_y = info['labels']['player_x'], 320 - info['labels']['player_y']\n", + " agent_locations.append([mean_x, mean_y])\n", + " check, goal_idx = agent_in_subgoal(SUBGOALS, mean_x, mean_y)\n", + " if check: \n", + " \n", + " box = SUBGOALS[goal_idx][0]\n", + " \n", + " subgoal_anno += [goal_idx[0]] * (i - len(subgoal_anno))\n", + " \n", + " img = cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0,0,255), 1)\n", + " \n", + " x1, x2, y1, y2 = mean_x - 5 , mean_x + 10, mean_y - 15, mean_y + 10\n", + " img = cv2.rectangle(img, (x1, y1), (x2, y2), (0,255,0), 2)\n", + " \n", + " # skull\n", + " mean_x, mean_y = info['labels']['enemy_skull_x'] + 35, info['labels']['enemy_skull_y'] - 65\n", + " skull_locations.append([mean_x, mean_y])\n", + " x1, x2, y1, y2 = mean_x - 5, mean_x + 5, mean_y - 10, mean_y + 5\n", + " img = cv2.rectangle(img, (x1, y1), (x2, y2), (255,0,0), 2)\n", + " \n", + " img = cv2.putText(img=img, text='Room ID: ' + str(info['labels']['room_number']) + ' index: ' + str(i), org=(5, 205), fontFace=cv2.FONT_HERSHEY_SIMPLEX, \n", + " fontScale=0.3, color=(255, 255, 255),thickness=1)\n", + " \n", + " screen.set_data(img) # just update the data\n", + " display.display(plt.gcf())\n", + " display.clear_output(wait=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "257d71ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 16.5 s, sys: 182 ms, total: 16.7 s\n", + "Wall time: 16.7 s\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
IDframe_idepisode_idscoreduration(ms)unclipped_rewardactiongaze_positionsimg_pathlevel...player_locationskull_locationnum_gaze_positionsgaze_duration_ratioangular_gaze_displacementgaze_velocitymax_gaze_velocityavg_gaze_velocitytime_stampscurrent_subgoal
0284_RZ_5540489_E00RZ_5540489_100281700[[80.4, 103.5], [80.34, 103.4], [80.34, 103.3].../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][92, 175]2614.00.927938[8.986148924666498, 0.00034557292731263054, 0....[96840.02112006705, 3.724096925170927, 2.97928...266.0795.051218[0.0, 0.9279375221867234, 1.8558750443734469, ...8
1284_RZ_5540489_E00RZ_5540489_2005000[[113.66, 98.28], [113.65, 98.42], [113.65, 98.../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][92, 175]50.01.000000[8.986165402283532, 0.00044873289690316374, 0....[89861.65402283533, 4.487328969031638, 0.0, 3....114.40104.255400[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ...8
2284_RZ_5540489_E00RZ_5540489_3005100[[87.26, 100.72], [86.4, 100.8], [85.61, 100.8.../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][92, 175]51.01.000000[8.986150521057317, 0.0029062899985003087, 0.0...[89861.50521057317, 29.06289998500309, 21.2663...102.5090.330686[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ...8
3284_RZ_5540489_E00RZ_5540489_4005100[[78.41, 101.5], [78.41, 101.5], [78.41, 101.6.../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][92, 175]51.01.000000[8.986146988240504, 0.00041474113941382504, 0....[89861.46988240504, 4.14741139413825, 7.982843...102.8890.392941[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ...8
4284_RZ_5540489_E00RZ_5540489_5005500[[78.41, 102.85], [78.42, 102.95], [78.41, 102.../datasets/public/anna/montezuma_revenge/284_RZ...0...[77, 85][91, 175]55.01.000000[8.986147775662209, 7.822521255579121e-05, 0.0...[89861.47775662209, 0.7822521255579121, 2.8601...103.9290.895364[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ...8
\n", + "

5 rows × 21 columns

\n", + "
" + ], + "text/plain": [ + " ID frame_id episode_id score duration(ms) \\\n", + "0 284_RZ_5540489_E00 RZ_5540489_1 0 0 2817 \n", + "1 284_RZ_5540489_E00 RZ_5540489_2 0 0 50 \n", + "2 284_RZ_5540489_E00 RZ_5540489_3 0 0 51 \n", + "3 284_RZ_5540489_E00 RZ_5540489_4 0 0 51 \n", + "4 284_RZ_5540489_E00 RZ_5540489_5 0 0 55 \n", + "\n", + " unclipped_reward action gaze_positions \\\n", + "0 0 0 [[80.4, 103.5], [80.34, 103.4], [80.34, 103.3]... \n", + "1 0 0 [[113.66, 98.28], [113.65, 98.42], [113.65, 98... \n", + "2 0 0 [[87.26, 100.72], [86.4, 100.8], [85.61, 100.8... \n", + "3 0 0 [[78.41, 101.5], [78.41, 101.5], [78.41, 101.6... \n", + "4 0 0 [[78.41, 102.85], [78.42, 102.95], [78.41, 102... \n", + "\n", + " img_path level ... \\\n", + "0 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "1 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "2 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "3 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "4 /datasets/public/anna/montezuma_revenge/284_RZ... 0 ... \n", + "\n", + " player_location skull_location num_gaze_positions gaze_duration_ratio \\\n", + "0 [77, 85] [92, 175] 2614.0 0.927938 \n", + "1 [77, 85] [92, 175] 50.0 1.000000 \n", + "2 [77, 85] [92, 175] 51.0 1.000000 \n", + "3 [77, 85] [92, 175] 51.0 1.000000 \n", + "4 [77, 85] [91, 175] 55.0 1.000000 \n", + "\n", + " angular_gaze_displacement \\\n", + "0 [8.986148924666498, 0.00034557292731263054, 0.... \n", + "1 [8.986165402283532, 0.00044873289690316374, 0.... \n", + "2 [8.986150521057317, 0.0029062899985003087, 0.0... \n", + "3 [8.986146988240504, 0.00041474113941382504, 0.... \n", + "4 [8.986147775662209, 7.822521255579121e-05, 0.0... \n", + "\n", + " gaze_velocity max_gaze_velocity \\\n", + "0 [96840.02112006705, 3.724096925170927, 2.97928... 266.07 \n", + "1 [89861.65402283533, 4.487328969031638, 0.0, 3.... 114.40 \n", + "2 [89861.50521057317, 29.06289998500309, 21.2663... 102.50 \n", + "3 [89861.46988240504, 4.14741139413825, 7.982843... 102.88 \n", + "4 [89861.47775662209, 0.7822521255579121, 2.8601... 103.92 \n", + "\n", + " avg_gaze_velocity time_stamps \\\n", + "0 95.051218 [0.0, 0.9279375221867234, 1.8558750443734469, ... \n", + "1 104.255400 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ... \n", + "2 90.330686 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ... \n", + "3 90.392941 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ... \n", + "4 90.895364 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, ... \n", + "\n", + " current_subgoal \n", + "0 8 \n", + "1 8 \n", + "2 8 \n", + "3 8 \n", + "4 8 \n", + "\n", + "[5 rows x 21 columns]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%time \n", + "df['current_subgoal'] = None \n", + "\n", + "for episode in df.ID.unique():\n", + " \n", + " obs = env.reset()\n", + " subgoal_anno = []\n", + " \n", + " valid_actions_idx = df.loc[df.ID == episode].loc[df.room_id == 1].loc[df.level==0].index\n", + " \n", + " if len(valid_actions_idx) > 0: \n", + "\n", + " for i, action in enumerate(df.iloc[valid_actions_idx].action.values): \n", + "\n", + " n_state, reward, done, info = env.step(action)\n", + "\n", + " # agent \n", + " mean_x, mean_y = info['labels']['player_x'], 320 - info['labels']['player_y']\n", + "\n", + " check, goal_idx = agent_in_subgoal(SUBGOALS, mean_x, mean_y)\n", + " if check: \n", + " box = SUBGOALS[goal_idx][0]\n", + " subgoal_anno += [goal_idx[0]] * (i - len(subgoal_anno))\n", + "\n", + " subgoal_anno += [None] * (i - len(subgoal_anno) + 1)\n", + "\n", + " assert len(valid_actions_idx) == len(subgoal_anno), f'{episode, i} Number of actions: {len(valid_actions_idx)} does not match length of subgoal annotation: { len(subgoal_anno)}'\n", + "\n", + " df.loc[valid_actions_idx, 'current_subgoal'] = subgoal_anno\n", + "\n", + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "0374a999", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(37879, 2)\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAM4AAAD8CAYAAAA/rZtiAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAABIVklEQVR4nO29f4xk2XXf97lbVd3V1dXdtV293bPzg5zZH1yKIq2VuKCIyFHsMHYkIgmtIGDIPyRKIbwSQCI2oCAhJSNRbBiQHVFCggSEKYgQFcikFdCyCIGJRTMObAGmrKW8En9ptbPcWc/sbPds92zNdk11dXfV3Pxx36l33qn7qqp/TVcP3xd4eK9evR/3vXe/9/y4557rvPcUKFDgYHjotAtQoMBZREGcAgUOgYI4BQocAgVxChQ4BAriFChwCBTEKVDgEDgx4jjnfsw594Jz7qpz7hMndZ8CBU4D7iT6cZxzJeAvgL8G3AD+GPiw9/7bx36zAgVOASclcd4DXPXef9d7vwd8AfjACd2rQIH7jvIJXfcCcF39vgH8cN7BztU8NE6oKAUKHBavbXrvH4n9c1LEGQvn3LPAs+HXUrpZoMDU4H9+Je+fk1LVXgUuqd8Xk30DeO8/471/xnv/DNROqBgFCpwMToo4fww86Zy74pybAT4EfOmE7lWgwH3Hiahq3vuec+7jwD8HSsBnvfffOol7FShwGjgxG8d7/2Xgyyd1/QIFThNF5ECBAodAQZwCBQ6BgjgFChwCBXEKFDgECuIUKHAIFMQpUOAQKIhToMAhUBCnQIFDoCBOgQKHQEGcAgUOgYI4BQocAgVxChQ4BAriFChwCJzaCNACJ4XKAY/fP5FSPOgoiPPAQBNm0s/aM+cVJJoUBXHOPGKEqZjfFr2cfRUK8kyGgjhnFpYwlZztGHrq/57ZL+cWBBqFQzsHnHOXnHP/0jn3befct5xzfyvZ/0vOuVedc88ny/uPr7gFAjQxhChlYE4tC8AisGyWxeQ/fay+hpVaBWI4isTpAT/vvf8T59wC8HXn3FeS/37Ne/8rRy9egWFYyWKJUzFrORbCJ9tX6x217pGVQIXqNgqHJo73/jXgtWR72zn3HUIiwgInhjzSzJllkWFJAlnC6KWSrC0K8uThWGwc59xl4AeBPwJ+BPi4c+6ngOcIUumN47hPAYiTZoGQm07UM1nPMUwcIcubybIDbI+4X8yRUODIHaDOuTrwReBve+/fBD4NPA48TZBIn8o571nn3HPOueegc9RifA/A2hyaNGLLrBGE/kWgCeUa1CvQcGGpVpJj5bi15DxNtLmcexXQOJLEcc5VCKT5be/9PwXw3m+o/38d+P3Yud77zwCfCcedL6a+HomYiiY2TY2sE6AJVUIq7jphW75yN1laQKvGMEn21VpXjULqWByaOM45B/wG8B3v/a+q/Y8m9g/ATwDfPFoRC2QRs2tEYiSkOQesEIhTJ0ucNuGYKrDuCGQTh4E4CyrJb02YwtbROIrE+RHgJ4FvOOeeT/b9AvBh59zTgAeuAT97hHsUGCDWV6MJtBx2r5hFSNIjlTaaSC1R33aS6+yQSrQyBVniOIpX7Q8BF/mryN55rIjZGzF1rRIIUieoabJoidNO1j1SadQGetoDJ4t42UT6FNAoIgfONEyEgEiXstrWNk4vsn/QdaO9bwXGoRhWMNUYFzpzANjImp7dv08hWSZH0cTcVxzVrWs/l9gfSYXvmqWtTuupffqYnlxHk2Y/Z7uAoCDOfUEeYbQONck1dBCmnLNP6Afbh3YlGP+ihglZ9HZLLUIstkk7RqVPzXrVCmgUxDlRTDpG5qCfQfpZdNjMm0Az9ZoJUSyJ2oRjNpNt3iSQRa4jhLGSppA8GgVxTgwx+ySPSLGWPa+i6s5JiTvbZuAN6y7COtk+G4HsE3WNDrAF3CaVNnJNuX4hdWIoiHPsyBtYNipc346BsSMzLUQqSIBmhSA5kmv0FmGzkkqfsjplYNNIrNptAvEkbk06QfNsngJQEOeYMW5w2SjyiCSx7q8YtNSx0kEkxlzon+nZkBpR60RSvWn2aVWtkDZ5KIhzIoiNlYmN0BRY0owikB03o68hUmib7LCC2DHaGaDtGytxCmkTQ0GcY8OoAWZ5JBLMkVZWS4pRkkhXcDlGpIqVcpCVJB2yRNlR/+t7F4ihIM6xIM8RoAMyy4TQGKuu7avf4xwCMckj52ji6Ptb2FGgsbUuV4EYCuIcO6wto0kTU5+0tIlhlJ2hu/+tqieOAw3TYZohjN1fYBQK4hwbYtJGFiFNjDhyrkQnQ7Yil80+u62hiQPx4dD6OEuYUdcuoFEQ58iomG0tbWwkc8zOsZVX/rOqGOZ4izxijUr3VBDmsCiIcyKwRNGLzTyjzzlI5Z3UVTzuuIIwh0FBnPuGcs72YXDY/pWCJMeFgjj3DTZAUxvz4xwD1ojXKMhwGjgycZxz1wg9bn2g571/xjm3DPwT4DJh+PQHv/dSROkeeK26Qdbg125gfU7McLe/C9KcFo5rINtf9d4/7b1/Jvn9CeCr3vsnga8mv7+HYCu3JYXuuY/9tgQqopWnDSc1AvQDwOeS7c8Bf+OE7jNlsB2INn7MDgOQ8BhLon1zLUHRzzItOA4bxwN/4JzzwD9K8qWtqRRR64TMdw8odDol2+eyY46TY2NhMDZFU16PfoFpwHEQ5y977191zq0CX3HO/bn+03vvE1Jl4Jx7Fng2/Fo6hmJMC6w0sAnN8/pTRoXBjLp+gdPAkYnjvX81Wd9yzv0u8B5gQxITOuceBW5FznsAM3lqqROTEPp/zHGx8JciFGZacSQbxzk3n0zxgXNuHvjrhMydXwI+khz2EeD3jnKfswdb+W0of94SU9cK0kwjjipx1oDfDdlwKQP/2Hv//zjn/hj4HefcR4FXgA8e8T5TDrFdIBsqo1PJxmLOUOfY/wq38zTjSMTx3n8X+IHI/i3gfUe59tmGjTMblQ3TkqIgzFlAETlwYhgnZeyxGgVhph0FcU4UB3UhF4Q5KyiIMxUoCHPWUBDn1FCQ5SyjIM59R0GYBwEFce4bCsI8SCiIc+IoCPMgopgf50RRkOZBRUGcE0NBmgcZhap2bCiI8r2EQuIUKHAIFMQpUOAQKIhToMAhUBCnQIFDoCBOgQKHwJQQx3H0qcwLFLh/mBJ3tEvWdvawAgWmE4cmjnPuKUK2TsFjwP8INIC/Cbye7P8F7/2Xx1yNdJ4YyKaLLQhUYPpwaOJ4718AngZwzpWAV4HfBX4G+DXv/a9MfrUS6dwxQpp9potAeRPeFvhexHGpau8DXvLev5Ik7jggSsAC2awwZfN71FR/x41J7a37WaZxyJt9rcBJ4LicAx8CPq9+f9w592fOuc865x4ef3qFkDBnGVhkePYyO9fMccPOYXMWMK7MZ/GZzg6OTBzn3AzwXwD/V7Lr08DjBDXuNeBTOec965x7zjn3HNwlTGxwgSyBNInsbGfHgeOoVPe7UuoylydYYucVOCqOQ1X7ceBPvPcbALIGcM79OvD7sZOymTyf8fAEcDtZ5ghJyd8knR9T52E+qup2FiuQLnNeAzIqi6jsnxab8WzjOIjzYZSaJqlvk58/QcjsORo1YNnBehN6y4SMuRuEj/ymOvAo5BlFlklfw2kkPh9FmDwCaXLYad7h/tuMDx6ORJwk7e1fA35W7f6HzrmnCbMYXDP/xVEHnkmOvuFgc41hVUMQI89hELt23rWkguW16CdRCccRJqa62vJZLyVkyVRIn8PiqJk87wJNs+8nD3yhBeC9hB6gOnCVIH2A4aTko2YoG4dJyBJLim4rWKwVP07kkcbOWm0JpNEj7RvTZbVlL6TPYTAVkQOu3sc/A1RJv2sP2GySnXDJZvA/rLQZRRYbvRB7RTEV6KiVL69MowiT5zTRMx/octoyF9LnsJgK4syVO9x75jbd3jK0yS7dZbLZ/IVE9oMfFHlksbNDa4lnVZ/jIM84wuhtIUuZYBiWGSYPDM+vI+rtKEkp+wvpMwmmgjhVdllbusZ3nngYNl2Yw20F2ATWa6SVQxYRScfVyueRKA/jyMOYco2TeHnSRt7DnNmWYzT09Iny3jrJbzlnh2EUqtskmArilOnRZIvqyht0G8uprVOVI3SHqCw6ti29Uj5iLW2ecT0JLHnsPcYRMEbWcmRbE0aTZi7yny7HvtpvCWJtHEGhuk2KqSCO4x4z7DJb3aNbJbV1BqXTLa+dMiNPzdLIqwjj3Lqyb5LKk2cP5SF27zxbRksWTRxr51gbR0dd6CUmaexzFKrbKEwFcTwPsccsu90Z6BKWqCoulUPPO6P/Q+2PTdJkpUOeihTDuAo0iafNXn+cdLE2zChVTccIHsTrFyNSQZ5xmAri7FNhg1W668vQQjkG0iNS6FZYw1b8uWStPXG2MmhYqWMJqrflOpaI41zbFla6WINf2y8xAlnCCBzD0iZPdczrm8rrPC0AU0KcXWa5fudS0gFKcA60COTBk+2/kQqgJYZVVTS0d0kIoF3aMcLIvjwpJhVqEmLYslgpaW0XrY7VyBInhyi2GIO6LuOcbJli70k8lba8k9o8B/Funn3pNRXE2enN4Z9fhj8nkGed4FFrQfigHXOGVmVGdQTq4QkWWlpY4zrvWuKQmLQSWRexhi27kMUGtppTrLc8hp5acAQC5klpW76Y2pZ3ziiMevaz73iYCuL4uyV4jpQ4NwjEwQPbpH04MJkxLdAdp3JuzDAeRUJ7Pciqf3nXyjs3dqxIl4VkTXCQaCfJKJLY7V5kyZRlVOWPTeQ7TmUbV43yROLZJdBUEIc2gTg31NLzhGDPN4mratYWiEkJOVb6LiBeAWLGuahGcp08Z4O+l5VgqH155yjpUia44cUVX2eYOFFS5GzLIg6XXAeIjTSwKmWevWNV2bz/HrxO1+khzvMEKbMJ4SXeJh1aYFt4bQeMIs4OgxacbXWudR3H3L3avsg1IkyZSM4dNwrWZ48RktRJ+7A0gWwd7JL1Pso6bxHidYGeI0g2SO29ObLRBpbktr9KI6aWxlTCUcQ4e52u00GcHYKahicljFXRBNqgtq5ZW8N0JZDao50EkCWNti8WOZjBq24x1A9li+PS4y1pVsiSR1Q2uYYQpm3WeSTqJudnjtXJUfRQdU0QLXViFTuvD2ycQ0W/EH2/s0We6SBO/x5h/M2bpGEiljTW22UJJP85AgHHQX9oLWkWCS2yyycBZOuAVqeqZq2P70XOs6SRpaH+02qaVP4Wkbg+hkkky1BZpMEQb5pteCqmsLJPI2Zv6v16exQx7HeefvJMB3HYJXgFhCxaNauYtUas+EIa+/JtrZXzteRSpLEtfkxl1x7psjo2ZtTHiCPSpkFKmHPJsgI0ulSqe8xWdwHY7c6y356DdiUQxy4xKSS/26b8bcg2PAIhjGV43rvT23nkySON/cZnp89oSoizR5A4duwIpCpDXrMf69eRD6+jqbVHTEN7tRL7xNoaQgrMJawWWGWYPLa4+pwqKWmEMBehfvF1GvMtGrSo0WGGPfqU6M+X6DRrbLNAa7fBnc0GbFZT170sVhLZOjz4rd+pVPpR4Tj6PL09yrMJk9k4cq2zobJNCXH6BNvGQqsCMYkBWaPVuotF5ZNzNYFsh2fEs9VgWIJY0uiixogTk1Syv05KmotQufwma80NznOTVW7RoMUC25SSE/eYpUONFg1asw22LjTZvLBCa6vB/vpiSiBZt5O1lFtIo8c9Db3LcYipZ3b94HeGTkQc59xngf8MuOW9f2eyb5mQyfMyQc/6oPf+DRcSq/2vwPsJPZc/7b3/k9F36DPc0sVaNcjqPB2CpIhF/3ZIyaJtJquCGNjKn+fZivkd7BKTOvoeDQakqV6+zaWl61zhGo9yk/O8xgqbLLDNDHsA7DHDNgu8QYMtVtiiyQZr3GqustlcYWujyb31+VDmgYeS1N6x0mfonUE2skJjXFXRpBl3bMzJYPdPNyaVOL8J/O/Ab6l9nwC+6r3/ZefcJ5Lf/wMh682TyfLDhHRRPzz68vfIVv6YjhNzh84RCGJfvpY4WtJo28leqzzy52BfTOhZTUP26f2yjqholYtvDkjzOC9xietc4jprbLDANnNJ5MQes2yzwBZNNmlyk/MDqbTANrW1DhvVVbrV5bQsmjAxbTej0uqHi9mIedUlT8LkqVwxx8PZwkTE8d7/K+fcZbP7A8BfSbY/B/x/BOJ8APgt770Hvuaca5jMN7E7kFWhYnpQzFMj2zaBR4w8tiWtkCVSL13FFszaRnBLRa2adUwKyfF1oOFpNjdZ4xaPcpNLXOcyL3OFa6yyEeycuyHada9aYbu0wCZNGqxRY4caO2FIBruU6MNSsBa7XTWa1qqag2fS7yrG+DxMqorlSZCzI1nycBQbZ02RYZ2QSRBCVsHr6rgbyb4McZxzzwLPhl9LZD+WJo987Zg6ph0HqHNihLGeOt1/IKrcQuggtGpNzO8wSl2zLmlt/2hSAZR7zLJHjQ4P06LBG4kStsXa7i3mb90L+RqB6uw+C0u3mVneo0x/8Cb6lOhTZjexgTpLNbqNBdisjPHs6eHoetHvV7/XGGJVyEqnPKlGzr7px7E4B7z33jk3SeeJPkclJDyvzpWXGHNR2u2Y7qRJorfzPo4QrwNsQ28xdd32yIa96FvrjkbBKPLojk6RNl2gFy5cGlT/PjPsMcMutbv3sv0yPXBlqM13qM9uD1S0BbapBcpQo8Msu1DuQ7mSllcvXQhSXtt/VlLH3rlGzHsGaWM2Tu0bpxpON45CnA1RwZxzjxICyyDMWnBJHXcx2TcGsfgoC1vcvJdvCZP3UUTNk3WSObRbSyuYdB6Okzj6kjECiZOhkRwn223Hdn+B7VKgQIcaO8yxQ43tpQqL3aTsfWA2LP1yiT1m6Q3oVsoUoUcJeqUsUXR/ThfSqHNNmp3BFeIRG3DgaIroN8nD2ZE8RyHOl4CPAL+crH9P7f+4c+4LBKfAndH2zUEQe7HjWjO7La2h9JpDeA3b2Wv0FqDtsoa1LsKk31hLnAbp4LyBjQO3V5psXFilyaZyQffpl0o0z29R63co9fr0yyU6pcQdnXjWWjTIyp4FdnZroZNUiKKXweDAHYY9jrH3KYWF/A5PLX1ijV+sIRunCUw3JnVHf57gCFhxzt0A/icCYX7HOfdR4BXgg8nhXya4oq8SmrSfOeYyJ4h5efJIoyuD3tbk0cdLZUpi2Hpz5vvmkVIjqVDdZC0klMorxa8C9SrXqleYbQa3c58S2wQnwMO0qJe2KZf69CgNPGtCnk2a3GKNm5wfeNtCxyj5HaMZG9A6COz70qTJI0xeIOykHZlnS02Dyb1qH875632RYz3wsaMUKsB2UNq1PiYv/inPPtLQ5JGKI/t0DjMYtqmsMR2DlHkOuouwXonmVNjvLfLNt7+L7QsLtHiYm5xnjQ3qiRwRZ8Aus4P+HHFNS7/OBqusv3oeblTTwYBCHiFND4YljXUMaMg7jg3jtkMv9HvRBLQN1ziVbfpxLM6B40FegKAmi05koY/VBIq5p2OwVr3s05VI2z/6PgJLmpjqIdeWGRia0FocNtbbQKvKK5ffzs3L51lrbtBkaxB2EyyZHn3Kietglh3mBh2irTsNupsPw7pLxzTpkbQt1FB02yFsjTX9nJos0ojYFFX2vejsqzK0o8Nkdqwuw/SSa4qIA1ki2LxhdvhArBLvqP0xwth9+rceKSoVSu4j2/r6+r5yrTzVTWLARJI1ob0M1yqKNISKfg32Ly5y49wiN1aehEaXar3DbHWPUjlInX6vRL9Xigd9bhKXNgMpZ+P2RjlOIPv+JQhWD/HW30OruuJ4kOEhgthQEds9MP2YEuLogV+WNHpY8aixN7qFs1IH4nZOXg92OfK/JZFGnsEr95GySPmTitVrwo3FbIW/QXZoQaNKt14N+eZibnEtsYSAcj1LnExZY14zHZ2hpby8fyFOk8zQi4HAriSPvsjAvU+F7FQt8m7ypmzR5JleqTMlxIFhlUBatFgrZ8mjDXoZNSqtnYZIFfkgsT4HfayGDL225MlrIfPCWETySJkXob0GVyuhogtxGmSjtEcNV4DsMIIWWSmTKaI16O270Mdp0iwTCLMcjqsTHy80kKA1UrVOV7M8m/BskWdKiGPzgOlBZcukrdxiOHwohKSSeK80yXQrJ9C+ZKtyjVPt9KuyFcyem9f5KpJQS8dtUttnIVS4dYaHNuRFXlsJZAezDT2WJY2WpBp6qIVMK5mQRktEKRtkiVsnNATdRVW4cTgbahpMDXEgK3H0h1omRPNUhivSgDgoNUUbr6MwyUeyKt1hOv9i3iWRWPuk0d0iiRZDZesmwwR0y66jtcdJINmnj+1COvLT5hmwLmg5boFU4lTSQXaytsRpk/0+6yTksXaVVgtj7226pc6UEEdsHG2IysdaC6Ej0srJx6qr0+WDiU6/7qCbme8qBzHy5BmuWo2Qc2PSpmfWsXvpc7XTQNaici5Du5ZWxjrhWWW7RzYngSaQjofTjrMWhHerI8elkdEqm7YzF4FaYnNhRqkyTBw9nKJHIE9vUd1PS+G8gXPT7SyYEuJA1hjValol/VD6Y4luLepJK9m/TtrStZtka09eqzXq48gHjLWC1gkQkyx5/Rbac9cj2xL3SDPRJNttl5UiVrLIfv1IZbJDpmV/WzLd7JMm7bCufS2ZEmKJ+ihLjDiyre2dOtDSDaK1/SYhz3RJnSkhjiOrHqhWTqRMMuAroyJI6UVN2yTb2t0AuqvJj6O0XJoso/6H0aErk0g4C9XhaE+PqWOyXypu3iC8rqiIQhwYHhNVYWDnWBvLqpCQJVDdLG2gpyWdfZhR5Jk+TAlxIGuoJuQpk01mIeSRlk5K3yW1B7SK0CNMxttbJr9n3Lqp8/qByiOO1VJFdyqOclFr6Jg57enS11SktWTRdo92moixLtBSoAtZ97iuzNpxUCbjdpZ7lyOLEDWvbBlJJxI19h70e9LfY3qkzpQQR2wco67Ji28wnAmmAVSTl9itZFu+nlluVAhqn4aOPhilxgk0GcQzFVPVLIFGkcYS1RIuAitlrONAf1EhiVxeq05toKdtylgfjorSyPPixbbt8VK2rtxTOwssbGepJvP0kGdKiKOhKnSsgiREeqhxl1o9DCnu9Up0qwvpuQN1RG2v10ihO+UkN4FGTPUSo0G27ceLSZhYp6u+tpZiUiFGeAOtpNEteoNhiaOLG/PE9Zz6YTt7dUiNeUy9xCSaFV663G1JhqgTPtpq2DNrmAayaEwhcXKQ+QD71Ood5uZ3BvFb5XKfNo3gbtUfT3/EdZ21X6sjWlWyzahV0WL79H+WNHY7D9oJYckTafVto2LtHF33Yq7rwZfXlVdLHL2QrSmaNG3SV6L7jzTk3uLU6IoGIB3UtgzJuKjMe5guL9sUEkdVMgnhiJEAKBGGHcMezCeSp/dwaEnt8fJN1ivQWyVLHAkNkfgqU45oGWPBilols+pZ7DpaLEyAGGliCwxn7xwJkTb2ZSUSJ6amCUksoSx5rLo2OF/sHXn/GnKyhE/p7gCYBnVtSoijs28qPV+khzZou0C3Qq+Xjnos0adGB5ag3yuz31vMJ04Z1c9jY95guILnKfJ5hNL/TSJpJkTZbGsj3aphlo+xxidaJGnhy6ThTZGspro8Wl3LU9UsaWR/W+busdDiUl9seqTOlBAHsipO0gnYrWVHL7bS7W67xl59h3KpPxhAPMsuC41ttiGQRxDzAK0DbXVMphxwcP06zy6KXfugEQgJYs9hVTBdHK1S2SVTNNuaKxsk5noWJ8QkpokupyaTOC3akCWPrgf6waZL6kwJcWS6Qnlp0nvuoeXSPpoGKj6qynZ1gVIzJc4Me1AKx7R6Je4xHy4f1e0x5FGSLmNvaIyzUfKOHeVNm+RaCjHy5FViLQEsaXqQTU4vlVcF0VbJRmyskH4DLeF02UTaWXLrYywG5OkRn3JkpJg8FYwlTk4Wz/8F+M8JSZ9fAn7Ge99Kcq99B3ghOf1r3vufm7w4PYKdIUGai6FibxINetyvLrBd7lFaCraOJk+/UWIbUvLY22RaZOmYE9LsMPyxJpFAo9S3Y8Q48kgxYpLG9u0MLgiBzCrpfINsXJoQR76BtXk0xCkxDprgg8BSPe7JMnM6vGuTSJzfZDiL51eAT3rve865fwB8kpCMEOAl7/3TByuGJCQsk40a3gLmQn6wmKpQdXTLC7TLfZjfHpCnRJ+52Q40yJInpra0UfPFiBdJ96bbjzWpFLpPhIl1fGLWUWljLwoZm0aIcs6sGwynBdYeNlHBNPKkkJBrQDr505LGVtPTt3PGEieWxdN7/wfq59eA/+roRdEtfIfUq3MbumtpOI2uKFWgXKFdTnqhE/IAlOkzM7tHrd6h3Z2BRmU440uLJI4KQsec7bcYRZqjECbWmlZytpPjRjkHYurQxEp4Jbu2ksYuDYaj04U0ujwt0s8pJMlTK6NljR1k7ZzTw3HYOP8NIfm64Ipz7t8RdK2/473/17GThjN56p5raXXeZKBzby4Oe5AGrV510ND153dCQr4EpXKfh6p73KtWhr1DemnLPVXfRS6OopLZa2vPnr63bn3dMFliz1Amm35qbEWVcVAJqgxPcHWONEbwHLDieajeYSaZs6ffK7PfnYFWNZ0ZAYwKZspgNeBoGzQ99kwMRyKOc+4XCU/428mu14C3eO+3nHPvBv6Zc+77vfdDo8qymTwvJFaqvCzRcaV/JZE+m5V8LxJV2r0SvV6JvepMZnz+PeW6PvpTx3CYFlBIodeyaLWxnB4eI432eOlLiGpmjxXJoCs1pKQRidNgaN4eznVZWmkxN9sZSPY+JTr9GtvVBfbLiaNF31urkEO2JYY42kGjMV2OAThCFXLO/TTBafC+JCUU3vtdwvRqeO+/7px7CXgbYU7pMeiZben8Ui1v70Log9GVKHOJCt3uMt16mMkMAnHozg7r+FFnzb5asH9GygkHI01eiImVdppISd9KTMIIYRpAvctD5X5oJNrVtKi2H6xhbivIXIvs8IEG0PAsrbRozLaSqUcSiUOZmdIepWafrV6Je7357MxwVtKMdFbYWSVi7//01TQ4JHGccz8G/PfAf+S976j9jwC3vfd959xjhKk+vjv+iuIW1Y5+WWvJUwn2znpOyeUj1Kvs103laTF6vsyhAVaTtHBHIY0OaLVjX3SSkrmslIlU8Icad1lobA+kbKc9F4/d0yiTNeTl2jnrh+od5mbT/NRzSYSFTr+715jhTrsGdZeqbVZ109ImQxrt0YzF+mmcvvQZS5ycLJ6fJGQy/kqYR2rgdv5R4O865/YJk978nPc+NtVaBLGQfjs+JFnazZQ8+gnkQzTISiRp/VpkCZQhjlYT5P6xeLPjgI0+1tJFL2Y+Ur00gBV4aOUuzbUtGrwxUJ86SzW2l9q06g32q4txb1yL4U5JawOq9yukTLNVp5V3ll06zIVjqrtQrg6TBrVtJT8Qjya3OH3CCMYSJyeL52/kHPtF4ItHK5J9y6K2KalDJST1s6XXaono/HKZNsMpYVtEkvTJx9NDBY4KLW1ido1OjKEH8s1lK3QDM7yiy+raBmvJtIe1ZAKqXWZos8AbzQa36mvcqa9B1WXVPHkXg4aDIbLoyr3fnWGvP8tuaYYZZgYEChQKUqdU6qePqb+NvR7qN5DtABfo92+/gRhNp4djN5OPBpE6sZcSaTY3a/EP3WV4fI6WOC2yEieTQE+PpRll3xyVUNausYn/TJxYxGh/5MItznOTNW7RZGswX6jMFbpFk4XZbW49vs2N6iWoViPeRFJ1SVRC/bgDaZ1GaoRsorPMsDvILLrHLHu7MwxmSZDrlc223TcWNgDV/nc6Ns+UEQfikcdlhj1tSSXbdBEnAVmvjhAntgDxlLDHhVF9NlriWFWtNixt6gykTfXcbdbCDKBc4jqrybSHs8kM1UKcMPtBm5kLe1yvXkpVNyFOC5VTWhUpQ5qwb59Ftnol9hozdGbnKCdSZ5cZdnZrdNq1MGSgZ65nvYE9te5COi7IDvmww6nlHZ6+g2AKiSPQlVfnbpb+naSi9ZqpIWo9bfoS2rOUcYN6sl865lk7rg8V67OpmP/UMTGHQANYgcZSiyZbnOfmYGnQGky022GOBi3qyaRTJXqUmj2uly/RLS+nBNFSJ0Ye7Rnrwb3uPHda89xRnksIqhztqskaqu5RJ95/UyfJvCMxajKnq7yLvAiO08WUEseqbPIVtYtapEQnpFCSCmD7DrSDwHp2hgTLNBifStpa9WZApC4LbNMI6dZpsjWY3l3bOTVCZ3CZ/sAO6S+VuXkZuiyn12wRJw+k5NESqErwXIoTQGA9lZkym2ey2y2dj6Bn1kKaWJ04HXVtSoljYV+WkEhlw9dOAfl4eU+nP1xP79TqgtznONUDHR0hv+1oz5z7qDJXqnvMBquCOTqDKaUavEGNMCp2hxp6apAONfaYZYca/aUSNy+WQ4el7hjV5NGSoavWLUzIk3kk2yhJx6omkSasPrflCPadEEbU8zmGGV0MZMtBXui9Nt7FC+ZDoGbM1WmfULfeZQhhJ7qnnshJx/EcUhjdSuqWNOKO1a23eq797kx0CsPywMcl84juhTFKar62JlvsMkO/WWIDAnk0cdrmnno7e7PRiz2mSuq0EfWsGjmnJRHa1i3dM2u9//5LnSkmjiDmntZ2SOIN69Xi6phAfzzrVRrqP9GDqPbNReSiR/lYNjJBt66ihs4FQ1skaWap0qE2mJlNaCFqmriJ95hJXcX0w4S8dFigHeYQbZZoV3dp1xdS+0SIY+85ZBua96ptGStZBJqM9cixgpYdFartTDtI8HRUtjNAHIsRA5ysxBHoFlCTRlrarqgDicNhUInthY5LXYOs1JHfYsOJ86OZjeDeZDAdyMa5VRpLrYHXLCQtKSXhMOJZmxuQao8ZgMFI2TA9fInZ+T3m5nfYuTuXzrfTqgwHbLbVWrZh2I7pkapmMWLIN2qQPlukw3WYPPr92bS99x9TTpw81/R+ZCGq1gwgH1g8Obrl60LQrXXy87yRoBnD6IDPEbOZxGPYMeck9pZNvt4I6259metPX2K2tJtcZY4WDw+mbofgINhRkmmHGntJ56WEzMywR40Ou/OzdObn2KmHgM171flhz5omjnYACEnkt7zXMlnJYonYUv/HJNUgK5GFjpwX3F+pM+XEEVixDFnJk/TB2NRQcqqVOJo8sm7reDGROOOkzmE+VIx0eelfE7QW0xAj1TLf5gLffvsM2/Nhot0VNfWhQOYMlQizYPWk0qdGJ9kT/pmd3aO00qdFcD0PdSRrNU47ALQXTXvQqkAjHYpQLvdDBHt3lnvtGjRcdlRpnazKt16B7gWykRYSxXV65DkjxBHIi7G2QSIddFYcWeSjy5PqDjjrUcrYOTY6+6gfIyY9NfQEvpEQE51QUR6/C+3WI3zniQYbF9doljYTibOTmTMUoK+izAQl0vizWXYH8Wf9Uom9+gxtkTpWzdJSB7LvWOxIRaqH6h0WGtsszG4PSrG3NMPu2izbK3W6rQVYqQznNWgQvs+6g801UicOpCmlRK3VL+fkyXPGiANZw91OjVEbJk7Mw2ZtHVm6dsx7rPdaLnBQdU3KrlW2GHnyrtuE9cUMaQbxd+sVbp+7wO2VC7CSnTO0VEoDMqWnfybp25lhjxK9gdu6r6rDIP2Wvp9eNHFkXSb73pN9M9Vd5maD23yODjriYG9plu2lBVrnGmGa+fVqOgboBimBrhGmfRx8+1uRdzcofc47PD6cAeLE3NJaXdP9OfvB0G+TtWO0KgHDHYoZ13RiW0T7dGIf5KCtWx555Ld+Nhs3tw+bzWwozDpm3tBkztCYzVDdp1LfYba6y1wyUlZUtFCCUohz69fY685mPWxa0rRIA0Qx97AeOKBc7lNjhzk6iSq5M3TP7dkFti8ssHWhyeYTTbrXlgNZzgFXSdW4azXoXoy8Uxg97+vx4gwQR6Arra3MejImVbFirk57KTCdeBWztgfC8X2QUS2jVT8UidrLYc7QFoE4VsWJ2Qp1oF5hv15hvw7tRkqimeoepVKffr/EXneGdmsBNqupF0+WFtlhGQJxHlRhyH3dTaVXjZ2kozYlT2nQSRscGS0abC6tcOsHVrn+xCXaf/7I8JxIVysJefR7kfdk68nJkOcMEUfD9rNo8uwHQ39ImihYz9uRJfthpU5sv4a2e2StZmvbXA5BruJ1axAfu2M8coFgKYnCq/TQc1kVcJMgzdYZJs9gKAbAXOj118RRBOr3wgcIttQeC2xnHBkSXydOjE2a3GKNa/OXefndl3nx3FPQqGa/5VVxGowax3NyKtsZIY6uaLZ1EXtEJ+puZuPWUKeU1anarTp4x/fDnRlTPW1BBdrukdZVS9jF4HVrVdJJd8eSxvxXBsoq8kLUsU3CNUUdFAKxn9xbE3ox2+ckSxv223PsNUO0wwx7LNCmyRZrbAyGQ6Qu9FlaNNhglWtc4RKPs3Jhi2803kW7+kh6uy5wtUaYH9aOpdIq8MlInTNCHIGuYFrqSDopqUxJ/0fLnCqVRBNHL5kBVSdFoFEqoIUtg7V59KzVNegtQEu1/nnkkW0tlSH7Xlpk1bT1ZB/7BHfwm2TDhUjf+SbpuKFNoFWhc7FGvxRUtll2adBilVtc4t+ztnuL+a17IVtFGfw8bCwvcY1radT3/B5ff++7aXcfydpb682kLHo2b90SnozUGUucnEyevwT8TeD15LBf8N5/Ofnvk8BHgT7w33rv//nxF1tI0zOLHilaDlHT2iskujhqXwulr2+TDmjTBJpkiMG4lm0cYSo5++W+Al02O/Fuh+AVXAgewq5LHSVV4h2O1t2sG5QWKRG6ECqoLFoSqmTtm7VwbWMfddpzdJZq9CkN+o9W2OT83XWqLwI3CXzsgZuHc6t3OPfkn7J6fmMgjfbmZ/g3T/8HIRWVVh27TcK3010IJ4tJ7vCbDGfyBPg17/2v6B3OuXcAHwK+HzgP/Avn3Nu89/2jFzUWLAnDUkfUtqQidmvph4+FfrTlUp3kPPkAWvyPIsw4jCJMHlli5xApQ6xMUtZkJKnEu9WJO01k0e2PsVGyUkZIE5t2EAaDC+sEKdUAzkF382G2lxboMDcgT51tqjeBlwkpXV4jbdxWgevw2DPr8PbnBirc5oUmLz7xA4l7OrnHjUVCYOhtsn1wJ6eujSVOLJPnCHwA+EKSJupl59xV4D3Avzl8EfOgpY4e6GaP6SUqTDJdoiYOENQzUXd0peiQr7JNIv4nIUze70mvB1mVFbWt+6AqKQksYQRaMoubeWDLSIOi34+ulLoRmwvJVMRhsUJiJzlalxvslGrsMpsc3Ye7hEzH1wnkkaCAZQYS6LHZdbauvMB1LvEyl3np8hPcuzifettuQJqzIRbxcfzq2lFk2sedcz9FyJn28977N4ALhJS4ghvJviEMZ/I8CKzUkX3Wj29VmnKILgCy3rgOqaTRaoiWNgeROuNIUyZLloMOZ7D9TPYeORBpkueHANL3pUliCWPn6RRVTQ9pr6VSJyFPa7NBa63BNnU6zNEjyU8g5LmVLLvJ7z4wD5yHy1eucYnrnOc1Vtc2WD/3WDr7+KDzWogzHapaDJ8G/h6huf57wKcIqXAnRjaT53k/5vAEo8JWYo8iEmObNPo5RhypDGLjyP5Yjq+81ku3wBYxouSt82Cvr8+J5WSzY4wUMo8gDhEtaXciiza88xoQEWNz0JsLoTINBsS5d2OezbUVtlihRYM2C1B/Pf10u8Bd6NyFmjhzEjKt3G6zsrxJgzdYYJt1a6t1Y2L05HCou3jvN2TbOffrwO8nP18FLqlDLyb7TgDawya/NTQxtN5rj9eVQlcOm6Awdg99LxjvANAVXW/HpJC9n73HONK49NTc16TTYlnJIu/BOksscWxYkpI6kv9uhYE9cuvuKlvzTbZY4Q0aQSVbIkiW+VDWirVDu+C6SC6dkD9uSOWsmDKcLIEOdXXn3KPe+9eSnz8BfDPZ/hLwj51zv0pwDjwJ/Nsjl3IkRrV+PeLDbgXac2bJokkzSkUb9V9MPdOzItTUtg5gHEccLaXkGknywjJxOyZqw2hvpCXNKEkTa0CEPLo/LVHZVF9Q+8YjbDy1xgar3GKN11frPHK+HWrLLeAOVErJMwiZquCroYN0EKh6st7msZjEHf15hjN5/hXn3NOEJusa8LMA3vtvOed+B/g24ZE+djweNY28XvdRb9C2kLYCWLLESBO7/iQ2zyQqlc6pJufE7qOJpSRLmeExLVV1uiaMuN67ch25viaRJc64hkQaKOmMFtV4MfVqbjIgz8ZTq7zGea5ziZs8yiNXXsy4o3mTMLPeGoFQq/DGcnUwKK+jg3kHPgBbvpNl1CRetYkzeSbH/33g7x+lUEdHTG2rRP6Lvex98/+ksJU9ZsMoGyCTeNASSM63UOpXnXjnpnY1aymj+2bKhIrcU9cDsu9gn3iDYmE6QQcqsXjhFsJoTiHOOqy/ep6bF0JSq2tc4fyV13jkVjs4CWaBO8ml1gg6y5PwGufZYI1NmrR2G5GkkpbcJ4v7Y0kdK0YZ0IJxjzWuP2TSl59XFnt/TRZRrRbVeoEhu0RfRnPOkkVcsg21X6tnQphNssOhpfM36pTQrtxxMWC6v0R+a/VvOXREt1BhPFU2LqxxM5E617hM4+3fpNIjqGZ3SPtynoT1K0u8zGWuc4lbrHFnvZmNm+vCsB12sjiDxMmDbtmBXINbY5+supL3OnSf0STlsPfXi80PrUgj5LA2iuwTsjTIzl2zQkqgKgwCNkXC6HgzMJ2/WsrJs6IOtPtjJLLeB2s3MhTGs7Xb5ObsowPyLCxt89T3vYJbIkOc16/UeYGn+Aue4lpCHm5U0rg5UT2H+t3GeUGPhjNGnFifxSjXrq64FtK7Lh9YE2MSksT+zyuTVdEiHjAhx9AYGrKEEbJcTJbLXZbPbQ1Gfw5mLEhyDWztNrlz7VywRMXuEdLUSTqGj8ONG+sQlYqcpO9SAaR3Nhu0LjS4xWogDtuUlvs0lzep3e3SLz/E5myT61ziL3gbV3mCa1zmxquXwrMIcVrhaYcDPXWZjh9njDiCvAqqjWy7z8LGn8UMYSGQDdkYJZ1iZdIqmiZzmYF6VjVLTCUTsjwBlSfe5HLz5UGn4AqbNGgNUkNts8AGa9ycfZRrT13hu/XHw4V19LMsQ67cMTkQcjGmOmVsriotHmaTJhus0UgicrdZYHZ+l11m2SIQR0jzMlfgWjUbqd0OZw2rajFpeXw4Q8SxlX+ci3fO7NeSRxuysY5Q6b+IkciqbXn9K1ZN06TWC8MqmSaPqGQJYXi75+LjV3lbosA8wdUk6fotGrxBmf4g4fpNznOVJ0JrfqHHi92/FCTMJtk5hHoxO+egyNMGDBR5JH2VDCMoJWWXTKSbNLnOW3iJx7nGZV5/5dGItPGkEke+2cnjDBFHYCtmns2wSGqA51WKSXrNNYnEY6SlkH2Fk7qflbTRTgCIE+cJ4J37PPnWb/MOvs0P8jzv4hs8xQs8fve7VK8TwlQAlmD/Ery09FYWkqQWO9RoPf4wr6+/JbTY4oErk4QhjSOPfV5r2wisRK2Qsd/U6TJdSCDPw5Tps5M0JtsssMkK17kU3Na75+FaJbXXBsTZZljiaBvrZHBGiJPXmlvCLBK6opcZmiZDWnNQrZ5LoocrYSzJoCddDy+QPgkdgRCziwRWZbSL5AobEwqjbZtzwGW4+NZrvINv8wxf54f5I97Ncyw/1w3dzy+TGtXLUHkS3v70K5SezEqg11feMjzletQLaPdblVX/ZyVsmew3IuvwSNDvl9grhWS929Qp0WOXWaVqrnKTR9lgLdhpljQ9iIdFnTzOCHEsYqRZBprAKpRd6mVawYx0JCVOm6zO33bBddqukUZNi1s11rOeR5yYJIxJnBHQnrQVqF68zWWu8QQv8S6+wdM8z/L/24U/BJ4nEOcuoeNQeuF78ETzBjeXr3GVx2myxUONu9yrz0fSAOsKD/HyCXlGFTry/NZbqIZ39JK5fCRJ/A5hzM4bNLjFGrdYY+PO6jBp2hCIoicE66lFcDJkOkPEGeWlWib0ljVDRTvHsKs2o5qQ7eNoxRYH7cVEEkmMe4xAViUYp6ZJ2VWfTY/4N5dK1gjz4ayxwSWu8wRXeeSb7RCX/kfAc/DirTQ935OvJdV7GdyTsLK8ycPJ+P5avZPNl1YmKYu2xySHm3USWCeJ/c8uybuwjo+k1vV7Jfql8iDXADCwc1o02KLJJk2668vZgWvy3e6zJ03jDBAn5hTQFVOpaHUSFy2pB+oiKXn0QDbrXdIfRm+3gM0KdNfUvbZI1TirJlhvWkza2B57sqSxKluVQXaYVTZ4lJvwImH5DvzZLfgOgwwEcAfecZ0gdW5DPclnNsPuYBLcYQ+0boxspEWeY0RjjuEGLVFL7cA5hT1mkmlI5gbJEoU4m6ywtbUSz7IzUNMKVW0CaDVCyJOQpuxS79NlgjF9OSwPXbxLY6VFrdTJpiParYUkeK1qNpOL3hYP1CYhYLEnXrstUtetHpMC2cDNCUgjGCF5ZC6cGjss3OmG229A51YIP3+VtO29DaFh2CVex2OvdFA+OUGeTZNF93tBlmDyvDWyBCIr3VSN2+3OsDs7yw5ziSctmW4+scla/Qb7m4s5qak8WfXMdhecrHftjBBHe3y0ESrEqWV70y+TuG5h6fI652dvDrKp6AlfO7M1WhdCR9zmxUQlkDB4IdA62QDKTYIKN3B960QRPVW+GHFySKO/sQ5cVATSqWtFaNCHXm+4na3Ia5oNZU6D8Wfp99LrZCpzT96l/kMTxcaxCZH0xexzl4fvo55zrztLZym4pHuUkJnjgit6hdsSWmPVtEGIjbx33SdXuKNzYL02ySNoD1QieZYur3N5NmRK0ZPLQpA42yzQTtyejaUWm0sttlZW2F9ZjKdaEpVjnWQaCrm/kMeWUXvQRkgayKpp3eFlN5lNbY8ZOvMPMT9/D5ZgcQmWbwUrbJ9g6V2AEOfVBJYZRBXvMsNud1YNK1DP1JbZ0ETS2H4sO7wg1o+lPYrJYh1zKkr7XmueVrUBSyT9N0nnbX+BlqTDtQ6BAXFs2e4PYQRnjDi2hzv5UGUiPe37NGZbNNlkNZmduUFrkD1SiNOiwRw7wXCmw2xzj63qLu3qSlD/cnRzIJk9bFmVR1ci68iYAFKxTAWjne0s3JxtMr/2evCenYfv24JKP9x9Gbh4HngLYUjhediiOWgk9ttz2cgi8dz1SOYJqhBsudjgtdj4HOvN0B3OkWeT4NJGWHfLD7PVKzNbDfbXbneGbruWzSTawkgbRpRBowi5yUHycbTpkywPVfcSMuwMUq/qxHczzCDJxiHN5t+jFCJ0z0GbFcBlpYG1QVqOUF21o0AKM6G00YiQhha07jTYXGomTcAab73yOlwBvg+aPVi+CTu7UFtmEIrPY7C+usQGawPyDKZTlyIKcVD37bnEltMQlShvZKjAeg8jz9QiSWwY7rXfXmRf97G1iaffHdg32g2tJV/hHJgQ5kWpCn2vJ7NhDi8lRZgyfcpJBv8wydIOO3TozNfo1Dvca8zHp/ero1QpmTFZf8hw9QPDqmpJRetuPszW0gobSVDk5SvXeOSd7dDpWQK3CrU+Qdt6G/BO4EmSUPzVwZSHmQmhtLSpMpzVtKfK00s6ipE+rlhkhUBJG30NIY3uFugyPAePJldUTbNetPtHGMHYL5uTkPCfAE8lhzSAlvf+6SSN1HeAF5L/vua9/7njK6728CiPirUJWkCrSuvCw7RoDM1Spo1QGVEo9sNA6gClcp97UsFsDJkmTo/QSg8iCyw8+VJH5ylJjtEVTVrfdcfG42Hk5DUuc56brLzrT3G7BAkp2WEWgceAd8Grl5a5zqVkANgK27sLw/aNljZyb7sesrtcGNnZ0x3FVmWSKOlKljRldV3ZX8/ZH5U2kE1PFRtbdfL2ziRN4m9iEhJ67/9r2XbOfYp0zB7AS977p4+pfAm0bqH3ieqwOJzv+Aa83lildqGTiRiW+VkGU1pQY5v6wAbYZiEod3fngj1gW2HrWs30vgt5pGxaz4+RR0gjIiA5xrbSSSXa2lrhZvP8IGK4sdzi7U+/khKnR0h8cR5uv7PKVZ5IiBMkTqddSwlipU2eQ0qrplaFbBOcCj0drmRVN9KUVDFHQVu9Q30//T1bqAFrnmyhTnbcTR7GEmdUQkLnnAM+CPzHx1yuEZCXJaRJhuduJi7pjPu4yiu9x+m8tTaQPEIcGJ7mb4ca27sLdNo19ltJEsMW0TlfhggkgnBADnEWVIgTXz+PrMvZ3VK5WmHZX19ko7nGdS4N3OuswqWl68zfuheOn4fXV+tc4zLXuMxNznOLNbbzHAMNsnUur4hWfZSKPVg7ghoXGxzostOCQFbaaOLo+7UYfv8Dj591Q+v1yeOoNs5/CGx4719U+6445/4doUb/He/9vz7iPRQS0U+PbEqjxaA2rDOc5rZV4fXNt/D6uVXqjW3m5ncGU/z1+yX6vVIgSncGurOhAthWtcWwnm0lUQZl9cek+nfkU1hP1KaMnDxPk62B+rk9u0DjUkuF46uoYs4POhNpV4YljkgbLRFi0sFKQKn0m+r4FsSHbswxGJEak15CnJi6ppdMYayX5v7iqMT5MPB59fs14C3e+y3n3LuBf+ac+37v/Zv2xMkzeWqVR1owWSTsZSv8bjXDeA2rJ98AVqq0G1Xa9gPZDzmkzxNvYUcSRyPvgFiwZE4ojrIR7qw32XhrGPg1k8xqts1CGHOTEKdFYzAk+SaPBvumFXEMoH7D5Olxrb2ij2uT/KFtjST7Ta+SkkeupW1HOVWra0NOAdvpCVPpHMiDc64M/JfAu2VfkjN6N9n+unPuJYKP5zl7/sEzecoXFmkDqbv3dnpYq5nVkdfJzuhliSPr2BLrU7FrfVymrKPip6Ri6WiISAJBW2FbwGaFrXNB6kgUxBuJGgohSiDEeTV5LVHTtnabQfXU6pImj7bX7P5YOfQ71P/Le+lJ0KhWq+UFVbKNjpY2WuW1UgfMtfS14X6T5ygS5z8B/tx7f0N2OOceAW577/vOuccIvQnfPWIZGZY6kCWQRg/ay0EtaZEziRJZbUouZ1XlcUTS25myTtKjXTbrEdCt7ybcubHGzcfDxLdCFD0x0zZ1tlhhiyYb/SQrTMtlyxpzcuh1TJro83XFrpOqXPa9Rp+/kj6TvNs858GgURJpEwu1ieFkiTT2q8USEnrvf4MwncfnzeE/Cvxd59w+cA/4Oe/9bY4NWq/Ic0Mqu6e1nPTuE58Pxl4675Z2HVWv9aRUtmNOI3bznLRQcj9j51B3vF5dhQupmlZLGhFxdrRo0LrboL2+EqbdaKGiitU9NGHyVDX7/NY9b0k4wChpUMk2RjFVbUAam6rXjr05qD15dIwlTk5CQrz3Px3Z90Xgi0cvVgzWQxX7X7dKbxJsnySsv12BtgQlymMfoEc/A61ZWrXMqhC6ploXNeo/Mz5H1tau2CSpZFVeb7+F1kqDWr3DzGwyj+buTHB2tOdCo9EitcnGeQVjBLANhy5j3gIMk0ZXarmBisrISG17rbzMoqfnWTuqc+CUEHs5uqmSmcnmCPaPRFLL4+ZVXo3YOKBRsB8xRpxxY/rVKXnGeFn91wou6jv1xXzbIOYBzHMOWNVVb1tniXWgDBHHSoIRzx4j5QA6EYclkOAMOQdOB/oD2C8rNUeOkTzGUmFVJDUwnjyVyP7YPg2rMugySvm0EdBjODTfpfo/DHceWtuinvN/HmE0+epkyaZd05AlQ8zD2CLr+RqogvsMS4QDkiejnsVG3Yo6HFPTTp5IZ4w4FjHJI6K8TJY8goNKm4o5xv6etFwx4kjFmlP/JSS3xrNt8cVm0zZGTGWyUqZMljB1UtJ0iZPGSjGtNrbIkihT4W3Q66SwNo3NOmQz2txfNQ3OJHFsyyUVbj+yT7YFQqYYLKFiZItJHntejiE8OF+H5NjwHC0h5xj0e2jStIiTRmCdFtoJIIsQRZMm5hCIueOtxGlhcpzpKQ/1MAt5N+Oq3CjS2KgBedhCVZsQk5BH/8eI/wUinTTK6j+IS5txto+cL4To5WzbtZwzB725NPwn5s0aBe0xk0VUvPaIa+VJujxVjX2GZ6S230mr2fLs2kGj491ipLHeNNtYwv0i0RklTgxWJYi5q8dVcttU62tYj54lVR402XRl0ttaksWIJOP45xjkgZsEQgZNmNhaOwsEljTaKdBCEcZKGWu8a+mvq5vdJ+8jjzSxPpzTkTZwpomTR4RROvVRXvIoUuXBBnhaD5t2XOj99pjtnHNiz6jO71WgVw7DACBOmljfTcwpYD11QDrpcB5p5GIxG3LH/CckyLNrtLMhzw03Rf04ZxeTGop5r2Bcr/Qkrb5VEy2JYg6MilrHJBFmWyPmwFC5tLuV1AEwTuWLEacH2WynsSSN9r2J53Anss9+IztxsbZrtBftdKUNPNDEmRQH8cTo13WYj6ZtsZgDQxNphziZbDny7DItrWQ4szgdpENYnZJHnAFEGoyaKzQmDeQ9WQMq1vjEVLVxToD750nTOOPEmbTlPy4c50fKIw5kHRXaEziuH8lKK1krR8OgM1h1CotKN1Q+XZHzpIGOWLbvR67ZUWWLOURsx7HcR187Fp2hcXaCPAtMjHGEs17BvMFvedexhNNePOupk99zxFVEgZRF2xja4xVLHxUjTk+tify2jYdcx15/ulAQ58A4bMtmIx5GORi0NIqRScNex9pRo0gEWfLEymGJkUeYcTahNeC1UwTzX+zasXudHqEK4kyMo6oCtjPwIK7vcdeNqavajtLqj3YsjIuC0JV0XGWetHzWi2bvp+9l7zs9eACIcz/snOP8aJZAMLoPKkY0jXGucd1vJMdqlU7fV8NKCNvSx6TIOGj7JuamnuS+04EHgDhnFZMQSB8nsI1EnuSKSa1xat+ocuZJg7xyyP+xe8Vsurzrxe5v71eE3BwSJyl1TvqjxMqe1xrL8YJR540iyajr55FN/5dHGLtv0o7iPBJPl3qm8YAQB/L7BUbhfrqyR+Gg5DnIeXlEm8RumIRE484fJ31suex99L2mh0gPjTvAOXfJOfcvnXPfds59yzn3t5L9y865rzjnXkzWDyf7nXPuf3POXXXO/Zlz7odO+iFS7JtlkuPHXes0MUnlPEinYN776eUsseOHekbNtUeVJY94o77b9Ng1GmOJQyj5z3vv3wG8F/iYc+4dwCeAr3rvnwS+mvwG+HHStN/PAp8+9lIfK+xHOy3CjHJNH+bcSfqOLAkmIVbs3vvm96iyxK47qozTibHE8d6/5r3/k2R7m5Ab+gLwAeBzyWGfA/5Gsv0B4Ld8wNeAhnPu0eMueIFJELNtYpV1lLQYJ73y/htVlphUsfeaXtLAZBJngCQV7g8Spmxd896/lvy1TpjTCAKprqvTbiT7CpwoRkmscQTIOyfv+KOqwaP+HyXZ9DGni4mJ45yrEzLY/G2bmdN778mmfpnkes86555zzj1HkhOsQB5OqqIcVMU7qgo7ytlwtjARcZxzFQJpftt7/0+T3RuigiXrW8n+VwlzgQkuJvsy8N5/xnv/jPf+mXR68AJHw3FX7OO89oOFSbxqDvgN4Dve+19Vf30J+Eiy/RHg99T+n0q8a+8F7iiVrsChcdxS5yBEOCxpjkOlm05M0o/zI8BPAt9wzj2f7PsF4JeB33HOfRR4hTDdB8CXgfcDVwk62M8cZ4EfbIzryJ2kb+ewuF92Q+wZxj339JFr7Ffw3v8h+Skv3xc53gMfO2K5CuRiHHkmiaKYvop4f2IOjw8H8qoVmBbcT6/SUUl20A7a477/yaAgzpnFWTfkJ3F3T6+NVBDnTOP0+zMmw2Eq91EiKU4eBXHOPKajIo3HYcKCphcFcR5YnAV1Dc4qeVxwgp1yIZx7HbhLyEL8oGCFB+d5HqRngcmf563e+0dif0wFcQCcc8+FKIIHAw/S8zxIzwLH8zyFqlagwCFQEKdAgUNgmojzmdMuwDHjQXqeB+lZ4BieZ2psnAIFzhKmSeIUKHBmcOrEcc79mHPuhSS5xyfGnzF9cM5dc859wzn3fBiYl5/MZBrhnPusc+6Wc+6bat8UJmOZDDnP80vOuVeTb/S8c+796r9PJs/zgnPuP53oJt77U1uAEvAS8BgwA/wp8I7TLNMhn+MasGL2/UPgE8n2J4B/cNrlHFH+HwV+CPjmuPIThoz834SI+fcCf3Ta5Z/weX4J+O8ix74jqXezwJWkPpbG3eO0Jc57gKve++967/eALxCSfTwIyEtmMnXw3v8r4LbZfWaTseQ8Tx4+AHzBe7/rvX+ZMI7sPeNOOm3iPCiJPTzwB865rzvnnk325SUzOSt4EJOxfDxRLz+rVOdDPc9pE+dBwV/23v8QIafcx5xzP6r/9EEnOLPuy7Ne/gSfBh4HngZeAz51lIudNnEmSuwx7fDev5qsbwG/SxD1eclMzgqOlIxl2uC93/De973394BfJ1XHDvU8p02cPwaedM5dcc7NAB8iJPs4M3DOzTvnFmQb+OvAN8lPZnJW8EAlYzF22E8QvhGE5/mQc27WOXeFkIH234694BR4QN4P/AXBm/GLp12eQ5T/MYJX5k+Bb8kzAE1CauAXgX8BLJ92WUc8w+cJ6ss+Qcf/aF75Cd60/yP5Xt8Anjnt8k/4PP9nUt4/S8jyqDr+F5PneQH48UnuUUQOFChwCJy2qlagwJlEQZwCBQ6BgjgFChwCBXEKFDgECuIUKHAIFMQpUOAQKIhToMAhUBCnQIFD4P8HepE98Oq0Lj4AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from scipy.ndimage import gaussian_filter\n", + "SIGMA = (210 / 44.6, 160 / 28.5)\n", + "\n", + "\n", + "saliency_map = np.zeros(init_screen.shape[:2])\n", + "\n", + "episode = '284_RZ_5540489_E00'\n", + "gaze = df.loc[df.ID == episode].loc[df.room_id == 1].loc[df.level==0].gaze_positions\n", + "\n", + "flat_list = []\n", + "for gaze_points in gaze:\n", + " if gaze_points is not None: \n", + " for item in gaze_points:\n", + " flat_list.append(item)\n", + "\n", + "print(np.array(flat_list).shape)\n", + "\n", + "\n", + "# Add gaze coordinates to saliency map\n", + "for cords in flat_list:\n", + " try: \n", + " saliency_map[int(cords[1])][int(cords[0])] += 1\n", + " except:\n", + " # Not all gaze points are on image \n", + " continue\n", + " \n", + "# Construct empirical saliency map\n", + "saliency_map = gaussian_filter(saliency_map, sigma=SIGMA, mode='nearest')\n", + "\n", + "plt.imshow(saliency_map, cmap='jet')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c6295363", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saved dataframe to /datasets/public/anna/montezuma_revenge/all_trials_labeled.pkl\n" + ] + } + ], + "source": [ + "path = os.path.join(DATA_PATH, \"all_trials_labeled.pkl\")\n", + "df.to_pickle(path) \n", + "print(f'Saved dataframe to {path}')" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "11aebc9c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "284_RZ_5540489_E00 [8 6 1 0 2 9 None]\n", + "285_RZ_5619207_E00 [8 6 1 0 2 7 9 None]\n", + "285_RZ_5619207_E01 [8 6 1 0 2 9 None]\n", + "291_RZ_7364933_E00 [8 6 1 0 2 7 9 None]\n", + "333_RZ_900705_E00 [8 6 1 0 2 9 None]\n", + "340_RZ_1323550_E00 [8 6 1 0 2 7 9 None]\n", + "359_RZ_1993616_E00 [8 6 1 0 2 7 9 None]\n", + "365_RZ_2079996_E00 [8 6 1 0 2 7 9 None]\n", + "371_RZ_2173469_E00 [8 6 1 0 2 7 9 None]\n", + "385_RZ_2344725_E00 [8 6 1 0 2 7 9 None]\n", + "398_RZ_2530473_E00 [8 6 1 0 2 9 None]\n", + "402_RZ_2603283_E00 [8 6 1 0 2 9 None]\n", + "416_RZ_2788252_E00 [8 6 1 0 2 7 9 None]\n", + "429_RZ_2945490_E00 [8 6 1 0 2 7 9 None]\n", + "436_RZ_3131841_E00 [8 6 1 0 2 7 9 None]\n", + "459_RZ_3291266_E00 [8 6 1 0 2 7 9 None]\n", + "469_RZ_3390904_E00 [8 6 1 0 2 7 9 None]\n", + "480_RZ_3470098_E00 [8 6 1 0 2 7 9 None]\n", + "493_RZ_3557734_E00 [8 6 1 0 2 7 9 None]\n" + ] + } + ], + "source": [ + "all_orders = []\n", + "for episode in df.ID.unique(): \n", + " subgoal_order = df.loc[df.ID == episode].loc[df.room_id == 1].loc[df.level==0].current_subgoal.unique()\n", + " if len(subgoal_order) > 0: \n", + " all_orders.append(subgoal_order)\n", + " print(episode, subgoal_order)" + ] + }, + { + "cell_type": "markdown", + "id": "34c2efb3", + "metadata": {}, + "source": [ + "## Get majority vote of subgoal order" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "6c8f80d7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[8, 6, 1, 0, 2, 7, 9, None]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from collections import Counter\n", + "\n", + "max_len = max([len(order) for order in all_orders])\n", + "majority_order = []\n", + "\n", + "for i in range(max_len):\n", + " votes = []\n", + " for order in all_orders:\n", + " \n", + " if i < len(order):\n", + " votes.append(order[i])\n", + " \n", + " vote_count = Counter(votes)\n", + " majority_order.append(vote_count.most_common(1)[0][0])\n", + "\n", + "majority_order" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "5649bb15", + "metadata": {}, + "outputs": [], + "source": [ + "subgoal_order = majority_order" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21af5234", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "msc_env", + "language": "python", + "name": "msc_env" + }, + "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.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/dataset_utils.py b/dataset_utils.py new file mode 100644 index 0000000..64801a3 --- /dev/null +++ b/dataset_utils.py @@ -0,0 +1,432 @@ +import random + +import cv2 +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +from scipy.ndimage import gaussian_filter +from tqdm import tqdm +from scipy import interpolate +from sklearn.preprocessing import normalize + + +# Atari-HEAD constants +SIGMA = (210 / 44.6, 160 / 28.5) +SUBJECT_TO_SCREEN = 787 + +SCREEN_WIDTH_MM = 646 +SCREEN_HEIGHT_MM = 400 + +SCREEN_WIDTH_PX = 1280 +SCREEN_HEIGHT_PX = 840 + + +TYPES = {'frame_id': str, 'episode_id': int, 'score': int, 'duration(ms)': int, + 'unclipped_reward': int, 'action': int, 'gaze_positions': list} + +ALE_ENUMS = {0: 'PLAYER_A_NOOP', 1: 'PLAYER_A_FIRE', 2: 'PLAYER_A_UP', 3: 'PLAYER_A_RIGHT', 4: 'PLAYER_A_LEFT', 5: 'PLAYER_A_DOWN', + 6: 'PLAYER_A_UPRIGHT', 7: 'PLAYER_A_UPLEFT', 8: 'PLAYER_A_DOWNRIGHT', 9: 'PLAYER_A_DOWNLEFT', + 10: 'PLAYER_A_UPFIRE', 11: 'PLAYER_A_RIGHTFIRE', 12: 'PLAYER_A_LEFTFIRE', 13: 'PLAYER_A_DOWNFIRE', + 14: 'PLAYER_A_UPRIGHTFIRE', 15: 'PLAYER_A_UPLEFTFIRE', 16: 'PLAYER_A_DOWNRIGHTFIRE', 17: 'PLAYER_A_DOWNLEFTFIRE'} + + +def txt_to_dataframe(path: str) -> pd.DataFrame: + """Read txt file with annotations for trial line by line and add to new dataframe. + + Parameters + ---------- + path : str + The path to the trial's txt file e.g. 291_RZ_7364933_May-08-20-23-25.txt + + Returns + ------- + pd.DataFrame + Dataframe with one frame per row and columns TYPES if available. + """ + + file = open(path, 'r') + Lines = file.readlines() + + columns = Lines[0].strip().split(',') + + trial_df = pd.DataFrame(columns=columns) + + for line in Lines[1:]: + raw_vals = line.strip().split(',') + vals = dict() + for i, c in enumerate(columns): + if not c == 'gaze_positions': + try: + vals[c] = [TYPES.get(c)((raw_vals[i]))] + except: + vals[c] = None + #print('WARNING', c, raw_vals[i]) + else: + # gaze_positions: x0,y0,x1,y1,...,xn,yn. Gaze positions for the current frame. + # Could be null if no gaze. (0,0) is the top-left corner. x: horizontal axis. y: vertical. + try: + gaze_positions = np.array([float(v) for v in raw_vals[i:]]).reshape(-1, 2) + except Exception as e: + gaze_positions = None + #print(f'WARNING: no gaze data available for frame_id: {vals["frame_id"]} because {e}', raw_vals[i]) + new_df = pd.DataFrame(vals) + new_df['gaze_positions'] = [gaze_positions] + + trial_df = pd.concat([trial_df, new_df], ignore_index=True) + + return trial_df + + +def get_subgoal_proposals(df, threshold=0.35, visualize=False, room=1) -> dict(): + + # Get init screen for visualizations + init_screen = cv2.imread(df.iloc[0].img_path) + init_screen = cv2.cvtColor(init_screen, cv2.COLOR_BGR2RGB) + + subgoal_proposals = {} + + for episode in df.ID.unique(): + + gaze = df.loc[df.ID == episode].loc[df.room_id == room].loc[df.level==0].gaze_positions + + if gaze is None: + continue + + # Generate saliency map + saliency_map = np.zeros(init_screen.shape[:2]) + for gaze_points in gaze: + if gaze_points is not None: + for item in gaze_points: + try: + saliency_map[int(item[1])][int(item[0])] += 1 + except: + # Not all gaze points are on image + continue + + # Construct fixation map + fix_map = saliency_map >= 1.0 + + # Construct empirical saliency map + saliency_map = gaussian_filter(saliency_map, sigma=SIGMA, mode='nearest') + + # Normalize saliency map into range [0, 1] + if not saliency_map.max() == 0: + saliency_map /= saliency_map.max() + + proposals_y, proposals_x = np.where(saliency_map > threshold) + + bboxes = [] + scores = [] + for x, y in zip(proposals_x, proposals_y): + # draw bounding box around saliency map peak in panama joe size + box = [x - 5, y - 10, x + 5, y + 10] + bboxes.append(box) + scores.append(saliency_map[y][x]) + + if len(bboxes) == 0: + continue + + # Non-max suppression + keep = apply_nms(np.array(bboxes), np.array(scores), thresh_iou=0.1) + + # Merge boxes with any iou > 0 + # Note: run might generate new ious > 0 + merged = merge_boxes(keep) + + subgoal_proposals[episode] = [keep, merged] + + if visualize: + print('Episode: ', episode) + mask = saliency_map > threshold + masked_saliency = saliency_map.copy() + masked_saliency[~mask] = 0 + + img = masked_saliency.copy() + for box in random.choices(bboxes, k=25): + img = cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (1,0,0), 1) + + print('Number of bounding box proposals: ', len(bboxes)) + fig = plt.figure(figsize=(8,8)) + plt.imshow(init_screen) + plt.imshow(img, cmap='jet', alpha=0.5) + plt.axis('off') + plt.show() + + print('Bounding boxes after non-maximum suppression') + img = init_screen.copy() + for box in keep: + img = cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (255,0,0), 1) + + fig = plt.figure(figsize=(8,8)) + plt.imshow(img) + plt.axis('off') + plt.show() + + print('Bounding boxes after merging') + img = init_screen.copy() + for box in keep: + img = cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (255,0,0), 1) + + fig = plt.figure(figsize=(8,8)) + plt.imshow(img) + plt.axis('off') + plt.show() + + return subgoal_proposals + + +def visualize_sample(image, target): + + fig = plt.figure(figsize=(12,6)) + + ax1 = fig.add_subplot(131) + ax2 = fig.add_subplot(132) + ax3 = fig.add_subplot(133) + + gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + fov_image = np.multiply(target, gray) # element-wise product + + ax1.imshow(image) + ax1.set_title('Input image') + ax1.axis('off') + + ax2.imshow(target, cmap='jet') + ax2.set_title('Saliency map') + ax2.axis('off') + + ax3.imshow(fov_image, cmap='gray') + ax3.set_title('Foveated image') + ax3.axis('off') + + plt.show() + + +def saliency_map_to_image(saliency_map): + minimum_value = saliency_map.min() + if minimum_value < 0: + saliency_map = saliency_map - minimum_value + + saliency_map = saliency_map * 255 / saliency_map.max() + + image_data = np.round(saliency_map).astype(np.uint8) + + return image_data + + +def apply_nms(boxes: np.ndarray, scores: np.ndarray = None, thresh_iou: float = 0.2) -> np.ndarray: + """ + adapted from https://learnopencv.com/non-maximum-suppression-theory-and-implementation-in-pytorch/ + Apply non-maximum suppression to avoid detecting too many + overlapping bounding boxes based on iou threshold. + """ + + x1 = boxes[:, 0] # x coordinate of the top-left corner + y1 = boxes[:, 1] # y coordinate of the top-left corner + x2 = boxes[:, 2] # x coordinate of the bottom-right corner + y2 = boxes[:, 3] # y coordinate of the bottom-right corner + + # calculate area of every block in boxes + areas = (x2 - x1) * (y2 - y1) + + if scores is not None: + # sort the prediction boxes according to their confidence scores + order = scores.argsort() + else: + order = y2.argsort() + + # initialise an empty list for filtered prediction boxes + keep = [] + + while len(order) > 0: + + # extract the index of the prediction with highest score and add to keep list + idx = order[-1] + keep.append(boxes[idx]) + order = order[:-1] + + # sanity check + if len(order) == 0: + break + + # select coordinates of boxes according to the indices in order + xx1 = np.take(x1, indices=order, axis=0) + xx2 = np.take(x2, indices=order, axis=0) + yy1 = np.take(y1, indices=order, axis=0) + yy2 = np.take(y2, indices=order, axis=0) + + # find the coordinates of the intersection boxes + xx1 = np.maximum(xx1, x1[idx]) + yy1 = np.maximum(yy1, y1[idx]) + xx2 = np.minimum(xx2, x2[idx]) + yy2 = np.minimum(yy2, y2[idx]) + + # find out the width and the height of the intersection box + w = np.maximum(0, xx2 - xx1) + h = np.maximum(0, yy2 - yy1) + + # find the intersection area + inter = w*h + + # find the areas of boxes according to indices in order + rem_areas = np.take(areas, indices=order, axis=0) + + # find the union of every box with currently selected box + union = (rem_areas - inter) + areas[idx] + + # find the IoU of every box with currently selected box + IoU = inter / union + + # keep the boxes with IoU less than thresh_iou + mask = IoU < thresh_iou + order = order[mask] + + return np.array(keep) + + +def merge_boxes(boxes: np.ndarray) -> np.ndarray: + x1 = boxes[:, 0] # x coordinate of the top-left corner + y1 = boxes[:, 1] # y coordinate of the top-left corner + x2 = boxes[:, 2] # x coordinate of the bottom-right corner + y2 = boxes[:, 3] # y coordinate of the bottom-right corner + + # calculate area of every block in boxes + areas = (x2 - x1) * (y2 - y1) + + merged = [] + indices = np.arange(len(boxes)) + + while len(indices) > 0: + idx = indices[0] + # find the coordinates of the intersection boxes + xx1 = np.maximum(x1, x1[idx]) + yy1 = np.maximum(y1, y1[idx]) + xx2 = np.minimum(x2, x2[idx]) + yy2 = np.minimum(y2, y2[idx]) + + # find out the width and the height of the intersection box + w = np.maximum(0, xx2 - xx1) + h = np.maximum(0, yy2 - yy1) + + # find the intersection over union of every box with currently selected box + inter = w * h + union = (areas - inter) + areas[idx] + iou = inter / union + + merge_idx = np.where(iou > 0.0)[0] + + # box surrounding all selected boxes --> [min(x1), min(y1)] x [max(x2), max(y2)] + big_box = [boxes[merge_idx, 0].min(), boxes[merge_idx, 1].min(), + boxes[merge_idx, 2].max(), boxes[merge_idx, 3].max()] + + merged.append(big_box) + delete_idx = [np.where(indices == i)[0] for i in merge_idx if len(np.where(indices == i)[0]) > 0] + indices = np.delete(indices, delete_idx) + + return np.array(merged) + + +def pixel_to_3D(gaze_positions): + if gaze_positions.shape[0] != 2: + gaze_positions = np.moveaxis(gaze_positions, 0, 1) + + x, y = gaze_positions + + x *= SCREEN_WIDTH_MM / SCREEN_WIDTH_PX + y *= SCREEN_HEIGHT_MM / SCREEN_HEIGHT_PX + + gaze_positions_3D = np.array([x, y, [SUBJECT_TO_SCREEN] * len(x)]) + + return np.moveaxis(gaze_positions_3D, 0, 1) + +def get_velocity_vectorized(gaze: np.ndarray, ratio: float): + + # FIXED: https://stackoverflow.com/questions/52457989/pandas-df-apply-unexpectedly-changes-dataframe-inplace + gaze = gaze.copy() + + # pixel coordinates to 3D world coordinates + gaze_3D = pixel_to_3D(gaze) + + # vectorize gaze[i], gaze[i+1] by shifting vector by 1 + u, v = gaze_3D[:-1], gaze_3D[1:] + assert len(u) == len(v) + + """ + # normalize + try: + u, v = normalize(u), normalize(v) + except Exception as e: + print(e) + """ + # Normalize each vector u and v --> ||u[i]|| = ||v[i]|| = 1 + norm_mat_u = np.stack([np.linalg.norm(u, axis=1), np.linalg.norm(u, axis=1), np.linalg.norm(u, axis=1)], axis=1) + norm_mat_v = np.stack([np.linalg.norm(v, axis=1), np.linalg.norm(v, axis=1), np.linalg.norm(v, axis=1)], axis=1) + + u /= norm_mat_u + v /= norm_mat_v + + u_minus_v = np.linalg.norm(u - v, axis=1) # || u - v || + u_plus_v = np.linalg.norm(u + v, axis=1) # || u + v || + + # angular displacement + theta = 2 * np.arctan2(u_minus_v, u_plus_v) * 5.73 # converts the unit from radians to degrees + + # velocity with average fps + velocity = (theta / ratio) * 10000 # converts the unit from microsecond to degrees per second + + return theta, velocity + +def interpolate_outliers(gaze, ratio, threshold=800, visualize=False): + + idx = np.where(gaze > threshold)[0][0] + + x = list(np.arange(len(gaze))) + x.pop(idx) + + gaze = list(gaze) + gaze.pop(idx) + + # outliers on border can't be interpolated -> remove entirely + if idx == 0 or idx == len(gaze): + return gaze + + else: + f = interpolate.interp1d(x, gaze) + + if visualize: + xnew = np.arange(0, ratio * (len(gaze) - 1) + 0.1, 0.1) + ynew = f(xnew) + + plt.plot(x, gaze, 'o', xnew, ynew, '-', idx, f(idx), '*') + plt.show() + + return np.array(gaze[:idx] + [float(f(idx))] + gaze[idx:]) + +def get_angle(center, point): + pf = [center[0], center[1], SUBJECT_TO_SCREEN] + cf = [point[0], point[1], SUBJECT_TO_SCREEN] + + v = np.dot(pf, cf) / np.dot(np.linalg.norm(pf), np.linalg.norm(cf)) + angle = np.arccos(np.clip(v, a_min=-1, a_max=1)) + + return angle * 5.73 * 1000 + +def get_idt_dispersion(cfg): + # Get dispersion of current fixation group to determine smooth pursuits + # see https://github.com/M3stark/Eye_tracking_proj/blob/main/ivdt.py + max_x, min_x = max(cfg[:, 0]), min(cfg[:, 0]) + max_y, min_y = max(cfg[:, 1]), min(cfg[:, 1]) + + return (max_x - min_x) + (max_y - min_y) + + +def agent_in_subgoal(subgoals, agent_x, agent_y): + + test_min_x = subgoals[:, 0] < agent_x + test_max_x = subgoals[:, 2] > agent_x + + test_min_y = subgoals[:, 1] < agent_y + test_max_y = subgoals[:, 3] > agent_y + + return np.any(test_min_x & test_max_x & test_min_y & test_max_y), np.where(test_min_x & test_max_x & test_min_y & test_max_y)[0] + +