adder_codec_rs/transcoder/source/
prophesee.rs

1use crate::framer::scale_intensity::{FrameValue, SaeTime};
2use crate::transcoder::source::video::FramedViewMode::SAE;
3use crate::transcoder::source::video::{
4    integrate_for_px, Source, SourceError, Video, VideoBuilder,
5};
6use crate::utils::cv::{clamp_u8, mid_clamp_u8};
7use crate::utils::viz::ShowFeatureMode;
8use adder_codec_core::codec::{EncoderOptions, EncoderType};
9use adder_codec_core::Mode::Continuous;
10use adder_codec_core::{
11    DeltaT, Event, PixelMultiMode, PlaneSize, SourceCamera, SourceType, TimeMode,
12};
13use ndarray::Array3;
14use rayon::ThreadPool;
15use serde::{Deserialize, Serialize};
16use std::error::Error;
17use std::fs::File;
18use std::io::{self, BufRead, BufReader, Read, Seek, SeekFrom, Write};
19use std::path::PathBuf;
20use std::str::FromStr;
21use tokio::runtime::Runtime;
22use video_rs_adder_dep::Frame;
23
24/// The temporal granularity of the source (ticks per second)
25const PROPHESEE_SOURCE_TPS: u32 = 1000000;
26
27/// Attributes of a framed video -> ADΔER transcode
28pub struct Prophesee<W: Write + std::marker::Send + std::marker::Sync + 'static> {
29    pub(crate) video: Video<W>,
30
31    input_reader: BufReader<File>,
32
33    running_t: u32,
34
35    t_subtract: u32,
36
37    /// The timestamp (in-camera) of the last DVS event integrated for each pixel
38    pub dvs_last_timestamps: Array3<u32>,
39
40    /// The log-space last intensity value for each pixel
41    pub dvs_last_ln_val: Array3<f64>,
42
43    camera_theta: f64,
44}
45
46/// A DVS-style contrast event
47#[derive(Debug, Serialize, Deserialize, Clone)]
48pub struct DvsEvent {
49    t: u32,
50    x: u16,
51    y: u16,
52    p: u8,
53}
54
55unsafe impl<W: Write + std::marker::Send + std::marker::Sync + 'static> Sync for Prophesee<W> {}
56
57impl<W: Write + std::marker::Send + std::marker::Sync + 'static> Prophesee<W> {
58    /// Create a new `Prophesee` transcoder
59    pub fn new(ref_time: u32, input_filename: String) -> Result<Self, Box<dyn Error>> {
60        let source = File::open(PathBuf::from(input_filename))?;
61        let mut input_reader = BufReader::new(source);
62
63        // Parse header
64        let (_, _, _, size) = parse_header(&mut input_reader).unwrap();
65
66        let plane = PlaneSize::new(size.1 as u16, size.0 as u16, 1)?;
67
68        let mut video = Video::new(plane, Continuous, None)?
69            .chunk_rows(1)
70            // Override the tps to assume the source has a temporal granularity of 1000000/second
71            // The `ref_time` in this case scales up the temporal granularity of the source.
72            // For example, with ref_time = 20, a timestamp of 12 in the source becomes 240
73            // ADDER ticks
74            .time_parameters(
75                ref_time * PROPHESEE_SOURCE_TPS,
76                ref_time,
77                ref_time * 2,
78                Some(TimeMode::AbsoluteT),
79            )?;
80
81        let start_intensities = vec![128_u8; video.state.plane.volume()];
82        video.state.running_intensities = Array3::from_shape_vec(
83            (plane.h().into(), plane.w().into(), plane.c().into()),
84            start_intensities,
85        )?;
86        video.display_frame_features = video.state.running_intensities.clone();
87
88        let timestamps = vec![2_u32; video.state.plane.volume()];
89
90        let dvs_last_timestamps: Array3<u32> = Array3::from_shape_vec(
91            (plane.h().into(), plane.w().into(), plane.c().into()),
92            timestamps,
93        )?;
94
95        let plane = &video.state.plane;
96
97        let start_vals = vec![(128.0_f64 / 255.0_f64).ln_1p(); video.state.plane.volume()];
98
99        let dvs_last_ln_val: Array3<f64> = Array3::from_shape_vec(
100            (plane.h() as usize, plane.w() as usize, plane.c() as usize),
101            start_vals,
102        )?;
103
104        let prophesee_source = Prophesee {
105            video,
106            input_reader,
107            running_t: 0,
108            t_subtract: 0,
109            dvs_last_timestamps,
110            dvs_last_ln_val,
111            camera_theta: 0.02, // A fixed assumption
112        };
113
114        Ok(prophesee_source)
115    }
116}
117
118impl<W: Write + std::marker::Send + std::marker::Sync + 'static> Source<W> for Prophesee<W> {
119    fn consume(&mut self) -> Result<Vec<Vec<Event>>, SourceError> {
120        if self.running_t == 0 {
121            self.video.integrate_matrix(
122                self.video.state.running_intensities.clone(),
123                self.video.state.params.ref_time as f32,
124            )?;
125            let first_events: Vec<Event> = self
126                .video
127                .integrate_matrix(
128                    self.video.state.running_intensities.clone(),
129                    self.video.state.params.ref_time as f32,
130                )?
131                .into_iter()
132                .flatten()
133                .collect();
134            assert_eq!(first_events.len(), self.video.state.plane.volume());
135            self.running_t = 2;
136        }
137
138        // TODO hardcoded: scale the view interval to be 60 FPS GUI display
139        let view_interval = PROPHESEE_SOURCE_TPS / 60;
140
141        // Read events from the source file until we find a timestamp that exceeds our `running_t`
142        // by at least `view_interval`
143        let mut dvs_events: Vec<DvsEvent> = Vec::new();
144        let mut dvs_event;
145        let start_running_t = self.running_t;
146        loop {
147            // TODO: integrate to fill in the rest of time once the eof is reached
148
149            dvs_event = match decode_event(&mut self.input_reader) {
150                Ok(mut dvs_event) => {
151                    // if self.running_t == 2 && dvs_events.is_empty() {
152                    //     self.t_subtract = dvs_event.t;
153                    //     eprintln!("t_subtract: {}", self.t_subtract);
154                    // }
155
156                    dvs_event.t -= self.t_subtract;
157
158                    if dvs_event.t > self.running_t {
159                        self.running_t = dvs_event.t;
160                    }
161                    dvs_event
162                }
163                Err(e) => {
164                    dbg!("End of input file");
165                    end_events(self);
166                    return Err(e.into());
167                }
168            };
169            dvs_events.push(dvs_event);
170            if dvs_events.last().unwrap().t > start_running_t + view_interval {
171                break;
172            }
173        }
174
175        let mut events: Vec<Event> = Vec::new();
176        let crf_parameters = *self.video.encoder.options.crf.get_parameters();
177
178        // For every dvs event in our queue, integrate the previously seen intensity for all the
179        // time between the pixel's last input and the current event
180        for dvs_event in dvs_events {
181            let x = dvs_event.x as usize;
182            let y = dvs_event.y as usize;
183            let p = dvs_event.p as usize;
184            let t = dvs_event.t;
185
186            // Get the last timestamp for this pixel
187            let last_t = self.dvs_last_timestamps[[y, x, 0]];
188
189            if t < last_t {
190                // dbg!("skipping event");
191                continue;
192            }
193
194            // Get the last ln intensity for this pixel
195            let mut last_ln_val = self.dvs_last_ln_val[[y, x, 0]];
196
197            let px = &mut self.video.event_pixel_trees[[y, x, 0]];
198
199            if t > last_t + 1 {
200                // Convert the ln intensity to a linear intensity
201                let mut last_val = (last_ln_val.exp() - 1.0) * 255.0;
202
203                mid_clamp_u8(&mut last_val, &mut last_ln_val);
204
205                // Integrate the last intensity for this pixel over the time since the last event
206                let time_spanned = (t - last_t - 1) * self.video.state.params.ref_time;
207                let intensity_to_integrate = last_val * (t - last_t - 1) as f64;
208
209                let mut base_val = 0;
210                let _ = integrate_for_px(
211                    px,
212                    &mut base_val,
213                    last_val as u8,
214                    intensity_to_integrate as f32,
215                    time_spanned as f32,
216                    &mut events,
217                    &self.video.state.params,
218                    &crf_parameters,
219                );
220            }
221
222            // Get the new ln intensity
223            let mut new_ln_val = match p {
224                0 => last_ln_val - self.camera_theta,
225                1 => last_ln_val + self.camera_theta,
226                _ => panic!("Invalid polarity"),
227            };
228
229            // Update the last intensity for this pixel
230            self.dvs_last_ln_val[[y, x, 0]] = new_ln_val;
231
232            // Update the last timestamp for this pixel
233            self.dvs_last_timestamps[[y, x, 0]] = t;
234
235            if t > last_t {
236                let mut new_val = (new_ln_val.exp() - 1.0) * 255.0;
237
238                mid_clamp_u8(&mut new_val, &mut new_ln_val);
239
240                // Update the last intensity for this pixel
241                self.dvs_last_ln_val[[y, x, 0]] = new_ln_val;
242
243                // Integrate 1 source time unit of the new intensity
244                let time_spanned = self.video.state.params.ref_time;
245                let intensity_to_integrate = new_val;
246
247                let mut base_val = 0;
248                let _ = integrate_for_px(
249                    px,
250                    &mut base_val,
251                    new_val as u8,
252                    intensity_to_integrate as f32,
253                    time_spanned as f32,
254                    &mut events,
255                    &self.video.state.params,
256                    &crf_parameters,
257                );
258            }
259
260            // Update the running intensity for this pixel
261            if let Some(event) = px.arena[0].best_event {
262                self.video.state.running_intensities[[y, x, 0]] = u8::get_frame_value(
263                    &event.into(),
264                    SourceType::U8,
265                    self.video.state.params.ref_time as f64,
266                    32.0,
267                    self.video.state.params.delta_t_max,
268                    self.video.instantaneous_view_mode,
269                    if self.video.instantaneous_view_mode == SAE {
270                        Some(SaeTime {
271                            running_t: px.running_t as DeltaT,
272                            last_fired_t: px.last_fired_t as DeltaT,
273                        })
274                    } else {
275                        None
276                    },
277                );
278                self.video.display_frame_features[[y, x, 0]] =
279                    self.video.state.running_intensities[[y, x, 0]];
280            };
281        }
282
283        if self.video.state.feature_detection {
284            self.video.display_frame_features = self.video.state.running_intensities.clone();
285        }
286
287        // It's expected that the function will spatially parallelize the integrations. With sparse
288        // data, though, this could be pretty wasteful. For now, just wrap the vec in another vec.
289        let events_nested: Vec<Vec<Event>> = vec![events];
290
291        self.video.handle_features(&events_nested)?;
292
293        for events in &events_nested {
294            for event in events {
295                self.video.encoder.ingest_event(*event)?;
296            }
297        }
298
299        Ok(events_nested)
300    }
301
302    fn crf(&mut self, crf: u8) {
303        self.video.update_crf(crf);
304    }
305
306    fn get_video_mut(&mut self) -> &mut Video<W> {
307        &mut self.video
308    }
309
310    fn get_video_ref(&self) -> &Video<W> {
311        &self.video
312    }
313
314    fn get_video(self) -> Video<W> {
315        self.video
316    }
317
318    fn get_input(&self) -> Option<&Frame> {
319        None
320    }
321
322    fn get_running_input_bitrate(&self) -> f64 {
323        // TODO
324        0.0
325    }
326}
327
328fn end_events<W: Write + std::marker::Send + std::marker::Sync + 'static>(
329    prophesee: &mut Prophesee<W>,
330) {
331    let mut events: Vec<Event> = Vec::new();
332    let crf_parameters = *prophesee.video.encoder.options.crf.get_parameters();
333
334    for y in 0..prophesee.video.state.plane.h_usize() {
335        for x in 0..prophesee.video.state.plane.w_usize() {
336            let px = &mut prophesee.video.event_pixel_trees[[y, x, 0]];
337            let mut base_val = 0;
338
339            // Get the last ln intensity for this pixel
340            let last_ln_val = prophesee.dvs_last_ln_val[[y, x, 0]];
341
342            // Convert the ln intensity to a linear intensity
343            let last_val = (last_ln_val.exp() - 1.0) * 255.0;
344
345            assert!(prophesee.running_t - prophesee.dvs_last_timestamps[[y, x, 0]] > 0);
346
347            // Integrate the last intensity for this pixel over the time since the last event
348            let time_spanned = (prophesee.running_t - prophesee.dvs_last_timestamps[[y, x, 0]])
349                * prophesee.video.state.params.ref_time;
350            let intensity_to_integrate = last_val * time_spanned as f64;
351
352            let _ = integrate_for_px(
353                px,
354                &mut base_val,
355                last_val as u8,
356                intensity_to_integrate as f32,
357                time_spanned as f32,
358                &mut events,
359                &prophesee.video.state.params,
360                &crf_parameters,
361            );
362        }
363    }
364
365    for event in &events {
366        prophesee.video.encoder.ingest_event(*event).unwrap();
367    }
368}
369
370fn parse_header(file: &mut BufReader<File>) -> io::Result<(u64, u8, u8, (u32, u32))> {
371    file.seek(SeekFrom::Start(0))?; // Seek to the beginning of the file
372    let mut bod = 0;
373    let mut end_of_header = false;
374    let mut num_comment_line = 0;
375    let mut size = [None, None];
376
377    // Parse header
378    while !end_of_header {
379        bod = file.seek(SeekFrom::Current(0))?; // Get the current position
380        let mut line = Vec::new(); // Change to Vec<u8>
381        file.read_until(b'\n', &mut line)?; // Read until newline as binary data
382        if line.is_empty() || line[0] != b'%' {
383            end_of_header = true;
384        } else {
385            let words: Vec<&[u8]> = line.split(|&x| x == b' ' || x == b'\t').collect(); // Use &[u8] instead of &str
386
387            if words.len() > 1 {
388                match words[1] {
389                    b"Height" => {
390                        size[0] = line_to_hw(words);
391                    }
392                    b"Width" => {
393                        size[1] = line_to_hw(words);
394                    }
395                    _ => {}
396                }
397            }
398            num_comment_line += 1;
399        }
400    }
401
402    // Parse data
403    file.seek(SeekFrom::Start(bod))?; // Seek back to the position after the header
404    let (ev_type, ev_size) = if num_comment_line > 0 {
405        // Read event type and size
406        let mut buf = [0; 2]; // Adjust the buffer size based on your data size
407        file.read_exact(&mut buf)?;
408        let ev_type = buf[0];
409        let ev_size = buf[1];
410        if ev_size != 8 || (ev_type != 0 && ev_type != 12) {
411            panic!("Invalid Prophesee event size");
412        }
413
414        (ev_type, ev_size)
415    } else {
416        (0, 0) // Placeholder values, replace with actual logic
417    };
418    bod = file.seek(SeekFrom::Current(0))?;
419    Ok((
420        bod,
421        ev_type,
422        ev_size,
423        (size[0].unwrap_or(70), size[1].unwrap_or(100)),
424    ))
425}
426
427fn line_to_hw(words: Vec<&[u8]>) -> Option<u32> {
428    let word = words.get(2).unwrap();
429    let mut new_word = *word;
430    if *word.last().unwrap() == '\n' as u8 {
431        // Remove the trailing newline
432        new_word = &word[..word.len() - 1];
433    }
434    std::str::from_utf8(new_word)
435        .ok()
436        .and_then(|s| s.parse().ok())
437}
438
439fn decode_event(reader: &mut BufReader<File>) -> io::Result<DvsEvent> {
440    // Read one record
441    let mut buffer = [0; 8]; // Adjust this size to match your record size
442    reader.read_exact(&mut buffer)?;
443
444    // Interpret the bytes as 't' and 'data'
445    let t = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
446    let data = i32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]);
447
448    // Perform bitwise operations
449    let x = (data & 0x3FF) as u16; // All but last 14 bits
450    let y = ((data & 0xFFFC000) >> 14) as u16; // All but second-to-last grouping of 14 bits
451    let p = ((data & 0x10000000) >> 28) as u8; // Just the 4th bit
452
453    Ok(DvsEvent { t, x, y, p })
454}
455
456impl<W: Write + std::marker::Send + std::marker::Sync + 'static> VideoBuilder<W> for Prophesee<W> {
457    fn crf(mut self, crf: u8) -> Self {
458        self.video.update_crf(crf);
459        self
460    }
461
462    fn quality_manual(
463        mut self,
464        c_thresh_baseline: u8,
465        c_thresh_max: u8,
466        delta_t_max_multiplier: u32,
467        c_increase_velocity: u8,
468        feature_c_radius_denom: f32,
469    ) -> Self {
470        self.video.update_quality_manual(
471            c_thresh_baseline,
472            c_thresh_max,
473            delta_t_max_multiplier,
474            c_increase_velocity,
475            feature_c_radius_denom,
476        );
477        self
478    }
479
480    fn chunk_rows(mut self, chunk_rows: usize) -> Self {
481        self.video = self.video.chunk_rows(chunk_rows);
482        self
483    }
484
485    fn time_parameters(
486        mut self,
487        tps: DeltaT,
488        ref_time: DeltaT,
489        delta_t_max: DeltaT,
490        time_mode: Option<TimeMode>,
491    ) -> Result<Self, SourceError> {
492        eprintln!("setting dtref to {}", ref_time);
493        self.video = self
494            .video
495            .time_parameters(tps, ref_time, delta_t_max, time_mode)?;
496        Ok(self)
497    }
498
499    fn write_out(
500        mut self,
501        source_camera: SourceCamera,
502        time_mode: TimeMode,
503        pixel_multi_mode: PixelMultiMode,
504        adu_interval: Option<usize>,
505        encoder_type: EncoderType,
506        encoder_options: EncoderOptions,
507        write: W,
508    ) -> Result<Box<Self>, SourceError> {
509        self.video = self.video.write_out(
510            Some(source_camera),
511            Some(time_mode),
512            Some(pixel_multi_mode),
513            adu_interval,
514            encoder_type,
515            encoder_options,
516            write,
517        )?;
518        Ok(Box::new(self))
519    }
520    fn detect_features(mut self, detect_features: bool, show_features: ShowFeatureMode) -> Self {
521        self.video = self.video.detect_features(detect_features, show_features);
522        self
523    }
524
525    #[cfg(feature = "feature-logging")]
526    fn log_path(self, _name: String) -> Self {
527        todo!()
528    }
529}