eventcv_core/representation/
count.rs1use super::{
2 event_index, frame_len, EventFrame, EventFrameData, Representation, RepresentationError,
3 RepresentationKind,
4};
5use crate::EventStream;
6
7#[derive(Clone, Copy, Debug, Default)]
12pub struct EventCount {
13 normalize: bool,
14}
15
16impl EventCount {
17 pub fn new(normalize: bool) -> Self {
18 Self { normalize }
19 }
20
21 pub fn is_normalized(&self) -> bool {
22 self.normalize
23 }
24}
25
26impl Representation for EventCount {
27 type Output = EventFrame;
28
29 fn generate(&self, stream: &EventStream) -> Result<EventFrame, RepresentationError> {
30 let (width, height, length) = frame_len(stream, 1)?;
31 let mut counts = vec![0_u64; length];
32
33 for event in stream.iter() {
34 counts[event_index(event, width, height)?] += 1;
35 }
36
37 Ok(self.frame(counts, width, height))
38 }
39
40 fn generate_on(
44 &self,
45 stream: &EventStream,
46 device: crate::accel::Device,
47 ) -> Result<EventFrame, RepresentationError> {
48 if device == crate::accel::Device::Cpu {
49 return self.generate(stream);
50 }
51 let (width, height, length) = frame_len(stream, 1)?;
52 let cells = super::on_gpu(
53 stream,
54 &crate::accel::GpuDispatch {
55 entry: "count",
56 cells: length,
57 initial: 0,
58 bins: 1,
59 span_ms: 0.0,
60 fixed_one: 1.0,
61 window_ms: None,
62 needs_ages: false,
63 },
64 )?;
65 let counts = cells.iter().map(|cell| *cell as u64).collect();
66 Ok(self.frame(counts, width, height))
67 }
68}
69
70impl EventCount {
71 fn frame(&self, counts: Vec<u64>, width: usize, height: usize) -> EventFrame {
72 EventFrame {
73 data: if self.normalize {
74 EventFrameData::U8(normalize_u8(&counts))
75 } else {
76 EventFrameData::U64(counts)
77 },
78 channels: 1,
79 width,
80 height,
81 kind: RepresentationKind::Count,
82 channel_names: vec!["count".to_owned()],
83 }
84 }
85}
86
87fn normalize_u8(counts: &[u64]) -> Vec<u8> {
89 let maximum = counts.iter().copied().max().unwrap_or(0);
90 if maximum == 0 {
91 return vec![0; counts.len()];
92 }
93 counts
94 .iter()
95 .map(|&count| {
96 let scaled = count * u64::from(u8::MAX);
97 ((scaled + maximum / 2) / maximum) as u8
98 })
99 .collect()
100}
101
102#[cfg(test)]
103mod tests {
104 use ndarray::array;
105
106 use super::{EventCount, Representation};
107 use crate::{representation::EventFrameData, EventStream};
108
109 #[test]
110 fn sums_both_polarities_into_one_plane() {
111 let stream = EventStream::from_array2(
112 array![[0, 0, 1, 1], [0, 0, 2, 0], [1, 1, 3, 1]],
113 2,
114 2,
115 0.001,
116 );
117
118 let frame = EventCount::default().generate(&stream).unwrap();
119
120 assert_eq!(frame.shape(), (1, 2, 2));
121 assert_eq!(frame.data(), &EventFrameData::U64(vec![2, 0, 0, 1]));
122 }
123
124 #[test]
125 fn normalizes_the_busiest_pixel_to_full_scale() {
126 let stream = EventStream::from_array2(
127 array![[0, 0, 1, 1], [0, 0, 2, 0], [1, 0, 3, 1]],
128 2,
129 1,
130 0.001,
131 );
132
133 let frame = EventCount::new(true).generate(&stream).unwrap();
134
135 assert_eq!(frame.data(), &EventFrameData::U8(vec![255, 128]));
136 }
137
138 #[test]
139 fn rejects_out_of_bounds_events() {
140 let stream = EventStream::from_array2(array![[2, 0, 10, 1]], 2, 2, 0.001);
141
142 let error = EventCount::default().generate(&stream).unwrap_err();
143
144 assert_eq!(
145 error.to_string(),
146 "event coordinate (2, 0) exceeds sensor size 2x2"
147 );
148 }
149}