0%

62- Introduction to Image Processing

Manipulate images with Python. Read, write, resize, crop, filter, and transform images. The foundation of computer vision and image analysis.

Images are everywhere. Profile pictures, product photos, scanned documents, medical scans, satellite imagery. Python can read, write, and manipulate images in dozens of ways.
The Python Imaging Library (PIL) and its modern fork, Pillow, are the standard tools for basic image processing. You can resize images, crop them, rotate them, change colors, apply filters, add text, and much more.
This lesson introduces image processing with Pillow. You will learn to open and save images, convert between formats, resize and crop, apply filters, and draw on images. These skills are essential for web development, data science, and automation.

🕯️ Magic Note

Pillow is a fork of the original PIL (Python Imaging Library), which stopped development in 2011. Pillow is actively maintained and compatible with PIL. It supports dozens of image formats: JPEG, PNG, GIF, BMP, TIFF, WebP, and many more.

Installing Pillow
Install Pillow using pip.

Bash

pip install Pillow

Python

from PIL import Image, ImageDraw, ImageFilter, ImageEnhance

Opening and Saving Images
Load an image from a file, display its properties, and save it in different formats.

Python

from PIL import Image

# Open an image

img = Image.open(“example.jpg”)

# Get image properties

print(f”Format: {img.format}”) # JPEG, PNG, etc.

print(f”Mode: {img.mode}”) # RGB, RGBA, L (grayscale), etc.

print(f”Size: {img.size}”) # (width, height) in pixels

print(f”Width: {img.width}”)

print(f”Height: {img.height}”)

print(f”Palette: {img.palette}”) # Palette for indexed images

# Display the image (opens in default viewer)

img.show()

# Save in different format

img.save(“output.png”) # Converts from JPEG to PNG

# Save with quality (for JPEG)

img.save(“output.jpg”, quality=85)

# Save with compression (for PNG)

img.save(“output.png”, optimize=True)

💡 When saving images, different formats support different features. JPEG does not support transparency, PNG does. JPEG is lossy (smaller files), PNG is lossless (larger files). Choose based on your needs.
Creating New Images
Create blank images from scratch with specified size and color.

Python

from PIL import Image

# Create a new RGB image (black by default)

black_img = Image.new(“RGB”, (800, 600))

# Create a new image with a specific color

red_img = Image.new(“RGB”, (800, 600), (255, 0, 0)) # Pure red

# Create a new RGBA image (with transparency)

transparent_img = Image.new(“RGBA”, (800, 600), (0, 0, 0, 0)) # Fully transparent

# Create a grayscale image

gray_img = Image.new(“L”, (800, 600), 128) # Middle gray (0-255)

# Save the created image

red_img.save(“red_background.png”)

🕯️ Magic Note

Color modes: “RGB” (red, green, blue) for color images, “RGBA” (red, green, blue, alpha) for color with transparency, “L” (luminance) for grayscale, “CMYK” for print, “P” for palette-based images.

Resizing Images
Change image dimensions, preserving or ignoring aspect ratio.

Python

from PIL import Image

img = Image.open(“example.jpg”)

print(f”Original size: {img.size}”)

# Resize to exact dimensions (may distort)

resized = img.resize((400, 300))

resized.save(“resized_exact.jpg”)

# Resize with aspect ratio preserved

new_width = 400

aspect_ratio = img.height / img.width

new_height = int(new_width * aspect_ratio)

scaled = img.resize((new_width, new_height))

# Using thumbnail (modifies in place, preserves aspect ratio, never enlarges)

img_copy = img.copy()

img_copy.thumbnail((400, 400)) # Scales down to fit within 400×400 box

img_copy.save(“thumbnail.jpg”)

# Resize with different resampling filters

img.resize((800, 600), Image.Resampling.LANCZOS) # High quality for downscaling

img.resize((800, 600), Image.Resampling.BICUBIC) # Good quality

img.resize((800, 600), Image.Resampling.NEAREST) # Fast, pixelated

💡 Use thumbnail() when you need to create preview images or ensure images fit within maximum dimensions. It never enlarges the image and preserves aspect ratio automatically.
Cropping Images
Extract a rectangular region from an image.

Python

from PIL import Image

img = Image.open(“example.jpg”)

# Crop using (left, top, right, bottom) coordinates

cropped = img.crop((100, 50, 300, 200))

cropped.save(“cropped.jpg”)

# Crop center square

width, height = img.size

square_size = min(width, height)

left = (width – square_size) // 2

top = (height – square_size) // 2

center_crop = img.crop((left, top, left + square_size, top + square_size))

center_crop.save(“center_square.jpg”)

# Crop and then resize to standard size

cropped_resized = img.crop((100, 50, 300, 200)).resize((200, 150))

cropped_resized.save(“cropped_resized.jpg”)

Rotating and Flipping Images
Rotate, flip, and transpose images.

Python

from PIL import Image

img = Image.open(“example.jpg”)

# Rotate 90 degrees

rotated_90 = img.rotate(90)

rotated_90.save(“rotated_90.jpg”)

# Rotate 45 degrees (expands canvas to fit)

rotated_45 = img.rotate(45, expand=True)

rotated_45.save(“rotated_45_expand.jpg”)

# Rotate 45 degrees (keep canvas size, parts cropped)

rotated_45_crop = img.rotate(45, expand=False)

rotated_45_crop.save(“rotated_45_crop.jpg”)

# Fill background when rotating (with transparency)

from PIL import ImageDraw

background = Image.new(“RGBA”, img.size, (255, 255, 255, 0))

rotated = img.rotate(45, expand=True)

# Flip horizontally (mirror)

flipped_h = img.transpose(Image.Transpose.FLIP_LEFT_RIGHT)

flipped_h.save(“flipped_horizontal.jpg”)

# Flip vertically

flipped_v = img.transpose(Image.Transpose.FLIP_TOP_BOTTOM)

flipped_v.save(“flipped_vertical.jpg”)

# Transpose (90, 180, 270, etc. are available)

transposed = img.transpose(Image.Transpose.TRANSPOSE) # Swap x and y

Color Conversions
Convert between color modes: RGB, grayscale, and more.

Python

from PIL import Image

img = Image.open(“example.jpg”)

print(f”Original mode: {img.mode}”)

# Convert to grayscale (L mode)

gray = img.convert(“L”)

gray.save(“grayscale.jpg”)

# Convert to RGB (if not already)

rgb = gray.convert(“RGB”)

# Convert to RGBA (adds alpha channel)

rgba = img.convert(“RGBA”)

# Convert to mode P (palette-based, 256 colors)

palette = img.convert(“P”, palette=Image.Palette.ADAPTIVE)

palette.save(“palette.png”)

# Split an RGB image into separate channels

if img.mode == “RGB”:

r, g, b = img.split()

r.save(“red_channel.jpg”)

g.save(“green_channel.jpg”)

b.save(“blue_channel.jpg”)

# Merge channels back

merged = Image.merge(“RGB”, (r, g, b))

🕯️ Magic Note

The split() method separates a color image into its component bands. merge() combines bands back together. This is useful for channel-based operations like swapping red and blue channels.

Applying Filters
Apply image filters like blur, sharpen, edge detection, and more.

Python

from PIL import Image, ImageFilter

img = Image.open(“example.jpg”)

# Blur filters

blurred = img.filter(ImageFilter.BLUR)

blurred.save(“blurred.jpg”)

gaussian_blur = img.filter(ImageFilter.GaussianBlur(radius=5))

gaussian_blur.save(“gaussian_blur.jpg”)

# Sharpen

sharpened = img.filter(ImageFilter.SHARPEN)

sharpened.save(“sharpened.jpg”)

# Edge detection

edges = img.filter(ImageFilter.FIND_EDGES)

edges.save(“edges.jpg”)

# Emboss

embossed = img.filter(ImageFilter.EMBOSS)

embossed.save(“embossed.jpg”)

# Contour

contoured = img.filter(ImageFilter.CONTOUR)

contoured.save(“contoured.jpg”)

# Detail enhancement

detailed = img.filter(ImageFilter.DETAIL)

detailed.save(“detailed.jpg”)

# Custom kernel convolution

kernel = [1, 0, -1, 1, 0, -1, 1, 0, -1] # Example edge detection

custom = img.filter(ImageFilter.Kernel((3, 3), kernel, scale=1))

custom.save(“custom_filter.jpg”)

Adjusting Brightness, Contrast, and Color
Enhance image properties with the ImageEnhance module.

Python

from PIL import Image, ImageEnhance

img = Image.open(“example.jpg”)

# Brightness (1.0 is original)

enhancer = ImageEnhance.Brightness(img)

brighter = enhancer.enhance(1.5)

darker = enhancer.enhance(0.5)

brighter.save(“brighter.jpg”)

# Contrast

enhancer = ImageEnhance.Contrast(img)

more_contrast = enhancer.enhance(1.5)

less_contrast = enhancer.enhance(0.5)

more_contrast.save(“more_contrast.jpg”)

# Color saturation

enhancer = ImageEnhance.Color(img)

more_color = enhancer.enhance(1.5)

less_color = enhancer.enhance(0.5)

grayscale = enhancer.enhance(0.0)

# Sharpness

enhancer = ImageEnhance.Sharpness(img)

sharper = enhancer.enhance(2.0)

sharper.save(“sharper.jpg”)

Drawing on Images
Add text, shapes, and lines to images with ImageDraw.

Python

from PIL import Image, ImageDraw, ImageFont

img = Image.open(“example.jpg”)

draw = ImageDraw.Draw(img)

# Draw a rectangle

draw.rectangle([(50, 50), (200, 150)], outline=”red”, width=3)

# Draw a filled rectangle

draw.rectangle([(50, 50), (200, 150)], fill=(255, 0, 0, 128)) # Semi-transparent red

# Draw a circle (ellipse with equal sides)

draw.ellipse([(300, 50), (450, 200)], outline=”blue”, width=2)

# Draw a line

draw.line([(0, 0), (img.width, img.height)], fill=”green”, width=3)

# Draw text

try:

font = ImageFont.truetype(“arial.ttf”, 36)

except IOError:

font = ImageFont.load_default()

draw.text((100, 300), “Hello, Image!”, fill=”white”, font=font)

# Draw a polygon

draw.polygon([(500, 100), (600, 200), (550, 300), (450, 200)], outline=”yellow”, fill=”orange”)

img.save(“drawn.jpg”)

🕯️ Magic Note

Drawing on images is great for adding watermarks, labels, bounding boxes, or annotations. You can also draw directly on a new blank image to create graphics from scratch.

Practical Example: Image Watermarking
Add a semi-transparent text watermark to a batch of images.

Python

from PIL import Image, ImageDraw, ImageFont

from pathlib import Path

def add_watermark(input_path, output_path, text, opacity=128):

“””Add a text watermark to an image.”””

img = Image.open(input_path)

# Create a transparent overlay layer

watermark = Image.new(“RGBA”, img.size, (0, 0, 0, 0))

draw = ImageDraw.Draw(watermark)

# Get font (fallback if truetype not available)

try:

font_size = int(img.width / 15)

font = ImageFont.truetype(“arial.ttf”, font_size)

except IOError:

font = ImageFont.load_default()

# Text position (bottom right corner)

bbox = draw.textbbox((0, 0), text, font=font)

text_width = bbox[2] – bbox[0]

text_height = bbox[3] – bbox[1]

padding = 20

position = (img.width – text_width – padding, img.height – text_height – padding)

# Draw white text with opacity

draw.text(position, text, fill=(255, 255, 255, opacity), font=font)

# Composite the watermark onto the original

if img.mode != “RGBA”:

img = img.convert(“RGBA”)

watermarked = Image.alpha_composite(img, watermark)

watermarked.save(output_path)

print(f”Watermarked: {output_path}”)

# Batch process all images in a folder

def batch_watermark(folder_path, watermark_text):

folder = Path(folder_path)

output_folder = folder / “watermarked”

output_folder.mkdir(exist_ok=True)

for img_path in folder.glob(“*.jpg”):

output_path = output_folder / img_path.name

add_watermark(img_path, output_path, watermark_text)

# batch_watermark(“photos”, “Feloriya Photography”)

Common Mistakes with Image Processing
  • Forgetting to convert mode before saving (RGBA to RGB for JPEG)
  • Not handling different color modes in operations
  • Using JPEG for images with transparency (use PNG)
  • Modifying the original image without copying (create a copy with .copy())
  • Not closing images (though Pillow handles this, use context managers for many files)
  • Assuming all images have the same size when resizing a batch
Check Your Understanding
  • How do you open an image and get its dimensions?
  • Write code to resize an image to 800×600 while preserving aspect ratio.
  • How do you convert an image to grayscale?
  • Write code to add a text watermark to the bottom-right corner of an image.
  • What is the difference between JPEG and PNG formats?
  • How do you apply a Gaussian blur to an image?

⚡ Whisper

Images are more than pixels. They are memories, products, documents, art. With Pillow, you can read them, write them, resize them, crop them, rotate them, filter them, enhance them, draw on them. Each operation is a transformation. A large image becomes a thumbnail. A color photo becomes grayscale. A noisy photo becomes sharp. A blank canvas becomes a masterpiece. The tools are simple. The possibilities are endless. Resize for web. Convert for format. Watermark for protection. Annotate for explanation. Process a thousand images in a loop. Pillow handles them all. Learn the basics. Open. Save. Resize. Crop. Convert. Filter. With these, you can build image processing pipelines. For web development, for data science, for automation. The images are waiting. Transform them.

Related posts