Skip to main content

Decoder

Struct Decoder 

Source
pub struct Decoder {
    pub iou_threshold: f32,
    pub score_threshold: f32,
    pub nms: Option<Nms>,
    pub pre_nms_top_k: usize,
    pub max_det: usize,
    /* private fields */
}

Fields§

§iou_threshold: f32§score_threshold: f32§nms: Option<Nms>

NMS mode (always a concrete variant after build — Nms::Auto is resolved during DecoderBuilder::build() and never stored here):

  • Some(ClassAgnostic) — class-agnostic NMS
  • Some(ClassAware) — class-aware NMS
  • None — NMS bypassed (end-to-end models)
§pre_nms_top_k: usize

Maximum number of candidate boxes fed into NMS after score filtering. Reduces O(N²) NMS cost when many low-confidence proposals pass the threshold (common during COCO mAP evaluation with threshold ≈ 0.001). Candidates are ranked by score; only the top pre_nms_top_k proceed to NMS. Default: 300. Ignored when nms is None.

§⚠️ Validation vs Deployment

The default of 300 is tuned for deployment (score threshold ≥ 0.25) where few anchors pass the score filter, making top-K a no-op in practice while bounding worst-case NMS cost.

For mAP evaluation (score threshold ≈ 0.001), most of the 8 400 YOLO anchors pass the score filter. At pre_nms_top_k = 300, roughly 74 % of candidates that would survive NMS are discarded before NMS runs, causing ~9 pp box mAP loss — a measurement artifact, not a model quality issue.

Use casepre_nms_top_kscore_threshold
Deployment300 (default)≥ 0.25
COCO mAP evaluation8 400 (all anchors)0.001
Unbounded0 (no limit)any

Post-processing latency scales with the number of candidates entering NMS. At deployment thresholds the candidate count is already small, so raising pre_nms_top_k has negligible cost. At validation thresholds the increase is measurable but necessary for correct recall.

§max_det: usize

Maximum number of detections returned after NMS. Matches the Ultralytics max_det parameter. Default: 300.

This bound applies uniformly across all segmentation and detection decode paths reached via Decoder::decode / Decoder::decode_proto. The output Vec’s capacity is only an allocation hint; the post-NMS detection count is bounded solely by max_det (EDGEAI-1302).

Implementations§

Source§

impl Decoder

Source

pub fn model_type(&self) -> &ModelType

This function returns the parsed model type of the decoder.

§Examples
    let decoder = DecoderBuilder::default()
        .with_config_yaml_str(config_yaml)
        .build()?;
    assert!(matches!(
        decoder.model_type(),
        ModelType::ModelPackDetSplit { .. }
    ));
Source

pub fn normalized_boxes(&self) -> Option<bool>

Returns the box coordinate format if known from the model config.

  • Some(true): Boxes are in normalized [0,1] coordinates
  • Some(false): Boxes are in pixel coordinates relative to model input
  • None: Unknown, caller must infer (e.g., check if any coordinate > 1.0)

This is determined by the model config’s normalized field, not the NMS mode. When coordinates are in pixels or unknown, the caller may need to normalize using the model input dimensions.

§Examples
    let decoder = DecoderBuilder::default()
        .with_config_yaml_str(config_yaml)
        .build()?;
    // Config doesn't specify normalized, so it's None
    assert!(decoder.normalized_boxes().is_none());
Source

pub fn input_dims(&self) -> Option<(usize, usize)>

Model input dimensions (width, height) captured from the schema’s input.shape / input.dshape, or None when the schema did not declare an input spec (e.g. flat YAML configs or DecoderBuilder::add_output(...) programmatic builds).

Used together with normalized_boxes: when the decoder reports normalized_boxes() == Some(false) and input_dims() is Some((w, h)), the decoder divides post-NMS bbox coordinates by (w, h) so they enter the canonical [0, 1] range before mask cropping (EDGEAI-1303). When input_dims() is None, the decoder cannot perform the division and the existing protobox > 2.0 reject acts as a safety net.

§Examples
    let json = r#"{
        "schema_version": 2,
        "nms": "class_agnostic",
        "input": {
            "shape": [1, 640, 640, 3],
            "dshape": [{"batch": 1}, {"height": 640}, {"width": 640}, {"num_features": 3}]
        },
        "outputs": [{
            "name": "out", "type": "detection",
            "shape": [1, 38, 256],
            "dshape": [{"batch": 1}, {"num_features": 38}, {"num_boxes": 256}],
            "decoder": "ultralytics", "encoding": "direct", "normalized": false
        }]
    }"#;
    let schema: SchemaV2 = serde_json::from_str(json).unwrap();
    let decoder = DecoderBuilder::default().with_schema(schema).build()?;
    assert_eq!(decoder.input_dims(), Some((640, 640)));
Source

pub fn decode( &self, outputs: &[&TensorDyn], output_boxes: &mut Vec<DetectBox>, output_masks: &mut Vec<Segmentation>, ) -> Result<(), DecoderError>

Decode model outputs into detection boxes and segmentation masks.

This is the primary decode API. Accepts TensorDyn outputs directly from model inference. Automatically dispatches to quantized or float paths based on the tensor dtype.

§Arguments
  • outputs - Tensor outputs from model inference
  • output_boxes - Destination for decoded detection boxes (cleared first)
  • output_masks - Destination for decoded segmentation masks (cleared first)
§output_boxes / output_masks capacity

The capacity of the supplied Vecs is only an allocation hint — it is not a cap on the number of detections returned. The post-NMS detection count is bounded by Decoder::max_det (set via DecoderBuilder::with_max_det, default 300). Passing Vec::new() (capacity 0) returns up to max_det detections; pre-allocating with Vec::with_capacity only avoids the reallocation when the decoder grows the buffer.

§Errors

Returns DecoderError if tensor mapping fails, dtypes are unsupported, or the outputs don’t match the decoder’s model configuration.

Source

pub fn decode_proto( &self, outputs: &[&TensorDyn], output_boxes: &mut Vec<DetectBox>, ) -> Result<Option<ProtoData>, DecoderError>

Decode model outputs into detection boxes, returning raw proto data for segmentation models instead of materialized masks.

Accepts TensorDyn outputs directly from model inference. Detections are always decoded into output_boxes regardless of model type. Returns Ok(None) for detection-only and ModelPack models. Returns Ok(Some(ProtoData)) for YOLO segmentation models.

§Arguments
  • outputs - Tensor outputs from model inference
  • output_boxes - Destination for decoded detection boxes (cleared first)
§output_boxes capacity

The capacity of output_boxes is only an allocation hint — it is not a cap on the number of detections returned. The post-NMS detection count is bounded by Decoder::max_det (set via DecoderBuilder::with_max_det, default 300). Passing Vec::new() (capacity 0) returns up to max_det detections.

§Errors

Returns DecoderError if tensor mapping fails, dtypes are unsupported, or the outputs don’t match the decoder’s model configuration.

Trait Implementations§

Source§

impl Clone for Decoder

Source§

fn clone(&self) -> Self

Cloning a Decoder preserves the legacy decode path (decode_program) but drops the per-scale fast path: PerScaleDecoder owns mutable per-frame scratch buffers and is not Clone. Decoders built from a per-scale schema should be rebuilt via DecoderBuilder rather than cloned to preserve the fast path; cloning is intended for tests and rare configs.

1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Decoder

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Decoder

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more