pillow-jxl-plugin
| Language | Python (Rust extension) |
|---|---|
| License | GPL-3.0 |
| Repository | github.com/Isotr0py/pillow-jpegxl-plugin |
| Documentation | README |
| Depends on | Pillow; libjxl (bundled in the wheels, through the jpegxl-rs Rust crate) |
| Read / write | yes / 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#
pip install pillow-jxl-plugin
Basic usage: open and save JXL#
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#
with Image.open("screenshot.png") as im:
im.save("screenshot.jxl", lossless=True)
How to set effort and other encoder options#
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#
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#
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#
with Image.open("photo.png") as im:
im.save("photo.jxl", exif=im.info.get("exif"), xmp=im.info.get("xmp"))
Tips#
- The plugin registers the
.jxlextension and theimage/jxlMIME type, soImage.save("x.jxl")picks it automatically. - For many files, pass
num_threads=to limit how many threads each encode uses.