EV-Utils (evutils) is a performant collection of utilities for working with event-based vision data. Built with minimal dependencies, it relies on a compiled C backend for speed while offering a clean, modular Python interface.
- Fast & Lightweight: Highly optimized C parsers for zero-bottleneck data ingestion.
- Minimal Footprint: Core features run entirely on NumPy and Numba.
- Lazy Loading: All heavy integrations (PyTorch, HDF5, etc.) are lazy-loaded. If you don't use them, you don't need them installed, and they won't slow down import times.
- Simple & Extensible: Clean modular APIs.
This project draws inspiration from several excellent libraries in the event-based vision ecosystem and attempts to fill in their shortcomings:
We recommend installing evutils using uv.
uv add evutils # Basic library
uv add evutils[all] # All groups (torch, hdf5, aedat, vis, etc..)
uv add evutils[dev] # Dev groupgit clone --recurse-submodules https://github.com/mandulaj/evutils.git
cd evutils
uv pip install -e ".[dev]"Note: You can also install specific optional dependency groups like uv add evutils[torch,hdf5].
The library is divided into several discrete modules. Many can be used independently without installing the full suite of dependencies:
└── chunking - Splitting event streams into fixed-size windows
└── dataset - Wrappers for various dataset loaders (planned, stub)
└── dense - Dense representations (voxel grids, time surfaces, histograms)
└── filtering - Event stream filtering (masking)
└── io - Event reading and writing interfaces
├── reader
└── writer
└── random - Random event generation and noise injection
└── torch - PyTorch integration (planned, stub; requires evutils[torch])
└── transforms - torchvision-style augmentation transforms (+ functional)
└── types - Standard types for representing Events in NumPy arrays
└── vis - Visualization methods
├── plot3d
└── reconstructor
A future sparse module will sit alongside dense once sparse representations
(event graphs, sparse tensors, point clouds) land. EventsChecker (event-array
validation) lives in types.
The io module provides methods for reading and writing events into various event formats. It provides a simple .read() and .write() interface as well as more advanced interfaces using iterators and slicing.
Supported formats (see the formats documentation for details):
| Format | Extensions | Read | Write | Notes |
|---|---|---|---|---|
| EVT3 / EVT2.1 / EVT2 (Prophesee RAW) |
.raw, .evt*
|
✅ | ✅ | native C decoder, external triggers |
| EVT4 (evutils variant) |
.raw, .evt4
|
✅ | ✅ | EVT2-style layout + vectorized CD; evutils' own % evt 4.0 header convention |
| DAT (Prophesee) | .dat |
✅ | ✅ | native C decoder |
| AER (Prophesee) | .aer |
✅ | ✅ | timestamp generation selectable |
| AEDAT 1.0 / 2.0 / 3.1 / 4.0 |
.aedat, .aedat4
|
✅ | ✅ (4.0) | AEDAT4 write incl. triggers; 1.0/2.0/3.1 write 🚧; compression: evutils[aedat]
|
| HDF5 (DSEC/RVT layout) |
.h5, .hdf5
|
✅ | ✅ |
evutils[hdf5], ms-index random access |
| HDF5 (Prophesee layout) |
.h5, .hdf5
|
✅ | 🚧 | ECF-compressed files need the ECF plugin |
| NPZ | .npz |
✅ | ✅ | streaming, np.load-compatible |
| CSV / TXT |
.csv, .txt
|
✅ | ✅ | native C parser |
| BIN | .bin |
🚧 | 🚧 | planned |
from evutils.io import EventReader
ev_file = EventReader("raw_file.raw", delta_t=10_000)
events = ev_file.read()It also supports random access — jump to an absolute timestamp or event index (forward or backward) and keep reading in the configured window mode:
with EventReader("raw_file.raw", delta_t=10_000) as r:
r.seek(t=2_000_000) # skip to t = 2.0 s
window = r.read() # first delta_t window from there
r.seek(n=1_000_000) # or jump to the 1,000,000th eventSeeking uses an index or exact record math, and falls back to iterate-and-skip
on non-seekable streams. For EVT the index is built in memory on the first seek
(exact) by default; pass EventReader(..., index="metavision") to instead read
a Metavision .tmp_index sidecar (fast, but approximate near large event gaps).
Dense representations — turn a sparse event stream into fixed-size per-pixel
tensors: histograms, voxel grids, time surfaces, accumulation frames and TORE.
(A future sparse module will hold event graphs, sparse tensors and point
clouds.)
Selecting and dropping events: spatial masking today, with denoising, ROI and downsampling to follow.
torchvision/tonic-style augmentation transforms (drops, spatial flips, jitter,
time skew/normalize, refractory filtering). Each composable Transform class
pairs with a pure functional kernel, and Compose chains them with minimal
unwrap/repack overhead.
Generating random events and adding noise to event recordings
The library is built around the EventArray type — a wrapper giving events a
struct-of-arrays (SoA) representation, which nearly every reader, writer and
transform exchanges:
- Fields (
Event_dtype):tint64(signed 64-bit µs),x/yuint16(up to 65,535 × 65,535 px),puint8. - SoA — four contiguous columns; cache-friendly, vectorizes over whole columns, no record padding: 8+2+2+1 = 13 bytes/event (≈ 13 MB/MEv). Best for the column-wise processing that dominates event workloads.
-
Array-of-structs equally supported (
from_aos/to_aos/np.asarray, cheap); C-aligned record is 16 bytes/event. Best for per-record iteration or an opaque buffer for another library/serializer. Transforms dispatch on input type, so you get back whichever form you passed in. - Slicing, field subsetting, and an optional lightweight
metadatadict (e.g.sensor_size) round out the type.
The vis modules provides several methods for visualizing the events (for example as histograms), but also provides a streamlined interface for more complex visualization techniques, such as using the E2Vid reconstructor.
from evutils.vis.reconstructor import RPG_Reconstructor
reconstructor = RPG_Reconstructor(1280, 720) # (width, height)
img = reconstructor.gen_frame(events)Tests are managed via pytest. If you installed the package with the [dev] or [test] flag, you can run the standard test suite via:
uv run pytest -sThe library uses doctest to ensure all Python >>> examples inside docstrings are correct and functional. Because the default configuration only scans the tests/ directory, you must explicitly tell pytest to scan the source code and ignore legacy submodules (like rpg_e2vid which contains Python 2 syntax):
uv run pytest --doctest-modules src/evutils --ignore=src/evutils/vis/reconstructor/rpg_e2vid/In-RAM read/write throughput benchmarks live in benchmarks/throughput.py and are kept out of the normal test run. They report M events/s as two matrices (format × library). Run explicitly:
uv run python benchmarks/throughput.py # evutils + installed peers
uv run python benchmarks/throughput.py --dataset small --events 2_000_000 # quick smokeThe benchmark downloads a real Prophesee recording on first use, decodes a capped in-RAM payload, and measures every format on a RAM disk (/dev/shm). Optional cross-library comparisons (expelliarmus, evlib, evt3) light up automatically once installed (uv pip install -e ".[compare]"); OpenEB/Metavision is compared via the Docker image in benchmarks/docker/. See benchmarks/README.md for details.
We aim for universal event format support, prioritizing blazing fast read/write speeds, completeness, and extensibility.
-
Universal format support (
.raw,.evt2,.dat,.aedat4,.hdf5,.npz,.csv, etc.) - Full Read/Write parity where possible
- Chunked & Streaming access
- External trigger data parsing
-
Random access / Timestamp indexing (
EventReader.seek(t=/n=)— by time or event index, forward/backward) - Arbitrary input sources: memory-mapped IO, pure in-memory streams (HTTP streams pending)
-
On-the-fly Compression wrappers: passing file handles through
zstdorlz4compression transparently before decoding -
EventStreamer Pipeline Refactor: Decouple
EventReader's monolithic chunking logic into composable functional generators inchunking.py, exposing a nativeEventStreamerfor power-users while turningEventReaderinto a clean Façade. (EventStreamerandstream_*generators exist butEventReaderdoes not use them yet; unification pending — see TODO.md.)
Thanks to all the contributors for supporting this project:
- Elia Franc
- Jakub Mandula
@PhDThesis{2024mandula_evutils,
author = {Jakub Mandula},
title = {EV-Utils: collection of utilities for working with event-based vision data},
school = {Dept. of Information Technology and Electrical Engineering, ETH Zurich},
year = 2024
}