use scirs2_core::ndarray::Array2;
use crate::error::{Result, VisionError};
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Polarity {
On,
Off,
}
impl Polarity {
pub fn sign(self) -> f64 {
match self {
Polarity::On => 1.0,
Polarity::Off => -1.0,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Event {
pub x: u16,
pub y: u16,
pub timestamp: f64,
pub polarity: Polarity,
}
impl Event {
pub fn new(x: u16, y: u16, timestamp: f64, polarity: Polarity) -> Self {
Self {
x,
y,
timestamp,
polarity,
}
}
}
pub struct EventSlice {
events: Vec<Event>,
t_start: f64,
t_end: f64,
width: u16,
height: u16,
}
impl EventSlice {
pub fn new(mut events: Vec<Event>, width: u16, height: u16) -> Result<Self> {
if events.is_empty() {
return Err(VisionError::InvalidParameter(
"EventSlice requires at least one event".to_string(),
));
}
for e in &events {
if e.x >= width || e.y >= height {
return Err(VisionError::InvalidParameter(format!(
"Event coordinate ({}, {}) exceeds sensor dimensions ({}x{})",
e.x, e.y, width, height
)));
}
}
events.sort_by(|a, b| {
a.timestamp
.partial_cmp(&b.timestamp)
.unwrap_or(std::cmp::Ordering::Equal)
});
let t_start = events.first().map(|e| e.timestamp).unwrap_or_default();
let t_end = events.last().map(|e| e.timestamp).unwrap_or_default();
Ok(Self {
events,
t_start,
t_end,
width,
height,
})
}
pub fn events(&self) -> &[Event] {
&self.events
}
pub fn time_range(&self) -> (f64, f64) {
(self.t_start, self.t_end)
}
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
pub fn filter_by_polarity(&self, polarity: Polarity) -> Result<Self> {
let filtered: Vec<Event> = self
.events
.iter()
.filter(|e| e.polarity == polarity)
.copied()
.collect();
if filtered.is_empty() {
return Err(VisionError::InvalidParameter(format!(
"No events with polarity {:?} found",
polarity
)));
}
Self::new(filtered, self.width, self.height)
}
pub fn split_at_time(&self, t: f64) -> Result<(Self, Self)> {
let before: Vec<Event> = self
.events
.iter()
.filter(|e| e.timestamp < t)
.copied()
.collect();
let after: Vec<Event> = self
.events
.iter()
.filter(|e| e.timestamp >= t)
.copied()
.collect();
if before.is_empty() {
return Err(VisionError::InvalidParameter(
"Split time is before all events; 'before' half would be empty".to_string(),
));
}
if after.is_empty() {
return Err(VisionError::InvalidParameter(
"Split time is after all events; 'after' half would be empty".to_string(),
));
}
Ok((
Self::new(before, self.width, self.height)?,
Self::new(after, self.width, self.height)?,
))
}
pub fn event_rate(&self) -> f64 {
let duration = self.t_end - self.t_start;
if duration <= 0.0 {
return 0.0;
}
self.events.len() as f64 / duration
}
pub fn len(&self) -> usize {
self.events.len()
}
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
pub fn time_window(&self, t0: f64, t1: f64) -> Result<Self> {
let sub: Vec<Event> = self
.events
.iter()
.filter(|e| e.timestamp >= t0 && e.timestamp < t1)
.copied()
.collect();
if sub.is_empty() {
return Err(VisionError::InvalidParameter(format!(
"No events in time window [{}, {})",
t0, t1
)));
}
Self::new(sub, self.width, self.height)
}
}
pub struct EventFrame {
pub data: Array2<f64>,
pub t_start: f64,
pub t_end: f64,
}
pub struct EventProcessingConfig {
pub width: u16,
pub height: u16,
pub time_window: f64,
pub decay_rate: f64,
pub polarity_threshold: f64,
}
impl Default for EventProcessingConfig {
fn default() -> Self {
Self {
width: 240,
height: 180,
time_window: 0.033, decay_rate: 0.01, polarity_threshold: 0.5,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_event_creation() {
let e = Event::new(10, 20, 1.0, Polarity::On);
assert_eq!(e.x, 10);
assert_eq!(e.y, 20);
assert!((e.timestamp - 1.0).abs() < f64::EPSILON);
assert_eq!(e.polarity, Polarity::On);
}
#[test]
fn test_polarity_sign() {
assert!((Polarity::On.sign() - 1.0).abs() < f64::EPSILON);
assert!((Polarity::Off.sign() - (-1.0)).abs() < f64::EPSILON);
}
#[test]
fn test_event_slice_basic() {
let events = vec![
Event::new(0, 0, 0.002, Polarity::On),
Event::new(1, 1, 0.001, Polarity::Off),
Event::new(2, 2, 0.003, Polarity::On),
];
let slice = EventSlice::new(events, 10, 10).expect("failed to create EventSlice");
assert_eq!(slice.len(), 3);
assert!(!slice.is_empty());
assert!(slice.events()[0].timestamp <= slice.events()[1].timestamp);
assert!(slice.events()[1].timestamp <= slice.events()[2].timestamp);
}
#[test]
fn test_event_slice_out_of_bounds() {
let events = vec![Event::new(10, 5, 0.0, Polarity::On)];
let result = EventSlice::new(events, 10, 10); assert!(result.is_err());
}
#[test]
fn test_event_slice_empty() {
let result = EventSlice::new(vec![], 10, 10);
assert!(result.is_err());
}
#[test]
fn test_filter_by_polarity() {
let events = vec![
Event::new(0, 0, 0.001, Polarity::On),
Event::new(1, 1, 0.002, Polarity::Off),
Event::new(2, 2, 0.003, Polarity::On),
];
let slice = EventSlice::new(events, 10, 10).expect("failed");
let on_slice = slice.filter_by_polarity(Polarity::On).expect("failed");
assert_eq!(on_slice.len(), 2);
for e in on_slice.events() {
assert_eq!(e.polarity, Polarity::On);
}
}
#[test]
fn test_split_at_time() {
let events = vec![
Event::new(0, 0, 0.001, Polarity::On),
Event::new(1, 1, 0.002, Polarity::Off),
Event::new(2, 2, 0.003, Polarity::On),
Event::new(3, 3, 0.004, Polarity::Off),
];
let slice = EventSlice::new(events, 10, 10).expect("failed");
let (before, after) = slice.split_at_time(0.0025).expect("failed");
assert_eq!(before.len(), 2);
assert_eq!(after.len(), 2);
}
#[test]
fn test_event_rate() {
let events = vec![
Event::new(0, 0, 0.0, Polarity::On),
Event::new(1, 1, 0.5, Polarity::Off),
Event::new(2, 2, 1.0, Polarity::On),
];
let slice = EventSlice::new(events, 10, 10).expect("failed");
let rate = slice.event_rate();
assert!((rate - 3.0).abs() < 1e-9); }
#[test]
fn test_time_window() {
let events = vec![
Event::new(0, 0, 0.0, Polarity::On),
Event::new(1, 1, 0.5, Polarity::Off),
Event::new(2, 2, 1.0, Polarity::On),
Event::new(3, 3, 1.5, Polarity::Off),
];
let slice = EventSlice::new(events, 10, 10).expect("failed");
let sub = slice.time_window(0.3, 1.2).expect("failed");
assert_eq!(sub.len(), 2); }
}