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