use crate::ev_formats::streaming::Event;
type Events = Vec<Event>;
use image::{ImageBuffer, Rgb};
use std::collections::VecDeque;
use std::time::Instant;
use polars::prelude::*;
#[derive(Debug, Clone)]
pub struct RealtimeVisualizationConfig {
pub display_width: u32,
pub display_height: u32,
pub event_decay_ms: f32,
pub max_events: usize,
pub show_fps: bool,
pub background_color: [u8; 3],
pub positive_color: [u8; 3],
pub negative_color: [u8; 3],
}
impl Default for RealtimeVisualizationConfig {
fn default() -> Self {
Self {
display_width: 640,
display_height: 480,
event_decay_ms: 50.0,
max_events: 5000,
show_fps: true,
background_color: [255, 255, 255],
positive_color: [255, 0, 0],
negative_color: [0, 0, 255],
}
}
}
pub struct RealtimeEventVisualizer {
pub config: RealtimeVisualizationConfig,
event_buffer: VecDeque<(Event, Instant)>,
frame_buffer: ImageBuffer<Rgb<u8>, Vec<u8>>,
last_frame_time: Instant,
fps_history: VecDeque<f32>,
current_fps: f32,
}
impl RealtimeEventVisualizer {
pub fn new(config: RealtimeVisualizationConfig) -> Self {
let frame_buffer = ImageBuffer::from_pixel(
config.display_width,
config.display_height,
Rgb(config.background_color),
);
Self {
config,
event_buffer: VecDeque::with_capacity(10000),
frame_buffer,
last_frame_time: Instant::now(),
fps_history: VecDeque::with_capacity(30),
current_fps: 0.0,
}
}
pub fn add_events(&mut self, events: Events) {
self.add_events_impl(events);
}
pub fn add_events_from_dataframe(&mut self, df: LazyFrame) -> Result<(), PolarsError> {
let events = dataframe_to_events_for_visualization(df)?;
self.add_events_impl(events);
Ok(())
}
fn add_events_impl(&mut self, events: Events) {
let now = Instant::now();
for event in events {
if self.event_buffer.len() >= self.config.max_events {
self.event_buffer.pop_front();
}
self.event_buffer.push_back((event, now));
}
let decay_duration = std::time::Duration::from_millis(self.config.event_decay_ms as u64);
while let Some((_, timestamp)) = self.event_buffer.front() {
if now.duration_since(*timestamp) > decay_duration {
self.event_buffer.pop_front();
} else {
break;
}
}
}
pub fn render_frame(&mut self) -> &[u8] {
let now = Instant::now();
for pixel in self.frame_buffer.pixels_mut() {
*pixel = Rgb(self.config.background_color);
}
let decay_ms = self.config.event_decay_ms;
for (event, timestamp) in &self.event_buffer {
let age_ms = now.duration_since(*timestamp).as_millis() as f32;
if age_ms < decay_ms {
let alpha = 1.0 - (age_ms / decay_ms);
let color = if event.polarity > 0 {
self.config.positive_color
} else {
self.config.negative_color
};
let x = event.x as u32;
let y = event.y as u32;
if x < self.config.display_width && y < self.config.display_height {
let pixel = self.frame_buffer.get_pixel_mut(x, y);
for i in 0..3 {
let bg = self.config.background_color[i] as f32;
let fg = color[i] as f32;
pixel[i] = (bg * (1.0 - alpha) + fg * alpha) as u8;
}
}
}
}
let frame_time = now.duration_since(self.last_frame_time).as_secs_f32();
self.last_frame_time = now;
let current_fps = 1.0 / frame_time;
self.fps_history.push_back(current_fps);
if self.fps_history.len() > 30 {
self.fps_history.pop_front();
}
self.current_fps = self.fps_history.iter().sum::<f32>() / self.fps_history.len() as f32;
if self.config.show_fps {
}
self.frame_buffer.as_raw()
}
pub fn get_fps(&self) -> f32 {
self.current_fps
}
pub fn clear(&mut self) {
self.event_buffer.clear();
}
pub fn get_frame_dimensions(&self) -> (u32, u32) {
(self.config.display_width, self.config.display_height)
}
}
pub struct EventVisualizationPipeline {
pub visualizer: RealtimeEventVisualizer,
frame_count: u64,
total_events: u64,
}
impl EventVisualizationPipeline {
pub fn new(config: RealtimeVisualizationConfig) -> Self {
Self {
visualizer: RealtimeEventVisualizer::new(config),
frame_count: 0,
total_events: 0,
}
}
pub fn process_events(&mut self, events: Events) -> (Vec<u8>, f32, (u32, u32)) {
self.process_events_impl(events)
}
pub fn process_events_from_dataframe(
&mut self,
df: LazyFrame,
) -> Result<(Vec<u8>, f32, (u32, u32)), PolarsError> {
let events = dataframe_to_events_for_visualization(df)?;
Ok(self.process_events_impl(events))
}
fn process_events_impl(&mut self, events: Events) -> (Vec<u8>, f32, (u32, u32)) {
self.total_events += events.len() as u64;
self.visualizer.add_events(events);
self.frame_count += 1;
let frame_data = self.visualizer.render_frame().to_vec();
let fps = self.visualizer.get_fps();
let dimensions = self.visualizer.get_frame_dimensions();
(frame_data, fps, dimensions)
}
pub fn get_stats(&self) -> (u64, u64, f32) {
(
self.frame_count,
self.total_events,
self.visualizer.get_fps(),
)
}
pub fn reset(&mut self) {
self.visualizer.clear();
self.frame_count = 0;
self.total_events = 0;
}
}
fn dataframe_to_events_for_visualization(df: LazyFrame) -> Result<Events, PolarsError> {
let df = df.collect()?;
let x_series = df.column("x")?;
let y_series = df.column("y")?;
let t_series = df.column("t")?;
let polarity_series = df.column("polarity")?;
let x_values = x_series.i64()?.into_no_null_iter().collect::<Vec<_>>();
let y_values = y_series.i64()?.into_no_null_iter().collect::<Vec<_>>();
let t_values = t_series.f64()?.into_no_null_iter().collect::<Vec<_>>();
let polarity_values = polarity_series
.i64()?
.into_no_null_iter()
.collect::<Vec<_>>();
let events = x_values
.into_iter()
.zip(y_values)
.zip(t_values)
.zip(polarity_values)
.map(|(((x, y), t), p)| Event {
x: x as u16,
y: y as u16,
t,
polarity: p > 0,
})
.collect();
Ok(events)
}