{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How do I wrap my own recordings?\n",
    "\n",
    "If you have your own recordings on disk and want to make use of Tonic for quick dataloading and applying transformations, then you can wrap them in a custom class.\n",
    "The easiest option is to make use of a torchvision [DatasetFolder](https://pytorch.org/vision/main/datasets.html#torchvision.datasets.DatasetFolder) class. If that doesn't apply in your case, you can write your own class, where you provide a minimum set of methods ``__init__``, ``__getitem__`` and ``__len__`` and you are good to go. This notebook is about a template class that reads event recordings from local numpy files. We'll start by creating some dummy files."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from tonic import Dataset, transforms\n",
    "\n",
    "sensor_size = (200, 100, 2)\n",
    "n_recordings = 10\n",
    "\n",
    "\n",
    "def create_random_input(\n",
    "    sensor_size=sensor_size,\n",
    "    n_events=10000,\n",
    "    dtype=np.dtype([(\"x\", int), (\"y\", int), (\"t\", int), (\"p\", int)]),\n",
    "):\n",
    "    events = np.zeros(n_events, dtype=dtype)\n",
    "    events[\"x\"] = np.random.rand(n_events) * sensor_size[0]\n",
    "    events[\"y\"] = np.random.rand(n_events) * sensor_size[1]\n",
    "    events[\"p\"] = np.random.rand(n_events) * sensor_size[2]\n",
    "    events[\"t\"] = np.sort(np.random.rand(n_events) * 1e6)\n",
    "    return events\n",
    "\n",
    "\n",
    "[\n",
    "    np.save(f\"../tutorials/data/recording{i}.npy\", create_random_input())\n",
    "    for i in range(n_recordings)\n",
    "];"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyRecordings(Dataset):\n",
    "    sensor_size = (\n",
    "        200,\n",
    "        100,\n",
    "        2,\n",
    "    )  # the sensor size of the event camera or the number of channels of the silicon cochlear that was used\n",
    "    ordering = (\n",
    "        \"xytp\"  # the order in which your event channels are provided in your recordings\n",
    "    )\n",
    "\n",
    "    def __init__(\n",
    "        self,\n",
    "        train=True,\n",
    "        transform=None,\n",
    "        target_transform=None,\n",
    "    ):\n",
    "        super(MyRecordings, self).__init__(\n",
    "            save_to='./', transform=transform, target_transform=target_transform\n",
    "        )\n",
    "        self.train = train\n",
    "\n",
    "        # replace the strings with your training/testing file locations or pass as an argument\n",
    "        if train:\n",
    "            self.filenames = [\n",
    "                f\"../tutorials/data/recording{i}.npy\" for i in range(n_recordings)\n",
    "            ]\n",
    "        else:\n",
    "            raise NotImplementedError\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        events = np.load(self.filenames[index])\n",
    "\n",
    "        if self.transform is not None:\n",
    "            events = self.transform(events)\n",
    "\n",
    "        return events\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.filenames)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Depending on the format of your recording files, your implementation might look a bit different. Oftentimes you will have a separate file for each recording. Or you might want to also load some image or IMU data. You can have a look at already existing datasets for some inspiration. :class:`DVSGesture` loads from multiple numpy files, :class:`DAVISDATA` or :class:`VPR` load events and other data from rosbag files, :class:`NCARS` loads eventstream files and :class:`ASLDVS` reads from matlab files.\n",
    "\n",
    "Afterwards you can call certain samples from the dataset or use a DataLoader wrapper, which will make use of ``__getitem__`` and ``__len__`` functions internally."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset = MyRecordings(train=True, transform=transforms.NumpyAsType(int))\n",
    "events = dataset[5]\n",
    "\n",
    "import torch\n",
    "\n",
    "dataloader = torch.utils.data.DataLoader(dataset, shuffle=True)\n",
    "events = next(iter(dataloader))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(events)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.8 ('base')",
   "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.8.8"
  },
  "vscode": {
   "interpreter": {
    "hash": "caf264bf03997fa53b380c84044763293a7a6f8ebb5555ee5243fd4d1f495be6"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
