evio
Event-camera I/O and representations. NumPy only.
pip install evio
import evio
events = evio.read("recording.dat") # or .bin
events = evio.denoise(events)
tensor = evio.voxel_grid(events, bins=5)
Every reader returns the same structured array, so nothing downstream needs to know which sensor or file format the events came from.
Why this exists
The established package in this space is tonic,
and it does more than this one does. Two things pushed me to write evio anyway:
1. tonic cannot coexist with NumPy 2. It pins numpy<2.0.0. Installing
it into a current environment silently downgrades:
$ pip install "numpy>=2" && python -c "import numpy; print(numpy.__version__)"
2.0.2
$ pip install tonic && python -c "import numpy; print(numpy.__version__)"
1.26.4
evio depends on NumPy and nothing else, and is tested against both 1.x and 2.x.
2. tonic does not read Prophesee .dat, which is the format N-CARS, GEN1
Automotive and the 1 Mpx detection dataset all ship in. That is most of
automotive event-vision research.
| evio | tonic | |
|---|---|---|
| Dependencies | 1 (numpy) |
8, including librosa and scipy |
| NumPy 2 | yes | no, pins <2.0.0 |
Prophesee .dat |
yes | no |
| Bundled datasets | no | many |
| Torch integration | no | yes |
If you want a dataset zoo and PyTorch wiring, use tonic. Use evio when
you want to read event files into arrays quickly, in an environment you would
rather not have dictated to you.
What is in it
Formats. N-MNIST / N-Caltech101 .bin and Prophesee .dat, read and
write, both decoded with vectorised NumPy rather than per-event loops.
Representations. count_image, voxel_grid (bilinear, after Zhu et al.
2019), time_surface (Lagorce et al. 2017), binary_tensor for spiking
networks, and frames for fixed-duration windows.
Filters. denoise (nearest-neighbour), refractory, hot_pixels,
polarity, crop.
A CLI, for looking at a recording without writing a script:
$ evio info recording.dat
recording.dat
events 1,048,576
width 304
height 240
duration_s 0.099999
rate_hz 10485836.5
positive 521,114
negative 527,462
$ evio clean noisy.dat clean.dat --denoise 5000 --refractory 1000
1,048,576 -> 793,004 events (255,572 removed, 24.4%)
Performance
Apple M1, Python 3.9.6, NumPy 2.0.2. Decoder figures are real recordings; the rest is a 2 M event synthetic stream on a 640×480 sensor.
| Operation | Throughput | Notes |
|---|---|---|
.bin decode, in memory |
156 M events/s | 28,000 recordings/s |
.bin read, from disk |
75 M events/s | 13,500 recordings/s |
.dat read, from disk |
75 M events/s | 15,700 recordings/s |
time_surface |
118 M events/s | |
binary_tensor (10 bins) |
101 M events/s | |
hot_pixels |
88 M events/s | |
voxel_grid (5 bins) |
29 M events/s | |
count_image |
21 M events/s | |
refractory |
3.5 M events/s | |
denoise |
0.3 M events/s | the slow one, see below |
denoise is an order of magnitude slower than everything else, and the
benchmark above is its worst case. It needs, for every event, the nearest event
in time at each of eight neighbouring pixels; that is eight binary searches
over the stream, and profiling shows those searches are essentially the whole
runtime — the sort, key arithmetic and gathers together are under 3%. Events
already known to be kept are dropped from later searches, so clustered data
(real recordings, where an edge fires several adjacent pixels at once) resolves
most events on the first offset or two, while uniformly random events do not.
Crop first if you can. A bucketed approximation would be much faster and is not
implemented, because it would change the result and this one is exact.
Reproduce with:
python benchmarks/bench.py --nmnist /path/to/NMNIST/Train --dat /path/to/ncars
Design notes
Events are a structured NumPy array, not a class.
EVENT_DTYPE = np.dtype([("x", "<u2"), ("y", "<u2"), ("t", "<i8"), ("p", "<i1")])
So they slice, sort, concatenate, np.save, and hand to any other library
without conversion. x, y in pixels, t in microseconds, p in {0, 1}.
Polarity is normalised on read. Sources disagree about {0,1} versus
{-1,1}; downstream code should not have to care.
Sensor size is asked for, not assumed. evio.sensor_size(events) infers it
from the data, but that is a lower bound — a recording where nothing happened
in the last column cannot tell you the column exists. Pass the real size where
you know it; the Prophesee reader can give you the one in the file header:
events, meta = prophesee.read(path, with_meta=True)
size = prophesee.sensor_size_from_meta(meta)
Two filters had to choose a rule, and say which. denoise uses a symmetric
time window, so its result does not depend on arrival order — the classic
backwards-only formulation discards the first event of every genuine edge.
refractory measures from the last kept event, not the last event seen,
because a suppressed event never physically happened.
Tests
pip install -e ".[dev]"
pytest
67 tests, 97% coverage. The two order-dependent filters are checked against
plain reference implementations written the slow, obvious way, because the
fast versions are not obviously equivalent to them. That caught a real bug: the
composite key packs pixel * span + t, and clamping only the lower end of the
time window let t + window overflow into the next pixel's key block, so
events were being rescued by neighbours they did not have.
Status
Version 0.1.0. The API above is what I use; I would rather change it in response to a real complaint than guess at more of it now. AEDAT and HDF5 readers are the obvious next formats.
MIT licensed.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file evio-0.1.0.tar.gz.
File metadata
- Download URL: evio-0.1.0.tar.gz
- Upload date:
- Size: 22.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eff063063344bee8da4d41eb95a75401a7f17bbe39ad19291a5631e554458b48
|
|
| MD5 |
98e38dfea2a98f3586ed2d72421b7722
|
|
| BLAKE2b-256 |
dc9a780d908200da2ee99e41b50878454cb8754dc8f77b0da0472fef72a09543
|
File details
Details for the file evio-0.1.0-py3-none-any.whl.
File metadata
- Download URL: evio-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b57d3dc9942bd0f507254e3165c5b9c1aeffab79fc0cb740e6398ea9df700361
|
|
| MD5 |
5565dfe4f2970de8793779d29978529c
|
|
| BLAKE2b-256 |
c47e695faab87b64bb5c3699754e3a7c14c9a6f200a5daa66caff428c9e8f3e2
|