API Reference

The Python API lives in the top-level eventcv package. Every operation exists both as a method on EventStream / EventFrame and as an OpenCV-style free function (listed under Functional API below); the free functions are generated from the methods, so the two forms stay in sync.

Loading & saving

eventcv.load(path: str, *, sensor_size: tuple[int, int] | None = None, time_unit: str | None = None, order: str = 'txyp', topic: str | None = None, max_events: int | None = None, offset: float | None = None) EventStream[source]

Load events from any supported file, detected by its extension.

Supported today: .npz (N-ImageNet), .txt/.csv (e.g. EV-IMO t x y p), .bag (ROS dvs_msgs/EventArray), .hdf5/.h5, .aedat (AEDAT 2.0, jAER/DAVIS), and .dat (Prophesee CD events).

sensor_size and time_unit are auto-detected when omitted and only act as overrides: rosbags carry both in the message; HDF5/text infer the time unit from the timestamps (a fractional text value means seconds) and the resolution from the coordinate range. Passing sensor_size for HDF5 also skips that scan. time_unit is seconds/milliseconds/microseconds/nanoseconds (or auto); order (txyp/xytp) applies to text. topic selects the rosbag topic (default /davis/left/events). offset is an absolute timestamp in milliseconds (the file’s own time base — the same base as stream.numpy()[:, 2] scaled to ms): events before it are skipped, and max_events then caps how many are kept after it — together they read a window, handy for previewing a slice of a very large file. For a recording whose timestamps are epoch-based, pass the epoch time in ms (e.g. offset=1_587_540_271_650); <= 0 reads from the start.

eventcv.from_numpy(events, *, sensor_size: tuple[int, int] | None = None, time_unit: str | None = None, order: str = 'xytp') EventStream[source]

Build an EventStream from an in-memory (N, 4) NumPy array.

The constructor mirror of EventStream.numpy(): order defaults to xytp (the column layout numpy() emits, with timestamps in microseconds), so ecv.from_numpy(stream.numpy(), time_unit="us") round-trips a stream. Pass order="txyp" for arrays in the common t x y p dataset layout. Any integer or float dtype is accepted; polarity is positive when its value is greater than zero (both 0/1 and -1/1 conventions work).

sensor_size and time_unit are auto-detected when omitted, exactly like load(): the sensor is the smallest grid holding every event, and the time unit is inferred from the timestamp span (fractional values mean seconds; the inference assumes a recording of at least ~1 s, so pass time_unit explicitly for short arrays). Events outside an explicit sensor_size are dropped.

Example:

events = np.array([[0, 0, 100, 1], [1, 2, 250, 0]])   # x y t p
stream = ecv.from_numpy(events, time_unit="us")
stream.count().numpy()
eventcv.open(path: str, *, dt_ms: float | None = None, max_events: int | None = None, offset: float | None = None, repr: str | None = None, sensor_size: tuple[int, int] | None = None, time_unit: str | None = None, order: str = 'txyp', topic: str | None = None) EventReader[source]

Open a file for lazy slicing without loading it whole.

Where load() is OpenCV’s imread (read the entire stream eagerly), open is its VideoCapture: it returns an EventReader that points at the original file and fetches a slice on demand. For HDF5 this binary-searches the on-disk timestamps, so a slice of a multi-gigabyte recording costs a handful of reads — the file is never fully materialised. Other formats are loaded once and sliced in memory.

Pass dt_ms to treat the recording as a sequence of fixed-duration frames: the reader reports n_slices and reader.slice(n) returns the n-th frame (reader[n] works too). Frame n is measured from the recording start, so you never deal with absolute timestamps (which may be epoch-based). max_events is the event-count twin: open(path, max_events=10_000) makes each slice exactly 10 000 consecutive events (the last one may be shorter), which keeps the event rate per frame constant instead of the duration. The two are mutually exclusive — pass one or the other, not both. Without either, slice by explicit time/count window instead.

offset is an absolute timestamp in milliseconds (the file’s own time base — exactly what slice(t0_ms=…) takes) that moves the framing origin: slice(0), windows(), and n_slices all begin at that time, and events before it fall outside every indexed frame. It is clamped up to t_min, so an offset before the recording is a no-op, and one past the end yields zero frames. For an epoch-based recording pass the epoch time in ms (e.g. offset=1_587_540_271_650). It composes with either dt_ms or max_events.

sensor_size and time_unit are auto-detected when omitted (see load()); order/topic match load(). For a multi-GB HDF5, pass sensor_size to skip the one-time coordinate scan resolution inference needs.

Pass repr (a representation name — "count", "voxel", "tsurf", "flow" for optical flow, …) to make the reader a PyTorch-style map dataset: len(reader) == n_slices, reader[i] returns the dense [C, H, W] array for frame i, and reader.batch(indices) stacks a [B, C, H, W] batch — so a DataLoader can collate the reader directly. Use reader.with_repr(name, **opts) to set per-representation options (e.g. bins=5, or window=5 for "flow"). Without repr, reader[i] stays a raw EventStream; to still batch those through a DataLoader pass collate_fn=eventcv.collate (each batch is a list[EventStream], since sparse streams can’t stack into a tensor).

Phase 5 algorithms apply per slice: reader.efast() / reader.harris_corners(thr) return a new reader whose every slice is the corner sub-stream, composing with slice/windows/with_repr. To render an algorithm as a video, map it over windows() and hand the frames to export_png() (then assemble with ffmpeg).

Note repr governs the array/dataset path (reader[i] → NumPy). To view a slice interactively, slice(i) returns the raw EventStream, so name the representation on the stream: data.slice(1000).view("flow") (or .slice(1000).optical_flow().view()).

Example:

r = eventcv.open("rec.hdf5", dt_ms=30)   # resolution + time unit auto-detected
r.n_slices                               # how many 30 ms frames
r.slice(50).mcts().view()                # the 50th 30 ms frame
for frame in r.windows():                # walk every frame (step defaults to dt_ms)
    voxel = frame.voxel()

# As a training dataset:
ds = eventcv.open("rec.hdf5", dt_ms=30, repr="count")
loader = torch.utils.data.DataLoader(ds, batch_size=32, shuffle=True)

# Corner-detection video (one PNG per frame):
corners = eventcv.open("rec.hdf5", dt_ms=30).efast()
eventcv.export_png((w.count() for w in corners.windows()), "corners/", colormap="turbo")
# Optical-flow video:
eventcv.export_png((w.optical_flow() for w in r.windows()), "flow/")
eventcv.save(obj, path: str, *, topic: str | None = None) None[source]

Save an EventStream or EventFrame to path.

The mirror of load(): the format is chosen by the file extension. Streams go to .npz/.txt/.h5/.bag (npz, HDF5, and rosbag round-trip exactly; txt stores t x y p and recovers the sensor size/unit on load via inference or options). Frames (computed representations) go to .npz or .h5, preserving shape, dtype, kind, and channel_names. topic names the rosbag connection. Equivalent to obj.save(path).

eventcv.load_frame(path: str) EventFrame[source]

Load an EventFrame written by save() (.npz or .h5).

Restores the representation’s shape, dtype, kind, and channel_names.

eventcv.export_png(frames, out_dir: str, *, colormap: str = 'viridis', normalize: bool = True, prefix: str = 'frame_', start: int = 0, digits: int = 5)[source]

Write one or many EventFrame s to numbered .png files — the “frame sequence → video frames” export.

frames is a single EventFrame or any iterable of them (e.g. a generator over a reader’s windows), so a whole recording renders lazily without materialising every frame at once:

r = eventcv.open("rec.hdf5", dt_ms=30)
eventcv.export_png((w.count() for w in r.windows()), "out/", colormap="turbo")

Each frame is colormapped through the same path as frame.save("x.png") (colormap: viridis/turbo/grayscale/redblue; normalize auto-contrasts). Files are named {prefix}{index:0{digits}d}.png counting from start. Returns the list of written paths (assemble a video with, e.g., ffmpeg -i out/frame_%05d.png out.mp4).

eventcv.collate(batch)[source]

collate_fn for torch.utils.data.DataLoader over an EventReader.

A reader opened with a representation (open(repr=…)) yields dense [C, H, W] arrays that torch’s default collate stacks into a [B, C, H, W] tensor with no help — so you only need this for a reader opened without repr, whose reader[i] is a raw EventStream. Those are variable-length and sparse, so they can’t stack into a tensor; this returns the batch as a plain list of streams instead (dense/array batches still defer to torch’s default collate). Pass it explicitly:

loader = torch.utils.data.DataLoader(reader, batch_size=32, collate_fn=eventcv.collate)
for batch in loader:        # batch is a list[EventStream]
    batch[0].view()

Core types

class eventcv.EventStream

Bases: object

atsurf(*, tau_ms=30.0)

Averaged time surface — the per-pixel mean of exp(-age/tau_ms) over all events (two polarity channels, float32). Brighter where activity recurs; see tsurf.

background_activity_filter(dt)

Background-activity (nearest-neighbour) noise filter: keeps an event only if a 3×3 neighbour fired within dt (raw timestamp units, e.g. microseconds).

concat(others)

Concatenates this stream with others (argument order; sensor = element-wise max).

count(*, normalize=False)

Event-count image — one channel of total events per pixel (both polarities). Raw uint64 counts by default; uint8 rescaled to the busiest pixel when normalize=True.

crop(x0, y0, w, h)

Keeps events inside the w`×`h window at (x0, y0), shifted to a new origin.

decimate(k)

Keeps every k-th event by index.

efast()

eFAST event corner detector (Mueggler et al., BMVC 2017). Keeps the events sitting on a moving corner, tested on two Bresenham rings over the per-polarity surface of active events.

filter_polarity(polarity)

Keeps only events of the given polarity (nonzero / True = ON, 0 / False = OFF).

flip_x()

Mirrors horizontally (x → width-1-x).

flip_y()

Mirrors vertically (y → height-1-y).

harris_corners(threshold=0.0)

Harris corner score on the Surface of Active Events: keeps events whose Harris response det - k·trace² of the SAE-ramp structure tensor exceeds threshold. The default threshold=0 keeps corners (rank-2, R>0) and rejects straight edges (rank-1, R<0); raise it to be stricter.

hot_pixel_filter(n_std=3.0)

Hot-pixel removal: drops pixels whose event count exceeds mean + n_std·std over the active pixels (default n_std=3.0).

invert_polarity()

Flips every event’s polarity.

mask(mask)

Keeps events where the (H, W) boolean mask is True.

normalize_time()

Shifts timestamps so the earliest event starts at zero.

optical_flow(*, window=3)

Dense Lucas-Kanade optical flow on the time surface. Returns a two-channel (flow_x, flow_y) frame in pixels/ms; window is the half-width of the least-squares neighbourhood.

refractory_filter(dt)

Refractory-period filter: suppresses a pixel’s events for dt after it fires.

repr

The default representation name carried from open(repr=…) / with_repr (what view()/flatten() render), or None for a raw stream.

resize(width, height)

Event-domain resize to a width`×`height grid (rebinned, not interpolated).

rotate90(k)

Rotates by k * 90° clockwise (quarter turns swap the sensor dims).

save(path, *, topic=None)

Saves the stream to path, format chosen by extension (.npz/.txt/.h5/.bag) — the counterpart of eventcv.load. npz/HDF5/rosbag round-trip exactly; topic names the rosbag connection.

scale(sx, sy)

Scales the sensor by (sx, sy).

sort_by_time()

Returns a copy reordered by ascending timestamp (stable).

time_scale(factor)

Scales every timestamp by factor (rounded).

time_shift(dt)

Shifts every timestamp by dt microseconds.

time_window(t0, t1)

Keeps events whose timestamp lies in the half-open window [t0, t1) (microseconds).

translate(dx, dy)

Translates by (dx, dy); events shifted off the sensor are dropped.

transpose()

Reflects across the main diagonal ((x, y) → (y, x)); swaps the sensor dims.

undistort(camera)

Rectifies events with a Camera’s intrinsics + distortion (lens undistortion).

view(representation=None, *, colormap='viridis', normalize=None)

Opens the interactive viewer on this stream. Pass a representation name to choose what to show — stream.view(“flow”), stream.view(“count”), stream.view(“voxel”), … — or omit it to use the stream’s stored representation (from open(repr=…)), falling back to the polarity image. (Equivalent to stream.<repr>().view().)

warp_affine(matrix)

Applies a 2×3 affine matrix [[a,b,c],[d,e,f]] (rounded, no interpolation).

warp_perspective(matrix)

Applies a 3×3 perspective (homography) matrix.

class eventcv.EventFrame

Bases: object

connected_components(*, connectivity=8)

Connected-component labelling (Phase 5): treats any non-zero pixel as foreground and labels each 4- or 8-connected blob 1..=k, background 0. Returns a single-channel u64 frame.

resize(width, height, *, pooling='average')

Resize spatial dimensions using average or sum pooling on shrinking axes.

save(path, *, colormap='viridis', normalize=True)

Saves the frame to path. .npz/.h5 store the raw array (shape, dtype, kind, channel_names) for eventcv.load_frame; .png writes a colormapped 2-D view (colormap = viridis/turbo/grayscale/redblue; normalize auto-contrasts).

view(*, colormap='viridis', normalize=True)

Opens the interactive GPU viewer. Image reprs are shown colour-mapped (colormap: viridis/turbo/grayscale/redblue; normalize auto-contrasts); volumetric reprs become an orbitable 3-D point cloud (drag to rotate, Esc to close).

class eventcv.EventReader

Bases: object

Lazy, seekable handle over a file’s events — the VideoCapture to load’s imread. Slices are fetched on demand (HDF5 by binary-searching its timestamps on disk), so multi-GB files never need to be fully resident. Opening with dt_ms fixes a frame duration so slice(n) returns the n-th frame (like seeking a video).

batch(indices)

Renders slice indices into one dense [B, C, H, W] array — the explicit-batch path for training. Requires a representation (open(repr=…) / with_repr). indices is any int sequence (list / range / …); each is a dt_ms frame index.

dt_ms

The fixed slice duration set at open, or None if it was not given.

efast()

Returns a new reader whose every slice is passed through [EventStream::efast], so the reader yields corner sub-streams (chain .count() / with_repr to visualise them).

harris_corners(threshold=0.0)

Returns a new reader whose every slice is passed through [EventStream::harris_corners].

max_events

The fixed events-per-slice set at open(max_events=…), or None if it was not given.

n_slices

Number of fixed slices spanning the recording (requires open(dt_ms=…) or open(max_events=…)).

offset

The absolute framing origin (ms, the file’s time base) set at open(offset=…), or None if it was not given.

repr

The per-slice representation name set at open/with_repr, or None (raw streams).

slice(index=None, *, t0_ms=None, t1_ms=None)

One slice as an EventStream. With a positional index n (requires open(dt_ms=…) or open(max_events=…)), returns the n-th fixed frame — the dt_ms-long window [t_min + n·dt, t_min + (n+1)·dt), or the max_events-sized event chunk [n·N, (n+1)·N); negative n counts from the end. Otherwise returns the half-open time window [t0_ms, t1_ms), with omitted bounds extending to the recording’s start / end.

slice_count(i0, i1)

Events whose index lies in [i0, i1) (clamped to the file).

windows(*, step_ms=None, span_ms=None)

Lazy iterator of consecutive windows: each is [start, start + span_ms) and start advances by step_ms. step_ms defaults to the dt_ms set at open (so windows() walks every slice(n)), and span_ms defaults to step_ms (non-overlapping). For a max_events reader, windows() without arguments walks the fixed-count slices instead. Streams a multi-GB file window-by-window without loading it.

with_repr(repr, *, bins=None, window_ms=None, tau_ms=None, max_window_ms=None, window=None, normalize=None)

Returns a new reader over the same file that renders each slice with repr (unset params take their method defaults). The dataset-mode counterpart of open(repr=…), but with per-representation options: e.g. reader.with_repr(“voxel”, bins=5).

class eventcv.EventPointSet

Bases: object

class eventcv.Camera(fx, fy, cx, cy, *, k1=0.0, k2=0.0, p1=0.0, p2=0.0, k3=0.0)

Bases: object

Pinhole intrinsics + Brown–Conrady distortion, e.g. an EV-IMO calib.txt (fx fy cx cy k1 k2 p1 p2). Pass to stream.undistort(camera).

undistort_point(u, v)

Maps a distorted pixel (u, v) to its undistorted location.

Functional (OpenCV-style) API

Each function below forwards to the identically named method on a stream or frame — e.g. eventcv.voxel(stream, bins=5) is stream.voxel(bins=5). They are generated by introspecting the compiled types, so this list always matches the methods above.

eventcv.atsurf(obj, *args, **kwargs)

Averaged time surface — the per-pixel mean of exp(-age/tau_ms) over all events

Free-function form of obj.atsurf(*args, **kwargs).

eventcv.background_activity_filter(obj, *args, **kwargs)

Background-activity (nearest-neighbour) noise filter: keeps an event only if a 3×3

Free-function form of obj.background_activity_filter(*args, **kwargs).

eventcv.concat(obj, *args, **kwargs)

Concatenates this stream with others (argument order; sensor = element-wise max).

Free-function form of obj.concat(*args, **kwargs).

eventcv.connected_components(obj, *args, **kwargs)

Connected-component labelling (Phase 5): treats any non-zero pixel as foreground and labels

Free-function form of obj.connected_components(*args, **kwargs).

eventcv.count(obj, *args, **kwargs)

Event-count image — one channel of total events per pixel (both polarities). Raw

Free-function form of obj.count(*args, **kwargs).

eventcv.crop(obj, *args, **kwargs)

Keeps events inside the w`×`h window at (x0, y0), shifted to a new origin.

Free-function form of obj.crop(*args, **kwargs).

eventcv.decimate(obj, *args, **kwargs)

Keeps every k-th event by index.

Free-function form of obj.decimate(*args, **kwargs).

eventcv.efast(obj, *args, **kwargs)

eFAST event corner detector (Mueggler et al., BMVC 2017). Keeps the events sitting on a

Free-function form of obj.efast(*args, **kwargs).

eventcv.filter_polarity(obj, *args, **kwargs)

Keeps only events of the given polarity (nonzero / True = ON, 0 / False = OFF).

Free-function form of obj.filter_polarity(*args, **kwargs).

eventcv.flatten(obj, *args, **kwargs)

Calls obj.flatten(...).

Free-function form of obj.flatten(*args, **kwargs).

eventcv.flip_x(obj, *args, **kwargs)

Mirrors horizontally (x → width-1-x).

Free-function form of obj.flip_x(*args, **kwargs).

eventcv.flip_y(obj, *args, **kwargs)

Mirrors vertically (y → height-1-y).

Free-function form of obj.flip_y(*args, **kwargs).

eventcv.harris_corners(obj, *args, **kwargs)

Harris corner score on the Surface of Active Events: keeps events whose Harris response

Free-function form of obj.harris_corners(*args, **kwargs).

eventcv.hot_pixel_filter(obj, *args, **kwargs)

Hot-pixel removal: drops pixels whose event count exceeds mean + n_std·std over the

Free-function form of obj.hot_pixel_filter(*args, **kwargs).

eventcv.invert_polarity(obj, *args, **kwargs)

Flips every event’s polarity.

Free-function form of obj.invert_polarity(*args, **kwargs).

eventcv.mask(obj, *args, **kwargs)

Keeps events where the (H, W) boolean mask is True.

Free-function form of obj.mask(*args, **kwargs).

eventcv.mcts(obj, *args, **kwargs)

Calls obj.mcts(...).

Free-function form of obj.mcts(*args, **kwargs).

eventcv.normalize_time(obj, *args, **kwargs)

Shifts timestamps so the earliest event starts at zero.

Free-function form of obj.normalize_time(*args, **kwargs).

eventcv.numpy(obj, *args, **kwargs)

Calls obj.numpy(...).

Free-function form of obj.numpy(*args, **kwargs).

eventcv.optical_flow(obj, *args, **kwargs)

Dense Lucas-Kanade optical flow on the time surface. Returns a two-channel `(flow_x,

Free-function form of obj.optical_flow(*args, **kwargs).

eventcv.pset(obj, *args, **kwargs)

Calls obj.pset(...).

Free-function form of obj.pset(*args, **kwargs).

eventcv.refractory_filter(obj, *args, **kwargs)

Refractory-period filter: suppresses a pixel’s events for dt after it fires.

Free-function form of obj.refractory_filter(*args, **kwargs).

eventcv.resize(obj, *args, **kwargs)

Event-domain resize to a width`×`height grid (rebinned, not interpolated).

Free-function form of obj.resize(*args, **kwargs).

eventcv.rotate90(obj, *args, **kwargs)

Rotates by k * 90° clockwise (quarter turns swap the sensor dims).

Free-function form of obj.rotate90(*args, **kwargs).

eventcv.scale(obj, *args, **kwargs)

Scales the sensor by (sx, sy).

Free-function form of obj.scale(*args, **kwargs).

eventcv.sort_by_time(obj, *args, **kwargs)

Returns a copy reordered by ascending timestamp (stable).

Free-function form of obj.sort_by_time(*args, **kwargs).

eventcv.tencode(obj, *args, **kwargs)

Calls obj.tencode(...).

Free-function form of obj.tencode(*args, **kwargs).

eventcv.time_scale(obj, *args, **kwargs)

Scales every timestamp by factor (rounded).

Free-function form of obj.time_scale(*args, **kwargs).

eventcv.time_shift(obj, *args, **kwargs)

Shifts every timestamp by dt microseconds.

Free-function form of obj.time_shift(*args, **kwargs).

eventcv.time_window(obj, *args, **kwargs)

Keeps events whose timestamp lies in the half-open window [t0, t1) (microseconds).

Free-function form of obj.time_window(*args, **kwargs).

eventcv.translate(obj, *args, **kwargs)

Translates by (dx, dy); events shifted off the sensor are dropped.

Free-function form of obj.translate(*args, **kwargs).

eventcv.transpose(obj, *args, **kwargs)

Reflects across the main diagonal ((x, y) → (y, x)); swaps the sensor dims.

Free-function form of obj.transpose(*args, **kwargs).

eventcv.tsurf(obj, *args, **kwargs)

Calls obj.tsurf(...).

Free-function form of obj.tsurf(*args, **kwargs).

eventcv.undistort(obj, *args, **kwargs)

Rectifies events with a Camera’s intrinsics + distortion (lens undistortion).

Free-function form of obj.undistort(*args, **kwargs).

eventcv.view(obj, *args, **kwargs)

Opens the interactive viewer on this stream. Pass a representation name to choose what

Free-function form of obj.view(*args, **kwargs).

eventcv.voxel(obj, *args, **kwargs)

Calls obj.voxel(...).

Free-function form of obj.voxel(*args, **kwargs).

eventcv.warp_affine(obj, *args, **kwargs)

Applies a 2×3 affine matrix [[a,b,c],[d,e,f]] (rounded, no interpolation).

Free-function form of obj.warp_affine(*args, **kwargs).

eventcv.warp_perspective(obj, *args, **kwargs)

Applies a 3×3 perspective (homography) matrix.

Free-function form of obj.warp_perspective(*args, **kwargs).