use std::{error::Error, fmt};
use crate::{Event, EventStream};
mod averaged_time_surface;
mod binary;
mod count;
mod mcts;
mod point_set;
mod polarity;
mod tencode;
mod time_surface;
mod voxel;
pub use averaged_time_surface::AveragedTimeSurface;
pub use binary::Binary;
pub use count::EventCount;
pub use mcts::Mcts;
pub use point_set::{EventPointSet, PointSet};
pub use polarity::Polarity;
pub use tencode::Tencode;
pub use time_surface::TimeSurface;
pub use voxel::VoxelGrid;
pub trait Representation {
type Output;
fn generate(&self, stream: &EventStream) -> Result<Self::Output, RepresentationError>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RepresentationKind {
AveragedTimeSurface,
Binary,
Count,
Flow,
Labels,
Mcts,
Polarity,
Tencode,
TimeSurface,
Voxel,
}
impl RepresentationKind {
pub fn as_str(self) -> &'static str {
match self {
Self::AveragedTimeSurface => "atsurf",
Self::Binary => "binary",
Self::Count => "count",
Self::Flow => "flow",
Self::Labels => "labels",
Self::Mcts => "mcts",
Self::Polarity => "polarity",
Self::Tencode => "tencode",
Self::TimeSurface => "tsurf",
Self::Voxel => "voxel",
}
}
pub fn from_tag(tag: &str) -> Option<Self> {
Some(match tag {
"atsurf" => Self::AveragedTimeSurface,
"binary" => Self::Binary,
"count" => Self::Count,
"flow" => Self::Flow,
"labels" => Self::Labels,
"mcts" => Self::Mcts,
"polarity" => Self::Polarity,
"tencode" => Self::Tencode,
"tsurf" => Self::TimeSurface,
"voxel" => Self::Voxel,
_ => return None,
})
}
}
#[derive(Clone, Debug)]
pub struct EventFrame {
pub(crate) data: EventFrameData,
pub(crate) channels: usize,
pub(crate) width: usize,
pub(crate) height: usize,
pub(crate) kind: RepresentationKind,
pub(crate) channel_names: Vec<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum EventFrameData {
U8(Vec<u8>),
U16(Vec<u16>),
U64(Vec<u64>),
F32(Vec<f32>),
}
impl EventFrame {
pub(crate) fn from_parts(
data: EventFrameData,
width: usize,
height: usize,
kind: RepresentationKind,
channel_names: Vec<String>,
) -> Self {
Self {
data,
channels: channel_names.len(),
width,
height,
kind,
channel_names,
}
}
pub fn data(&self) -> &EventFrameData {
&self.data
}
pub fn shape(&self) -> (usize, usize, usize) {
(self.channels, self.height, self.width)
}
pub fn channel_names(&self) -> &[String] {
&self.channel_names
}
pub fn kind(&self) -> RepresentationKind {
self.kind
}
}
impl EventFrameData {
pub(crate) fn len(&self) -> usize {
match self {
Self::U8(values) => values.len(),
Self::U16(values) => values.len(),
Self::U64(values) => values.len(),
Self::F32(values) => values.len(),
}
}
}
pub(crate) fn frame_len(
stream: &EventStream,
channels: usize,
) -> Result<(usize, usize, usize), RepresentationError> {
let (width, height) = stream.sensor_size();
let plane_len = width
.checked_mul(height)
.ok_or(RepresentationError::SizeOverflow)?;
let length = plane_len
.checked_mul(channels)
.ok_or(RepresentationError::SizeOverflow)?;
Ok((width, height, length))
}
pub(crate) fn event_index(
event: Event,
width: usize,
height: usize,
) -> Result<usize, RepresentationError> {
if event.x >= width || event.y >= height {
return Err(RepresentationError::EventOutOfBounds {
x: event.x,
y: event.y,
width,
height,
});
}
Ok(event.y * width + event.x)
}
pub(crate) fn validate_positive(value: f64, name: &'static str) -> Result<(), RepresentationError> {
if !value.is_finite() || value <= 0.0 {
return Err(RepresentationError::InvalidParameter(name));
}
Ok(())
}
pub(crate) fn reference_time(stream: &EventStream) -> Option<u64> {
stream.iter().map(|event| event.timestamp).max()
}
pub(crate) fn age_ms(stream: &EventStream, reference: u64, timestamp: u64) -> f64 {
reference.saturating_sub(timestamp) as f64 * stream.timestamp_scale_ms()
}
#[derive(Debug, PartialEq, Eq)]
pub enum RepresentationError {
SizeOverflow,
CountOverflow {
x: usize,
y: usize,
},
EventOutOfBounds {
x: usize,
y: usize,
width: usize,
height: usize,
},
InvalidParameter(&'static str),
}
impl fmt::Display for RepresentationError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SizeOverflow => formatter.write_str("representation dimensions are too large"),
Self::CountOverflow { x, y } => {
write!(
formatter,
"event count at ({x}, {y}) exceeds uint16 capacity"
)
}
Self::EventOutOfBounds {
x,
y,
width,
height,
} => write!(
formatter,
"event coordinate ({x}, {y}) exceeds sensor size {width}x{height}"
),
Self::InvalidParameter(name) => match *name {
"bins" => formatter.write_str("bins must be at least 1"),
"max_window_ms" => {
formatter.write_str("max_window_ms must be finite and at least 1")
}
_ => write!(formatter, "{name} must be finite and positive"),
},
}
}
}
impl Error for RepresentationError {}
#[cfg(test)]
mod tests {
use ndarray::Array2;
use super::{
AveragedTimeSurface, Binary, EventCount, EventFrameData, Mcts, PointSet, Representation,
RepresentationError, Tencode, TimeSurface, VoxelGrid,
};
use crate::EventStream;
fn empty_stream(width: usize, height: usize) -> EventStream {
EventStream::from_array2(Array2::zeros((0, 4)), width, height, 0.001)
}
#[test]
fn empty_streams_produce_zero_outputs() {
let stream = empty_stream(2, 3);
for frame in [
Binary.generate(&stream).unwrap(),
EventCount::new(true).generate(&stream).unwrap(),
VoxelGrid::default().generate(&stream).unwrap(),
TimeSurface::default().generate(&stream).unwrap(),
AveragedTimeSurface::default().generate(&stream).unwrap(),
Tencode::default().generate(&stream).unwrap(),
Mcts::default().generate(&stream).unwrap(),
] {
match frame.data() {
EventFrameData::U8(values) => assert!(values.iter().all(|&value| value == 0)),
EventFrameData::F32(values) => assert!(values.iter().all(|&value| value == 0.0)),
_ => panic!("unexpected empty representation dtype"),
}
}
assert_eq!(PointSet.generate(&stream).unwrap().shape(), (0, 4));
}
#[test]
fn rejects_invalid_parameters_and_size_overflow() {
let stream = empty_stream(2, 3);
assert_eq!(
VoxelGrid::new(0, 30.0).generate(&stream).unwrap_err(),
RepresentationError::InvalidParameter("bins")
);
assert_eq!(
TimeSurface::new(f64::NAN).generate(&stream).unwrap_err(),
RepresentationError::InvalidParameter("tau_ms")
);
assert_eq!(
Tencode::new(0.0).generate(&stream).unwrap_err(),
RepresentationError::InvalidParameter("window_ms")
);
assert_eq!(
Mcts::new(0.5).generate(&stream).unwrap_err(),
RepresentationError::InvalidParameter("max_window_ms")
);
let oversized = empty_stream(usize::MAX, 2);
assert_eq!(
Binary.generate(&oversized).unwrap_err(),
RepresentationError::SizeOverflow
);
}
}