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