1use crate::camera::Camera;
5use crate::EventStream;
6
7impl EventStream {
8 pub fn crop(&self, x0: i64, y0: i64, w: usize, h: usize) -> EventStream {
11 self.remap(w, h, |x, y, t, p| Some((x - x0, y - y0, t, p)))
12 }
13
14 pub fn flip_x(&self) -> EventStream {
16 let (width, height) = self.sensor_size();
17 let max_x = width.saturating_sub(1) as u16;
18 self.map_columns(width, height, |out| {
19 for x in &mut out.xs {
20 *x = max_x - *x;
21 }
22 })
23 }
24
25 pub fn flip_y(&self) -> EventStream {
27 let (width, height) = self.sensor_size();
28 let max_y = height.saturating_sub(1) as u16;
29 self.map_columns(width, height, |out| {
30 for y in &mut out.ys {
31 *y = max_y - *y;
32 }
33 })
34 }
35
36 pub fn rotate90(&self, k: i32) -> EventStream {
41 let (width, height) = self.sensor_size();
42 let (max_x, max_y) = (
43 width.saturating_sub(1) as u16,
44 height.saturating_sub(1) as u16,
45 );
46 match k.rem_euclid(4) {
47 0 => self.map_columns(width, height, |_| {}),
48 1 => self.map_columns(height, width, |out| {
49 std::mem::swap(&mut out.xs, &mut out.ys);
50 for x in &mut out.xs {
51 *x = max_y - *x;
52 }
53 }),
54 2 => self.map_columns(width, height, |out| {
55 for x in &mut out.xs {
56 *x = max_x - *x;
57 }
58 for y in &mut out.ys {
59 *y = max_y - *y;
60 }
61 }),
62 _ => self.map_columns(height, width, |out| {
63 std::mem::swap(&mut out.xs, &mut out.ys);
64 for y in &mut out.ys {
65 *y = max_x - *y;
66 }
67 }),
68 }
69 }
70
71 pub fn transpose(&self) -> EventStream {
73 let (width, height) = self.sensor_size();
74 self.map_columns(height, width, |out| {
75 std::mem::swap(&mut out.xs, &mut out.ys)
76 })
77 }
78
79 pub fn translate(&self, dx: i64, dy: i64) -> EventStream {
81 let (width, height) = self.sensor_size();
82 self.remap(width, height, |x, y, t, p| Some((x + dx, y + dy, t, p)))
83 }
84
85 pub fn resize(&self, w: usize, h: usize) -> EventStream {
89 let (width, height) = self.sensor_size();
90 let sx = if width > 0 {
91 w as f64 / width as f64
92 } else {
93 0.0
94 };
95 let sy = if height > 0 {
96 h as f64 / height as f64
97 } else {
98 0.0
99 };
100 self.map_columns(w, h, |out| {
101 for x in &mut out.xs {
102 *x = (f64::from(*x) * sx).floor() as u16;
103 }
104 for y in &mut out.ys {
105 *y = (f64::from(*y) * sy).floor() as u16;
106 }
107 })
108 }
109
110 pub fn scale(&self, sx: f64, sy: f64) -> EventStream {
112 let (width, height) = self.sensor_size();
113 let w = (width as f64 * sx).round().max(0.0) as usize;
114 let h = (height as f64 * sy).round().max(0.0) as usize;
115 self.resize(w, h)
116 }
117
118 pub fn warp_affine(&self, m: [[f64; 3]; 2]) -> EventStream {
121 let (width, height) = self.sensor_size();
122 self.remap(width, height, |x, y, t, p| {
123 let (xf, yf) = (x as f64, y as f64);
124 let nx = m[0][0] * xf + m[0][1] * yf + m[0][2];
125 let ny = m[1][0] * xf + m[1][1] * yf + m[1][2];
126 Some((nx.round() as i64, ny.round() as i64, t, p))
127 })
128 }
129
130 pub fn warp_perspective(&self, m: [[f64; 3]; 3]) -> EventStream {
133 let (width, height) = self.sensor_size();
134 self.remap(width, height, |x, y, t, p| {
135 let (xf, yf) = (x as f64, y as f64);
136 let w = m[2][0] * xf + m[2][1] * yf + m[2][2];
137 if w == 0.0 {
138 return None;
139 }
140 let nx = (m[0][0] * xf + m[0][1] * yf + m[0][2]) / w;
141 let ny = (m[1][0] * xf + m[1][1] * yf + m[1][2]) / w;
142 Some((nx.round() as i64, ny.round() as i64, t, p))
143 })
144 }
145
146 pub fn undistort(&self, camera: &Camera) -> EventStream {
151 let (width, height) = self.sensor_size();
152 if width == 0 || height == 0 {
153 return self.clone();
154 }
155 let lut: Vec<(i64, i64)> = (0..width * height)
156 .map(|i| {
157 let (u, v) = camera.undistort_point((i % width) as f64, (i / width) as f64);
158 (u.round() as i64, v.round() as i64)
159 })
160 .collect();
161 self.remap(width, height, |x, y, t, p| {
162 let (nx, ny) = lut[y as usize * width + x as usize];
163 Some((nx, ny, t, p))
164 })
165 }
166
167 pub fn mask(&self, mask: &[bool], mask_w: usize, mask_h: usize) -> EventStream {
170 let (width, height) = self.sensor_size();
171 self.remap(width, height, |x, y, t, p| {
172 let (ux, uy) = (x as usize, y as usize);
173 let keep = ux < mask_w && uy < mask_h && mask[uy * mask_w + ux];
174 keep.then_some((x, y, t, p))
175 })
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use crate::{EventStream, EventStreamBuilder};
182
183 fn sample() -> EventStream {
185 let mut builder = EventStreamBuilder::new(4, 3, 0.001);
186 builder.push(0, 0, 10, true);
187 builder.push(1, 1, 20, false);
188 builder.push(2, 2, 30, true);
189 builder.push(3, 0, 40, false);
190 builder.build()
191 }
192
193 fn coords(stream: &EventStream) -> Vec<(u16, u16)> {
194 stream
195 .xs()
196 .iter()
197 .copied()
198 .zip(stream.ys().iter().copied())
199 .collect()
200 }
201
202 #[test]
203 fn crop_subsets_and_shifts_to_new_origin() {
204 let cropped = sample().crop(1, 1, 2, 2);
205 assert_eq!(cropped.sensor_size(), (2, 2));
206 assert_eq!(coords(&cropped), vec![(0, 0), (1, 1)]); assert_eq!(cropped.ts(), &[20, 30]);
208 }
209
210 #[test]
211 fn flips_are_their_own_inverse() {
212 let s = sample();
213 assert_eq!(coords(&s.flip_x().flip_x()), coords(&s));
214 assert_eq!(coords(&s.flip_y().flip_y()), coords(&s));
215 assert_eq!(s.flip_x().sensor_size(), (4, 3));
216 assert_eq!(coords(&s.flip_x()), vec![(3, 0), (2, 1), (1, 2), (0, 0)]);
217 }
218
219 #[test]
220 fn rotate90_swaps_dims_and_round_trips() {
221 let s = sample();
222 assert_eq!(s.rotate90(1).sensor_size(), (3, 4)); assert_eq!(s.rotate90(2).sensor_size(), (4, 3));
224 assert_eq!(coords(&s.rotate90(4)), coords(&s));
226 assert_eq!(coords(&s.rotate90(1).rotate90(3)), coords(&s));
227 assert_eq!(coords(&s.rotate90(-1)), coords(&s.rotate90(3)));
228 }
229
230 #[test]
231 fn transpose_swaps_axes_and_dims() {
232 let t = sample().transpose();
233 assert_eq!(t.sensor_size(), (3, 4));
234 assert_eq!(coords(&t), vec![(0, 0), (1, 1), (2, 2), (0, 3)]);
235 }
236
237 #[test]
238 fn translate_shifts_and_drops_out_of_bounds() {
239 let shifted = sample().translate(2, 0);
241 assert_eq!(shifted.xs(), &[2, 3]);
242 assert_eq!(shifted.ys(), &[0, 1]);
243 assert_eq!(coords(&shifted.translate(-2, 0)), vec![(0, 0), (1, 1)]);
245 }
246
247 #[test]
248 fn resize_rebins_and_conserves_count() {
249 let down = sample().resize(2, 2); assert_eq!(down.sensor_size(), (2, 2));
251 assert_eq!(down.len(), 4);
252 assert!(down.xs().iter().all(|&x| x < 2) && down.ys().iter().all(|&y| y < 2));
253 assert_eq!(sample().scale(2.0, 2.0).sensor_size(), (8, 6));
254 }
255
256 #[test]
257 fn warp_affine_identity_and_translation() {
258 let s = sample();
259 let identity = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
260 assert_eq!(coords(&s.warp_affine(identity)), coords(&s));
261 let shift = [[1.0, 0.0, 1.0], [0.0, 1.0, 0.0]];
262 assert_eq!(coords(&s.warp_affine(shift)), coords(&s.translate(1, 0)));
263 }
264
265 #[test]
266 fn warp_perspective_identity_round_trips() {
267 let identity = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
268 let s = sample();
269 assert_eq!(coords(&s.warp_perspective(identity)), coords(&s));
270 }
271
272 #[test]
273 fn mask_keeps_only_selected_pixels() {
274 let s = sample(); let mut mask = vec![false; 4 * 3];
276 mask[4 + 1] = true; let masked = s.mask(&mask, 4, 3);
278 assert_eq!(coords(&masked), vec![(1, 1)]);
279 }
280
281 #[test]
282 fn undistort_without_distortion_keeps_events_in_place() {
283 use crate::camera::Camera;
284 let s = sample(); let camera = Camera::new(100.0, 100.0, 2.0, 1.5); assert_eq!(coords(&s.undistort(&camera)), coords(&s));
287 assert_eq!(s.undistort(&camera).sensor_size(), (4, 3));
288 }
289
290 #[test]
291 fn undistort_remaps_under_distortion() {
292 use crate::camera::Camera;
293 let s = sample();
294 let camera = Camera::with_distortion(50.0, 50.0, 2.0, 1.5, -0.2, 0.0, 0.0, 0.0, 0.0);
297 let out = s.undistort(&camera);
298 assert_eq!(out.sensor_size(), (4, 3));
299 assert!(out.len() <= s.len());
300 assert!(out.xs().iter().all(|&x| x < 4) && out.ys().iter().all(|&y| y < 3));
301 }
302
303 #[test]
306 fn bijective_transforms_agree_with_the_general_path() {
307 let mut builder = EventStreamBuilder::new(13, 7, 0.001);
308 for index in 0..60_u16 {
309 builder.push(index % 13, index % 7, i64::from(index) * 3, index % 3 == 0);
310 }
311 let s = builder.build();
312 let (w, h) = s.sensor_size();
313 let (max_x, max_y) = (w as i64 - 1, h as i64 - 1);
314 let (sx, sy) = (5.0 / w as f64, 4.0 / h as f64);
315
316 let cases: [(EventStream, EventStream); 6] = [
317 (
318 s.flip_x(),
319 s.remap(w, h, |x, y, t, p| Some((max_x - x, y, t, p))),
320 ),
321 (
322 s.flip_y(),
323 s.remap(w, h, |x, y, t, p| Some((x, max_y - y, t, p))),
324 ),
325 (
326 s.transpose(),
327 s.remap(h, w, |x, y, t, p| Some((y, x, t, p))),
328 ),
329 (
330 s.rotate90(1),
331 s.remap(h, w, |x, y, t, p| Some((max_y - y, x, t, p))),
332 ),
333 (
334 s.rotate90(3),
335 s.remap(h, w, |x, y, t, p| Some((y, max_x - x, t, p))),
336 ),
337 (
338 s.resize(5, 4),
339 s.remap(5, 4, |x, y, t, p| {
340 Some((
341 (x as f64 * sx).floor() as i64,
342 (y as f64 * sy).floor() as i64,
343 t,
344 p,
345 ))
346 }),
347 ),
348 ];
349
350 for (fast, general) in cases {
351 assert_eq!(fast.sensor_size(), general.sensor_size());
352 assert_eq!(coords(&fast), coords(&general));
353 assert_eq!(fast.ts(), general.ts());
354 assert_eq!(fast.ps(), general.ps());
355 }
356 }
357
358 #[test]
361 fn bijective_transforms_keep_every_event_in_bounds() {
362 let s = sample();
363 for out in [
364 s.flip_x(),
365 s.flip_y(),
366 s.transpose(),
367 s.rotate90(1),
368 s.rotate90(2),
369 s.rotate90(3),
370 s.resize(2, 2),
371 s.resize(9, 7),
372 s.time_shift(-5),
373 s.invert_polarity(),
374 ] {
375 let (width, height) = out.sensor_size();
376 assert_eq!(out.len(), s.len());
377 assert!(out.xs().iter().all(|&x| (x as usize) < width));
378 assert!(out.ys().iter().all(|&y| (y as usize) < height));
379 }
380 }
381
382 #[test]
383 fn transforms_handle_the_empty_stream() {
384 let empty = EventStreamBuilder::new(4, 3, 0.001).build();
385 assert!(empty.flip_x().is_empty());
386 assert!(empty.rotate90(1).is_empty());
387 assert_eq!(empty.crop(0, 0, 2, 2).sensor_size(), (2, 2));
388 assert!(empty.resize(2, 2).is_empty());
389 }
390}