Read Prophesee and Metavision event camera
recordings into NumPy, CSV, or Rust. evt3 decodes the EVT3 (EVT 3.0)
encoding used by Prophesee event-based vision sensors, from .raw and
optionally HDF5 files. It ships as a command-line tool, a Python package, and
a Rust library.
1.62x faster than the optimized C++ reference in a like-for-like full-CSV benchmark, with byte-identical checked output.
Built for event-based vision, neuromorphic engineering, and DVS data analysis
when you need event streams as plain x, y, p, t arrays without
installing a full camera SDK.
pip install evt3 # Python + NumPy
cargo install evt3-cli # command-line tool
cargo add evt3 # Rust library- 🚀 High Performance - 55M events/second for Python decode-only and 1.62x faster than C++ for full CSV output
- 📦 Multiple Interfaces - CLI tool, Python bindings, Rust library
- 🐍 NumPy-native Python - stable
Eventsarrays for analysis without clone-on-access surprises - 🔭 AugurRS Ingress - publish decoded or transformed NumPy event arrays into AugurRS for interactive preview, 3D inspection, viewer tools, and plugin workflows
- 🧪 Optional HDF5 Input -
.h5and.hdf5support behind a cargo feature - ✅ Validated - Checked CSV output matches the C++ reference byte for byte
- 🔧 Customizable - Configurable output field order
cargo install evt3-cliThe evt3-cli crate installs a binary named evt3. To use the decoder as a
Rust library instead, depend on the evt3 crate:
cargo add evt3curl -sSL https://raw.githubusercontent.com/muthmann/evt3/main/install.sh | bashThis downloads the binary to ~/.local/bin/evt3. You may need to add it to your PATH:
# Add to ~/.bashrc or ~/.zshrc
export PATH="$HOME/.local/bin:$PATH"Download from Releases:
| Platform | Binary |
|---|---|
| Linux x64 | evt3-linux-x64 |
| Linux ARM64 | evt3-linux-arm64 |
| macOS Intel | evt3-macos-x64 |
| macOS Apple Silicon | evt3-macos-arm64 |
| Windows | evt3-windows-x64.exe |
# Example for macOS Apple Silicon
curl -LO https://github.com/muthmann/evt3/releases/latest/download/evt3-macos-arm64
chmod +x evt3-macos-arm64
./evt3-macos-arm64 recording.raw events.csvUsing uv (recommended):
uv pip install evt3Or with pip:
pip install evt3Published wheels target CPython 3.9 through 3.14, including the free-threaded CPython 3.14 build.
Note: The pip package supports
.rawfiles only. HDF5 (.h5/.hdf5) requires building from source — see HDF5 Inputs below.
# Clone repository
git clone https://github.com/muthmann/evt3.git
cd evt3
# Build CLI (requires Rust)
cargo build --release
# The binary is at: ./target/release/evt3
./target/release/evt3 recording.raw events.csv
# Optional HDF5 support
HDF5_DIR="$(brew --prefix hdf5)" cargo build --release -p evt3-cli --features hdf5
# Optional: Install to PATH
cp target/release/evt3 ~/.local/bin/
# Build Python package (requires Python 3.9+, uv + Rust 1.83+)
cd evt3-python
uv venv
uv pip install maturin
source .venv/bin/activate
maturin develop --release# Decode to CSV (default: x,y,p,t)
evt3 recording.raw events.csv
# Timestamp-first format
evt3 recording.raw events.csv --format "t,x,y,p"
# Binary output (more efficient)
evt3 recording.raw events.bin
# Include trigger events
evt3 recording.raw events.csv --triggers triggers.csv
# Quiet mode
evt3 recording.raw events.csv --quietimport evt3
import numpy as np
# Decode a .raw or .h5 file — format is auto-detected by extension
events = evt3.decode_file("recording.raw")
events = evt3.decode_file("recording.h5") # requires hdf5 feature at build time
print(f"Decoded {len(events):,} events")
print(f"Sensor: {events.sensor_width}x{events.sensor_height}")
# Access as NumPy arrays (zero-copy)
x = events.x # np.ndarray[uint16]
y = events.y # np.ndarray[uint16]
p = events.polarity # np.ndarray[uint8]
t = events.timestamp # np.ndarray[uint64] (microseconds)
# Repeated access returns stable array objects
assert events.x is events.x
assert events.y is events.y
assert events.p is events.p
assert events.t is events.t
# Basic analysis
print(f"Duration: {(t[-1] - t[0]) / 1e6:.2f} seconds")
print(f"Event rate: {len(events) / ((t[-1] - t[0]) / 1e6):.0f} events/sec")
# Create pandas DataFrame
import pandas as pd
df = pd.DataFrame(events.to_dict())
# Existing decode_file code stays unchanged and now uses the optimized
# columnar decoder internally. Process bounded batches when the full recording
# does not need to stay in memory:
for batch in evt3.decode_file_batches("recording.raw", batch_bytes=8 << 20):
process(batch.x, batch.y, batch.p, batch.t)
# Preserve external trigger events in the bounded-memory workflow:
for events, triggers in evt3.decode_file_batches_with_triggers("recording.raw"):
process(events, triggers.timestamp, triggers.id, triggers.value)
# Preserve decoder state across arbitrary live-input chunk borders:
decoder = evt3.Decoder(sensor_width=1280, sensor_height=720)
for raw_chunk in camera_chunks:
process(decoder.feed(raw_chunk))
decoder.finish()evt3 can publish decoded or transformed NumPy event arrays into a running
AugurRS session. This makes Python a
lightweight analysis and filtering environment while AugurRS provides the
interactive event-camera application: live-style preview, 3D raw-event
inspection, viewer tools, exports, and plugins.
import evt3
events = evt3.decode_file("recording.raw")
# Optional Python-side filtering or analysis.
x = events.x
y = events.y
p = events.p
t = events.t
evt3.augur.publish_events(
x=x,
y=y,
p=p,
t=t,
geometry=events.sensor_size,
name="recording-analysis-window",
)You can also create an Events container from existing NumPy arrays:
events = evt3.Events.from_arrays(
x=x,
y=y,
p=p,
t=t,
geometry=(1280, 720),
copy=False,
)
evt3.augur.publish_events(events, name="filtered-events")For repeated sends, reuse the loopback session:
with evt3.augur.connect() as augur:
augur.publish_events(events, name="raw")
augur.publish_events(filtered_events, name="filtered")The first ingress stage is deliberately copy-based and bounded: event chunks
are packed into AugurRS' 14-byte packed_xypt_v1 decoded-event transport and
sent over loopback TCP. The connector validates dtype, shape, geometry, and
timestamp ordering before sending so mistakes fail close to the Python code.
cargo add evt3use evt3::Evt3Decoder;
let mut decoder = Evt3Decoder::new();
let result = decoder.decode_file("recording.raw")?;
println!("Decoded {} events", result.cd_events.len());
for event in result.cd_events.iter().take(10) {
println!("x={}, y={}, p={}, t={}",
event.x, event.y, event.polarity, event.timestamp);
}For live camera pipelines or embedded integrations, you can stream raw USB
packet bytes directly into the decoder without converting to Vec<u16> first:
use evt3::Evt3Decoder;
let mut decoder = Evt3Decoder::new();
let mut cd_events = Vec::new();
let mut trigger_events = Vec::new();
for chunk in usb_packet_chunks {
decoder.decode_bytes(chunk, &mut cd_events, &mut trigger_events)?;
}
decoder.finish_stream()?;This keeps evt3 usable in incremental preview paths while preserving the
existing file and word-based APIs.
Notes:
decode_bufferstill expects 16-bit EVT3 words, not raw bytes.decode_bytesexpects little-endian EVT3 payload bytes and can be called with odd-sized chunks.- Call
finish_stream()only when the stream is complete so a trailing half-word is reported as an error instead of being buffered for the next chunk. .h5and.hdf5decoding is available when the crate or binary is built with thehdf5feature.decode_fileremains source-compatible and returns the sameEventsAPI. It now decodes directly into NumPy's columnar layout and releases the Python GIL.decode_file_batchesis the bounded-memory option for large recordings. Usedecode_file_batches_with_triggerswhen external trigger edges are also required. The arrays in a batch remain valid after the iterator advances, but retaining all batches naturally retains the full recording.
Important: HDF5 support requires
libhdf5(a native C library) and is not included inpip install evt3or pre-built CLI binaries. It must be built from source. See docs/features/hdf5-file-support.md for the full limitations table.
# macOS
brew install hdf5
HDF5_DIR="$(brew --prefix hdf5)" cargo build --release -p evt3-cli --features hdf5
./target/release/evt3 recording.h5 events.csv
# Ubuntu / Debian
sudo apt install libhdf5-dev
cargo build --release -p evt3-cli --features hdf5
./target/release/evt3 recording.h5 events.csvMost Prophesee HDF5 files use the ECF compression codec, which requires an additional runtime plugin:
# Build and install the ECF plugin (one-time, macOS/Linux/Windows)
./scripts/install-ecf-plugin.sh
# Then set the plugin path before running
export HDF5_PLUGIN_PATH="$HOME/.local/share/hdf5/plugin"
./target/release/evt3 recording.h5 events.csvNotes:
- Builds without
--features hdf5return a clear error rather than silently failing. - Real-data integration tests that skip still show as
ok. Run with-- --show-outputto see[SKIP]reasons. - Full plugin and dependency documentation: docs/features/hdf5-file-support.md
Tested on Apple Silicon macOS with laser.raw (325 MB, 116,300,447 events).
Both CLI implementations decoded the complete file, formatted the same CSV,
and wrote it to /dev/null. Each mean uses five alternating measured runs
after one warm-up per implementation.
| Decoder | Mean time | Events/sec | Speedup |
|---|---|---|---|
| Rust CLI | 7.414 s | 15.69M/s | 1.62x |
C++ reference (-O3 -DNDEBUG) |
12.028 s | 9.67M/s | 1.00x |
An instrumented run measured 63.3 MB maximum RSS for Rust and 28.3 MB for C++. Rust is faster in this workload; the C++ reference uses less memory. The CSV outputs had the same SHA-256 hash on an 8-MiB input prefix. Python's 2.108-second full-memory decode is reported separately because it does not format CSV and is not directly comparable with this table.
Run benchmarks yourself:
cargo bench
python benchmarks/benchmark.py --csv-comparison-only --iterations 5Human-readable, with optional geometry header:
%geometry:1280,720
642,481,1,10960097
783,415,1,10960139
...Efficient packed format for programmatic access:
- 8-byte magic header:
EVT3BIN\0 - 24-byte metadata: version, width, height, event count
- Events: 14 bytes each (x:u16, y:u16, polarity:u8, pad:u8, timestamp:u64)
EVT 3.0 is a 16-bit vectorized event encoding from Prophesee. This decoder supports:
| Event Type | Code | Description |
|---|---|---|
| EVT_ADDR_Y | 0x0 | Y coordinate |
| EVT_ADDR_X | 0x2 | Single event (X + polarity) |
| VECT_BASE_X | 0x3 | Base X for vectors |
| VECT_12 | 0x4 | 12-event vector |
| VECT_8 | 0x5 | 8-event vector |
| EVT_TIME_LOW | 0x6 | Lower 12 bits of timestamp |
| EVT_TIME_HIGH | 0x8 | Upper 12 bits of timestamp |
| EXT_TRIGGER | 0xA | External trigger |
For full specification: Prophesee EVT 3.0 Documentation
evt3/
├── evt3-core/ # Rust decoder library -> crate `evt3`
├── evt3-cli/ # Command-line tool -> crate `evt3-cli`, binary `evt3`
├── evt3-python/ # Python bindings (PyO3) -> PyPI package `evt3`
├── benchmarks/ # Performance benchmarks
└── test_data/ # Sample EVT3 files
See CONTRIBUTING.md for development setup and guidelines.
Licensed under the MIT License - see LICENSE-MIT for details.