use ndarray::{Array, Axis, IxDyn}; #[derive(Clone, PartialEq, Default)] pub struct YOLOResult { // YOLO tasks results of an image pub probs: Option, pub bboxes: Option>, pub keypoints: Option>>, pub masks: Option>>, } impl std::fmt::Debug for YOLOResult { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("YOLOResult") .field( "Probs(top5)", &format_args!("{:?}", self.probs().map(|probs| probs.topk(5))), ) .field("Bboxes", &self.bboxes) .field("Keypoints", &self.keypoints) .field( "Masks", &format_args!("{:?}", self.masks().map(|masks| masks.len())), ) .finish() } } impl YOLOResult { pub fn new( probs: Option, bboxes: Option>, keypoints: Option>>, masks: Option>>, ) -> Self { Self { probs, bboxes, keypoints, masks, } } pub fn probs(&self) -> Option<&Embedding> { self.probs.as_ref() } pub fn keypoints(&self) -> Option<&Vec>> { self.keypoints.as_ref() } pub fn masks(&self) -> Option<&Vec>> { self.masks.as_ref() } pub fn bboxes(&self) -> Option<&Vec> { self.bboxes.as_ref() } pub fn bboxes_mut(&mut self) -> Option<&mut Vec> { self.bboxes.as_mut() } } #[derive(Debug, PartialEq, Clone, Default)] pub struct Point2 { // A point2d with x, y, conf x: f32, y: f32, confidence: f32, } impl Point2 { pub fn new_with_conf(x: f32, y: f32, confidence: f32) -> Self { Self { x, y, confidence } } pub fn new(x: f32, y: f32) -> Self { Self { x, y, ..Default::default() } } pub fn x(&self) -> f32 { self.x } pub fn y(&self) -> f32 { self.y } pub fn confidence(&self) -> f32 { self.confidence } } #[derive(Debug, Clone, PartialEq, Default)] pub struct Embedding { // An float32 n-dims tensor data: Array, } impl Embedding { pub fn new(data: Array) -> Self { Self { data } } pub fn data(&self) -> &Array { &self.data } pub fn topk(&self, k: usize) -> Vec<(usize, f32)> { let mut probs = self .data .iter() .enumerate() .map(|(a, b)| (a, *b)) .collect::>(); probs.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); let mut topk = Vec::new(); for &(id, confidence) in probs.iter().take(k) { topk.push((id, confidence)); } topk } pub fn norm(&self) -> Array { let std_ = self.data.mapv(|x| x * x).sum_axis(Axis(0)).mapv(f32::sqrt); self.data.clone() / std_ } pub fn top1(&self) -> (usize, f32) { self.topk(1)[0] } } #[derive(Debug, Clone, PartialEq, Default)] pub struct Bbox { // a bounding box around an object xmin: f32, ymin: f32, width: f32, height: f32, id: usize, confidence: f32, } impl Bbox { pub fn new_from_xywh(xmin: f32, ymin: f32, width: f32, height: f32) -> Self { Self { xmin, ymin, width, height, ..Default::default() } } pub fn new(xmin: f32, ymin: f32, width: f32, height: f32, id: usize, confidence: f32) -> Self { Self { xmin, ymin, width, height, id, confidence, } } pub fn width(&self) -> f32 { self.width } pub fn height(&self) -> f32 { self.height } pub fn xmin(&self) -> f32 { self.xmin } pub fn ymin(&self) -> f32 { self.ymin } pub fn xmax(&self) -> f32 { self.xmin + self.width } pub fn ymax(&self) -> f32 { self.ymin + self.height } pub fn tl(&self) -> Point2 { Point2::new(self.xmin, self.ymin) } pub fn br(&self) -> Point2 { Point2::new(self.xmax(), self.ymax()) } pub fn cxcy(&self) -> Point2 { Point2::new(self.xmin + self.width / 2., self.ymin + self.height / 2.) } pub fn id(&self) -> usize { self.id } pub fn confidence(&self) -> f32 { self.confidence } pub fn area(&self) -> f32 { self.width * self.height } pub fn intersection_area(&self, another: &Bbox) -> f32 { let l = self.xmin.max(another.xmin); let r = (self.xmin + self.width).min(another.xmin + another.width); let t = self.ymin.max(another.ymin); let b = (self.ymin + self.height).min(another.ymin + another.height); (r - l + 1.).max(0.) * (b - t + 1.).max(0.) } pub fn union(&self, another: &Bbox) -> f32 { self.area() + another.area() - self.intersection_area(another) } pub fn iou(&self, another: &Bbox) -> f32 { self.intersection_area(another) / self.union(another) } }