Skip to main content

eventcv_core/
transform.rs

1//! Event-domain geometry and stream algebra — OpenCV-style transforms that operate on the
2//! sparse [`EventStream`] itself (not a dense frame), preserving per-event timestamps. Every
3//! op returns a **new** stream so calls chain. Coordinates are remapped and rounded — there is
4//! no pixel interpolation — and out-of-bounds events are dropped (downscale therefore lets
5//! several events share a pixel, which is lossless: representations handle the collisions).
6//!
7//! Frame-domain resize lives separately in [`crate::image`]; the two are complementary.
8
9mod algebra;
10mod spatial;
11mod temporal;
12
13use crate::{EventStream, EventStreamBuilder};
14
15impl EventStream {
16    /// Builds a new stream over a `(width, height)` grid by mapping each event through `f`.
17    /// `f` returns `None` to drop an event, or new `(x, y, t, p)` as `i64` coordinates;
18    /// events landing outside the grid (including negative coordinates) are dropped before the
19    /// `u16` cast. The single construction path shared by the geometric transforms.
20    pub(crate) fn remap(
21        &self,
22        width: usize,
23        height: usize,
24        f: impl Fn(i64, i64, i64, bool) -> Option<(i64, i64, i64, bool)>,
25    ) -> EventStream {
26        let mut builder =
27            EventStreamBuilder::with_capacity(width, height, self.timestamp_scale_ms(), self.len());
28        let (xs, ys, ts, ps) = (self.xs(), self.ys(), self.ts(), self.ps());
29        for index in 0..self.len() {
30            let Some((x, y, t, p)) = f(
31                i64::from(xs[index]),
32                i64::from(ys[index]),
33                ts[index],
34                ps[index],
35            ) else {
36                continue;
37            };
38            if (0..width as i64).contains(&x) && (0..height as i64).contains(&y) {
39                builder.push(x as u16, y as u16, t, p);
40            }
41        }
42        builder.build()
43    }
44}