EV-Utils#
Overview#
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.
Core Philosophy#
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.
Installation#
We recommend installing evutils using uv.
From PyPi#
uv add evutils # Basic library
uv add evutils[all] # All groups (torch, hdf5, aedat, vis, etc..)
uv add evutils[dev] # Dev group
From Git#
git 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].
Architecture#
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.
Quick API overview#
io: Reading and Writing Events#
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) |
|
✅ |
✅ |
native C decoder, external triggers |
EVT4 (evutils variant) |
|
✅ |
✅ |
EVT2-style layout + vectorized CD; evutils’ own |
DAT (Prophesee) |
|
✅ |
✅ |
native C decoder |
AER (Prophesee) |
|
✅ |
✅ |
timestamp generation selectable |
AEDAT 1.0 / 2.0 / 3.1 / 4.0 |
|
✅ |
✅ (4.0) |
AEDAT4 write incl. triggers; 1.0/2.0/3.1 write 🚧; compression: |
HDF5 (DSEC/RVT layout) |
|
✅ |
✅ |
|
HDF5 (Prophesee layout) |
|
✅ |
🚧 |
ECF-compressed files need the ECF plugin |
NPZ |
|
✅ |
✅ |
streaming, |
CSV / TXT |
|
✅ |
✅ |
native C parser |
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 event
Seeking 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#
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.)
filtering#
Selecting and dropping events: spatial masking today, with denoising, ROI and downsampling to follow.
transforms#
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.
random#
Generating random events and adding noise to event recordings
types#
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.
vis#
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)
Running tests#
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 -s
Testing Docstrings#
The 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/
Benchmarks#
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 smoke
The 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.
Goals & Roadmap#
We aim for universal event format support, prioritizing blazing fast read/write speeds, completeness, and extensibility.
[x] Universal format support (
.raw,.evt2,.dat,.aedat4,.hdf5,.npz,.csv, etc.)[x] Full Read/Write parity where possible
[x] Chunked & Streaming access
[x] External trigger data parsing
[x] Random access / Timestamp indexing (
EventReader.seek(t=/n=)— by time or event index, forward/backward)[x] 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.)
Acknowledgements#
Thanks to all the contributors for supporting this project:
Elia Franc
Jakub Mandula
Cite#
@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
}