Python Image Converter Project

Python project 18

Python Image Converter Project

Create an image converter with Pillow that supports PNG, JPEG, WebP, BMP, and TIFF output. It preserves the source file, corrects orientation metadata, handles transparent pixels safely for non-alpha formats, and chooses the output format from the filename.

Learn Python with mentor-guided projectsView all project ideas

What you will build

The user provides an input image and a different output filename. The converter validates the requested extension, creates the destination folder, reads the first frame, applies EXIF orientation, flattens transparency onto white for JPEG or BMP, and saves the result.

Skills practised

  • Pillow image processing
  • File formats and colour modes
  • EXIF orientation
  • Command-line interfaces

Requirements

  • Python 3.10 or later
  • Pillow: pip install pillow
  • A local practice image
  • About 30 minutes to build

Full Python code

Save this code as image_converter.py, then follow the run instructions below.

"""Convert a local image to PNG, JPEG, WEBP, BMP, or TIFF with Pillow."""

from __future__ import annotations

import argparse
from pathlib import Path

from PIL import Image, ImageOps, UnidentifiedImageError


FORMATS = {
    ".png": "PNG",
    ".jpg": "JPEG",
    ".jpeg": "JPEG",
    ".webp": "WEBP",
    ".bmp": "BMP",
    ".tif": "TIFF",
    ".tiff": "TIFF",
}


def flatten_transparency(image: Image.Image) -> Image.Image:
    """Place transparent pixels on white before saving as JPEG or BMP."""
    rgba = image.convert("RGBA")
    background = Image.new("RGBA", rgba.size, "white")
    background.alpha_composite(rgba)
    return background.convert("RGB")


def convert_image(source: Path, destination: Path) -> tuple[int, int]:
    if not source.is_file():
        raise ValueError(f"Input image does not exist: {source}")
    if source.resolve() == destination.resolve():
        raise ValueError("Choose a different output filename so the source is preserved.")

    output_format = FORMATS.get(destination.suffix.lower())
    if output_format is None:
        supported = ", ".join(sorted(FORMATS))
        raise ValueError(f"Unsupported output extension. Choose one of: {supported}")

    destination.parent.mkdir(parents=True, exist_ok=True)
    with Image.open(source) as opened:
        opened.seek(0)
        image = ImageOps.exif_transpose(opened)
        image.load()

        if output_format in {"JPEG", "BMP"}:
            image = flatten_transparency(image)
        elif image.mode not in {"1", "L", "LA", "P", "RGB", "RGBA", "CMYK"}:
            image = image.convert("RGBA")

        save_options = {"quality": 90, "optimize": True} if output_format in {"JPEG", "WEBP"} else {}
        image.save(destination, format=output_format, **save_options)
        return image.size


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Convert an image based on the output extension.")
    parser.add_argument("source", type=Path)
    parser.add_argument("destination", type=Path)
    return parser.parse_args()


def main() -> None:
    args = parse_args()
    try:
        width, height = convert_image(args.source, args.destination)
    except (ValueError, OSError, UnidentifiedImageError) as error:
        raise SystemExit(f"Conversion failed: {error}") from error
    print(f"Created {args.destination} ({width} x {height} pixels).")


if __name__ == "__main__":
    main()

How the project works

Image formats do not all support the same colour modes. JPEG and BMP cannot preserve an alpha channel, so flatten_transparency() composites the image onto white before converting it to RGB. This avoids a save error and makes the lost transparency explicit.

ImageOps.exif_transpose() applies camera-orientation metadata to the pixels. The output extension selects the Pillow writer, and JPEG or WebP receives a practical quality setting. Animated files are intentionally treated as a single-frame conversion in this beginner version.

Run the project

  1. Install Pillow with python -m pip install pillow.
  2. Run python image_converter.py source.png converted.jpg.
  3. Open both files and compare orientation, dimensions, colour, and transparency.
  4. Try another supported output extension such as .webp.

Accuracy and safety notes

  • Transparency: JPEG and BMP output uses a white background.
  • Animation: only the first frame is converted.
  • Preservation: the destination must differ from the source.

Ways to extend it

Add resize controls, batch conversion, metadata preservation choices, animated-image support, or compression settings. Consult Pillow’s official Image documentation for format-specific options.

Continue learning Python

Build the next project, return to the Softenant project library, or explore the Python programming course in Vizag for structured lessons, mentor feedback, and portfolio practice.