This release is a pre-release and may not be stable for production use.
edgefirst-codec
JPEG and PNG decoding straight into pre-allocated tensors — no per-frame allocations, with optional hardware acceleration on Linux.
Part of the EdgeFirst HAL
edgefirst-codec 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 (this package) |
edgefirst-image |
GPU-accelerated colour conversion, resize, letterbox, tiling and drawing |
edgefirst-decoder |
YOLO and ModelPack output decoding |
edgefirst-tracker |
ByteTrack multi-object tracking |
Installation
pip install edgefirst-codec
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), and are self-contained — there is no system JPEG library to install.
Packages install under the PEP 420 edgefirst.* namespace, so the import is edgefirst.codec.
Quick start
For maximum performance, decode straight into a tensor allocated by edgefirst.image's ImageProcessor.create_image() — DMA/PBO-backed and GPU-pitch-aligned — then hand it to ImageProcessor.convert() for colour conversion and resize. decode_file_into / decode_into are free functions rather than Tensor methods precisely so they can take a tensor from another edgefirst.* package:
import numpy as np
from edgefirst.codec import Tensor, decode_file_into
from edgefirst.image import Flip, ImageProcessor, PixelFormat, Rotation
processor = ImageProcessor()
# peek_image_info_file reads the header only — no pixels are decoded.
info = Tensor.peek_image_info_file("frame.jpg")
print(info.width, info.height, info.format) # e.g. 1280 720 PixelFormat.Nv16
# Allocate once, outside the loop. The decoder reconfigures the tensor's
# dimensions and format within this allocation, so one tensor sized for the
# largest expected frame can receive smaller images without reallocating.
src = processor.create_image(
info.width, info.height, PixelFormat.Nv12, "uint8", "readwrite"
)
dst = processor.create_image(640, 640, PixelFormat.Rgb, "uint8", "readwrite")
info = decode_file_into(src, "frame.jpg") # or decode_into(src, jpeg_bytes)
# convert() performs colour conversion (native → RGB) and resize; the codec
# reports EXIF orientation in `info` but does not apply it, so pass it on.
rotation = Rotation.degrees_clockwise(info.rotation_degrees)
flip = Flip.Horizontal if info.flip_horizontal else Flip.NoFlip
processor.convert(src, dst, rotation, flip)
with dst.map() as view:
data = np.frombuffer(view, dtype=np.uint8)
Same-package pipelines that never leave edgefirst.codec can use the equivalent Tensor.decode_image_file() method instead — tensor.decode_image_file("frame.jpg") — but decode_image_file is a method, and its self must literally be an edgefirst.codec.Tensor; a DMA/PBO-backed destination from edgefirst.image.ImageProcessor.create_image() is a different package's type and can never be that self, so it must go through the free functions decode_into / decode_file_into instead.
Images decode in their native pixel format and are never colour-converted, rotated or resized. A colour JPEG lands on the NV format matching its own chroma sampling (4:2:0 → Nv12, 4:2:2 → Nv16, 4:4:4 → Nv24), greyscale on Grey, and PNG on Rgb / Rgba / Grey. Nothing is resampled on the way out.
EXIF orientation is reported, never applied: info.rotation_degrees and info.flip_horizontal carry the transform your pipeline should apply downstream, and the reported dimensions are unrotated.
Tuning the decode
from edgefirst.codec import DctMethod, PixelFormat, set_dct_method, set_output_format
set_dct_method(DctMethod.Fast) # faster IDCT, small bounded accuracy cost
set_output_format(PixelFormat.Rgb) # fused colour conversion inside the decode
Both settings are thread-local — apply them on every thread that decodes. set_output_format fuses colour conversion into the decode's MCU write stage, which is a pure-CPU single-pass path; pass None to restore native output.
PixelFormat and the other value types (TensorMemory, Region, the colour axis enums) are accepted from any edgefirst.* package, not just this one — they compare and hash equal across packages by value, so ==, dict keys and set membership all work regardless of which package's copy you pass. Tensors, Decoder and ProtoData cross packages too, through the capsule protocols. Importing PixelFormat from edgefirst.codec 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.
What this package provides
| API | Purpose |
|---|---|
Tensor.peek_image_info() / peek_image_info_file() |
Read dimensions, format and EXIF orientation from the header alone |
Tensor.decode_image() / decode_image_file() |
Decode into a pre-allocated edgefirst.codec.Tensor |
decode_into() / decode_file_into() |
Decode into a tensor from any edgefirst.* package (e.g. edgefirst.image.ImageProcessor.create_image()) |
ImageInfo |
Decoded dimensions, native format, row stride, EXIF orientation |
set_dct_method() / DctMethod |
IDCT accuracy/speed selection |
set_output_format() |
Fused Rgb / Nv12 decode output |
is_v4l2_available() |
Whether a V4L2 hardware JPEG decoder is present |
Hardware acceleration
On Linux the decoder transparently tries hardware backends before the software path and falls back without any API change:
- V4L2 mem2mem — SoC JPEG blocks such as the i.MX
mxc-jpeg. Discovery is capability-based, with no hardcoded device node. Opt out withEDGEFIRST_DISABLE_V4L2=1. - nvJPEG — CUDA GPU decode on NVIDIA platforms such as Jetson Orin. Loaded via
dlopen, so there is no link-time CUDA dependency. Opt in withEDGEFIRST_ENABLE_NVJPEG=1.
Footprint
Adding edgefirst-codec to a project costs roughly 1.5 MB of downloads (this package plus edgefirst-tensor), excluding NumPy. That is roughly an order of magnitude smaller than Pillow or OpenCV, and a few times larger than a bare libjpeg-turbo binding — while bundling PNG, EXIF and the hardware backends, with no system libraries to install. See the Rust crate README for the comparison table.
Supported inputs
JPEG decoding covers baseline DCT, 8-bit precision, 1 or 3 components. Progressive, lossless, hierarchical and arithmetic-coded JPEG, CMYK/YCCK and non-8-bit precision are rejected with a typed error rather than mis-decoded. PNG goes through zune-png and supports 8-bit and 16-bit Luma / LumaA / RGB / RGBA. The Rust crate README documents the full matrix.
Interoperability
decode_into / decode_file_into accept a tensor from any edgefirst.* package because each extension module registers its own Tensor type object (PyO3 issue #1444 — isinstance across packages is always False, even for two objects wrapping the same Rust type). Acceptance goes through the __edgefirst_tensor__ capsule protocol every Tensor implements:
# 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.tensor.EdgeFirstTensorExportable (re-exported here as edgefirst.codec.EdgeFirstTensorExportable) is a typing.Protocol 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
- Changelog — release notes for all packages
- Source — the
halmonorepo - Issue tracker
- Package documentation — the underlying Rust crate
- EdgeFirst
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file edgefirst_codec-0.29.0rc1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: edgefirst_codec-0.29.0rc1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 791.2 kB
- Tags: CPython 3.11, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a93beab95ef326ead7285ef1218466e98b2a2d8cf1855532ba07d62aa66c6385
|
|
| MD5 |
de24e605cc60302dad2215982af2fe59
|
|
| BLAKE2b-256 |
007b523649dc70986cc7ba8d419459d70d28e5ac1e45c1fb177e6401a7176558
|