///|
/// Represents a timestamped video frame.
pub(all) struct VideoFrame {
frame_id : Int
timestamp_seconds : Double
image : Image
} derive(Debug)
///|
/// Creates a new VideoFrame.
pub fn VideoFrame::new(
frame_id : Int,
timestamp_seconds : Double,
image : Image,
) -> VideoFrame {
{ frame_id, timestamp_seconds, image }
}
///|
/// Ring-buffer video frame stream with temporal smoothing and motion compensation.
pub(all) struct VideoStreamPipeline {
capacity : Int
frames : Array[VideoFrame]
detector_params : DetectorParameters
dictionary : MarkerDictionary
tracker : MultiMarkerTracker
camera : CameraIntrinsics?
marker_length : Double
} derive(Debug)
///|
/// Creates a new VideoStreamPipeline.
pub fn VideoStreamPipeline::new(
dictionary : MarkerDictionary,
capacity? : Int = 30,
camera? : CameraIntrinsics?,
marker_length? : Double = 0.05,
) -> VideoStreamPipeline {
let cam = match camera {
Some(c) => c
None => None
}
{
capacity,
frames: [],
detector_params: DetectorParameters::default(),
dictionary,
tracker: MultiMarkerTracker::new(),
camera: cam,
marker_length,
}
}
///|
/// Pushes a new frame into the stream and performs integrated detection and multi-target tracking.
pub fn VideoStreamPipeline::process_frame(
self : VideoStreamPipeline,
frame : VideoFrame,
) -> Array[MarkerTrack] {
// Add to ring buffer
self.frames.push(frame)
if self.frames.length() > self.capacity {
let _ = self.frames.remove(0)
}
// Detect markers
let detections = detect_markers_with_params(
frame.image,
self.dictionary,
self.detector_params,
camera_matrix=self.camera,
marker_length=self.marker_length,
)
// Compute dt from previous frame if available
let dt = if self.frames.length() >= 2 {
let prev = self.frames[self.frames.length() - 2]
let diff = frame.timestamp_seconds - prev.timestamp_seconds
if diff > 0.001 && diff < 1.0 {
diff
} else {
0.033
}
} else {
0.033
}
// Update multi-target Kalman / EKF tracker
self.tracker.update(detections, dt~)
}