Skip to main content

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

evio-0.1.0.tar.gz (22.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

evio-0.1.0-py3-none-any.whl (20.0 kB view details)

Uploaded Python 3

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

Hashes for evio-0.1.0.tar.gz
Algorithm Hash digest
SHA256 eff063063344bee8da4d41eb95a75401a7f17bbe39ad19291a5631e554458b48
MD5 98e38dfea2a98f3586ed2d72421b7722
BLAKE2b-256 dc9a780d908200da2ee99e41b50878454cb8754dc8f77b0da0472fef72a09543

See more details on using hashes here.

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

Hashes for evio-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b57d3dc9942bd0f507254e3165c5b9c1aeffab79fc0cb740e6398ea9df700361
MD5 5565dfe4f2970de8793779d29978529c
BLAKE2b-256 c47e695faab87b64bb5c3699754e3a7c14c9a6f200a5daa66caff428c9e8f3e2

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page