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-IMOt x y p),.bag(ROSdvs_msgs/EventArray),.hdf5/.h5,.aedat(AEDAT 2.0, jAER/DAVIS), and.dat(Prophesee CD events).sensor_sizeandtime_unitare 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. Passingsensor_sizefor HDF5 also skips that scan.time_unitisseconds/milliseconds/microseconds/nanoseconds(orauto);order(txyp/xytp) applies to text.topicselects the rosbag topic (default/davis/left/events).offsetis an absolute timestamp in milliseconds (the file’s own time base — the same base asstream.numpy()[:, 2]scaled to ms): events before it are skipped, andmax_eventsthen 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);<= 0reads 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
EventStreamfrom an in-memory(N, 4)NumPy array.The constructor mirror of
EventStream.numpy():orderdefaults toxytp(the column layoutnumpy()emits, with timestamps in microseconds), soecv.from_numpy(stream.numpy(), time_unit="us")round-trips a stream. Passorder="txyp"for arrays in the commont x y pdataset layout. Any integer or float dtype is accepted; polarity is positive when its value is greater than zero (both0/1and-1/1conventions work).sensor_sizeandtime_unitare auto-detected when omitted, exactly likeload(): 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 passtime_unitexplicitly for short arrays). Events outside an explicitsensor_sizeare 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’simread(read the entire stream eagerly),openis itsVideoCapture: it returns anEventReaderthat 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_msto treat the recording as a sequence of fixed-duration frames: the reader reportsn_slicesandreader.slice(n)returns then-th frame (reader[n]works too). Framenis measured from the recording start, so you never deal with absolute timestamps (which may be epoch-based).max_eventsis 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.offsetis an absolute timestamp in milliseconds (the file’s own time base — exactly whatslice(t0_ms=…)takes) that moves the framing origin:slice(0),windows(), andn_slicesall begin at that time, and events before it fall outside every indexed frame. It is clamped up tot_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 eitherdt_msormax_events.sensor_sizeandtime_unitare auto-detected when omitted (seeload());order/topicmatchload(). For a multi-GB HDF5, passsensor_sizeto 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 framei, andreader.batch(indices)stacks a[B, C, H, W]batch — so aDataLoadercan collate the reader directly. Usereader.with_repr(name, **opts)to set per-representation options (e.g.bins=5, orwindow=5for"flow"). Withoutrepr,reader[i]stays a rawEventStream; to still batch those through aDataLoaderpasscollate_fn=eventcv.collate(each batch is alist[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 withslice/windows/with_repr. To render an algorithm as a video, map it overwindows()and hand the frames toexport_png()(then assemble with ffmpeg).Note
reprgoverns the array/dataset path (reader[i]→ NumPy). To view a slice interactively,slice(i)returns the rawEventStream, 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
EventStreamorEventFrametopath.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 storest x y pand recovers the sensor size/unit on load via inference or options). Frames (computed representations) go to.npzor.h5, preserving shape, dtype,kind, andchannel_names.topicnames the rosbag connection. Equivalent toobj.save(path).
- eventcv.load_frame(path: str) EventFrame[source]¶
Load an
EventFramewritten bysave()(.npzor.h5).Restores the representation’s shape, dtype,
kind, andchannel_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
EventFrames to numbered.pngfiles — the “frame sequence → video frames” export.framesis a singleEventFrameor 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;normalizeauto-contrasts). Files are named{prefix}{index:0{digits}d}.pngcounting fromstart. 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_fnfortorch.utils.data.DataLoaderover anEventReader.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 withoutrepr, whosereader[i]is a rawEventStream. Those are variable-length and sparse, so they can’t stack into a tensor; this returns the batch as a plainlistof 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:
objectLazy, 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.Camera(fx, fy, cx, cy, *, k1=0.0, k2=0.0, p1=0.0, p2=0.0, k3=0.0)¶
Bases:
objectPinhole 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).