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
- Install Pillow with
python -m pip install pillow. - Run
python image_converter.py source.png converted.jpg. - Open both files and compare orientation, dimensions, colour, and transparency.
- 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.