Skip to main content

Python bindings for libjxl using pybind11

Project description

pylibjxl

Fast Python bindings for libjxl and libjpeg-turbo. Built with pybind11, with GIL-free encoding/decoding and native async support.

Features

  • 🚀 High performance — C++ core with GIL release during encode/decode
  • 📦 Metadata support — Read/write EXIF, XMP, and JUMBF metadata
  • Async-first — Native asyncio support for concurrent I/O
  • 🎯 Simple API — Free functions for quick use, context managers for control
  • 🖼️ NumPy native — Direct ndarray input/output (RGB/RGBA, uint8)
  • 🔄 JPEG support — Encode/decode JPEG via libjpeg-turbo + lossless JPEG↔JXL transcoding

Installation

Prerequisites

  • Python ≥ 3.11
  • CMake ≥ 3.15
  • C++17 compiler (GCC, Clang, MSVC)

Note: libjxl and libjpeg-turbo are bundled as Git submodules in third_party/ and statically linked — no system-level installation required.

Install

git clone --recurse-submodules https://github.com/user/pylibjxl.git
cd pylibjxl
pip install .

Quick Start

import numpy as np
import pylibjxl

# Create a test image (H, W, C)
image = np.random.randint(0, 256, (512, 512, 3), dtype=np.uint8)

# Encode → Decode
data = pylibjxl.encode(image, effort=7, distance=1.0)
decoded = pylibjxl.decode(data)

Usage

Encode / Decode (In-Memory)

import pylibjxl

# Lossy encoding (default)
data = pylibjxl.encode(image, effort=7, distance=1.0)

# Lossless encoding
data = pylibjxl.encode(image, lossless=True)

# Decode
image = pylibjxl.decode(data)

File I/O

# Write to file (creates parent directories automatically)
pylibjxl.write("output.jxl", image, effort=7, distance=1.0)

# Read from file
image = pylibjxl.read("output.jxl")

Metadata (EXIF / XMP / JUMBF)

# Encode with metadata
data = pylibjxl.encode(image, exif=exif_bytes, xmp=xmp_bytes)

# Decode with metadata extraction
image, meta = pylibjxl.decode(data, metadata=True)
print(meta.keys())  # dict_keys(['exif', 'xmp'])

# File I/O with metadata
pylibjxl.write("photo.jxl", image, exif=exif_bytes, xmp=xmp_bytes, jumbf=jumbf_bytes)
image, meta = pylibjxl.read("photo.jxl", metadata=True)

Context Manager

with pylibjxl.JXL(effort=7, distance=1.0) as jxl:
    # Encode/decode with shared defaults
    data = jxl.encode(image)
    result = jxl.decode(data)

    # Per-call overrides
    data_hq = jxl.encode(image, distance=0.5)

    # File I/O
    jxl.write("output.jxl", image, exif=exif_bytes)
    result, meta = jxl.read("output.jxl", metadata=True)

Async

import asyncio
import pylibjxl

async def main():
    # In-memory async
    data = await pylibjxl.encode_async(image, exif=exif_bytes)
    image, meta = await pylibjxl.decode_async(data, metadata=True)

    # File async
    await pylibjxl.write_async("output.jxl", image, xmp=xmp_bytes)
    image = await pylibjxl.read_async("output.jxl")

    # Async context manager
    async with pylibjxl.AsyncJXL(effort=5) as jxl:
        data = await jxl.encode_async(image)
        result = await jxl.decode_async(data)

asyncio.run(main())

JPEG Encode / Decode

import pylibjxl

# Encode to JPEG (via libjpeg-turbo)
jpeg_data = pylibjxl.encode_jpeg(image, quality=95)

# Decode JPEG to numpy array
image = pylibjxl.decode_jpeg(jpeg_data)

JPEG ↔ JXL Transcoding

# Losslessly recompress JPEG → JXL (preserves JPEG reconstruction data)
jxl_data = pylibjxl.jpeg_to_jxl(jpeg_data, effort=7)

# Reconstruct original JPEG from JXL (lossless roundtrip)
jpeg_restored = pylibjxl.jxl_to_jpeg(jxl_data)

# Async variants
jxl_data = await pylibjxl.jpeg_to_jxl_async(jpeg_data)
jpeg_data = await pylibjxl.jxl_to_jpeg_async(jxl_data)

JPEG File I/O

# Write JPEG file
pylibjxl.write_jpeg("photo.jpg", image, quality=95)

# Read JPEG file
image = pylibjxl.read_jpeg("photo.jpg")

# Async
await pylibjxl.write_jpeg_async("photo.jpg", image)
image = await pylibjxl.read_jpeg_async("photo.jpg")

Cross-Format File Conversion

# JPEG → JXL (lossless transcoding, preserves JPEG reconstruction data)
pylibjxl.convert_jpeg_to_jxl("photo.jpg", "photo.jxl")

# JXL → JPEG (lossless reconstruction from transcoded JXL)
pylibjxl.convert_jxl_to_jpeg("photo.jxl", "restored.jpg")

# Async
await pylibjxl.convert_jpeg_to_jxl_async("photo.jpg", "photo.jxl")
await pylibjxl.convert_jxl_to_jpeg_async("photo.jxl", "restored.jpg")

API Reference

Free Functions

encode(input, effort=7, distance=1.0, lossless=False, *, exif=None, xmp=None, jumbf=None) → bytes

Encode a NumPy array to JXL bytes.

Parameter Type Default Description
input ndarray required uint8 array of shape (H, W, 3) or (H, W, 4)
effort int 7 Encoding effort [1-10], higher = slower + smaller
distance float 1.0 Perceptual distance [0.0-25.0], 0 = lossless
lossless bool False If True, encode losslessly (overrides distance)
exif bytes | None None Raw EXIF metadata to embed
xmp bytes | None None Raw XMP (XML) metadata to embed
jumbf bytes | None None Raw JUMBF metadata to embed

decode(data, *, metadata=False) → ndarray | tuple[ndarray, dict]

Decode JXL bytes to a NumPy array.

Parameter Type Default Description
data bytes required JXL-encoded data
metadata bool False If True, also return metadata dict

Returns:

  • metadata=Falsendarray of shape (H, W, C), dtype uint8
  • metadata=Truetuple(ndarray, dict) where dict may contain keys: "exif", "xmp", "jumbf" (as bytes)

read(path, *, metadata=False)

Read a .jxl file from disk. Returns same types as decode().

write(path, image, effort=7, distance=1.0, lossless=False, *, exif=None, xmp=None, jumbf=None)

Encode and write to a .jxl file. Creates parent directories automatically.


encode_async(...) / decode_async(...) / read_async(...) / write_async(...)

Async versions of the above functions — same parameters, returns Awaitable.


Context Managers

JXL(effort=7, distance=1.0, lossless=False)

Synchronous codec context manager with shared defaults.

Method Description
encode(input, effort=None, distance=None, lossless=None, *, exif=None, xmp=None, jumbf=None) Encode in-memory
decode(data, *, metadata=False) Decode in-memory
read(path, *, metadata=False) Read from file
write(path, image, effort=None, distance=None, lossless=None, *, exif=None, xmp=None, jumbf=None) Write to file

AsyncJXL(effort=7, distance=1.0, lossless=False)

Async codec context manager. Methods: encode_async, decode_async, read_async, write_async.


JPEG & Transcoding Functions

encode_jpeg(input, quality=95) → bytes

Encode a NumPy array to JPEG bytes using libjpeg-turbo.

Parameter Type Default Description
input ndarray required uint8 array of shape (H, W, 3) or (H, W, 4)
quality int 95 JPEG quality [1-100]

decode_jpeg(data) → ndarray

Decode JPEG bytes to a NumPy array (H, W, 3) using libjpeg-turbo.

read_jpeg(path) → ndarray

Read a .jpg/.jpeg file from disk. Returns ndarray of shape (H, W, 3).

write_jpeg(path, image, quality=95)

Encode and write to a JPEG file. Creates parent directories automatically.

jpeg_to_jxl(data, effort=7) → bytes

Losslessly recompress JPEG bytes to JXL. Embeds JPEG reconstruction data so the original JPEG can be restored.

jxl_to_jpeg(data) → bytes

Reconstruct the original JPEG bytes from a JXL file (only works if the JXL was created via jpeg_to_jxl).

convert_jpeg_to_jxl(jpeg_path, jxl_path, effort=7)

Convert a JPEG file to JXL file via lossless transcoding. Creates parent directories automatically.

convert_jxl_to_jpeg(jxl_path, jpeg_path)

Reconstruct the original JPEG file from a JXL file. Only works for JPEG-transcoded JXL files.

Async variants

encode_jpeg_async, decode_jpeg_async, read_jpeg_async, write_jpeg_async, jpeg_to_jxl_async, jxl_to_jpeg_async, convert_jpeg_to_jxl_async, convert_jxl_to_jpeg_async — same parameters, returns Awaitable.


Utility Functions

Function Returns Description
version() dict libjxl version {"major", "minor", "patch"}
decoder_version() int Decoder version number
encoder_version() int Encoder version number

License

MIT

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

pylibjxl-0.1.1.tar.gz (58.6 MB view details)

Uploaded Source

Built Distribution

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

pylibjxl-0.1.1-cp312-cp312-macosx_26_0_arm64.whl (1.8 MB view details)

Uploaded CPython 3.12macOS 26.0+ ARM64

File details

Details for the file pylibjxl-0.1.1.tar.gz.

File metadata

  • Download URL: pylibjxl-0.1.1.tar.gz
  • Upload date:
  • Size: 58.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pylibjxl-0.1.1.tar.gz
Algorithm Hash digest
SHA256 e1b48d23686bd116585669260be0305604400daac4037e6f0b2dcc0473bbdbde
MD5 bcd12bfb98451ef8923cfe3a8721304c
BLAKE2b-256 430a6e2242bff383bf4f769fc9498ff123a500ca0d65d98133bcfe3530947a83

See more details on using hashes here.

File details

Details for the file pylibjxl-0.1.1-cp312-cp312-macosx_26_0_arm64.whl.

File metadata

  • Download URL: pylibjxl-0.1.1-cp312-cp312-macosx_26_0_arm64.whl
  • Upload date:
  • Size: 1.8 MB
  • Tags: CPython 3.12, macOS 26.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pylibjxl-0.1.1-cp312-cp312-macosx_26_0_arm64.whl
Algorithm Hash digest
SHA256 5c17f478acd0f1c52a65726901ad9255e5db841eb8c7ea3e6b11279695ecdb68
MD5 42c438a4a64271009d41f6782f69b162
BLAKE2b-256 97ba6a46b7682ee2a1f6ea1113781d3180aa25eb8f6f42a10597d47fb50992d7

See more details on using hashes here.

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