Skip to main content

Python bindings for event camera utilities

Project description

evlib: Event Camera Utilities in Rust

PyPI Version Python Versions Python Build Codecov License

A high-performance (or some might say: blazingly fast) implementation of event camera utilities using Rust with Python bindings via PyO3.

This library is insipred by numerous event camera libraries such event_utils Python library but reimplemented in Rust for significantly better performance.

[!Warning]

This is a super experimental project and will have frequent breaking changes. It is primary being developed as a learning project for understanding Event Camera data processing and Event-Vision algorithms.

⬇ Installation

# Using pip
pip install evlib

# Using uv (recommended)
uv pip install evlib

For development:

# Using pip
pip install -e ".[dev]"

# Using uv (recommended)
uv pip install -e ".[dev]"

Installing with visualisation tools:

# Using pip
pip install -e ".[plot]"

# Using uv (recommended)
uv pip install -e ".[plot]"

For all dependencies including development, plotting, numpy, and Jupyter support:

# Using pip
pip install -e ".[all]"

# Using uv (recommended)
uv pip install -e ".[all]"

Development Setup

For detailed development setup instructions, see BUILD.md.

Quick setup:

# Clone repository
git clone https://github.com/yourusername/evlib.git
cd evlib

# Create virtual environment
uv venv --python <python-version> # 3.12 recommended
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
uv pip install pip

# Install for development using uv (recommended)
uv pip install -e ".[dev]"

# Or using pip
pip install -e ".[dev]"

# Install pre-commit hooks
pip install pre-commit
pre-commit install

# Run tests
pytest

🗺️ Roadmap and Current Features

  • Core event data structures and manipulation
  • Event data loading and saving
  • Event augmentation
    • Random event addition
    • Correlated event addition
    • Event removal
  • Event transformations
    • Flipping events along x and y axes
    • Clipping events to bounds
    • Rotating events
  • Event representations
    • Voxel grid representation
  • Event visualisation and display
  • Event-to-video reconstruction

evlib aims to become a comprehensive toolkit for event camera data processing, combining high-performance Rust implementations with Python bindings for ease of use. A tracking issue can be found here

Algorithm/Feature Description Status
Core Event Data Structures Basic event representation and manipulation ✅ Implemented
Event Augmentation Random/correlated event addition/removal ✅ Implemented
Event Transformations Flipping, rotation, clipping ✅ Implemented
Voxel Grid Event-to-voxel grid conversion ✅ Implemented
Visualisation Event-to-image conversion tools ✅ Implemented
E2VID (Basic) Simple event-to-video reconstruction ✅ Implemented
OpenEB Format Support Compatibility with OpenEB data formats ⏳ Planned
OpenEB HAL Integration Hardware abstraction for cameras ⏳ Planned
OpenEB Streaming Real-time event stream processing ⏳ Planned
E2VID (Advanced) Neural network reconstruction ⏳ Planned
Vid2E Simulation Video-to-event conversion ⏳ Planned
ESIM Framework Event camera simulation ⏳ Planned
HyperE2VID Advanced reconstruction with hypernetworks ⏳ Planned
RVT Object Detection Event-based object detection ⏳ Planned
Optical Flow Event-based optical flow estimation ⏳ Planned
Depth Estimation Event-based depth estimation ⏳ Planned

🚀 Performance

Evlib is significantly faster than pure Python implementations, thanks to its Rust backend. The benchmark compares the Rust-backed evlib implementation against equivalent pure Python implementations of the same functions, in both single-core and multi-core scenarios.

Single-core Performance

Operation Python Time (s) Rust Time (s) Speedup
events_to_block 0.040431 0.000860 47.03x
add_random_events 0.018615 0.003421 5.44x
flip_events_x 0.000023 0.000283 0.08x

Benchmark performed with 100,000 events on a single core

Multi-core vs Single-core Performance

Operation Python (1 core) Python (10 cores) Rust (1 core) Rust vs Py (1 core) Rust vs Py (10 cores)
events_to_block 0.040431 s 0.315156 s 0.000860 s 47.03x 366.58x
add_random_events 0.018615 s 0.360760 s 0.003421 s 5.44x 105.44x
flip_events_x 0.000023 s 0.303467 s 0.000283 s 0.08x 1072.67x

Benchmark performed with 100,000 events. Note that for these specific operations and data sizes, the multi-core Python implementation is slower due to process creation overhead.

Why Rust is Faster

The significant performance gains come from several factors:

  1. Compiled vs Interpreted: Rust is compiled to native machine code, while Python is interpreted
  2. Memory Management: Rust's ownership model allows for efficient memory use without garbage collection
  3. Low-level Optimisations: Rust can take advantage of SIMD (Single Instruction Multiple Data) vectorisation
  4. Static Typing: Rust's type system enables compiler optimisations that aren't possible with Python's dynamic typing
  5. Zero-cost Abstractions: Rust provides high-level abstractions without runtime overhead
  6. Efficient Concurrency: Rust's thread safety guarantees and lack of GIL allow for better parallelisation

Run python examples/benchmark.py to benchmark on your own system.

⮑ Module Structure

The library is organized into the following modules:

  • evlib.core: Core event data structures and functions
  • evlib.augmentation: Event augmentation utilities
  • evlib.formats: Data loading and saving
  • evlib.representations: Event representation algorithms (e.g., voxel grid)
  • evlib.visualization: Visualisation tools
  • evlib.processing: Advanced event processing (including event-to-video reconstruction)

Basic Usage

import numpy as np
import evlib

# Create example event data
xs = np.array([10, 20, 30, 40], dtype=np.int64)
ys = np.array([50, 60, 70, 80], dtype=np.int64)
ts = np.array([0.1, 0.2, 0.3, 0.4], dtype=np.float64)
ps = np.array([1, -1, 1, -1], dtype=np.int64)

# Convert to block representation
block = evlib.core.events_to_block_py(xs, ys, ts, ps)
print(f"Block shape: {block.shape}")  # (4, 4)

Loading Event Data

import evlib

# Load events from file (automatically detects format)
xs, ys, ts, ps = evlib.formats.load_events_py("data/slider_depth/events.txt")

# Save events to HDF5 format
evlib.formats.save_events_to_hdf5_py(xs, ys, ts, ps, "output.h5")

# Save events to text format
evlib.formats.save_events_to_text_py(xs, ys, ts, ps, "output.txt")

Event Augmentation

import numpy as np
import evlib
import matplotlib.pyplot as plt

# Create sample event data
xs = np.array([50, 60, 70, 80, 90], dtype=np.int64)
ys = np.array([50, 60, 70, 80, 90], dtype=np.int64)
ts = np.array([0.1, 0.2, 0.3, 0.4, 0.5], dtype=np.float64)
ps = np.array([1, -1, 1, -1, 1], dtype=np.int64)

# Add random events
to_add = 20
new_xs, new_ys, new_ts, new_ps = evlib.augmentation.add_random_events_py(xs, ys, ts, ps, to_add)
print(f"Original events: {len(xs)}, After adding random events: {len(new_xs)}")

# Add correlated events (events near existing ones)
to_add = 15
xy_std = 2.0  # Standard deviation for x,y coordinates
ts_std = 0.005  # Standard deviation for timestamps

new_xs, new_ys, new_ts, new_ps = evlib.augmentation.add_correlated_events(
    xs, ys, ts, ps, to_add,
    xy_std=xy_std,
    ts_std=ts_std
)

Event Transformations

import numpy as np
import evlib

# Create sample event data
xs = np.array([10, 20, 30, 40, 50, 60, 70], dtype=np.int64)
ys = np.array([15, 25, 35, 45, 55, 65, 75], dtype=np.int64)
ts = np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7], dtype=np.float64)
ps = np.array([1, -1, 1, -1, 1, -1, 1], dtype=np.int64)

# Set the sensor resolution
sensor_resolution = (100, 100)  # (height, width)

# Flip events along x-axis
flipped_x_xs, flipped_x_ys, flipped_x_ts, flipped_x_ps = evlib.augmentation.flip_events_x(
    xs, ys, ts, ps, sensor_resolution
)

# Flip events along y-axis
flipped_y_xs, flipped_y_ys, flipped_y_ts, flipped_y_ps = evlib.augmentation.flip_events_y(
    xs, ys, ts, ps, sensor_resolution
)

# Rotate events by 45 degrees
theta_radians = np.pi / 4  # 45 degrees
center_of_rotation = (50, 50)  # Center of rotation
rotated_xs, rotated_ys, theta_returned, center_returned = evlib.augmentation.rotate_events(
    xs, ys, ts, ps,
    sensor_resolution=sensor_resolution,
    theta_radians=theta_radians,
    center_of_rotation=center_of_rotation
)

# Clip events to bounds
bounds = [30, 70, 30, 70]  # [min_y, max_y, min_x, max_x]
clipped_xs, clipped_ys, clipped_ts, clipped_ps = evlib.augmentation.clip_events_to_bounds(
    xs, ys, ts, ps, bounds
)

Event Representations (Voxel Grid)

import numpy as np
import evlib

# Create event data (1D arrays)
xs = np.array([10, 20, 30, 40, 50], dtype=np.int64)
ys = np.array([15, 25, 35, 45, 55], dtype=np.int64)
ts = np.array([0.1, 0.2, 0.3, 0.4, 0.5], dtype=np.float64)
ps = np.array([1, -1, 1, -1, 1], dtype=np.int64)

# Convert events to voxel grid
num_bins = 5
resolution = (100, 100)  # (width, height)
method = "count"  # Options: "count", "polarity", "time"

# Pass parameters in correct order
voxel_grid = evlib.representations.events_to_voxel_grid_py(
    xs, ys, ts, ps, num_bins, resolution, method
)

print(f"Voxel grid shape: {voxel_grid.shape}")  # (5, 100, 100)

Event Visualisation

import numpy as np
import matplotlib.pyplot as plt
import evlib
import os

# Create directory for saved figures
os.makedirs("examples/figures", exist_ok=True)

# Create event data
xs = np.array([10, 20, 30, 40, 50], dtype=np.int64)
ys = np.array([15, 25, 35, 45, 55], dtype=np.int64)
ts = np.array([0.1, 0.2, 0.3, 0.4, 0.5], dtype=np.float64)
ps = np.array([1, -1, 1, -1, 1], dtype=np.int64)

# Draw events to image
resolution = (100, 100)  # (width, height)
color_mode = "red-blue"  # Options: "red-blue", "grayscale"

# Pass parameters in correct order
event_image = evlib.visualization.draw_events_to_image_py(
    xs, ys, ts, ps, resolution, color_mode
)

plt.figure(figsize=(10, 8))
plt.imshow(event_image)
plt.title("Event Visualisation")
plt.axis('off')

# Save figure (optional)
plt.savefig("examples/figures/event_visualization.png", bbox_inches="tight")
plt.show()

Event-to-Video Reconstruction

import numpy as np
import matplotlib.pyplot as plt
import evlib
import os

# Create directory for saved figures
os.makedirs("examples/figures", exist_ok=True)

# Load events
xs, ys, ts, ps = evlib.formats.load_events_py("data/slider_depth/events.txt")

# Use subset of events for faster processing (optional)
max_events = 10000
xs = xs[:max_events]
ys = ys[:max_events]
ts = ts[:max_events]
ps = ps[:max_events]

# Determine sensor resolution from events
height = int(max(ys)) + 1
width = int(max(xs)) + 1

# Reconstruct a single frame from events
num_bins = 5  # Number of time bins for voxel grid
reconstructed_frame = evlib.processing.events_to_video_py(
    xs, ys, ts, ps,
    height=height,
    width=width,
    num_bins=num_bins
)

# Display the reconstructed frame
plt.figure(figsize=(10, 8))
plt.imshow(reconstructed_frame, cmap='gray')
plt.title("Reconstructed Frame from Events")
plt.axis('off')

# Save figure (optional)
plt.savefig("examples/figures/reconstructed_frame.png", bbox_inches="tight")
plt.show()

# For multiple frames (reconstructing a sequence)
# Define time windows and reconstruct frames for each
reconstructed_frames = []
t_min, t_max = ts.min(), ts.max()
num_frames = 5
time_step = (t_max - t_min) / num_frames

for i in range(num_frames):
    t_end = t_min + time_step * (i + 1)
    mask = ts <= t_end
    frame_xs = xs[mask]
    frame_ys = ys[mask]
    frame_ts = ts[mask]
    frame_ps = ps[mask]

    frame = evlib.processing.events_to_video_py(
        frame_xs, frame_ys, frame_ts, frame_ps,
        height=height,
        width=width,
        num_bins=num_bins
    )

    reconstructed_frames.append(frame)

    # Save each frame (optional)
    plt.figure(figsize=(10, 8))
    plt.imshow(frame, cmap="gray")
    plt.title(f"Reconstructed Frame {i+1}")
    plt.axis("off")
    plt.savefig(f"examples/figures/reconstructed_frame_{i+1}.png", bbox_inches="tight")
    plt.close()

⚖️ License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

evlib-0.1.26-cp312-cp312-manylinux_2_34_x86_64.whl (9.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.34+ x86-64

evlib-0.1.26-cp311-cp311-manylinux_2_34_x86_64.whl (9.1 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.34+ x86-64

evlib-0.1.26-cp310-cp310-manylinux_2_34_x86_64.whl (9.1 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.34+ x86-64

File details

Details for the file evlib-0.1.26-cp312-cp312-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for evlib-0.1.26-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 10146a5f36be48daf41fba7c33b4c88c301c00a2a73137b990952f4e67c0fdab
MD5 0cf7136947c3180497caf63c1df22271
BLAKE2b-256 575dd84a39274ab3e9ef637f33a3c988920f6f23e7138b49ce9cb22db39ef3c4

See more details on using hashes here.

File details

Details for the file evlib-0.1.26-cp311-cp311-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for evlib-0.1.26-cp311-cp311-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 5ae95e485239981706cbf02a058cb8c8bc05afff6ab441250affb89b7c291829
MD5 34719366ad595f22f15974476454f562
BLAKE2b-256 63be5237d9cad77074f53a35c700d7dec0605a9bb73e8c2ef927f75b04359938

See more details on using hashes here.

File details

Details for the file evlib-0.1.26-cp310-cp310-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for evlib-0.1.26-cp310-cp310-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 890697a5de7bd2d53d6081c245049c4d118a0caf4c5cfa2bdee8456c5e45fd67
MD5 9c28de034f890066b4c25f4368f194ad
BLAKE2b-256 9c115a690d154c2c335e2a1d99e16cfbcd5c5660aa189ab06cb8607855689e7b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page