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 Distribution

evlib-0.1.25.tar.gz (24.1 MB view details)

Uploaded Source

Built Distributions

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

evlib-0.1.25-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.25-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.25-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.25.tar.gz.

File metadata

  • Download URL: evlib-0.1.25.tar.gz
  • Upload date:
  • Size: 24.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for evlib-0.1.25.tar.gz
Algorithm Hash digest
SHA256 9754a87c862d9b6dda3fc0eda58a1ff46fcb50578ce2cc54edf23343ee1ca310
MD5 c59bd992746b4077c94bcaa0dd45d8cb
BLAKE2b-256 fae3f2279db3f405a03f74a1a8fb374d11ac326858041e7222fbc1fa690fe004

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for evlib-0.1.25-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 9540308b996f3e756cddfa0e38a6f257f207b06644726fa924b1a0991f189ac1
MD5 d94fec9f0f7b85ebc1ba0d6e87f7aeb4
BLAKE2b-256 b805d3e8c7cfdfa83c0988d9966e38bb62e3d6da135fe770b5cff6788e4a82d3

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for evlib-0.1.25-cp311-cp311-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 7fbf479beaacf9cac6d2f055df2c41e8598b61d317d4c14b514b96419df3989c
MD5 9a1647ee89ba80a9b01f8bacecbc6cbb
BLAKE2b-256 1c67ad88c99392da881c694938e8b33f8e73d28d0b859d5e55c2533def2f7bd9

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for evlib-0.1.25-cp310-cp310-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 cdd5f4b1a2f63f0531263899ed7b38f0ad3d1a54f336f380d3ab4b72186d2d42
MD5 bff9a5ebae00241d6bc0b1ffc962a0bb
BLAKE2b-256 75fe1e37536033f93b42c81933afc063600fb29904c152cd62a210f6e2df70c6

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