warden_vision/core/
keypoints.rs1#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct Keypoint {
8 pub x: f32,
10 pub y: f32,
12 pub confidence: f32,
14}
15
16impl Keypoint {
17 pub fn new(x: f32, y: f32, confidence: f32) -> Self {
19 Self { x, y, confidence }
20 }
21}
22
23#[derive(Debug, Clone, Default, PartialEq)]
30pub struct KeypointSet {
31 pub points: Vec<Option<Keypoint>>,
33 pub class_id: usize,
35}
36
37impl KeypointSet {
38 pub fn new(points: Vec<Option<Keypoint>>, class_id: usize) -> Self {
40 Self { points, class_id }
41 }
42
43 pub fn len(&self) -> usize {
45 self.points.len()
46 }
47
48 pub fn is_empty(&self) -> bool {
50 self.points.is_empty()
51 }
52
53 pub fn get(&self, index: usize) -> Option<Keypoint> {
55 self.points.get(index).copied().flatten()
56 }
57}
58
59#[derive(Debug, Clone, Default, PartialEq)]
61pub struct KeyPoints {
62 pub keypoint_sets: Vec<KeypointSet>,
64}
65
66impl KeyPoints {
67 pub fn new(keypoint_sets: Vec<KeypointSet>) -> Self {
69 Self { keypoint_sets }
70 }
71
72 pub fn empty() -> Self {
74 Self::default()
75 }
76
77 pub fn len(&self) -> usize {
79 self.keypoint_sets.len()
80 }
81
82 pub fn is_empty(&self) -> bool {
84 self.keypoint_sets.is_empty()
85 }
86
87 pub fn iter(&self) -> std::slice::Iter<'_, KeypointSet> {
89 self.keypoint_sets.iter()
90 }
91
92 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, KeypointSet> {
94 self.keypoint_sets.iter_mut()
95 }
96
97 pub fn filter_by_confidence(&self, threshold: f32) -> KeyPoints {
104 let sets = self
105 .keypoint_sets
106 .iter()
107 .map(|set| {
108 let points = set
109 .points
110 .iter()
111 .map(|p| p.filter(|kp| kp.confidence >= threshold))
112 .collect();
113 KeypointSet::new(points, set.class_id)
114 })
115 .collect();
116 KeyPoints::new(sets)
117 }
118}
119
120impl IntoIterator for KeyPoints {
121 type Item = KeypointSet;
122 type IntoIter = std::vec::IntoIter<KeypointSet>;
123
124 fn into_iter(self) -> Self::IntoIter {
125 self.keypoint_sets.into_iter()
126 }
127}
128
129impl<'a> IntoIterator for &'a KeyPoints {
130 type Item = &'a KeypointSet;
131 type IntoIter = std::slice::Iter<'a, KeypointSet>;
132
133 fn into_iter(self) -> Self::IntoIter {
134 self.keypoint_sets.iter()
135 }
136}
137
138pub const COCO_17_EDGES: &[(usize, usize)] = &[
146 (15, 13),
147 (13, 11),
148 (16, 14),
149 (14, 12),
150 (11, 12),
151 (5, 11),
152 (6, 12),
153 (5, 6),
154 (5, 7),
155 (6, 8),
156 (7, 9),
157 (8, 10),
158 (1, 2),
159 (0, 1),
160 (0, 2),
161 (1, 3),
162 (2, 4),
163 (3, 5),
164 (4, 6),
165];
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn keypoint_set_get_returns_none_for_missing_joint() {
173 let set = KeypointSet::new(vec![Some(Keypoint::new(1.0, 2.0, 0.9)), None], 0);
174 assert!(set.get(0).is_some());
175 assert!(set.get(1).is_none());
176 assert!(set.get(5).is_none());
177 }
178
179 #[test]
180 fn filter_by_confidence_clears_low_confidence_joints_only() {
181 let sets = vec![KeypointSet::new(
182 vec![
183 Some(Keypoint::new(0.0, 0.0, 0.9)),
184 Some(Keypoint::new(1.0, 1.0, 0.1)),
185 None,
186 ],
187 0,
188 )];
189 let filtered = KeyPoints::new(sets).filter_by_confidence(0.5);
190 assert!(filtered.keypoint_sets[0].get(0).is_some());
191 assert!(filtered.keypoint_sets[0].get(1).is_none());
192 assert!(filtered.keypoint_sets[0].get(2).is_none());
193 assert_eq!(filtered.len(), 1);
195 }
196
197 #[test]
198 fn coco_17_edges_reference_valid_joint_indices() {
199 for &(a, b) in COCO_17_EDGES {
200 assert!(a < 17);
201 assert!(b < 17);
202 }
203 assert_eq!(COCO_17_EDGES.len(), 19);
204 }
205}