Quickstart

Load a recording

eventcv.load() reads an entire stream, detecting the format from the file extension. Sensor size and time unit are auto-detected; pass them only to override.

import eventcv as ecv

stream = ecv.load("recording.hdf5")
print(stream.sensor_size)      # (width, height)
print(len(stream))             # number of events
events = stream.numpy()        # N×4 array of [x, y, t, p]

To read only part of a large recording, offset skips events before an absolute timestamp (in milliseconds, the file’s own time base) and max_events caps how many are kept after it:

window = ecv.load("recording.hdf5", offset=1_000, max_events=500_000)   # from t = 1 s on

Already have events in memory (e.g. from another tool)? eventcv.from_numpy() is the inverse of numpy() — build a stream from an (N, 4) array. Columns default to xytp (what numpy() emits); sensor size and time unit are inferred when omitted:

import numpy as np

events = np.array([[0, 0, 100, 1], [1, 2, 250, 0]])   # x y t p
stream = ecv.from_numpy(events, time_unit="us")

Transform (chainable, functional)

Transforms return a new stream, so they chain — and every one is also a free function taking the stream as its first argument.

# method form
clipped = stream.crop(0, 0, 320, 240).flip_x().hot_pixel_filter()

# equivalent free-function (OpenCV-style) form
clipped = ecv.hot_pixel_filter(ecv.flip_x(ecv.crop(stream, 0, 0, 320, 240)))

Represent as a dense tensor

voxel = stream.voxel(bins=5)   # or ecv.voxel(stream, bins=5)
array = voxel.numpy()          # [C, H, W] ndarray, ready for NumPy / PyTorch

Stream a large file

eventcv.open() returns a lazy EventReader that slices the file on demand instead of materialising it.

reader = ecv.open("huge.hdf5", dt_ms=30)   # treat as 30 ms frames
print(reader.n_slices)
frame = reader.slice(50).voxel()           # the 50th 30 ms frame → voxel grid

Slice by a fixed event count instead of a fixed duration with max_events — each slice holds exactly that many consecutive events (the last may be shorter), which keeps the number of events per frame constant rather than the time span. It is mutually exclusive with dt_ms:

reader = ecv.open("huge.hdf5", max_events=10_000)   # 10k-event frames
frame = reader.slice(50).voxel()                    # the 50th 10k-event frame

Pass offset — an absolute timestamp in milliseconds (the same time base as slice(t0_ms=…)) — to move the framing origin: slice(0), windows(), and n_slices all begin at that time, and earlier events fall outside every frame. It composes with either dt_ms or max_events:

reader = ecv.open("huge.hdf5", dt_ms=30, offset=1_000)   # frame 0 starts at t = 1 s

For a recording whose timestamps are epoch-based, offset is just that epoch time in ms (e.g. offset=1_587_540_271_650). Values before the recording clamp to the start; values past the end give zero frames.

Pass repr= to give the reader a default representation. It is remembered by every slice, so slice(n).view() renders it (no need to name it again), and it makes the reader a PyTorch map-style dataset where reader[i] is the dense [C, H, W] array:

import torch

reader = ecv.open("huge.hdf5", dt_ms=30, repr="mcts")
reader.slice(500).view()               # shows MCTS — the stored repr (not raw polarity)
reader.slice(500).view("count")        # an explicit name still overrides it

# as a PyTorch map-style dataset (dense arrays collate straight into a tensor)
ds = ecv.open("huge.hdf5", dt_ms=30, repr="count")
# len(ds) == n_slices; ds[i] -> [C, H, W]; ds.batch(idxs) -> [B, C, H, W]
loader = torch.utils.data.DataLoader(ds, batch_size=32, shuffle=True)

Without repr, reader[i] stays a raw EventStream. Those are sparse and variable-length, so a DataLoader can’t stack them — pass eventcv.collate() to batch them as a list instead:

import torch

reader = ecv.open("huge.hdf5", dt_ms=30)
loader = torch.utils.data.DataLoader(reader, batch_size=32, collate_fn=ecv.collate)
for batch in loader:                   # batch is a list[EventStream]
    batch[0].view()

Save

eventcv.save() mirrors load(), choosing the format by extension.

ecv.save(clipped, "out.npz")        # streams: .npz / .txt / .h5 / .bag
ecv.save(voxel, "voxel.h5")         # frames: .npz / .h5

See the Representations page for every available representation (voxel, tsurf, atsurf, count, tencode, mcts, flow, pset, …) with its parameters and output shape, and the API reference for the full list of readers and transforms.