Convert JPG, PNG, and WebP with Python Pillow
Convert JPG, PNG, and WebP images with Python Pillow. Covers save options, transparency handling, metadata preservation, batch conversion, and choosing the right output format.
Converting images between JPG, PNG, and WebP is a common task in Python. Pillow is the standard choice for this kind of work. The core workflow is simple: open an image with Image.open() and save it with a new format. The details matter when you care about quality, transparency, metadata, or processing many files. This article walks through the conversion process and the decisions that come with each format.
The Basic Conversion Workflow
The simplest conversion looks like this:
from PIL import Image im = Image.open("input.jpg") im.save("output.png")
Pillow infers the output format from the file extension. The same pattern works for any combination of JPG, PNG, and WebP as long as the image mode is compatible with the target format. If you need to specify the format explicitly, pass it as the second argument to save():
im.save("output", format="WEBP")
The format parameter is useful when the filename does not carry a clear extension or when you are writing to a file-like object.
Format-Specific Save Options
Each format accepts parameters that control compression and output quality. These are passed directly to save().
For JPEG:
im.save("output.jpg", quality=85, optimize=True, progressive=True)
qualitycontrols the compression level, typically 1–95.optimizespends extra time to produce a smaller file.progressivewrites a progressive JPEG, which loads in stages.
For PNG:
im.save("output.png", optimize=True, compress_level=9)
PNG uses lossless compression, so compress_level trades encoding time for file size. optimize also improves compression at the cost of speed.
For WebP:
im.save("output.webp", quality=80, lossless=False, method=4)
qualityworks like JPEG whenlosslessisFalse.lossless=Trueproduces a lossless WebP, which is useful for images with sharp edges or text.methodcontrols the compression effort, from 0 (fast) to 6 (slow, smaller output).
These options are not interchangeable. Pillow generally ignores save options that do not apply to the target format, so keep them format-specific.
Handling Transparency and Color Modes
PNG and WebP support an alpha channel; JPEG does not. If you try to save an RGBA image as JPEG, Pillow raises an error because JPEG cannot store an alpha channel. To convert a transparent PNG to JPEG, composite the image onto a solid background first:
from PIL import Image im = Image.open("transparent.png").convert("RGBA") background = Image.new("RGB", im.size, (255, 255, 255)) background.paste(im, mask=im.split()[3]) background.save("output.jpg")
The mask uses the alpha channel so only transparent areas show the white background. If you flatten the image with convert("RGB") instead, the alpha channel is discarded and transparent areas can become black.
PNG and WebP both support alpha, so Pillow preserves the alpha channel when converting between them. If exact transparency matters, inspect the saved image after conversion.
Converting Multiple Images in a Batch
For a directory of images, loop over the files and convert each one. Use pathlib for clean path handling:
from pathlib import Path from PIL import Image input_dir = Path("images") output_dir = Path("converted") output_dir.mkdir(exist_ok=True) for src in input_dir.glob("*.png"): im = Image.open(src) dest = output_dir / (src.stem + ".webp") im.save(dest, "WEBP", quality=80)
This pattern keeps memory usage low because each image is loaded and saved before the next file is processed. If you need to convert a large number of files, process them sequentially rather than loading all images into memory at once.
Preserving Metadata and Color Profiles
By default, Pillow does not carry over EXIF, ICC profiles, or other metadata when you save a converted image. To preserve them, copy the relevant entries from the source image's info dictionary:
im = Image.open("input.jpg") exif = im.info.get("exif") icc = im.info.get("icc_profile") im.save("output.webp", exif=exif, icc_profile=icc)
Not every format supports every metadata type. JPEG and WebP can store EXIF and ICC profiles, while PNG has its own metadata conventions. If the target format does not support a particular field, Pillow may ignore it or raise an error, depending on the version. Test the output to confirm that the metadata you care about survived the conversion.
Choosing the Right Output Format
The decision between JPG, PNG, and WebP depends on the image content and the use case.
| Format | Compression | Transparency | Typical Use |
|---|---|---|---|
| JPG | Lossy | No | Photographs, web images without alpha |
| PNG | Lossless | Yes | Screenshots, diagrams, images with text |
| WebP | Both | Yes | Web delivery, smaller files than PNG or JPG |
WebP can produce smaller files than JPEG at a similar visual quality, but encoding tends to be slower. PNG is the right choice when lossless output matters more than file size. JPEG remains a safe default for photographic content that does not need transparency.
Performance Considerations for Large Batches
Pillow loads the entire image into memory, so a batch conversion of large files can consume a lot of RAM. Process one image at a time and let the image object go out of scope before moving to the next. If you are converting to JPEG or WebP, the optimize and method options increase encoding time but reduce file size. There is no free lunch: smaller files cost CPU time.
For very large images, consider resizing before conversion if the final dimensions are known. That reduces memory usage and encoding time. If you need to convert thousands of files, parallelize with a process pool, but be aware that Pillow releases the GIL during some operations, so the speedup may not be linear.