pub mod file;
pub mod pipeline;
pub mod preprocess;
#[cfg(target_os = "linux")]
pub mod v4l2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PixelFormat {
Rgb,
Yuyv,
Mjpeg,
Gray,
}
#[derive(Debug, Clone)]
pub struct CaptureConfig {
pub width: u32,
pub height: u32,
pub format: PixelFormat,
pub fps: u32,
}
impl Default for CaptureConfig {
fn default() -> Self {
Self {
width: 640,
height: 480,
format: PixelFormat::Yuyv,
fps: 30,
}
}
}
#[derive(Debug, Clone)]
pub struct FrameBuffer {
pub data: Vec<u8>,
pub width: u32,
pub height: u32,
pub format: PixelFormat,
pub timestamp_us: u64,
}
impl FrameBuffer {
pub fn new(data: Vec<u8>, width: u32, height: u32, format: PixelFormat) -> Self {
Self {
data,
width,
height,
format,
timestamp_us: 0,
}
}
pub fn num_pixels(&self) -> usize {
self.width as usize * self.height as usize
}
}
#[derive(Debug)]
pub enum CaptureError {
DeviceNotFound(String),
FormatError(String),
CaptureError(String),
NotOpen,
IoError(std::io::Error),
}
impl std::fmt::Display for CaptureError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DeviceNotFound(s) => write!(f, "Device not found: {s}"),
Self::FormatError(s) => write!(f, "Format error: {s}"),
Self::CaptureError(s) => write!(f, "Capture error: {s}"),
Self::NotOpen => write!(f, "Device not open"),
Self::IoError(e) => write!(f, "I/O error: {e}"),
}
}
}
impl std::error::Error for CaptureError {}
impl From<std::io::Error> for CaptureError {
fn from(e: std::io::Error) -> Self {
Self::IoError(e)
}
}
pub trait CaptureBackend {
fn open(&mut self, config: &CaptureConfig) -> Result<(), CaptureError>;
fn grab_frame(&mut self) -> Result<FrameBuffer, CaptureError>;
fn is_open(&self) -> bool;
fn close(&mut self);
fn resolution(&self) -> (u32, u32);
}
pub use file::FileBackend;
pub use pipeline::{DetectionModel, InferencePipeline, PipelineStats};
pub use preprocess::{normalize_imagenet, resize_bilinear, yuyv_to_rgb};
#[cfg(target_os = "linux")]
pub use v4l2::V4L2Backend;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_capture_config_default() {
let cfg = CaptureConfig::default();
assert_eq!(cfg.width, 640);
assert_eq!(cfg.height, 480);
assert_eq!(cfg.format, PixelFormat::Yuyv);
assert_eq!(cfg.fps, 30);
}
#[test]
fn test_frame_buffer_creation() {
let fb = FrameBuffer::new(vec![0u8; 640 * 480 * 3], 640, 480, PixelFormat::Rgb);
assert_eq!(fb.num_pixels(), 640 * 480);
assert_eq!(fb.data.len(), 640 * 480 * 3);
}
#[test]
fn test_pixel_format_equality() {
assert_eq!(PixelFormat::Rgb, PixelFormat::Rgb);
assert_ne!(PixelFormat::Rgb, PixelFormat::Yuyv);
}
}