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 by default in class-agnostic mode; pass nms=Nms.ClassAware or nms=None to change or bypass it.

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]. "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 "int8", "uint8", "int16", "uint16", "int32", "uint32", "float16" and "float32".

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 rather than inheriting Decoder's class-agnostic default, which would suppress a box against an overlapping box of a different class. 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()
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 #1444isinstance 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.

Links

License

Apache-2.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

edgefirst_decoder-0.30.0-cp311-abi3-win_amd64.whl (2.2 MB view details)

Uploaded CPython 3.11+Windows x86-64

edgefirst_decoder-0.30.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.2 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

edgefirst_decoder-0.30.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.0 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

edgefirst_decoder-0.30.0-cp311-abi3-macosx_11_0_arm64.whl (2.0 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

edgefirst_decoder-0.30.0-cp38-abi3-win_amd64.whl (2.2 MB view details)

Uploaded CPython 3.8+Windows x86-64

edgefirst_decoder-0.30.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.2 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

edgefirst_decoder-0.30.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.0 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

edgefirst_decoder-0.30.0-cp38-abi3-macosx_11_0_arm64.whl (2.0 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

Details for the file edgefirst_decoder-0.30.0-cp311-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for edgefirst_decoder-0.30.0-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 53eb10f0aeea3834ce118a52745d598a79b4fc40d2ef24ad6783e473a1beb223
MD5 4cc1b219585caff750e905ee08d8d62e
BLAKE2b-256 67c1b49ccf663d3a20fc9417c47bb18fef22b41a156f1056b555373bab266df3

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_decoder-0.30.0-cp311-abi3-win_amd64.whl:

Publisher: release.yml on EdgeFirstAI/hal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file edgefirst_decoder-0.30.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for edgefirst_decoder-0.30.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 14b6669924ab59673273229f795ed9260ae5533e124dae2592d2255fc287f3ba
MD5 952f938b435f2542593368d354f5b8e7
BLAKE2b-256 191df83f56fe42a5a89242b354f2ad097b844ab45a963ffb0087a93fe94e06e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_decoder-0.30.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on EdgeFirstAI/hal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file edgefirst_decoder-0.30.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for edgefirst_decoder-0.30.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1da75dfd2c5f0db2a220dbedf88aa33008a17efab5bdb3e53e028b2ead506cf7
MD5 077e56eb5d6cb60740222bc0276d3dac
BLAKE2b-256 e8b03df0aba5fb9cfc1645965a021fc3efa510ec5d21089b7573bd3b2b3e140c

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_decoder-0.30.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on EdgeFirstAI/hal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file edgefirst_decoder-0.30.0-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for edgefirst_decoder-0.30.0-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0d021d333cc190a6da97c2e78b0c89a1760569267e0ebbdfbce950f5468642d8
MD5 c64a29a1e1b33f8b97f53f4b194271a4
BLAKE2b-256 e645dae31a028d5caf38f22778fe2dae401e24f10c8c619a435c5c1f2f1d2169

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_decoder-0.30.0-cp311-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on EdgeFirstAI/hal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file edgefirst_decoder-0.30.0-cp38-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for edgefirst_decoder-0.30.0-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 d57e3680cfccf3cd5e3af5b69d83b933b6cb63ea01ccbb0b502b50a7ac0ed513
MD5 c90444f252c0a94cd297d341095f18e9
BLAKE2b-256 63ef32be79c5b47368a7ea1da1a683cdb951acf96adf2e344403e6706d2ee0ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_decoder-0.30.0-cp38-abi3-win_amd64.whl:

Publisher: release.yml on EdgeFirstAI/hal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file edgefirst_decoder-0.30.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for edgefirst_decoder-0.30.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 24e1f92cb01294be8c39e477c60887f404dd54bfd6c7eaba9be91a26dc2aae70
MD5 4473c5e426cd05d0f58640da46a2d85b
BLAKE2b-256 dc2c7e46c2ab819e761c5a91d139a25b34e217ea120e4ddfeb14600f90ff689f

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_decoder-0.30.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on EdgeFirstAI/hal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file edgefirst_decoder-0.30.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for edgefirst_decoder-0.30.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 6c9eded0180c47c6ca9836ee320d9146ca5148ac55286c0606d64f583f1af107
MD5 cde54c4fe87758f1e799d433cd015515
BLAKE2b-256 7c47ae514694931120c834353422293889eeafe15da2455ac5f6ffce2fac2f24

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_decoder-0.30.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on EdgeFirstAI/hal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file edgefirst_decoder-0.30.0-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for edgefirst_decoder-0.30.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 87566119effc15604363a6f96595384802272e2f1b7b6feb4e726d1d71f813e8
MD5 f9495456640ad83078ddc7340b81669c
BLAKE2b-256 4c82363d5e5269b2af94693f661d24ac5afdc96c0a419fff882a85da99f5c1a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_decoder-0.30.0-cp38-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on EdgeFirstAI/hal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.30.0 This release

8 files

0.29.4

8 files

0.29.3

8 files

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