nv_perception/
detection.rs1use nv_core::{BBox, DetectionId, TypedMetadata};
4
5#[derive(Clone, Debug)]
12pub struct Detection {
13 pub id: DetectionId,
15 pub class_id: u32,
17 pub confidence: f32,
19 pub bbox: BBox,
21 pub embedding: Option<Vec<f32>>,
23 pub metadata: TypedMetadata,
25}
26
27impl Detection {
28 #[must_use]
46 pub fn builder(
47 id: DetectionId,
48 class_id: u32,
49 confidence: f32,
50 bbox: BBox,
51 ) -> DetectionBuilder {
52 DetectionBuilder {
53 id,
54 class_id,
55 confidence,
56 bbox,
57 embedding: None,
58 metadata: TypedMetadata::new(),
59 }
60 }
61}
62
63pub struct DetectionBuilder {
68 id: DetectionId,
69 class_id: u32,
70 confidence: f32,
71 bbox: BBox,
72 embedding: Option<Vec<f32>>,
73 metadata: TypedMetadata,
74}
75
76impl DetectionBuilder {
77 #[must_use]
79 pub fn embedding(mut self, embedding: Vec<f32>) -> Self {
80 self.embedding = Some(embedding);
81 self
82 }
83
84 #[must_use]
86 pub fn meta<T: Clone + Send + Sync + 'static>(mut self, val: T) -> Self {
87 self.metadata.insert(val);
88 self
89 }
90
91 #[must_use]
94 pub fn build(self) -> Detection {
95 Detection {
96 id: self.id,
97 class_id: self.class_id,
98 confidence: clamp_unit(self.confidence),
99 bbox: self.bbox,
100 embedding: self.embedding,
101 metadata: self.metadata,
102 }
103 }
104}
105
106fn clamp_unit(v: f32) -> f32 {
108 if v.is_finite() {
109 v.clamp(0.0, 1.0)
110 } else {
111 0.0
112 }
113}
114
115#[derive(Clone, Debug, Default)]
121pub struct DetectionSet {
122 pub detections: Vec<Detection>,
124}
125
126impl DetectionSet {
127 #[must_use]
129 pub fn empty() -> Self {
130 Self {
131 detections: Vec::new(),
132 }
133 }
134
135 #[must_use]
137 pub fn len(&self) -> usize {
138 self.detections.len()
139 }
140
141 #[must_use]
143 pub fn is_empty(&self) -> bool {
144 self.detections.is_empty()
145 }
146}
147
148impl From<Vec<Detection>> for DetectionSet {
149 fn from(detections: Vec<Detection>) -> Self {
150 Self { detections }
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use nv_core::BBox;
158
159 #[test]
160 fn builder_required_fields_only() {
161 let det =
162 Detection::builder(DetectionId::new(1), 0, 0.95, BBox::new(0.1, 0.2, 0.3, 0.4)).build();
163
164 assert_eq!(det.id, DetectionId::new(1));
165 assert_eq!(det.class_id, 0);
166 assert!((det.confidence - 0.95).abs() < f32::EPSILON);
167 assert!(det.embedding.is_none());
168 assert!(det.metadata.is_empty());
169 }
170
171 #[test]
172 fn builder_with_embedding_and_meta() {
173 #[derive(Clone, Debug, PartialEq)]
174 struct Extra(u32);
175
176 let det = Detection::builder(DetectionId::new(2), 5, 0.8, BBox::new(0.0, 0.0, 1.0, 1.0))
177 .embedding(vec![0.1, 0.2, 0.3])
178 .meta(Extra(42))
179 .build();
180
181 assert_eq!(det.embedding.as_ref().unwrap().len(), 3);
182 assert_eq!(det.metadata.get::<Extra>(), Some(&Extra(42)));
183 }
184
185 #[test]
186 fn detection_set_from_vec() {
187 let dets = vec![
188 Detection::builder(DetectionId::new(1), 0, 0.9, BBox::new(0.0, 0.0, 0.5, 0.5)).build(),
189 Detection::builder(DetectionId::new(2), 1, 0.7, BBox::new(0.5, 0.5, 1.0, 1.0)).build(),
190 ];
191 let set: DetectionSet = dets.into();
192 assert_eq!(set.len(), 2);
193 assert!(!set.is_empty());
194 }
195}