Skip to main content

Hardware Abstraction Layer for edge AI with zero-copy tensors, image processing, and YOLO decoding

Project description

edgefirst-hal

PyPI Python License

Hardware-accelerated image processing, zero-copy tensors, and YOLO decoding for edge AI inference pipelines. Built in Rust with Python bindings via PyO3.

Installation

pip install edgefirst-hal

Pre-built wheels are available for Linux (x86_64, aarch64), macOS, and Windows. No Rust toolchain required.

Python 3.11+ wheels use the improved stable ABI for zero-copy buffer protocol support. Python 3.8–3.10 wheels use a compatible fallback. Pip selects the best wheel automatically.

Quick Start

import edgefirst_hal as ef

# Load a source image
src = ef.Tensor.load("photo.jpg", ef.PixelFormat.Rgb)

# Create an image processor (auto-selects best backend: GPU > G2D > CPU)
processor = ef.ImageProcessor()

# Allocate a GPU-optimal output buffer — always use create_image() for
# destinations passed to convert(), so the processor can select the best
# memory type (DMA-buf, PBO, or system memory) for zero-copy GPU paths.
# access declares CPU involvement (hardware access is implied): this
# script reads the pixels below, so declare "readwrite". Hardware-only
# pipelines keep the strict default access="none".
dst = processor.create_image(640, 640, ef.PixelFormat.Rgb, access="readwrite")

# Convert with a letterbox resize (preserves aspect ratio, pads with grey).
# Omit `letterbox=` to stretch-to-fill instead.
processor.convert(src, dst, letterbox=[114, 114, 114, 255])

# Access pixel data as a numpy array. Use the context manager + .numpy()
# form — this is the portable pattern that works on both wheel variants.
import numpy as np
with dst.map() as m:
    pixels = np.frombuffer(m.numpy(), dtype=np.uint8).reshape(dst.shape())

# The shorter `np.frombuffer(dst.map(), ...)` form only works on the
# abi3-py311 wheel, where `TensorMap` exposes Python's buffer protocol
# directly. The abi3-py38 compatibility wheel disables `__getbuffer__`,
# so use `.numpy()` if your code needs to run on Python 3.8–3.10.

Role in edgefirst-hal

The edgefirst-hal package on PyPI is the Python face of the EdgeFirst HAL Rust workspace:

  • Built from crates/python, which is a PyO3 binding over the edgefirst-hal Rust umbrella crate.
  • Does not consume the C API (edgefirst-hal-capi); the binding goes directly through Rust.
  • Exposes the same Tensor, ImageProcessor, Decoder, and Tracker surfaces as the Rust crate, with numpy-friendly conversions and the buffer protocol for zero-copy interop.
  • Wheels are distributed as two stable-ABI variants per platform — abi3-py311 (preferred, supports buffer protocol features added in 3.11) and abi3-py38 (compatibility fallback for 3.8–3.10). Pip selects the best wheel automatically.

Key Features

  • Zero-copy tensors — DMA-BUF, POSIX shared memory, and PBO-backed buffers with automatic fallback to system memory
  • Hardware-accelerated image processing — OpenGL, NXP G2D, and optimized CPU backends with automatic selection
  • Letterbox resize — aspect-ratio-preserving resize with configurable padding color, rotation, and flip
  • Int8 outputcreate_image(..., dtype="int8") for direct signed int8 tensor output with GPU-accelerated XOR bias
  • Declared CPU accesscreate_image(..., access="readwrite") for scripts that touch pixels (map/numpy); the strict "none" default keeps hardware pipelines eligible for vendor tile compression (compression="any" on Android, outcome on Tensor.compression)
  • YOLO decoding — YOLOv5, YOLOv8, YOLO11, and YOLO26 detection and instance segmentation (including end-to-end models)
  • Object tracking — ByteTrack multi-object tracker with Kalman filtering
  • Fully typed — ships with .pyi stubs for IDE autocompletion and type checking with mypy / pyright

Image Processing

import edgefirst_hal as ef

processor = ef.ImageProcessor()
src = ef.Tensor.load("frame.jpg", ef.PixelFormat.Rgb)

# Letterbox resize to model input size
dst = processor.create_image(640, 640, ef.PixelFormat.Rgb)
processor.convert(src, dst)

# With rotation and horizontal flip
processor.convert(src, dst, rotation=ef.Rotation.Rotate90, flip=ef.Flip.Horizontal)

# Crop source region
processor.convert(src, dst, src_crop=ef.Rect(100, 100, 400, 400))

# Int8 output for quantized models
dst_i8 = processor.create_image(640, 640, ef.PixelFormat.Rgb, dtype="int8")
processor.convert(src, dst_i8)

Zero-Copy External Buffer (Linux)

When integrating with an NPU delegate that owns DMA-BUF buffers, render directly into the delegate's buffer to eliminate a memcpy:

import edgefirst_hal as ef

processor = ef.ImageProcessor()
src = ef.Tensor.load("frame.jpg", ef.PixelFormat.Rgb)

# Render directly into the delegate's DMA-BUF — zero copies
dst = processor.import_image(fd=vx_fd, width=640, height=640, format=ef.PixelFormat.Rgb)
processor.convert(src, dst)

# Reverse: HAL allocates, consumer imports the fd
hal_dst = processor.create_image(640, 640, ef.PixelFormat.Rgb)
fd = hal_dst.dmabuf_clone()  # Raises if not DMA-backed
delegate.register(fd)

You can also attach format metadata to any raw tensor created via from_fd():

t = ef.Tensor.from_fd(some_fd, [480, 640, 3])
t.set_format(ef.PixelFormat.Rgb)
processor.convert(src, t)

Performance tip: When rotating through a pool of DMA-BUFs (e.g. 2-3 from an NPU delegate), create the Tensor wrappers once at init and reuse them across frames. This avoids EGL image cache misses (~100-300us each on Vivante GPUs).

CUDA Zero-Copy (TensorRT)

When running inference with TensorRT or cupy, Tensor.cuda_map() exposes a raw CUDA device pointer to a tensor that has been registered with CUDA (e.g. via the GL-CUDA interop path). The mapping is scoped by a context manager so the GPU buffer is released automatically for the next convert() call.

Check availability first, then try cuda_map() and fall back to map() for CPU paths:

import edgefirst_hal as ef

# One-time check — cached after first call
if not ef.is_cuda_available():
    print("libcudart not found; falling back to CPU tensors")

tensor = ef.ImageProcessor().create_image(640, 640, ef.PixelFormat.Rgb)

cm = tensor.cuda_map()
if cm is not None:
    with cm as m:
        # m.device_ptr is the raw CUDA device pointer (int)
        # m.size is the buffer size in bytes
        trt_context.set_input_tensor_address("input", m.device_ptr)
        trt_context.execute_async_v3(stream)
else:
    # No CUDA handle on this tensor — use the CPU path
    with tensor.map() as host:
        run_cpu_inference(host)

CudaMap exposes:

  • device_ptr (int) — raw CUDA device pointer, suitable for cupy.ndarray.from_dlpack, pycuda.gpuarray, or TensorRT set_input_tensor_address.
  • size (int) — buffer size in bytes.
  • release() — explicitly release before the with block ends (idempotent).

YOLO Decoding

import edgefirst_hal as ef

# Configure decoder from model metadata
decoder = ef.Decoder(
    {"detection": {"shape": [1, 84, 8400], "dtype": "float32"}},
    score_threshold=0.5,
    iou_threshold=0.45,
)

# Decode model outputs → (boxes, scores, class_ids)
boxes, scores, classes = decoder.decode([output_tensor])

Object Tracking

ByteTrack is a multi-object tracker based on ByteTrack with Kalman filtering. It assigns consistent track IDs across frames.

import edgefirst_hal as ef

tracker = ef.ByteTrack(
    high_conf=0.7,         # High-confidence detection threshold
    iou=0.25,              # IoU threshold for association
    update=0.25,           # Update/low-confidence threshold
    lifespan_ns=500_000_000,  # Track lifespan without detection (nanoseconds)
)

# Decode and track in one call (returns boxes, scores, classes, masks, track_infos)
boxes, scores, classes, masks, tracks = decoder.decode_tracked(
    tracker, timestamp_ns, [output_tensor]
)
# masks is empty for detection-only models

# Or query currently active tracks
active = tracker.get_active_tracks()

Segmentation Mask Rendering

draw_decoded_masks()

Draw pre-decoded masks onto a destination image:

processor.draw_decoded_masks(
    dst,
    bbox,           # numpy array [N, 4]
    scores,         # numpy array [N]
    classes,        # numpy array [N]
    seg=[],         # list of segmentation arrays (optional)
    background=None,  # optional background tensor to blit before drawing
    opacity=1.0,    # mask alpha scale (0.0 – 1.0)
)

draw_masks()

Decode model outputs and draw segmentation masks in a single call. Masks never leave Rust, eliminating the Python round-trip overhead of decode() + draw_decoded_masks().

Without a tracker, returns (boxes, scores, classes). With a tracker, returns (boxes, scores, classes, track_infos).

import edgefirst_hal as ef

processor = ef.ImageProcessor()
tracker = ef.ByteTrack()

# Without tracking
boxes, scores, classes = processor.draw_masks(decoder, outputs, dst)

# With overlay parameters
boxes, scores, classes = processor.draw_masks(
    decoder, outputs, dst,
    background=bg_tensor,  # blit bg_tensor into dst before masks
    opacity=0.7,           # semi-transparent masks
)

# With tracking (requires tracker= and timestamp=)
import time
ts = time.monotonic_ns()
boxes, scores, classes, tracks = processor.draw_masks(
    decoder, outputs, dst,
    tracker=tracker,
    timestamp=ts,
)

Platform Support

Platform GPU Acceleration Memory Types
Linux (NXP i.MX8/i.MX95) OpenGL + G2D DMA-buf, SHM, PBO, Mem
Linux (x86_64, other ARM) OpenGL SHM, PBO, Mem
macOS / Windows CPU only Mem

Hardware acceleration is used automatically when available. All platforms fall back to CPU.

Part of the EdgeFirst Ecosystem

edgefirst-hal is the runtime inference library in the EdgeFirst platform for deploying AI at the edge.

  • EdgeFirst Studio — label, train, and deploy models for edge devices
  • Rust crates — use the same library directly from Rust or C
  • GitHub — source code, architecture docs, benchmarks, and contribution guide

Documentation

License

Apache-2.0 — see LICENSE.

Project details


Download files

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

Source Distribution

edgefirst_hal-0.27.0.tar.gz (1.3 MB view details)

Uploaded Source

Built Distributions

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

edgefirst_hal-0.27.0-cp311-abi3-win_amd64.whl (5.3 MB view details)

Uploaded CPython 3.11+Windows x86-64

edgefirst_hal-0.27.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.8 MB view details)

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

edgefirst_hal-0.27.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (4.7 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

edgefirst_hal-0.27.0-cp311-abi3-macosx_11_0_arm64.whl (4.5 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

edgefirst_hal-0.27.0-cp38-abi3-win_amd64.whl (5.2 MB view details)

Uploaded CPython 3.8+Windows x86-64

edgefirst_hal-0.27.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.8 MB view details)

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

edgefirst_hal-0.27.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (4.7 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

edgefirst_hal-0.27.0-cp38-abi3-macosx_11_0_arm64.whl (4.5 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

Details for the file edgefirst_hal-0.27.0.tar.gz.

File metadata

  • Download URL: edgefirst_hal-0.27.0.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for edgefirst_hal-0.27.0.tar.gz
Algorithm Hash digest
SHA256 2434d881a88aa026b4c75ee432d4ee655abfcd91ccd9a13419c51207c9550493
MD5 146d39a639dff61e41c571510a68d02d
BLAKE2b-256 35cdf1b9323a4bf2be16d7ec53cee4a5225e7e69b579d31290043beb01144103

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_hal-0.27.0.tar.gz:

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_hal-0.27.0-cp311-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for edgefirst_hal-0.27.0-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 befc669ee44675b6e8a07655250d252a2699508739dddb517a9fb6c0ed2ad4bc
MD5 12f28e54e33aae26ba14165b8ecf6173
BLAKE2b-256 9720267afd6bc75fffeac619d47023eaf7344a1d9b0af2f0c6741431503900a7

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_hal-0.27.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_hal-0.27.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for edgefirst_hal-0.27.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 758d1cd43cfc17842bd95e57ca16b4a62b267a3bf285ac67aaf0a0f841fb3c65
MD5 7becc8ddebb3e2b109ce4adca81b3a0c
BLAKE2b-256 9cf26f653f240e6a466dea7321e402040ca7f006a0d4ef3be162380402731f54

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_hal-0.27.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_hal-0.27.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for edgefirst_hal-0.27.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 fb47d3c85ca32fa9d80e4eb2881cd8813ad7f525c97cf3ddb11b589e57b75aa3
MD5 82130a3d9643171ec0a3849f2d8d204d
BLAKE2b-256 c64970ea8d7ce02303b61ad5da53c56241b60a52979bde18ffc031316444883a

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_hal-0.27.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_hal-0.27.0-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for edgefirst_hal-0.27.0-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 672ef76bfcf3e2f82df723fcbc9044a8d8293f319edd9ac16341e8f55c622a68
MD5 594e8f97685c65090ce6eb8e940d35b3
BLAKE2b-256 5b8ddd529e5d4987dd8c97ccad9303c94768fb4f235df3bf94b0f88c147fe5b3

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_hal-0.27.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_hal-0.27.0-cp38-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for edgefirst_hal-0.27.0-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 3f9b5dfbd5859e4ce0ba88ed98b6bd067b85ef8a2914d23cb0ee28923b67eb2c
MD5 e69394170ecc1013d19afae81d3a119f
BLAKE2b-256 bc29238228c8cc63c6eabefa2c4ec7616e0788d51bf761fe7aedd9fa5e750d9c

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_hal-0.27.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_hal-0.27.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for edgefirst_hal-0.27.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 a5dd8042e3bc71b47fee9024b904ea12fbcf5a9eca09dd7451ca6ab6f46c8379
MD5 d4a8a9ab4b022d86869c47635e95d566
BLAKE2b-256 72d1bd2366e984ff5d3f67df60b10e6d03de5eee914d3f5188287758d9e74c44

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_hal-0.27.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_hal-0.27.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for edgefirst_hal-0.27.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 53ca7f8e7eaed453b66b3a2c76ddc715ba1a5472714b0375f13ccb8cbb5483c7
MD5 0c3e8832ce0eb0bb36171176ea2a8ec9
BLAKE2b-256 401f2a919e76e3c19dfd0c5a6f58c6fd4cdc540248978952b0ebed33de041a31

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_hal-0.27.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_hal-0.27.0-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for edgefirst_hal-0.27.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0c0fe7c8100342575d48708d9a89d2b17219384019dcb8f82d054eae033f774c
MD5 3c29b245422fa37bf5974863e497d0eb
BLAKE2b-256 a7bb54f457f0b06bab75f61ca5458697d7f82ce8b4919b89d376c1efc40733ba

See more details on using hashes here.

Provenance

The following attestation bundles were made for edgefirst_hal-0.27.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.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page