eventcv_core/representation/
averaged_time_surface.rs1use super::{
2 age_ms, event_index, frame_len, reference_time, validate_positive, EventFrame, EventFrameData,
3 Representation, RepresentationError, RepresentationKind,
4};
5use crate::EventStream;
6
7#[derive(Clone, Copy, Debug)]
12pub struct AveragedTimeSurface {
13 tau_ms: f64,
14}
15
16impl AveragedTimeSurface {
17 pub fn new(tau_ms: f64) -> Self {
18 Self { tau_ms }
19 }
20}
21
22impl Default for AveragedTimeSurface {
23 fn default() -> Self {
24 Self::new(30.0)
25 }
26}
27
28impl Representation for AveragedTimeSurface {
29 type Output = EventFrame;
30
31 fn generate(&self, stream: &EventStream) -> Result<EventFrame, RepresentationError> {
32 validate_positive(self.tau_ms, "tau_ms")?;
33 let (width, height, length) = frame_len(stream, 2)?;
34 let plane_len = width * height;
35 let mut sums = vec![0.0_f64; length];
36 let mut counts = vec![0_u64; length];
37
38 if let Some(reference) = reference_time(stream) {
39 for event in stream.iter() {
40 let index =
41 event_index(event, width, height)? + if event.polarity { 0 } else { plane_len };
42 let response = (-age_ms(stream, reference, event.timestamp) / self.tau_ms).exp();
43 sums[index] += response;
44 counts[index] += 1;
45 }
46 }
47
48 let values = sums
49 .into_iter()
50 .zip(counts)
51 .map(|(sum, count)| {
52 if count == 0 {
53 0.0
54 } else {
55 (sum / count as f64) as f32
56 }
57 })
58 .collect();
59
60 Ok(EventFrame {
61 data: EventFrameData::F32(values),
62 channels: 2,
63 width,
64 height,
65 kind: RepresentationKind::AveragedTimeSurface,
66 channel_names: vec!["positive".to_owned(), "negative".to_owned()],
67 })
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use ndarray::array;
74
75 use super::{AveragedTimeSurface, Representation};
76 use crate::{representation::EventFrameData, EventStream};
77
78 #[test]
79 fn averages_all_events_at_a_pixel() {
80 let stream = EventStream::from_array2(
83 array![[0, 0, 30_000, 1], [0, 0, 20_000, 1], [1, 0, 10_000, 0]],
84 2,
85 1,
86 0.001,
87 );
88
89 let frame = AveragedTimeSurface::new(10.0).generate(&stream).unwrap();
90 let EventFrameData::F32(values) = frame.data() else {
91 panic!("averaged time surfaces must use float32 data");
92 };
93
94 let expected_mean = (1.0 + (-1.0_f32).exp()) / 2.0;
95 assert!((values[0] - expected_mean).abs() < 1e-6);
96 assert_eq!(values[1], 0.0); assert!((values[3] - (-2.0_f32).exp()).abs() < 1e-6);
99 }
100
101 #[test]
102 fn rejects_invalid_tau() {
103 let stream = EventStream::from_array2(array![[0, 0, 10, 1]], 1, 1, 0.001);
104
105 let error = AveragedTimeSurface::new(0.0).generate(&stream).unwrap_err();
106
107 assert_eq!(error.to_string(), "tau_ms must be finite and positive");
108 }
109}