All formats

pyvips

LanguagePython
LicenseMIT (libvips: LGPL-2.1)
Repositorygithub.com/libvips/pyvips
Documentationpyvips docs, jxlsave options
Depends onlibvips built with libjxl
Read / writeyes / yes, lossy and lossless, 8/16-bit and float, animation

pyvips is the Python binding of libvips, an image processing library that streams images in small pieces instead of loading them whole. It is much faster than Pillow for resizing and converting, and handles images larger than memory. JPEG XL support comes from libjxl.

You need a libvips built with libjxl. The prebuilt pyvips-binary wheels don’t include libjxl, so install libvips from your system package manager instead: Homebrew’s vips and the libvips packages of current Linux distributions include JPEG XL. Check with vips -l | grep jxl.

Installation#

Shell
sudo apt install libvips-dev && pip install pyvips   # Debian / Ubuntu
sudo dnf install vips-devel && pip install pyvips    # Fedora
brew install vips && pip install pyvips              # macOS

On Windows, download a libvips build from libvips releases (“all” variant, which includes libjxl), add its bin folder to PATH, then pip install pyvips.

Basic usage: convert JXL to PNG and back#

Python
import pyvips

image = pyvips.Image.new_from_file("in.jxl")
print(image.width, image.height, image.bands, image.format)
image.write_to_file("out.png")

photo = pyvips.Image.new_from_file("photo.png")
photo.write_to_file("photo.jxl", Q=90)

Save options can be given to write_to_file as keyword arguments, or after the file name: photo.write_to_file("photo.jxl[Q=90]").

How to save a lossless JPEG XL#

Python
image = pyvips.Image.new_from_file("screenshot.png")
image.jxlsave("screenshot.jxl", lossless=True)

How to set distance and effort#

Python
image.jxlsave(
    "photo.jxl",
    distance=1.0,  # max error: 0 lossless, 1.0 visually lossless (default), up to 15
    effort=7,      # 1-9: higher is slower and smaller
)

Q (JPEG-like quality) is an alternative to distance; set one of them.

How to make a JXL thumbnail#

thumbnail decodes only what it needs and resizes in one step:

Python
thumb = pyvips.Image.thumbnail("big.jxl", 400)  # fit in 400 x 400
thumb.write_to_file("thumb.jpg", Q=85)

How to read metadata#

Python
image = pyvips.Image.new_from_file("in.jxl")
for name in image.get_fields():
    print(name)
if image.get_typeof("exif-data") != 0:
    exif = image.get("exif-data")  # raw Exif bytes

How to convert JXL to JPEG in memory#

Python
data = open("in.jxl", "rb").read()
image = pyvips.Image.new_from_buffer(data, "")
jpeg_bytes = image.write_to_buffer(".jpg", Q=85)

Tips#