edgefirst_decoder/decoder/config.rs
1// SPDX-FileCopyrightText: Copyright 2025 Au-Zone Technologies
2// SPDX-License-Identifier: Apache-2.0
3
4use super::configs::{self, DimName, QuantTuple};
5use serde::{Deserialize, Serialize};
6
7/// Used to represent the outputs in the model configuration.
8/// # Examples
9/// ```rust
10/// # use edgefirst_decoder::{DecoderBuilder, DecoderResult, ConfigOutputs};
11/// # fn main() -> DecoderResult<()> {
12/// let config_json = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata/modelpack_split.json"));
13/// let config: ConfigOutputs = serde_json::from_str(config_json)?;
14/// let decoder = DecoderBuilder::new().with_config(config).build()?;
15///
16/// # Ok(())
17/// # }
18#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
19pub struct ConfigOutputs {
20 #[serde(default)]
21 pub outputs: Vec<ConfigOutput>,
22 /// NMS mode from config file. When present, overrides the builder's NMS
23 /// setting.
24 /// - `Some(Nms::ClassAgnostic)` — class-agnostic NMS: suppress overlapping
25 /// boxes regardless of class
26 /// - `Some(Nms::ClassAware)` — class-aware NMS: only suppress boxes with
27 /// the same class
28 /// - `None` — use builder default or skip NMS (user handles it externally)
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub nms: Option<configs::Nms>,
31 /// Decoder version for Ultralytics models. Determines the decoding
32 /// strategy.
33 /// - `Some(Yolo26)` — end-to-end model with embedded NMS
34 /// - `Some(Yolov5/Yolov8/Yolo11)` — traditional models requiring external
35 /// NMS
36 /// - `None` — infer from other settings (legacy behavior)
37 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub decoder_version: Option<configs::DecoderVersion>,
39}
40
41#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
42#[serde(tag = "type")]
43pub enum ConfigOutput {
44 #[serde(rename = "detection")]
45 Detection(configs::Detection),
46 #[serde(rename = "masks")]
47 Mask(configs::Mask),
48 #[serde(rename = "segmentation")]
49 Segmentation(configs::Segmentation),
50 #[serde(rename = "protos")]
51 Protos(configs::Protos),
52 #[serde(rename = "scores")]
53 Scores(configs::Scores),
54 #[serde(rename = "boxes")]
55 Boxes(configs::Boxes),
56 #[serde(rename = "mask_coefficients")]
57 MaskCoefficients(configs::MaskCoefficients),
58 #[serde(rename = "classes")]
59 Classes(configs::Classes),
60}
61
62#[derive(Debug, PartialEq, Clone)]
63pub enum ConfigOutputRef<'a> {
64 Detection(&'a configs::Detection),
65 Mask(&'a configs::Mask),
66 Segmentation(&'a configs::Segmentation),
67 Protos(&'a configs::Protos),
68 Scores(&'a configs::Scores),
69 Boxes(&'a configs::Boxes),
70 MaskCoefficients(&'a configs::MaskCoefficients),
71 Classes(&'a configs::Classes),
72}
73
74impl<'a> ConfigOutputRef<'a> {
75 pub(super) fn decoder(&self) -> configs::DecoderType {
76 match self {
77 ConfigOutputRef::Detection(v) => v.decoder,
78 ConfigOutputRef::Mask(v) => v.decoder,
79 ConfigOutputRef::Segmentation(v) => v.decoder,
80 ConfigOutputRef::Protos(v) => v.decoder,
81 ConfigOutputRef::Scores(v) => v.decoder,
82 ConfigOutputRef::Boxes(v) => v.decoder,
83 ConfigOutputRef::MaskCoefficients(v) => v.decoder,
84 ConfigOutputRef::Classes(v) => v.decoder,
85 }
86 }
87
88 pub(super) fn dshape(&self) -> &[(DimName, usize)] {
89 match self {
90 ConfigOutputRef::Detection(v) => &v.dshape,
91 ConfigOutputRef::Mask(v) => &v.dshape,
92 ConfigOutputRef::Segmentation(v) => &v.dshape,
93 ConfigOutputRef::Protos(v) => &v.dshape,
94 ConfigOutputRef::Scores(v) => &v.dshape,
95 ConfigOutputRef::Boxes(v) => &v.dshape,
96 ConfigOutputRef::MaskCoefficients(v) => &v.dshape,
97 ConfigOutputRef::Classes(v) => &v.dshape,
98 }
99 }
100}
101
102impl<'a> From<&'a configs::Detection> for ConfigOutputRef<'a> {
103 /// Converts from references of config structs to ConfigOutputRef
104 /// # Examples
105 /// ```rust
106 /// # use edgefirst_decoder::{configs, ConfigOutputRef};
107 /// let detection_config = configs::Detection {
108 /// anchors: None,
109 /// decoder: configs::DecoderType::Ultralytics,
110 /// quantization: None,
111 /// shape: vec![1, 84, 8400],
112 /// dshape: Vec::new(),
113 /// normalized: Some(true),
114 /// };
115 /// let output: ConfigOutputRef = (&detection_config).into();
116 /// ```
117 fn from(v: &'a configs::Detection) -> ConfigOutputRef<'a> {
118 ConfigOutputRef::Detection(v)
119 }
120}
121
122impl<'a> From<&'a configs::Mask> for ConfigOutputRef<'a> {
123 /// Converts from references of config structs to ConfigOutputRef
124 /// # Examples
125 /// ```rust
126 /// # use edgefirst_decoder::{configs, ConfigOutputRef};
127 /// let mask = configs::Mask {
128 /// decoder: configs::DecoderType::ModelPack,
129 /// quantization: None,
130 /// shape: vec![1, 160, 160, 1],
131 /// dshape: Vec::new(),
132 /// };
133 /// let output: ConfigOutputRef = (&mask).into();
134 /// ```
135 fn from(v: &'a configs::Mask) -> ConfigOutputRef<'a> {
136 ConfigOutputRef::Mask(v)
137 }
138}
139
140impl<'a> From<&'a configs::Segmentation> for ConfigOutputRef<'a> {
141 /// Converts from references of config structs to ConfigOutputRef
142 /// # Examples
143 /// ```rust
144 /// # use edgefirst_decoder::{configs, ConfigOutputRef};
145 /// let seg = configs::Segmentation {
146 /// decoder: configs::DecoderType::ModelPack,
147 /// quantization: None,
148 /// shape: vec![1, 160, 160, 3],
149 /// dshape: Vec::new(),
150 /// };
151 /// let output: ConfigOutputRef = (&seg).into();
152 /// ```
153 fn from(v: &'a configs::Segmentation) -> ConfigOutputRef<'a> {
154 ConfigOutputRef::Segmentation(v)
155 }
156}
157
158impl<'a> From<&'a configs::Protos> for ConfigOutputRef<'a> {
159 /// Converts from references of config structs to ConfigOutputRef
160 /// # Examples
161 /// ```rust
162 /// # use edgefirst_decoder::{configs, ConfigOutputRef};
163 /// let protos = configs::Protos {
164 /// decoder: configs::DecoderType::Ultralytics,
165 /// quantization: None,
166 /// shape: vec![1, 160, 160, 32],
167 /// dshape: Vec::new(),
168 /// };
169 /// let output: ConfigOutputRef = (&protos).into();
170 /// ```
171 fn from(v: &'a configs::Protos) -> ConfigOutputRef<'a> {
172 ConfigOutputRef::Protos(v)
173 }
174}
175
176impl<'a> From<&'a configs::Scores> for ConfigOutputRef<'a> {
177 /// Converts from references of config structs to ConfigOutputRef
178 /// # Examples
179 /// ```rust
180 /// # use edgefirst_decoder::{configs, ConfigOutputRef};
181 /// let scores = configs::Scores {
182 /// decoder: configs::DecoderType::Ultralytics,
183 /// quantization: None,
184 /// shape: vec![1, 40, 8400],
185 /// dshape: Vec::new(),
186 /// };
187 /// let output: ConfigOutputRef = (&scores).into();
188 /// ```
189 fn from(v: &'a configs::Scores) -> ConfigOutputRef<'a> {
190 ConfigOutputRef::Scores(v)
191 }
192}
193
194impl<'a> From<&'a configs::Boxes> for ConfigOutputRef<'a> {
195 /// Converts from references of config structs to ConfigOutputRef
196 /// # Examples
197 /// ```rust
198 /// # use edgefirst_decoder::{configs, ConfigOutputRef};
199 /// let boxes = configs::Boxes {
200 /// decoder: configs::DecoderType::Ultralytics,
201 /// quantization: None,
202 /// shape: vec![1, 4, 8400],
203 /// dshape: Vec::new(),
204 /// normalized: Some(true),
205 /// };
206 /// let output: ConfigOutputRef = (&boxes).into();
207 /// ```
208 fn from(v: &'a configs::Boxes) -> ConfigOutputRef<'a> {
209 ConfigOutputRef::Boxes(v)
210 }
211}
212
213impl<'a> From<&'a configs::MaskCoefficients> for ConfigOutputRef<'a> {
214 /// Converts from references of config structs to ConfigOutputRef
215 /// # Examples
216 /// ```rust
217 /// # use edgefirst_decoder::{configs, ConfigOutputRef};
218 /// let mask_coefficients = configs::MaskCoefficients {
219 /// decoder: configs::DecoderType::Ultralytics,
220 /// quantization: None,
221 /// shape: vec![1, 32, 8400],
222 /// dshape: Vec::new(),
223 /// };
224 /// let output: ConfigOutputRef = (&mask_coefficients).into();
225 /// ```
226 fn from(v: &'a configs::MaskCoefficients) -> ConfigOutputRef<'a> {
227 ConfigOutputRef::MaskCoefficients(v)
228 }
229}
230
231impl<'a> From<&'a configs::Classes> for ConfigOutputRef<'a> {
232 fn from(v: &'a configs::Classes) -> ConfigOutputRef<'a> {
233 ConfigOutputRef::Classes(v)
234 }
235}
236
237impl ConfigOutput {
238 /// Returns the shape of the output.
239 ///
240 /// # Examples
241 /// ```rust
242 /// # use edgefirst_decoder::{configs, ConfigOutput};
243 /// let detection_config = configs::Detection {
244 /// anchors: None,
245 /// decoder: configs::DecoderType::Ultralytics,
246 /// quantization: None,
247 /// shape: vec![1, 84, 8400],
248 /// dshape: Vec::new(),
249 /// normalized: Some(true),
250 /// };
251 /// let output = ConfigOutput::Detection(detection_config);
252 /// assert_eq!(output.shape(), &[1, 84, 8400]);
253 /// ```
254 pub fn shape(&self) -> &[usize] {
255 match self {
256 ConfigOutput::Detection(detection) => &detection.shape,
257 ConfigOutput::Mask(mask) => &mask.shape,
258 ConfigOutput::Segmentation(segmentation) => &segmentation.shape,
259 ConfigOutput::Scores(scores) => &scores.shape,
260 ConfigOutput::Boxes(boxes) => &boxes.shape,
261 ConfigOutput::Protos(protos) => &protos.shape,
262 ConfigOutput::MaskCoefficients(mask_coefficients) => &mask_coefficients.shape,
263 ConfigOutput::Classes(classes) => &classes.shape,
264 }
265 }
266
267 /// Returns the decoder type of the output.
268 ///
269 /// # Examples
270 /// ```rust
271 /// # use edgefirst_decoder::{configs, ConfigOutput};
272 /// let detection_config = configs::Detection {
273 /// anchors: None,
274 /// decoder: configs::DecoderType::Ultralytics,
275 /// quantization: None,
276 /// shape: vec![1, 84, 8400],
277 /// dshape: Vec::new(),
278 /// normalized: Some(true),
279 /// };
280 /// let output = ConfigOutput::Detection(detection_config);
281 /// assert_eq!(output.decoder(), &configs::DecoderType::Ultralytics);
282 /// ```
283 pub fn decoder(&self) -> &configs::DecoderType {
284 match self {
285 ConfigOutput::Detection(detection) => &detection.decoder,
286 ConfigOutput::Mask(mask) => &mask.decoder,
287 ConfigOutput::Segmentation(segmentation) => &segmentation.decoder,
288 ConfigOutput::Scores(scores) => &scores.decoder,
289 ConfigOutput::Boxes(boxes) => &boxes.decoder,
290 ConfigOutput::Protos(protos) => &protos.decoder,
291 ConfigOutput::MaskCoefficients(mask_coefficients) => &mask_coefficients.decoder,
292 ConfigOutput::Classes(classes) => &classes.decoder,
293 }
294 }
295
296 /// Returns the quantization of the output.
297 ///
298 /// # Examples
299 /// ```rust
300 /// # use edgefirst_decoder::{configs, ConfigOutput};
301 /// let detection_config = configs::Detection {
302 /// anchors: None,
303 /// decoder: configs::DecoderType::Ultralytics,
304 /// quantization: Some(configs::QuantTuple(0.012345, 26)),
305 /// shape: vec![1, 84, 8400],
306 /// dshape: Vec::new(),
307 /// normalized: Some(true),
308 /// };
309 /// let output = ConfigOutput::Detection(detection_config);
310 /// assert_eq!(output.quantization(),
311 /// Some(configs::QuantTuple(0.012345,26))); ```
312 pub fn quantization(&self) -> Option<QuantTuple> {
313 match self {
314 ConfigOutput::Detection(detection) => detection.quantization,
315 ConfigOutput::Mask(mask) => mask.quantization,
316 ConfigOutput::Segmentation(segmentation) => segmentation.quantization,
317 ConfigOutput::Scores(scores) => scores.quantization,
318 ConfigOutput::Boxes(boxes) => boxes.quantization,
319 ConfigOutput::Protos(protos) => protos.quantization,
320 ConfigOutput::MaskCoefficients(mask_coefficients) => mask_coefficients.quantization,
321 ConfigOutput::Classes(classes) => classes.quantization,
322 }
323 }
324}