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
Engine.ingest()at most once per snapshot_interval_mssnapshot() / wait_for_newer()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.
| Kernel | Since | Representation |
|---|---|---|
event_count | 1.0 | Events per pixel since the previous publication |
polarity | 1.0 | Windowed counts split by polarity (brightness up or down) |
time_surface | 1.0 | Latest event timestamp per pixel |
ExpDecay | 1.0 | Exponential decay applied once per call |
TimestampDecay | 1.0 | Decay in event time, independent of how events are batched |
StackedHistogram | 1.1 | Events per polarity and time bin, as consumed by RVT |
VoxelGrid | 1.1 | Signed 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.
| Kernel | CPython 3.11.14 | CPython 3.14.2t |
|---|---|---|
event_count | 176.0 | 170.4 |
polarity | 110.2 | 108.1 |
time_surface | 119.0 | 104.8 |
exp_decay | 127.9 | 129.5 |
timestamp_decay | 97.4 | 80.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
| Version | Date | Notes |
|---|---|---|
| 1.1.0 | 7 Oct 2026 | wait_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.0 | 29 Sep 2026 | First stable release, MIT licence. |
| 1.0.0rc1 | 29 Sep 2026 | Release candidate. |
06References
- siddiquifaras/frames2py. GitHub, retrieved 9 October 2026.
- Performance. Frames2Py documentation, retrieved 9 October 2026.
- Changelog. Frames2Py documentation, retrieved 9 October 2026.
- Releases. siddiquifaras/frames2py on GitHub, retrieved 9 October 2026.