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