{ "cells": [ { "cell_type": "markdown", "id": "44aa4d4b", "metadata": {}, "source": [ "# Transforms & representations\n", "\n", "EventCV transforms return a **new** stream, so they chain like a pipeline. Every transform and representation is available both as a method (`stream.flip_x()`) and as an OpenCV-style free function (`ecv.flip_x(stream)`)." ] }, { "cell_type": "code", "execution_count": 1, "id": "4b42e071", "metadata": { "execution": { "iopub.execute_input": "2026-07-03T23:59:46.501645Z", "iopub.status.busy": "2026-07-03T23:59:46.501557Z", "iopub.status.idle": "2026-07-03T23:59:46.572590Z", "shell.execute_reply": "2026-07-03T23:59:46.570426Z" } }, "outputs": [ { "data": { "text/plain": [ "(106295, (640, 480))" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import eventcv as ecv\n", "import numpy as np\n", "from pathlib import Path\n", "\n", "# Point PATH at your own recording (.npz / .hdf5 / .bag / .txt / .aedat / .dat).\n", "# Here we locate the small test fixture bundled with the EventCV source.\n", "def _fixture(rel=\"data/test/example.npz\"):\n", " for base in (Path.cwd(), *Path.cwd().parents):\n", " if (base / rel).exists():\n", " return str(base / rel)\n", " raise FileNotFoundError(rel)\n", "\n", "PATH = _fixture()\n", "\n", "stream = ecv.load(PATH)\n", "len(stream), stream.sensor_size" ] }, { "cell_type": "markdown", "id": "8fbdfe8a", "metadata": {}, "source": [ "## Chaining transforms\n", "\n", "Geometry, temporal, and polarity ops compose; each returns a new stream." ] }, { "cell_type": "code", "execution_count": 2, "id": "9f1f904e", "metadata": { "execution": { "iopub.execute_input": "2026-07-03T23:59:46.587100Z", "iopub.status.busy": "2026-07-03T23:59:46.586792Z", "iopub.status.idle": "2026-07-03T23:59:46.593754Z", "shell.execute_reply": "2026-07-03T23:59:46.591861Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "events: 106295 -> 19859\n", "sensor size: (320, 240)\n" ] } ], "source": [ "pipe = (stream\n", " .crop(0, 0, 320, 240) # x, y, width, height\n", " .hot_pixel_filter() # drop noisy always-on pixels\n", " .flip_x())\n", "print(\"events:\", len(stream), \"->\", len(pipe))\n", "print(\"sensor size:\", pipe.sensor_size)" ] }, { "cell_type": "markdown", "id": "cc2b3040", "metadata": {}, "source": [ "## Two spellings, one operation\n", "\n", "The functional form mirrors the methods exactly, so pick whichever reads better." ] }, { "cell_type": "code", "execution_count": 3, "id": "3b82109a", "metadata": { "execution": { "iopub.execute_input": "2026-07-03T23:59:46.600590Z", "iopub.status.busy": "2026-07-03T23:59:46.600207Z", "iopub.status.idle": "2026-07-03T23:59:46.647965Z", "shell.execute_reply": "2026-07-03T23:59:46.638032Z" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = stream.flip_x().voxel(bins=5).numpy()\n", "b = ecv.voxel(ecv.flip_x(stream), bins=5).numpy()\n", "np.array_equal(a, b)" ] }, { "cell_type": "markdown", "id": "93a8ec81", "metadata": {}, "source": [ "## Representations\n", "\n", "Render the pipeline output as dense tensors ready for NumPy / PyTorch." ] }, { "cell_type": "code", "execution_count": 4, "id": "c89de08a", "metadata": { "execution": { "iopub.execute_input": "2026-07-03T23:59:46.654571Z", "iopub.status.busy": "2026-07-03T23:59:46.654262Z", "iopub.status.idle": "2026-07-03T23:59:46.662854Z", "shell.execute_reply": "2026-07-03T23:59:46.661435Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "count (1, 240, 320) uint64\n", "voxel(bins=5) (5, 240, 320) float32\n", "time surface (2, 240, 320) float32\n" ] } ], "source": [ "for name, frame in [(\"count\", pipe.count()),\n", " (\"voxel(bins=5)\", pipe.voxel(bins=5)),\n", " (\"time surface\", pipe.tsurf())]:\n", " arr = frame.numpy()\n", " print(f\"{name:16} {str(arr.shape):18} {arr.dtype}\")" ] }, { "cell_type": "markdown", "id": "a2df7c5f", "metadata": {}, "source": [ "## As a PyTorch dataset\n", "\n", "`open(..., repr=…)` gives the reader a **default representation**. Every slice remembers it, so `reader.slice(n).view()` renders that representation instead of raw polarity (pass a name to `view(...)` to override). It also makes the reader a map-style dataset: `len(ds)` frames, `ds[i]` a `[C, H, W]` array, and `ds.batch(idx)` a `[B, C, H, W]` stack — no PyTorch needed to produce the arrays." ] }, { "cell_type": "code", "execution_count": 5, "id": "22b800f8", "metadata": { "execution": { "iopub.execute_input": "2026-07-03T23:59:46.667116Z", "iopub.status.busy": "2026-07-03T23:59:46.666774Z", "iopub.status.idle": "2026-07-03T23:59:46.687245Z", "shell.execute_reply": "2026-07-03T23:59:46.685508Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "frames: 3\n", "ds[0]: (1, 480, 640) uint64\n", "batch([0, 1]): (2, 1, 480, 640)\n" ] } ], "source": [ "ds = ecv.open(PATH, dt_ms=20, repr=\"count\")\n", "print(\"frames:\", len(ds))\n", "print(\"ds[0]:\", ds[0].shape, ds[0].dtype)\n", "print(\"batch([0, 1]):\", ds.batch([0, 1]).shape)" ] }, { "cell_type": "markdown", "id": "2c340f2a", "metadata": {}, "source": [ "With PyTorch installed, hand the reader straight to a `DataLoader`. With a `repr` set, slices are dense arrays that collate into a `[B, C, H, W]` tensor automatically:\n", "\n", "```python\n", "import torch\n", "\n", "ds = ecv.open(\"recording.hdf5\", dt_ms=30, repr=\"count\")\n", "loader = torch.utils.data.DataLoader(ds, batch_size=32, shuffle=True)\n", "for batch in loader: # batch: [B, C, H, W]\n", " ...\n", "```\n", "\n", "Without a `repr`, `reader[i]` is a raw `EventStream`. Those are sparse and variable-length, so they can't stack into a tensor — pass `eventcv.collate` to batch them as a `list` instead:\n", "\n", "```python\n", "reader = ecv.open(\"recording.hdf5\", dt_ms=30)\n", "loader = torch.utils.data.DataLoader(reader, batch_size=32, collate_fn=ecv.collate)\n", "for batch in loader: # batch: list[EventStream]\n", " batch[0].view()\n", "```" ] } ], "metadata": { "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.11.15" } }, "nbformat": 4, "nbformat_minor": 5 }