Skip to main content

edgefirst-decoder

YOLO and ModelPack output decoding for edge AI — bounding boxes, segmentation masks and multi-object tracking, without leaving native code.

PyPI License

Part of the EdgeFirst HAL

edgefirst-decoder is one of five Python packages built from the EdgeFirst Hardware Abstraction Layer.

The EdgeFirstAI/hal repository is the home for all of them — source, issue tracker, architecture documentation and release notes.

Package Provides
edgefirst-tensor Zero-copy tensor allocation and host/GPU/CUDA mapping
edgefirst-codec JPEG and PNG decoding directly into pre-allocated tensors
edgefirst-image GPU-accelerated colour conversion, resize, letterbox, tiling and drawing
edgefirst-decoder YOLO and ModelPack output decoding (this package)
edgefirst-tracker ByteTrack multi-object tracking

Installation

pip install edgefirst-decoder

Requires Python 3.8 or newer; edgefirst-tensor and NumPy are installed automatically. Wheels are published for Linux (x86_64, aarch64), macOS (arm64), and Windows (x86_64).

Packages install under the PEP 420 edgefirst.* namespace, so the import is edgefirst.decoder.

Quick start

Describe your model's outputs, then decode inference results into detections:

import numpy as np
from edgefirst.decoder import Decoder, Output, Tensor

decoder = Decoder.new_from_outputs(
    outputs=[Output.detection(shape=[1, 84, 8400])],
    score_threshold=0.25,
    iou_threshold=0.45,
)

# `raw` is what your inference runtime produced. Copy it into a HAL tensor;
# in a real pipeline the runtime writes into the tensor directly instead.
raw = np.zeros((1, 84, 8400), dtype=np.float32)
raw[0, 0:4, 0] = [0.5, 0.5, 0.2, 0.2]  # cx, cy, w, h (normalized)
raw[0, 4, 0] = 0.9  # class 0 score

output = Tensor(raw.shape, "float32")
output.from_numpy(raw)

boxes, scores, classes, masks = decoder.decode([output])
print(np.asarray(boxes), np.asarray(scores), np.asarray(classes))

Boxes come back normalized. NMS runs class-aware by default (unless the model config sets a mode); pass nms=Nms.ClassAgnostic or nms=None to change or bypass it. For validation and mAP runs, pass multi_label=True to emit one box per class above the score threshold instead of one per anchor; decode_tracked ignores it. Multi-label candidates go through the NMS mode unchanged, so nms=Nms.ClassAgnostic suppresses across the classes of one anchor. Multi-label also raises the default pre_nms_top_k from 300 to 30000 (Ultralytics' max_nms); decoder.pre_nms_top_k = 0 removes the cap.

For a quantized model, attach the quantization parameters to the output description so the decoder dequantizes as it reads:

Output.detection(shape=[1, 84, 8400]).with_quantization(scale=0.004, zero_point=-123)

Configurations can also be supplied as a dictionary, or as JSON or YAML, with Decoder(config_dict), Decoder.new_from_json_str() and Decoder.new_from_yaml_str(). Set decoder_version=DecoderVersion.Yolo26 for end-to-end Ultralytics models.

decode() and friends accept a model-output tensor from any edgefirst.* package, not just this one — they cross packages through the capsule protocol, not by type. Value types such as PixelFormat and TensorMemory are accepted from any package too, comparing and hashing equal across packages by value. Importing Tensor from edgefirst.decoder when calling into this package is still good style for readability, not a requirement. See the Interoperability section below for the one thing that does not cross: isinstance against a concrete class.

Segmentation masks

decode() returns masks at prototype resolution as arrays of shape (H, W, C):

  • Instance segmentation (YOLO): C == 1, a binary per-instance mask — threshold at 128.
  • Semantic segmentation (ModelPack): C == num_classes, per-pixel class scores — take argmax over the last axis.

Multi-object tracking

Tracking lives in the standalone edgefirst-tracker wheel. Decoder.decode_tracked accepts any object with an update method, including edgefirst.tracker.ByteTrack:

from edgefirst.tracker import ByteTrack

tracker = ByteTrack()
boxes, scores, classes, masks, tracks = decoder.decode_tracked(
    tracker, timestamp_ns, outputs
)

Schema inference for Ultralytics exports

A vanilla Ultralytics YOLOv8/11/26 export carries no edgefirst.json, but its own metadata and tensor shapes are enough to derive one. infer_ultralytics_schema reads what your runtime reports and returns a schema you can hand straight to Decoder():

import onnxruntime as ort
from edgefirst.decoder import Decoder, infer_ultralytics_schema

sess = ort.InferenceSession("yolov8n.onnx", providers=["CPUExecutionProvider"])

# Ultralytics ONNX exports are float32 throughout and unquantized. A TFLite
# interpreter reports dtype and quantization per tensor instead; pass those as
# a 4th element on each output: (name, shape, dtype, (scales, zero_points)).
inputs = [(t.name, list(t.shape), "float32") for t in sess.get_inputs()]
outputs = [(t.name, list(t.shape), "float32") for t in sess.get_outputs()]
metadata = dict(sess.get_modelmeta().custom_metadata_map)

inferred = infer_ultralytics_schema("onnx", inputs, outputs, metadata)
print(inferred.description)  # "Ultralytics YOLOv8/11 detect, 80 classes"

decoder = Decoder(inferred.schema, score_threshold=0.25, iou_threshold=0.45)

boxes, scores, classes, masks = decoder.decode(model_outputs)
print(inferred.labels[classes[0]])  # class name for the first detection

The result is a named tuple, so schema, labels, description = inferred works too — but two of the three fields are strings, and naming them is cheaper than remembering the order.

source decides the box convention: Ultralytics ONNX exports report pixel-space coordinates, TFLite exports report [0, 1], and a CoreML export (no-NMS) resolves pixel-space too, the same as ONNX. A nms=True CoreML export produces Apple's own NMS-pipeline artifact instead, which is refused rather than resolved. "other" is accepted but refused by inference rather than defaulted — that convention follows the exporter, is not derivable from shapes, and guessing it scales every box by the input size. Supported dtype strings are the tensor dtype names "i8", "u8", "i16", "u16", "i32", "u32", "f16" and "f32"; the older "int8", "uint8", "int16", "uint16", "int32", "uint32", "float16" and "float32" are still accepted. The schema reports dtypes with the tensor names.

schema is a plain dict, ready for Decoder(schema) — there is no JSON string to parse back. labels is the class names in index order, which is what maps decode()'s class indices back to names.

Shapes must be concrete. A model exported with dynamic=True reports a symbolic axis ('batch' from ONNX, -1 from TFLite); those are refused with a ValueError naming the tensor and axis, because the layout rules need real sizes.

The schema pins the NMS mode and leaves the thresholds to you. Ultralytics runs NMS class-aware (agnostic=False), so an inferred pre-NMS schema says so explicitly. Passing nms= still overrides. Thresholds are not inferable, and Decoder's defaults (score_threshold=0.1, iou_threshold=0.7) are not Ultralytics' (0.25/0.45) — pass them as shown above. YOLO26 end-to-end exports apply NMS in-graph and carry no mode at all.

ValueError is raised for anything that is not a recognizable Ultralytics export — missing or unparsable metadata, an unsupported task (only detect and segment; pose, OBB and classify are refused), or a class count that does not fit the output width. Metadata and shapes are cross-checked against each other, so a disagreement is reported rather than resolved by preference.

What this package provides

API Purpose
Decoder Model output decoding to boxes, scores, classes and masks
Decoder.new_from_outputs() Programmatic configuration from Output descriptions
Decoder.new_from_json_str() / new_from_yaml_str() Configuration from JSON or YAML
infer_ultralytics_schema() Schema derived from a vanilla Ultralytics export's own metadata and shapes
InferredSchema Named tuple returned by infer_ultralytics_schema()
resolve_model_config(), ModelSignals, ModelOverrides Model configuration discovery (edgefirst.json, Hugging Face configs, Ultralytics metadata) with string overrides
ResolvedModelConfig, PreprocessSpec Resolved schema, labels and preprocessing with per-field provenance (ConfigField, ConfigSource, ModelFamily, ModelTask)
Output, DimName Output shape and semantics description
Nms, DecoderType, DecoderVersion NMS mode and model family selection
ProtoData Mask prototypes and coefficients
Decoder.draw_onto Fused decode + draw onto an ImageProcessor
MatchMetric, MergeMode, MergeConfig, TiledFrameAccumulator SAHI tile-merge (keep-best by default; MergeMode.Union for the enclosing union)

Interoperability

decode() / decode_proto() / decode_tracked() accept model-output tensors from any edgefirst.* package, and Decoder / ProtoData instances produced here hand off to edgefirst.image's ImageProcessor the same way. Each extension module registers its own type objects (PyO3 issue #1444 — isinstance across packages is always False, even for two objects wrapping the same Rust type), so acceptance goes through duck-typed capsule protocols instead:

# CORRECT — works regardless of which edgefirst.* package produced obj
if hasattr(obj, "__edgefirst_tensor__"):
    ...

# WRONG — always False for a tensor from a sibling package
if isinstance(obj, edgefirst.image.Tensor):
    ...

edgefirst.decoder.EdgeFirstTensorExportable (re-exported from edgefirst.tensor), EdgeFirstDecoderExportable and EdgeFirstProtoDataExportable are typing.Protocol classes you can annotate a cross-package parameter with. See crates/python-common/INTEROP.md for the full protocol.

Versioning and changelog

All four edgefirst-* packages are versioned and released together with the HAL itself, so a given version number refers to the same source tree in every language. Because of that there is no per-package changelog: release notes for every version live in the single CHANGELOG.md in the hal repository, which follows Keep a Changelog and Semantic Versioning.

License

Apache-2.0

Metadata

Release files for edgefirst-decoder 0.38.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for edgefirst-decoder 0.38.2
File Actions
edgefirst_decoder-0.38.2-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details for edgefirst_decoder-0.38.2-cp311-abi3-win_amd64.whl
edgefirst_decoder-0.38.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 abi3 Linux glibc 2.17+ x86-64 Details for edgefirst_decoder-0.38.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
edgefirst_decoder-0.38.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details for edgefirst_decoder-0.38.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
edgefirst_decoder-0.38.2-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details for edgefirst_decoder-0.38.2-cp311-abi3-macosx_11_0_arm64.whl
edgefirst_decoder-0.38.2-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details for edgefirst_decoder-0.38.2-cp38-abi3-win_amd64.whl
edgefirst_decoder-0.38.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.8 abi3 Linux glibc 2.17+ x86-64 Details for edgefirst_decoder-0.38.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
edgefirst_decoder-0.38.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.8 abi3 Linux glibc 2.17+ ARM64 Details for edgefirst_decoder-0.38.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
edgefirst_decoder-0.38.2-cp38-abi3-macosx_11_0_arm64.whl CPython 3.8 abi3 macOS 11.0+ ARM64 Details for edgefirst_decoder-0.38.2-cp38-abi3-macosx_11_0_arm64.whl

Total release size: 24.9 MB

Release files / edgefirst_decoder-0.38.2-cp311-abi3-win_amd64.whl

Download URL edgefirst_decoder-0.38.2-cp311-abi3-win_amd64.whl
Size 3.2 MB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
5202601c89049d0dc4992f532e5f137eddd898c12c8502ee5c8038d3c4f9dc1a
BLAKE2b-256 checksum
How to use checksums
3653e21427902995cd8f668af4085f259a3a016dfec6e6e33f9ede90b1108987
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / edgefirst_decoder-0.38.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL edgefirst_decoder-0.38.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 3.4 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
fd09fec0907518df2b11f61408796298245cf1aa852385de32a927b218d989c6
BLAKE2b-256 checksum
How to use checksums
155fcdbf9323fe1d4d366d332ebeea1d8e38b36e1088f879f82e65e7d31346e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / edgefirst_decoder-0.38.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL edgefirst_decoder-0.38.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 2.8 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
72f30781d9e332f86ed1f24764faef71c46c639aef953eaf55e9ff62c97418bd
BLAKE2b-256 checksum
How to use checksums
17e526f4ac89fbcb92c9dc2fe95ee1adf5c34e4abcdc1be6f3a069c2bb214860
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / edgefirst_decoder-0.38.2-cp311-abi3-macosx_11_0_arm64.whl

Download URL edgefirst_decoder-0.38.2-cp311-abi3-macosx_11_0_arm64.whl
Size 3.0 MB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
66ce39e804b65262ae5dc7a6dbb85ad931875ebb4707d1e625d73cb2685d3f8d
BLAKE2b-256 checksum
How to use checksums
541c0e225938a7a72c1ca2a33700b122de6b63ace83b8645f4ab8fbf2c90965f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / edgefirst_decoder-0.38.2-cp38-abi3-win_amd64.whl

Download URL edgefirst_decoder-0.38.2-cp38-abi3-win_amd64.whl
Size 3.2 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
b7574fd6c3fa58a77a4a9c88e2055a1ef2a52490bcd9dc97ba183791029daed8
BLAKE2b-256 checksum
How to use checksums
adf5136b87d85a79bd57ab6d488ee5304f8b9bb3b9e64dd7ba4bfc5260983d3c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / edgefirst_decoder-0.38.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL edgefirst_decoder-0.38.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 3.4 MB
Tags CPython 3.8 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
09ce22bf1766005b77d35e9a4663cf13aaab333c30bc022ebcfd2970b793a02c
BLAKE2b-256 checksum
How to use checksums
4acf4b76cb58f09a7cb92b54e7d8f147816b7f55a066243aac795763bb664adf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / edgefirst_decoder-0.38.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL edgefirst_decoder-0.38.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 2.8 MB
Tags CPython 3.8 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
bda3c3d84fdf0519df4abb84465075fd49ec0faec446e8d81cab19d456d5098f
BLAKE2b-256 checksum
How to use checksums
c6b691ed81d39744b45ed894e9211566de81ecbbbb7a0c400ebdef602026092b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / edgefirst_decoder-0.38.2-cp38-abi3-macosx_11_0_arm64.whl

Download URL edgefirst_decoder-0.38.2-cp38-abi3-macosx_11_0_arm64.whl
Size 3.0 MB
Tags CPython 3.8 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
d24158ef6845ec3569b62bdfd079fcab0eed36f64d8e982253d0017fceeb76aa
BLAKE2b-256 checksum
How to use checksums
4bc61173a1f3899cc71548db39b01c934b1fd87edcefa5d070ccbfb5460cb872
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Security for edgefirst-decoder 0.38.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 8 files in this release have been uploaded with provenance metadata.

Select from 8 files

Attestation for edgefirst_decoder-0.38.2-cp311-abi3-win_amd64.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page