Frames2Py is an open-source Python library for live, decoupled observation of event-camera state. I wrote it. A producer thread feeds camera events to an engine that accumulates them and publishes immutable snapshots. Any number of consumers read the latest snapshot at their own pace, and the producer never waits for them.

The library depends only on NumPy, reads the common event-camera recording formats, and runs on CPython 3.11 to 3.14, including the free-threaded build of 3.14 that runs without the global interpreter lock. Version 1.0 sustained more than 20 million events per second in every cell of its performance gate.

01Background

An event camera does not capture whole frames at a fixed rate. It reports per-pixel brightness changes as a stream of asynchronous events, often millions a second. The loop that consumes that stream for tracking, inference or control has to keep up. At the same time something else usually needs to see what the sensor sees right now, such as a display, a monitor or a second algorithm. If that observation runs inside the loop, the loop slows down to its speed.

Frames2Py separates the two concerns: the loop stays the only writer, and every observer reads finished snapshots from the side. The repository was created in February 2026, and version 1.0.0 was published on 29 September 2026, the same day as its release candidate.

02Design

The pipeline, after the project's README. All accumulation work happens on the producer's thread; consumers only read finished snapshots.

Engine

The Engine is the live runtime. One producer thread calls ingest() with arrays of events, and ingest() does its CPU work on that thread without ever waiting on a consumer or on I/O. The first call always publishes; after that the engine publishes at most once per configured interval. Consumers call snapshot() for the latest publication or, from version 1.1, block in wait_for_newer(sequence) until a newer one appears. Calling wait_for_newer() from the producer's own thread raises RuntimeError.

Accumulator

The Accumulator performs the same accumulation without publication or threads, for offline processing, tests and loops that the caller drives directly.

Snapshots

A snapshot holds the frame and metadata (a watermark and a sequence number) from one publication. The frame is shared by every consumer and is read-only; snapshot.copy() gives a consumer its own writable copy.

Kernels

A kernel defines what a frame represents. Version 1.1 ships seven built-in kernels, and users can write their own.

KernelSinceRepresentation
event_count1.0Events per pixel since the previous publication
polarity1.0Windowed counts split by polarity (brightness up or down)
time_surface1.0Latest event timestamp per pixel
ExpDecay1.0Exponential decay applied once per call
TimestampDecay1.0Decay in event time, independent of how events are batched
StackedHistogram1.1Events per polarity and time bin, as consumed by RVT
VoxelGrid1.1Signed events split linearly between time knots, as in E2VID and E-RAFT

The two temporal kernels use bins on an absolute event-time grid and show only completed bins. They count exactly in integers and produce the same frame bit for bit whatever the order and batching of the events.

03Data sources and tools

File adapters
Read Prophesee EVT 2.0 and 3.0 (RAW), AEDAT 4.0 and HDF5 recordings as arrays in the library's event format.
Recorder
Writes events to HDF5 alongside ingest() in the producer's loop; the engine never calls it.
Replay
replay.paced() yields a recording's batches at their recorded pace. replay.windows(), added in 1.1, turns a recording into a frame every N µs of event time, deterministically and independently of batching.
Viewer
Renders a snapshot as an RGB image, or shows a running engine in a window.

The library ships no vendor SDK adapters: users convert their SDK's buffers to the event format and call ingest(). PyTorch is not a dependency. Instead, the documentation includes a recipe for handing snapshots to a model, and that recipe is tested in its own CI workflow.

04Performance

The version 1 performance gate checks whether the five 1.0 kernels and Engine.ingest() sustain at least 20 million events per second across 150 conditions. The conditions combine three sensor resolutions (346×260, 640×480 and 1280×720), five combinations of batch size and publication interval, and uniform and clustered event distributions. On an Apple M4 with 16 GB of memory, every cell passed at both the kernel and the engine level, on CPython 3.11.14 and on free-threaded CPython 3.14.2t.

Lowest gate result per kernel, engine level · millions of events per second
KernelCPython 3.11.14CPython 3.14.2t
event_count176.0170.4
polarity110.2108.1
time_surface119.0104.8
exp_decay127.9129.5
timestamp_decay97.480.4

These are back-to-back figures, so they show headroom above the 20-million target rather than the rate of a live stream. The project documents that only one machine has been measured, and that the ingest path has changed since the gate ran without being re-measured. The two temporal kernels added in 1.1 have their own gate, which they do not meet in every cell.

05Release history

VersionDateNotes
1.1.07 Oct 2026wait_for_newer(), the StackedHistogram and VoxelGrid kernels, replay.windows() and a PyTorch recipe. The producer-thread check now follows the thread rather than its reusable identifier. Licence changed to Apache 2.0.
1.0.029 Sep 2026First stable release, MIT licence.
1.0.0rc129 Sep 2026Release candidate.

06References

  1. siddiquifaras/frames2py. GitHub, retrieved 9 October 2026.
  2. Performance. Frames2Py documentation, retrieved 9 October 2026.
  3. Changelog. Frames2Py documentation, retrieved 9 October 2026.
  4. Releases. siddiquifaras/frames2py on GitHub, retrieved 9 October 2026.