use crate::geometry::Rect;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Detection {
pub timestamp: u64,
pub class_id: u32,
pub class_name: String,
pub confidence: f32,
pub bbox: BoundingBox,
#[serde(skip_serializing_if = "Option::is_none")]
pub track_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position_3d: Option<[f32; 3]>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BoundingBox {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl Rect for BoundingBox {
fn x(&self) -> f32 {
self.x
}
fn y(&self) -> f32 {
self.y
}
fn width(&self) -> f32 {
self.width
}
fn height(&self) -> f32 {
self.height
}
}
impl BoundingBox {
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
Self { x, y, width, height }
}
pub fn center(&self) -> (f32, f32) {
Rect::center(self)
}
pub fn area(&self) -> f32 {
Rect::area(self)
}
pub fn iou(&self, other: &BoundingBox) -> f32 {
Rect::iou(self, other)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pose {
pub timestamp: u64,
pub position: [f32; 3],
pub orientation: [f32; 4],
#[serde(skip_serializing_if = "Option::is_none")]
pub frame_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub covariance: Option<Vec<f32>>,
}
impl Detection {
pub fn new(class_id: u32, class_name: String, confidence: f32, bbox: BoundingBox) -> Self {
Self {
timestamp: crate::prelude::now_micros(),
class_id,
class_name,
confidence,
bbox,
track_id: None,
position_3d: None,
}
}
pub fn is_confident(&self, threshold: f32) -> bool {
self.confidence >= threshold
}
pub fn center(&self) -> (f32, f32) {
self.bbox.center()
}
pub fn area(&self) -> f32 {
self.bbox.area()
}
}
impl Pose {
pub fn identity() -> Self {
Self {
timestamp: crate::prelude::now_micros(),
position: [0.0, 0.0, 0.0],
orientation: [0.0, 0.0, 0.0, 1.0],
frame_id: None,
covariance: None,
}
}
pub fn new_2d(x: f32, y: f32, yaw: f32) -> Self {
let half_yaw = yaw / 2.0;
let qz = half_yaw.sin();
let qw = half_yaw.cos();
Self {
timestamp: crate::prelude::now_micros(),
position: [x, y, 0.0],
orientation: [0.0, 0.0, qz, qw],
frame_id: None,
covariance: None,
}
}
pub fn distance_to(&self, other: &Pose) -> f32 {
let dx = self.position[0] - other.position[0];
let dy = self.position[1] - other.position[1];
let dz = self.position[2] - other.position[2];
(dx * dx + dy * dy + dz * dz).sqrt()
}
pub fn distance_2d_to(&self, other: &Pose) -> f32 {
let dx = self.position[0] - other.position[0];
let dy = self.position[1] - other.position[1];
(dx * dx + dy * dy).sqrt()
}
pub fn normalize_orientation(&mut self) {
let q = self.orientation;
let mag = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]).sqrt();
if mag > 0.0 {
self.orientation = [q[0] / mag, q[1] / mag, q[2] / mag, q[3] / mag];
}
}
}
impl Default for Pose {
fn default() -> Self {
Self::identity()
}
}
impl TrackingResult {
pub fn new(track_id: u32, detection: Detection) -> Self {
Self {
timestamp: crate::prelude::now_micros(),
track_id,
detection,
velocity_2d: None,
age: 0,
time_since_update: 0,
confidence: 1.0,
}
}
pub fn is_active(&self) -> bool {
self.time_since_update == 0
}
pub fn speed(&self) -> Option<f32> {
self.velocity_2d.map(|v| (v[0] * v[0] + v[1] * v[1]).sqrt())
}
pub fn is_confident(&self, threshold: f32) -> bool {
self.confidence >= threshold
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrackingResult {
pub timestamp: u64,
pub track_id: u32,
pub detection: Detection,
#[serde(skip_serializing_if = "Option::is_none")]
pub velocity_2d: Option<[f32; 2]>,
pub age: u32,
pub time_since_update: u32,
pub confidence: f32,
}