1use ndarray::Array2;
24
25pub mod camera;
26pub mod cluster;
27pub mod features;
28pub mod filter;
29pub mod flow;
30pub mod image;
31pub mod io;
32pub mod representation;
33pub mod transform;
34pub mod viz;
35
36const COLUMN_COUNT: usize = 4;
37
38#[derive(Clone, Debug)]
42pub struct EventStream {
43 xs: Vec<u16>,
44 ys: Vec<u16>,
45 ts: Vec<i64>,
46 ps: Vec<bool>,
47 width: usize,
48 height: usize,
49 timestamp_scale_ms: f64,
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
53pub struct Event {
54 pub x: usize,
55 pub y: usize,
56 pub timestamp: u64,
57 pub polarity: bool,
58}
59
60impl EventStream {
61 pub fn len(&self) -> usize {
62 self.xs.len()
63 }
64
65 pub fn is_empty(&self) -> bool {
66 self.xs.is_empty()
67 }
68
69 pub fn sensor_size(&self) -> (usize, usize) {
70 (self.width, self.height)
71 }
72
73 pub fn timestamp_scale_ms(&self) -> f64 {
74 self.timestamp_scale_ms
75 }
76
77 pub fn xs(&self) -> &[u16] {
78 &self.xs
79 }
80
81 pub fn ys(&self) -> &[u16] {
82 &self.ys
83 }
84
85 pub fn ts(&self) -> &[i64] {
86 &self.ts
87 }
88
89 pub fn ps(&self) -> &[bool] {
90 &self.ps
91 }
92
93 pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
94 (0..self.len()).map(move |index| Event {
95 x: self.xs[index] as usize,
96 y: self.ys[index] as usize,
97 timestamp: self.ts[index] as u64,
98 polarity: self.ps[index],
99 })
100 }
101
102 pub fn to_array2(&self) -> Array2<u64> {
104 let mut values = Vec::with_capacity(self.len() * COLUMN_COUNT);
105 for index in 0..self.len() {
106 values.push(u64::from(self.xs[index]));
107 values.push(u64::from(self.ys[index]));
108 values.push(self.ts[index] as u64);
109 values.push(u64::from(self.ps[index]));
110 }
111 Array2::from_shape_vec((self.len(), COLUMN_COUNT), values)
112 .expect("columns share a length by construction")
113 }
114
115 #[cfg(test)]
118 pub(crate) fn from_array2(
119 events: Array2<u64>,
120 width: usize,
121 height: usize,
122 timestamp_scale_ms: f64,
123 ) -> Self {
124 Self {
125 xs: events.column(0).iter().map(|&value| value as u16).collect(),
126 ys: events.column(1).iter().map(|&value| value as u16).collect(),
127 ts: events.column(2).iter().map(|&value| value as i64).collect(),
128 ps: events.column(3).iter().map(|&value| value != 0).collect(),
129 width,
130 height,
131 timestamp_scale_ms,
132 }
133 }
134}
135
136#[derive(Clone, Debug)]
139pub struct EventStreamBuilder {
140 xs: Vec<u16>,
141 ys: Vec<u16>,
142 ts: Vec<i64>,
143 ps: Vec<bool>,
144 width: usize,
145 height: usize,
146 timestamp_scale_ms: f64,
147}
148
149impl EventStreamBuilder {
150 pub fn new(width: usize, height: usize, timestamp_scale_ms: f64) -> Self {
151 Self::with_capacity(width, height, timestamp_scale_ms, 0)
152 }
153
154 pub fn with_capacity(
155 width: usize,
156 height: usize,
157 timestamp_scale_ms: f64,
158 capacity: usize,
159 ) -> Self {
160 Self {
161 xs: Vec::with_capacity(capacity),
162 ys: Vec::with_capacity(capacity),
163 ts: Vec::with_capacity(capacity),
164 ps: Vec::with_capacity(capacity),
165 width,
166 height,
167 timestamp_scale_ms,
168 }
169 }
170
171 pub fn push(&mut self, x: u16, y: u16, timestamp: i64, polarity: bool) -> bool {
174 if usize::from(x) >= self.width || usize::from(y) >= self.height {
175 return false;
176 }
177 self.xs.push(x);
178 self.ys.push(y);
179 self.ts.push(timestamp);
180 self.ps.push(polarity);
181 true
182 }
183
184 pub fn len(&self) -> usize {
185 self.xs.len()
186 }
187
188 pub fn is_empty(&self) -> bool {
189 self.xs.is_empty()
190 }
191
192 pub fn build(self) -> EventStream {
193 EventStream {
194 xs: self.xs,
195 ys: self.ys,
196 ts: self.ts,
197 ps: self.ps,
198 width: self.width,
199 height: self.height,
200 timestamp_scale_ms: self.timestamp_scale_ms,
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use std::path::PathBuf;
208
209 use crate::io::{load, LoadOptions};
210
211 use super::{EventStream, EventStreamBuilder};
212
213 #[test]
214 fn loads_n_imagenet_events() {
215 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../data/test/example.npz");
216 let stream = load(path, LoadOptions::default()).unwrap();
217 let events = stream.to_array2();
218
219 assert!(!stream.is_empty());
220 assert_eq!(stream.sensor_size(), (640, 480));
221 assert_eq!(events.dim(), (stream.len(), 4));
222 assert!(events.column(0).iter().all(|&x| x < 640));
223 assert!(events.column(1).iter().all(|&y| y < 480));
224 assert!(events.column(3).iter().all(|&polarity| polarity <= 1));
225 }
226
227 #[test]
228 fn builder_drops_out_of_bounds_events_and_keeps_columns_aligned() {
229 let mut builder = EventStreamBuilder::new(4, 3, 0.001);
230
231 assert!(builder.push(1, 2, 10, true));
232 assert!(!builder.push(4, 0, 20, false)); assert!(!builder.push(0, 3, 30, true)); assert!(builder.push(3, 0, 40, false));
235
236 let stream = builder.build();
237 assert_eq!(stream.len(), 2);
238 assert_eq!(stream.xs(), &[1, 3]);
239 assert_eq!(stream.ys(), &[2, 0]);
240 assert_eq!(stream.ts(), &[10, 40]);
241 assert_eq!(stream.ps(), &[true, false]);
242 assert_eq!(stream.sensor_size(), (4, 3));
243 }
244
245 #[test]
246 fn to_array2_round_trips_columns_in_xytp_order() {
247 let stream =
248 EventStream::from_array2(ndarray::array![[1, 2, 100, 1], [3, 0, 250, 0]], 4, 3, 0.001);
249 let events = stream.to_array2();
250
251 assert_eq!(events.dim(), (2, 4));
252 assert_eq!(events.row(0).to_vec(), vec![1, 2, 100, 1]);
253 assert_eq!(events.row(1).to_vec(), vec![3, 0, 250, 0]);
254 }
255}