1use std::{error::Error, fmt};
2
3use crate::{Event, EventStream};
4
5mod averaged_time_surface;
6mod binary;
7mod count;
8mod mcts;
9mod point_set;
10mod polarity;
11mod tencode;
12mod time_surface;
13mod voxel;
14
15pub use averaged_time_surface::AveragedTimeSurface;
16pub use binary::Binary;
17pub use count::EventCount;
18pub use mcts::Mcts;
19pub use point_set::{EventPointSet, PointSet};
20pub use polarity::Polarity;
21pub use tencode::Tencode;
22pub use time_surface::TimeSurface;
23pub use voxel::VoxelGrid;
24
25pub trait Representation {
26 type Output;
27
28 fn generate(&self, stream: &EventStream) -> Result<Self::Output, RepresentationError>;
29}
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
32pub enum RepresentationKind {
33 AveragedTimeSurface,
34 Binary,
35 Count,
36 Flow,
37 Labels,
38 Mcts,
39 Polarity,
40 Tencode,
41 TimeSurface,
42 Voxel,
43}
44
45impl RepresentationKind {
46 pub fn as_str(self) -> &'static str {
47 match self {
48 Self::AveragedTimeSurface => "atsurf",
49 Self::Binary => "binary",
50 Self::Count => "count",
51 Self::Flow => "flow",
52 Self::Labels => "labels",
53 Self::Mcts => "mcts",
54 Self::Polarity => "polarity",
55 Self::Tencode => "tencode",
56 Self::TimeSurface => "tsurf",
57 Self::Voxel => "voxel",
58 }
59 }
60
61 pub fn from_tag(tag: &str) -> Option<Self> {
63 Some(match tag {
64 "atsurf" => Self::AveragedTimeSurface,
65 "binary" => Self::Binary,
66 "count" => Self::Count,
67 "flow" => Self::Flow,
68 "labels" => Self::Labels,
69 "mcts" => Self::Mcts,
70 "polarity" => Self::Polarity,
71 "tencode" => Self::Tencode,
72 "tsurf" => Self::TimeSurface,
73 "voxel" => Self::Voxel,
74 _ => return None,
75 })
76 }
77}
78
79#[derive(Clone, Debug)]
80pub struct EventFrame {
81 pub(crate) data: EventFrameData,
82 pub(crate) channels: usize,
83 pub(crate) width: usize,
84 pub(crate) height: usize,
85 pub(crate) kind: RepresentationKind,
86 pub(crate) channel_names: Vec<String>,
87}
88
89#[derive(Clone, Debug, PartialEq)]
90pub enum EventFrameData {
91 U8(Vec<u8>),
92 U16(Vec<u16>),
93 U64(Vec<u64>),
94 F32(Vec<f32>),
95}
96
97impl EventFrame {
98 pub(crate) fn from_parts(
101 data: EventFrameData,
102 width: usize,
103 height: usize,
104 kind: RepresentationKind,
105 channel_names: Vec<String>,
106 ) -> Self {
107 Self {
108 data,
109 channels: channel_names.len(),
110 width,
111 height,
112 kind,
113 channel_names,
114 }
115 }
116
117 pub fn data(&self) -> &EventFrameData {
118 &self.data
119 }
120
121 pub fn shape(&self) -> (usize, usize, usize) {
122 (self.channels, self.height, self.width)
123 }
124
125 pub fn channel_names(&self) -> &[String] {
126 &self.channel_names
127 }
128
129 pub fn kind(&self) -> RepresentationKind {
130 self.kind
131 }
132}
133
134impl EventFrameData {
135 pub(crate) fn len(&self) -> usize {
137 match self {
138 Self::U8(values) => values.len(),
139 Self::U16(values) => values.len(),
140 Self::U64(values) => values.len(),
141 Self::F32(values) => values.len(),
142 }
143 }
144}
145
146pub(crate) fn frame_len(
147 stream: &EventStream,
148 channels: usize,
149) -> Result<(usize, usize, usize), RepresentationError> {
150 let (width, height) = stream.sensor_size();
151 let plane_len = width
152 .checked_mul(height)
153 .ok_or(RepresentationError::SizeOverflow)?;
154 let length = plane_len
155 .checked_mul(channels)
156 .ok_or(RepresentationError::SizeOverflow)?;
157 Ok((width, height, length))
158}
159
160pub(crate) fn event_index(
161 event: Event,
162 width: usize,
163 height: usize,
164) -> Result<usize, RepresentationError> {
165 if event.x >= width || event.y >= height {
166 return Err(RepresentationError::EventOutOfBounds {
167 x: event.x,
168 y: event.y,
169 width,
170 height,
171 });
172 }
173 Ok(event.y * width + event.x)
174}
175
176pub(crate) fn validate_positive(value: f64, name: &'static str) -> Result<(), RepresentationError> {
177 if !value.is_finite() || value <= 0.0 {
178 return Err(RepresentationError::InvalidParameter(name));
179 }
180 Ok(())
181}
182
183pub(crate) fn reference_time(stream: &EventStream) -> Option<u64> {
184 stream.iter().map(|event| event.timestamp).max()
185}
186
187pub(crate) fn age_ms(stream: &EventStream, reference: u64, timestamp: u64) -> f64 {
188 reference.saturating_sub(timestamp) as f64 * stream.timestamp_scale_ms()
189}
190
191#[derive(Debug, PartialEq, Eq)]
192pub enum RepresentationError {
193 SizeOverflow,
194 CountOverflow {
195 x: usize,
196 y: usize,
197 },
198 EventOutOfBounds {
199 x: usize,
200 y: usize,
201 width: usize,
202 height: usize,
203 },
204 InvalidParameter(&'static str),
205}
206
207impl fmt::Display for RepresentationError {
208 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
209 match self {
210 Self::SizeOverflow => formatter.write_str("representation dimensions are too large"),
211 Self::CountOverflow { x, y } => {
212 write!(
213 formatter,
214 "event count at ({x}, {y}) exceeds uint16 capacity"
215 )
216 }
217 Self::EventOutOfBounds {
218 x,
219 y,
220 width,
221 height,
222 } => write!(
223 formatter,
224 "event coordinate ({x}, {y}) exceeds sensor size {width}x{height}"
225 ),
226 Self::InvalidParameter(name) => match *name {
227 "bins" => formatter.write_str("bins must be at least 1"),
228 "max_window_ms" => {
229 formatter.write_str("max_window_ms must be finite and at least 1")
230 }
231 _ => write!(formatter, "{name} must be finite and positive"),
232 },
233 }
234 }
235}
236
237impl Error for RepresentationError {}
238
239#[cfg(test)]
240mod tests {
241 use ndarray::Array2;
242
243 use super::{
244 AveragedTimeSurface, Binary, EventCount, EventFrameData, Mcts, PointSet, Representation,
245 RepresentationError, Tencode, TimeSurface, VoxelGrid,
246 };
247 use crate::EventStream;
248
249 fn empty_stream(width: usize, height: usize) -> EventStream {
250 EventStream::from_array2(Array2::zeros((0, 4)), width, height, 0.001)
251 }
252
253 #[test]
254 fn empty_streams_produce_zero_outputs() {
255 let stream = empty_stream(2, 3);
256
257 for frame in [
258 Binary.generate(&stream).unwrap(),
259 EventCount::new(true).generate(&stream).unwrap(),
260 VoxelGrid::default().generate(&stream).unwrap(),
261 TimeSurface::default().generate(&stream).unwrap(),
262 AveragedTimeSurface::default().generate(&stream).unwrap(),
263 Tencode::default().generate(&stream).unwrap(),
264 Mcts::default().generate(&stream).unwrap(),
265 ] {
266 match frame.data() {
267 EventFrameData::U8(values) => assert!(values.iter().all(|&value| value == 0)),
268 EventFrameData::F32(values) => assert!(values.iter().all(|&value| value == 0.0)),
269 _ => panic!("unexpected empty representation dtype"),
270 }
271 }
272 assert_eq!(PointSet.generate(&stream).unwrap().shape(), (0, 4));
273 }
274
275 #[test]
276 fn rejects_invalid_parameters_and_size_overflow() {
277 let stream = empty_stream(2, 3);
278
279 assert_eq!(
280 VoxelGrid::new(0, 30.0).generate(&stream).unwrap_err(),
281 RepresentationError::InvalidParameter("bins")
282 );
283 assert_eq!(
284 TimeSurface::new(f64::NAN).generate(&stream).unwrap_err(),
285 RepresentationError::InvalidParameter("tau_ms")
286 );
287 assert_eq!(
288 Tencode::new(0.0).generate(&stream).unwrap_err(),
289 RepresentationError::InvalidParameter("window_ms")
290 );
291 assert_eq!(
292 Mcts::new(0.5).generate(&stream).unwrap_err(),
293 RepresentationError::InvalidParameter("max_window_ms")
294 );
295
296 let oversized = empty_stream(usize::MAX, 2);
297 assert_eq!(
298 Binary.generate(&oversized).unwrap_err(),
299 RepresentationError::SizeOverflow
300 );
301 }
302}