Skip to content

Recorder, viewer and replay API

recorder.open() returns a Recorder. The class is not part of the public API (not in frames2py.recorder.__all__); it is documented here because open() returns one. See Recorder, Viewer and Replay.

frames2py.recorder.open

open(path: str | PathLike[str], *, sensor_size: tuple[int, int], group: str = 'events', compression: str | None = 'blosc', overwrite: bool = False) -> Recorder

Start a recording at path; events go in with write(), and close() finishes it.

The recording is written to a temporary file next to path and moved onto path only once it is complete.

Parameters:

Name Type Description Default
path str | PathLike[str]

The recording to create.

required
sensor_size tuple[int, int]

(width, height), stored as the sensor_width and sensor_height attributes.

required
group str

The HDF5 group that holds the datasets.

'events'
compression str | None

"blosc" (LZ4 through hdf5plugin), "gzip" or None.

'blosc'
overwrite bool

Replace an existing file at path when the recording is complete. Otherwise an existing file is an error.

False

Raises:

Type Description
ImportError

h5py or hdf5plugin is not installed (frames2py[recorder]).

TypeError

path is not a str or path, group not a str, overwrite not a bool, or sensor_size doesn't hold ints.

ValueError

sensor_size is not a pair of positive ints, compression is not one of the three, or group is not a valid group name.

FileExistsError

path exists and overwrite is false.

IsADirectoryError

path is a directory.

FileNotFoundError

path's directory doesn't exist.

PermissionError

the directory can't be written.

Recorder

An open recording. write() adds events; close() or leaving the with block finishes it.

write() runs on the caller's thread, compression and file writes included; the recorder starts no thread. Events are kept in a buffer of at most one chunk until a chunk is full, so a file's bytes depend only on the events written, not on how they were split across calls.

close() writes the buffered events, closes the file and moves it onto the target path. Leaving the with block through an exception, KeyboardInterrupt included, closes it the same way: the recording then holds every event of every completed write() call, plus a prefix, possibly empty, of the events of a call the exception interrupted. A process that is killed or loses power leaves only the temporary file, which is not a valid recording.

write

write(events: object) -> None

Record one call's events, in order, exactly as given.

Raises TypeError for a malformed array and ValueError if any event has t >= 2**63 (nothing of the call is recorded) or if the recorder is closed.

close

close() -> None

Finish the recording and move it onto the target path. Closing twice is a no-op.

Raises FileExistsError if overwrite is false and a file appeared at the target after open(); the finished recording is then left at the temporary path the message names. If finishing the file fails, the temporary file is removed and the error raised.

frames2py.viewer.render

render(snapshot: Snapshot, *, scale: float | None = None, window_us: float = 50000) -> RGB

The snapshot as a new (height, width, 3) uint8 RGB image.

Reads snapshot.frame and snapshot.meta and never writes either. The mapping follows the frame's dtype and shape, which are fixed per kernel:

  • (H, W) uint32 (event_count) and (H, W) float32 (exp_decay, timestamp_decay): grey, floor(255 * min(v, s) / s).
  • (H, W, 2) uint32 (polarity; channel 0 OFF, channel 1 ON): OFF in blue, ON in red and green (yellow), both white, each floor(255 * min(v, s) / s).
  • (H, W) uint64 (time_surface): floor(255 * max(0, 1 - (T - v) / window_us)) with T the snapshot's watermark; v == 0, no event yet, is black.

s is scale when given. With scale=None it comes from the frame's nonzero values (both channels together for polarity): their maximum if there are fewer than 100, otherwise their 99th percentile (NumPy's default linear interpolation, in float64). Values above s show at full brightness. A frame with no nonzero value is black.

Parameters:

Name Type Description Default
snapshot Snapshot

A published snapshot, such as engine.snapshot() returns.

required
scale float | None

The value shown at full brightness, or None for automatic scaling.

None
window_us float

For time_surface: how far behind the watermark, in µs, a pixel's last event fades to black.

50000

Raises:

Type Description
TypeError

snapshot is not a Snapshot, or its frame's dtype and shape are not one of the above.

ValueError

scale or window_us is not a finite positive number.

frames2py.viewer.run

run(source: Source, *, interval_ms: float = 16.0, title: str = 'Frames2Py', scale: float | None = None, window_us: float = 50000) -> None

Show the snapshots source returns in a window until the window is closed.

Once per interval_ms the viewer calls source, typically engine.snapshot, and, when the publication is not the one it last showed, renders it with render(); a None shows black. The window is as large as the frame. The viewer runs entirely on the calling thread, which must be the main thread; it starts no thread. Run the producer on a thread of its own.

Parameters:

Name Type Description Default
source Source

Called once per interval; returns a Snapshot or None.

required
interval_ms float

How often to read source, in milliseconds.

16.0
title str

The window title.

'Frames2Py'
scale float | None

Passed to render().

None
window_us float

Passed to render().

50000

Raises:

Type Description
RuntimeError

called from a thread other than the main thread.

ImportError

pyglet is not installed (frames2py[viewer]).

ValueError

interval_ms, scale or window_us is not a finite positive number.

frames2py.replay.paced

paced(batches: Iterable[EventArray], *, speed: float = 1.0, clock: Callable[[], int] = time.monotonic_ns, sleep: Callable[[float], Any] = time.sleep) -> Iterator[EventArray]

Yield each batch of batches, unchanged, once its timestamps say it is due.

The first nonempty batch sets the start: its smallest timestamp t0 and the clock's reading when it arrives. M is the largest timestamp seen so far, the batch about to be yielded included. A batch is yielded once the clock has advanced (M - t0) / speed µs (rounded up to a whole ns) past the start. Timestamps are never repaired and no reset is inferred; a discontinuity is the caller's to handle. A batch whose timestamps go back leaves M where it was and is yielded without waiting; a jump forward makes the batch, and the ones after it, wait for it. Empty batches are yielded at once. A consumer slower than the recording gets every batch late; nothing is skipped to catch up.

Parameters:

Name Type Description Default
batches Iterable[EventArray]

EVENT_DTYPE arrays, such as a file adapter's reader.

required
speed float

Replay rate relative to the recording; 2 is twice as fast.

1.0
clock Callable[[], int]

Nanoseconds, monotonic. Injectable for tests.

monotonic_ns
sleep Callable[[float], Any]

Waits the given seconds. Injectable for tests.

sleep

Raises:

Type Description
ValueError

speed is not a finite number above 0 (on the call).

TypeError

a batch is not an EVENT_DTYPE-compatible array (when it is reached).