edgefirst-codec 0.23.0

Image codec for decoding JPEG/PNG into pre-allocated EdgeFirst tensors
Documentation

EdgeFirst Codec

Zero-allocation image decoding framework for pre-allocated tensor buffers in real-time vision pipelines.

Overview

edgefirst-codec decodes JPEG and PNG images directly into pre-allocated Tensor<T> or TensorDyn buffers, supporting strided memory layouts (GPU pitch-aligned DMA-BUF, PBO). This eliminates per-frame allocations in the hot loop — the primary design goal.

JPEG decoding uses a custom from-scratch baseline decoder with reusable state, achieving zero heap allocations after the first decode at each resolution. SIMD-optimized kernels (NEON on AArch64, SSE4.1/SSSE3/SSE2 on x86-64) are selected automatically at init via dynamic dispatch. Vectorised type conversion (NEON/SSE2) accelerates f32, u16, and i16 output paths. PNG decoding uses zune-png.

Quick Start

use edgefirst_codec::{ImageDecoder, DecodeOptions, ImageLoad};
use edgefirst_tensor::{Tensor, PixelFormat, TensorMemory};

// Allocate once at init (prefer ImageProcessor::create_image() for DMA/PBO)
let mut tensor = Tensor::<u8>::image(1920, 1080, PixelFormat::Rgb,
    Some(TensorMemory::Mem)).unwrap();
let mut decoder = ImageDecoder::new();

// Decode in the hot loop — zero allocations after warmup for JPEG
let jpeg_bytes = std::fs::read("frame.jpg").unwrap();
let info = tensor.load_image(&mut decoder, &jpeg_bytes,
    &DecodeOptions::default()).unwrap();
println!("Decoded {}x{} {:?}", info.width, info.height, info.format);

Recommended Pattern

For maximum performance, use tensors allocated by ImageProcessor::create_image():

use edgefirst_image::{ImageProcessor, ImageProcessorTrait, Crop};
use edgefirst_codec::{ImageDecoder, DecodeOptions, ImageLoad};

let mut processor = ImageProcessor::new()?;
let mut src = processor.create_image(1920, 1080, PixelFormat::Rgb,
    DType::U8, None)?;
let mut dst = processor.create_image(640, 640, PixelFormat::Rgb,
    DType::U8, None)?;
let mut decoder = ImageDecoder::new();

loop {
    let bytes = capture_frame();
    let info = src.load_image(&mut decoder, &bytes,
        &DecodeOptions::default())?;
    processor.convert(&src, &mut dst, Rotation::None, Flip::None,
        Crop::new(0, 0, info.width, info.height))?;
}

Benefits of ImageProcessor::create_image() tensors:

  • DMA-BUF backing for zero-copy GPU import
  • PBO backing when OpenGL is the active transfer path
  • GPU pitch alignment (64-byte for Mali compatibility)

Free-standing tensors work but cannot use PBO and may lack GPU-aligned pitch.

Supported Formats

Format Input Output Formats
JPEG &[u8] RGB, RGBA, Grey, BGRA, NV12
PNG &[u8] RGB, RGBA, Grey, BGRA

Decoder Limitations

The codec decodes a strict subset of the JPEG / PNG specs. Inputs that fall outside the supported subset surface a typed CodecError::Unsupported(...) variant so callers can pattern-match programmatically (no string parsing required).

JPEG

JPEG feature Status
Baseline DCT (SOF0) Supported
8-bit sample precision Supported
1 component (greyscale) or 3 components (YCbCr) Supported
Chroma subsampling 4:4:4 / 4:2:2 / 4:2:0 / 4:4:0 Supported
EXIF orientation (apply_exif) Supported
Progressive DCT (SOF2) UnsupportedUnsupported(ProgressiveJpeg)
Extended sequential DCT (SOF1) Unsupported
Lossless predictive (SOF3) UnsupportedUnsupported(LosslessJpeg)
Hierarchical (SOF5/6/7) UnsupportedUnsupported(HierarchicalJpeg)
Arithmetic coding (SOF9/10/11/13/14/15) UnsupportedUnsupported(ArithmeticCodedJpeg)
Sample precision other than 8-bit UnsupportedUnsupported(JpegPrecision { bits })
CMYK / YCCK / >3 components UnsupportedUnsupported(JpegComponentCount { components })
Chroma sampling that exceeds luma UnsupportedUnsupported(JpegChromaSubsampling)
Thumbnails (JFIF / APP markers) Ignored
EXIF rotation with planar NV12 output Not applied (apply_exif silently ignored for NV12)

PNG

PNG decoding goes through zune-png; the codec applies its own post-processing (format conversion, EXIF rotation, stride-aware writes).

PNG feature Status
8-bit colorspace: Luma / LumaA / RGB / RGBA Supported
16-bit colorspace: RGB / RGBA / Luma → u16 / i16 / f32 tensors Supported
eXIf chunk orientation, u8 output Supported
Palette (indexed-color) PNG Per zune-png (expanded to RGB/RGBA by the decoder)
eXIf chunk orientation, 16-bit output paths Not applied (apply_exif ignored on u16/i16/f32)
APNG (animated) Not exercised (decoder set to png_set_decode_animated(false))
Interlaced (Adam7) Per zune-png

Data Types

Type Support Notes
u8 Direct copy (identity)
u16 Scaled * 257 from 8-bit; native from 16-bit PNG
i8 XOR 0x80 sign-bit flip
i16 XOR 0x8000 sign-bit flip
f32 Normalized to [0.0, 1.0]

API Reference

ImageDecoder

Reusable decoder with internal scratch buffers. Create once, reuse across frames — scratch buffers amortize after the first decode.

ImageLoad Extension Trait

  • load_image(&mut self, decoder, data, opts) — decode from &[u8]
  • load_image_read(&mut self, decoder, reader, opts) — decode from Read
  • load_image_file(&mut self, decoder, path, opts) — decode from file path

DecodeOptions

  • format: Output pixel format (None = native from file)
  • apply_exif: Apply EXIF orientation (default true)

ImageInfo

Returned by all decode methods with actual decoded dimensions:

  • width, height: Decoded image size
  • format: Output pixel format
  • row_stride: Row stride in bytes

License

Apache-2.0