Skip to content

Accumulator

frames2py.Accumulator(sensor_size, kernel) accumulates events through one kernel, synchronously, on the calling thread. It is what the Engine uses internally, without publication, threads or lifecycle. Use it when you drive the loop yourself and want the representation on demand: offline processing of a recording, tests, a pipeline stage that runs in step with its input.

accumulator.py
import numpy as np

import frames2py


def events(t, x, y, p=1):
    out = np.zeros(len(t), dtype=frames2py.EVENT_DTYPE)
    out["t"], out["x"], out["y"], out["p"] = t, x, y, p
    return out


acc = frames2py.Accumulator((4, 3), "time_surface")  # 4 columns, 3 rows
print("watermark before any event:", acc.watermark)

# Out of order, and one event outside the 4x3 sensor (x = 7).
acc.accumulate(events(t=[30, 10, 20, 99], x=[0, 1, 0, 7], y=[0, 0, 0, 2]))

print(acc.read())                        # a copy; reading changes nothing
print("watermark:", acc.watermark)       # the largest in-bounds timestamp, not the last one
print("out of bounds:", acc.events_out_of_bounds)

acc.reset()
print("after reset:", acc.watermark, acc.events_out_of_bounds, int(acc.read().sum()))
Output
watermark before any event: None
[[30 10  0  0]
 [ 0  0  0  0]
 [ 0  0  0  0]]
watermark: 30
out of bounds: 1
after reset: None 0 0

Constructor

  • sensor_size: (width, height). The representation is (height, width), or (height, width, 2) for polarity.
  • kernel: a configured kernel instance (frames2py.ExpDecay(0.9), frames2py.TimestampDecay(10_000.0), any object implementing the Kernel protocol), or one of the names "event_count", "polarity", "time_surface" for the kernels without parameters. Any other name raises ValueError. Unlike Engine, Accumulator has no default kernel.

Methods and properties

accumulate(events) accumulates one call's events, in the order of the event contract: structural validation (TypeError), the timestamp-range check (ValueError if any t >= 2**63, rejecting the whole call before anything changes), the bounds check (out-of-bounds events are counted and otherwise ignored), then the kernel. For exp_decay, each accepted call is one decay step.

read() returns a new array holding the current representation, with the kernel's public dtype. It changes no state: reading twice gives the same result, and reading never closes a window. (The Engine's windowed kernels start a new window at each publication; an Accumulator never publishes, so its event_count and polarity windows run from construction or reset().)

watermark is the largest timestamp among accumulated in-bounds events, or None before the first. Out-of-bounds events never move it.

events_out_of_bounds counts the events the bounds check has rejected.

reset() clears the kernel state, the watermark and the out-of-bounds count, as if the Accumulator were new. Call it when your source's timestamps restart (timestamp discontinuities).

The full signatures are in the API reference.

Accumulator or Engine?

Accumulator Engine
threads the caller's only one producer, any number of consumers
output read(): a copy, on demand snapshot(): the latest publication, shared
windowed kernels window spans everything since construction or reset() each publication starts a new window
statistics watermark, events_out_of_bounds stats (EngineStats), and the watermark in each SnapshotMeta
lifecycle reset() start(), stop(), reset()
default kernel none "event_count"