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 as i64 - 1;
18 self.remap(width, height, |x, y, t, p| Some((max_x - x, y, t, p)))
19 }
20
21 pub fn flip_y(&self) -> EventStream {
23 let (width, height) = self.sensor_size();
24 let max_y = height as i64 - 1;
25 self.remap(width, height, |x, y, t, p| Some((x, max_y - y, t, p)))
26 }
27
28 pub fn rotate90(&self, k: i32) -> EventStream {
30 let (width, height) = self.sensor_size();
31 let (max_x, max_y) = (width as i64 - 1, height as i64 - 1);
32 match k.rem_euclid(4) {
33 0 => self.remap(width, height, |x, y, t, p| Some((x, y, t, p))),
34 1 => self.remap(height, width, |x, y, t, p| Some((max_y - y, x, t, p))),
35 2 => self.remap(width, height, |x, y, t, p| {
36 Some((max_x - x, max_y - y, t, p))
37 }),
38 _ => self.remap(height, width, |x, y, t, p| Some((y, max_x - x, t, p))),
39 }
40 }
41
42 pub fn transpose(&self) -> EventStream {
44 let (width, height) = self.sensor_size();
45 self.remap(height, width, |x, y, t, p| Some((y, x, t, p)))
46 }
47
48 pub fn translate(&self, dx: i64, dy: i64) -> EventStream {
50 let (width, height) = self.sensor_size();
51 self.remap(width, height, |x, y, t, p| Some((x + dx, y + dy, t, p)))
52 }
53
54 pub fn resize(&self, w: usize, h: usize) -> EventStream {
58 let (width, height) = self.sensor_size();
59 let sx = if width > 0 {
60 w as f64 / width as f64
61 } else {
62 0.0
63 };
64 let sy = if height > 0 {
65 h as f64 / height as f64
66 } else {
67 0.0
68 };
69 self.remap(w, h, |x, y, t, p| {
70 Some((
71 (x as f64 * sx).floor() as i64,
72 (y as f64 * sy).floor() as i64,
73 t,
74 p,
75 ))
76 })
77 }
78
79 pub fn scale(&self, sx: f64, sy: f64) -> EventStream {
81 let (width, height) = self.sensor_size();
82 let w = (width as f64 * sx).round().max(0.0) as usize;
83 let h = (height as f64 * sy).round().max(0.0) as usize;
84 self.resize(w, h)
85 }
86
87 pub fn warp_affine(&self, m: [[f64; 3]; 2]) -> EventStream {
90 let (width, height) = self.sensor_size();
91 self.remap(width, height, |x, y, t, p| {
92 let (xf, yf) = (x as f64, y as f64);
93 let nx = m[0][0] * xf + m[0][1] * yf + m[0][2];
94 let ny = m[1][0] * xf + m[1][1] * yf + m[1][2];
95 Some((nx.round() as i64, ny.round() as i64, t, p))
96 })
97 }
98
99 pub fn warp_perspective(&self, m: [[f64; 3]; 3]) -> EventStream {
102 let (width, height) = self.sensor_size();
103 self.remap(width, height, |x, y, t, p| {
104 let (xf, yf) = (x as f64, y as f64);
105 let w = m[2][0] * xf + m[2][1] * yf + m[2][2];
106 if w == 0.0 {
107 return None;
108 }
109 let nx = (m[0][0] * xf + m[0][1] * yf + m[0][2]) / w;
110 let ny = (m[1][0] * xf + m[1][1] * yf + m[1][2]) / w;
111 Some((nx.round() as i64, ny.round() as i64, t, p))
112 })
113 }
114
115 pub fn undistort(&self, camera: &Camera) -> EventStream {
120 let (width, height) = self.sensor_size();
121 if width == 0 || height == 0 {
122 return self.clone();
123 }
124 let lut: Vec<(i64, i64)> = (0..width * height)
125 .map(|i| {
126 let (u, v) = camera.undistort_point((i % width) as f64, (i / width) as f64);
127 (u.round() as i64, v.round() as i64)
128 })
129 .collect();
130 self.remap(width, height, |x, y, t, p| {
131 let (nx, ny) = lut[y as usize * width + x as usize];
132 Some((nx, ny, t, p))
133 })
134 }
135
136 pub fn mask(&self, mask: &[bool], mask_w: usize, mask_h: usize) -> EventStream {
139 let (width, height) = self.sensor_size();
140 self.remap(width, height, |x, y, t, p| {
141 let (ux, uy) = (x as usize, y as usize);
142 let keep = ux < mask_w && uy < mask_h && mask[uy * mask_w + ux];
143 keep.then_some((x, y, t, p))
144 })
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use crate::{EventStream, EventStreamBuilder};
151
152 fn sample() -> EventStream {
154 let mut builder = EventStreamBuilder::new(4, 3, 0.001);
155 builder.push(0, 0, 10, true);
156 builder.push(1, 1, 20, false);
157 builder.push(2, 2, 30, true);
158 builder.push(3, 0, 40, false);
159 builder.build()
160 }
161
162 fn coords(stream: &EventStream) -> Vec<(u16, u16)> {
163 stream
164 .xs()
165 .iter()
166 .copied()
167 .zip(stream.ys().iter().copied())
168 .collect()
169 }
170
171 #[test]
172 fn crop_subsets_and_shifts_to_new_origin() {
173 let cropped = sample().crop(1, 1, 2, 2);
174 assert_eq!(cropped.sensor_size(), (2, 2));
175 assert_eq!(coords(&cropped), vec![(0, 0), (1, 1)]); assert_eq!(cropped.ts(), &[20, 30]);
177 }
178
179 #[test]
180 fn flips_are_their_own_inverse() {
181 let s = sample();
182 assert_eq!(coords(&s.flip_x().flip_x()), coords(&s));
183 assert_eq!(coords(&s.flip_y().flip_y()), coords(&s));
184 assert_eq!(s.flip_x().sensor_size(), (4, 3));
185 assert_eq!(coords(&s.flip_x()), vec![(3, 0), (2, 1), (1, 2), (0, 0)]);
186 }
187
188 #[test]
189 fn rotate90_swaps_dims_and_round_trips() {
190 let s = sample();
191 assert_eq!(s.rotate90(1).sensor_size(), (3, 4)); assert_eq!(s.rotate90(2).sensor_size(), (4, 3));
193 assert_eq!(coords(&s.rotate90(4)), coords(&s));
195 assert_eq!(coords(&s.rotate90(1).rotate90(3)), coords(&s));
196 assert_eq!(coords(&s.rotate90(-1)), coords(&s.rotate90(3)));
197 }
198
199 #[test]
200 fn transpose_swaps_axes_and_dims() {
201 let t = sample().transpose();
202 assert_eq!(t.sensor_size(), (3, 4));
203 assert_eq!(coords(&t), vec![(0, 0), (1, 1), (2, 2), (0, 3)]);
204 }
205
206 #[test]
207 fn translate_shifts_and_drops_out_of_bounds() {
208 let shifted = sample().translate(2, 0);
210 assert_eq!(shifted.xs(), &[2, 3]);
211 assert_eq!(shifted.ys(), &[0, 1]);
212 assert_eq!(coords(&shifted.translate(-2, 0)), vec![(0, 0), (1, 1)]);
214 }
215
216 #[test]
217 fn resize_rebins_and_conserves_count() {
218 let down = sample().resize(2, 2); assert_eq!(down.sensor_size(), (2, 2));
220 assert_eq!(down.len(), 4);
221 assert!(down.xs().iter().all(|&x| x < 2) && down.ys().iter().all(|&y| y < 2));
222 assert_eq!(sample().scale(2.0, 2.0).sensor_size(), (8, 6));
223 }
224
225 #[test]
226 fn warp_affine_identity_and_translation() {
227 let s = sample();
228 let identity = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
229 assert_eq!(coords(&s.warp_affine(identity)), coords(&s));
230 let shift = [[1.0, 0.0, 1.0], [0.0, 1.0, 0.0]];
231 assert_eq!(coords(&s.warp_affine(shift)), coords(&s.translate(1, 0)));
232 }
233
234 #[test]
235 fn warp_perspective_identity_round_trips() {
236 let identity = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
237 let s = sample();
238 assert_eq!(coords(&s.warp_perspective(identity)), coords(&s));
239 }
240
241 #[test]
242 fn mask_keeps_only_selected_pixels() {
243 let s = sample(); let mut mask = vec![false; 4 * 3];
245 mask[4 + 1] = true; let masked = s.mask(&mask, 4, 3);
247 assert_eq!(coords(&masked), vec![(1, 1)]);
248 }
249
250 #[test]
251 fn undistort_without_distortion_keeps_events_in_place() {
252 use crate::camera::Camera;
253 let s = sample(); let camera = Camera::new(100.0, 100.0, 2.0, 1.5); assert_eq!(coords(&s.undistort(&camera)), coords(&s));
256 assert_eq!(s.undistort(&camera).sensor_size(), (4, 3));
257 }
258
259 #[test]
260 fn undistort_remaps_under_distortion() {
261 use crate::camera::Camera;
262 let s = sample();
263 let camera = Camera::with_distortion(50.0, 50.0, 2.0, 1.5, -0.2, 0.0, 0.0, 0.0, 0.0);
266 let out = s.undistort(&camera);
267 assert_eq!(out.sensor_size(), (4, 3));
268 assert!(out.len() <= s.len());
269 assert!(out.xs().iter().all(|&x| x < 4) && out.ys().iter().all(|&y| y < 3));
270 }
271
272 #[test]
273 fn transforms_handle_the_empty_stream() {
274 let empty = EventStreamBuilder::new(4, 3, 0.001).build();
275 assert!(empty.flip_x().is_empty());
276 assert!(empty.rotate90(1).is_empty());
277 assert_eq!(empty.crop(0, 0, 2, 2).sensor_size(), (2, 2));
278 assert!(empty.resize(2, 2).is_empty());
279 }
280}