All formats

pillow-jxl-plugin

LanguagePython (Rust extension)
LicenseGPL-3.0
Repositorygithub.com/Isotr0py/pillow-jpegxl-plugin
DocumentationREADME
Depends onPillow; libjxl (bundled in the wheels, through the jpegxl-rs Rust crate)
Read / writeyes / yes, lossy, lossless, lossless JPEG recompression, animation read

pillow-jxl-plugin registers a JPEG XL codec with Pillow. After import pillow_jxl, Image.open("x.jxl") and image.save("x.jxl") work like for any other format. Wheels for Windows, macOS and Linux include libjxl, so pip install is all you need.

When you open a JPEG with Pillow and save it as JXL, the plugin by default recompresses the original JPEG file losslessly instead of re-encoding pixels, so the JPEG can be restored exactly. The license is GPL-3.0: programs that distribute it must be GPL-compatible.

Installation#

Shell
pip install pillow-jxl-plugin

Basic usage: open and save JXL#

Python
from PIL import Image
import pillow_jxl  # registers the JXL plugin

with Image.open("in.jxl") as im:
    print(im.size, im.mode)
    im.save("out.png")

with Image.open("photo.png") as im:
    im.save("photo.jxl", quality=90)

How to save a lossless JPEG XL#

Python
with Image.open("screenshot.png") as im:
    im.save("screenshot.jxl", lossless=True)

How to set effort and other encoder options#

Python
im.save(
    "photo.jxl",
    quality=85,        # JPEG-like scale, default 90
    effort=9,          # 1-10, default 7: higher is slower and smaller
    decoding_speed=0,  # 0-4: higher decodes faster, larger files
    use_container=True # write the ISOBMFF container instead of a bare codestream
)

Supported image modes for saving are RGB, RGBA, L, LA, I;16 and F. Convert other modes first, e.g. im.convert("RGB").

How to recompress a JPEG losslessly#

Python
with Image.open("photo.jpg") as im:
    im.save("photo.jxl", lossless_jpeg=True)  # keeps the original JPEG data

This works when the image was opened from a JPEG file. Pass lossless_jpeg=False to decode and re-encode the pixels instead (lossy, usually smaller at low quality).

How to convert JXL to bytes in memory#

Python
from io import BytesIO
from PIL import Image
import pillow_jxl

buf = BytesIO()
with Image.open("photo.png") as im:
    im.save(buf, format="JXL", quality=90)
jxl_bytes = buf.getvalue()

im = Image.open(BytesIO(jxl_bytes))

How to keep Exif and XMP metadata#

Python
with Image.open("photo.png") as im:
    im.save("photo.jxl", exif=im.info.get("exif"), xmp=im.info.get("xmp"))

Tips#