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.24.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.24-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.24-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.24-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.24.tar.gz.

File metadata

  • Download URL: evlib-0.1.24.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.24.tar.gz
Algorithm Hash digest
SHA256 aaf0bb3cc64f46f5662e7a839f73aa52f3fae5f82122918ce49729ba10947e4c
MD5 c3d3ffdbbb3229770740359843773269
BLAKE2b-256 f281e5c28dadc32ff11c88b4fc05cf5c4a316dfd180a28d7c1f2f8f3527fdbc2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for evlib-0.1.24-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 191e8b419f2eb2cad4176309d0984c63188088272dad186e5612ec03a7c8bc6a
MD5 67c2b8c84cab0e6ea9ba2291278b417d
BLAKE2b-256 0e0170ad4dee77e2016b7951f886dce3484e92423105db2e4081bc069c134ec5

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for evlib-0.1.24-cp311-cp311-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 a11f8b6a8e7c7dddccbdbd282f02f5cd96f7f36d33d5d293ed8aed7ea4d02514
MD5 de269e836ac0a690fded9deb46800c83
BLAKE2b-256 61d3602d0086303a19e1dae1b4f264a2c17550b951ffb5e58d2a00c188bcebdc

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for evlib-0.1.24-cp310-cp310-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 4e9b30cdaf5a2c70c75a37972f33447c80b99f53563e3a592bd40a56164f73f7
MD5 e725c27ef11c9ee1482b38a9a815f389
BLAKE2b-256 e67a6fe3d90fee40849a6a5e574c29432ff58e40e943256274666d3cd0623b5b

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