#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Keypoint {
pub x: f32,
pub y: f32,
pub confidence: f32,
}
impl Keypoint {
pub fn new(x: f32, y: f32, confidence: f32) -> Self {
Self { x, y, confidence }
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct KeypointSet {
pub points: Vec<Option<Keypoint>>,
pub class_id: usize,
}
impl KeypointSet {
pub fn new(points: Vec<Option<Keypoint>>, class_id: usize) -> Self {
Self { points, class_id }
}
pub fn len(&self) -> usize {
self.points.len()
}
pub fn is_empty(&self) -> bool {
self.points.is_empty()
}
pub fn get(&self, index: usize) -> Option<Keypoint> {
self.points.get(index).copied().flatten()
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct KeyPoints {
pub keypoint_sets: Vec<KeypointSet>,
}
impl KeyPoints {
pub fn new(keypoint_sets: Vec<KeypointSet>) -> Self {
Self { keypoint_sets }
}
pub fn empty() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.keypoint_sets.len()
}
pub fn is_empty(&self) -> bool {
self.keypoint_sets.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, KeypointSet> {
self.keypoint_sets.iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, KeypointSet> {
self.keypoint_sets.iter_mut()
}
pub fn filter_by_confidence(&self, threshold: f32) -> KeyPoints {
let sets = self
.keypoint_sets
.iter()
.map(|set| {
let points = set
.points
.iter()
.map(|p| p.filter(|kp| kp.confidence >= threshold))
.collect();
KeypointSet::new(points, set.class_id)
})
.collect();
KeyPoints::new(sets)
}
}
impl IntoIterator for KeyPoints {
type Item = KeypointSet;
type IntoIter = std::vec::IntoIter<KeypointSet>;
fn into_iter(self) -> Self::IntoIter {
self.keypoint_sets.into_iter()
}
}
impl<'a> IntoIterator for &'a KeyPoints {
type Item = &'a KeypointSet;
type IntoIter = std::slice::Iter<'a, KeypointSet>;
fn into_iter(self) -> Self::IntoIter {
self.keypoint_sets.iter()
}
}
pub const COCO_17_EDGES: &[(usize, usize)] = &[
(15, 13),
(13, 11),
(16, 14),
(14, 12),
(11, 12),
(5, 11),
(6, 12),
(5, 6),
(5, 7),
(6, 8),
(7, 9),
(8, 10),
(1, 2),
(0, 1),
(0, 2),
(1, 3),
(2, 4),
(3, 5),
(4, 6),
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keypoint_set_get_returns_none_for_missing_joint() {
let set = KeypointSet::new(vec![Some(Keypoint::new(1.0, 2.0, 0.9)), None], 0);
assert!(set.get(0).is_some());
assert!(set.get(1).is_none());
assert!(set.get(5).is_none());
}
#[test]
fn filter_by_confidence_clears_low_confidence_joints_only() {
let sets = vec![KeypointSet::new(
vec![
Some(Keypoint::new(0.0, 0.0, 0.9)),
Some(Keypoint::new(1.0, 1.0, 0.1)),
None,
],
0,
)];
let filtered = KeyPoints::new(sets).filter_by_confidence(0.5);
assert!(filtered.keypoint_sets[0].get(0).is_some());
assert!(filtered.keypoint_sets[0].get(1).is_none());
assert!(filtered.keypoint_sets[0].get(2).is_none());
assert_eq!(filtered.len(), 1);
}
#[test]
fn coco_17_edges_reference_valid_joint_indices() {
for &(a, b) in COCO_17_EDGES {
assert!(a < 17);
assert!(b < 17);
}
assert_eq!(COCO_17_EDGES.len(), 19);
}
}