Python Imaging Library (Fork) providing comprehensive image processing capabilities for reading, writing, and manipulating images across dozens of formats.
—
Advanced image manipulation functions including automatic contrast adjustment, color space operations, geometric transformations, and specialized processing operations provided by the ImageOps module.
Functions for automatic image quality improvement and adjustment.
def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False):
"""
Maximize image contrast by stretching histogram.
Parameters:
- image (Image): Input image
- cutoff (float | tuple): Percentage to cut from histogram ends (0-100)
- ignore (int | sequence): Pixel values to ignore in histogram
- mask (Image): Optional mask for selective processing
- preserve_tone (bool): Preserve image tone in Photoshop-like style
Returns:
Image: Auto-contrast adjusted image
"""
def equalize(image, mask=None):
"""
Equalize image histogram for improved contrast distribution.
Parameters:
- image (Image): Input image
- mask (Image): Optional mask for selective equalization
Returns:
Image: Histogram equalized image
"""
def posterize(image, bits):
"""
Reduce the number of bits for each color channel.
Parameters:
- image (Image): Input image
- bits (int): Number of bits to keep per channel (1-8)
Returns:
Image: Posterized image
"""
def solarize(image, threshold=128):
"""
Invert all pixel values above threshold (solarization effect).
Parameters:
- image (Image): Input image
- threshold (int): Threshold value (0-255)
Returns:
Image: Solarized image
"""Functions for resizing, fitting, and transforming images.
def fit(image, size, method=3, bleed=0.0, centering=(0.5, 0.5)):
"""
Fit image within given size, cropping as needed.
Parameters:
- image (Image): Input image
- size (tuple): Target size as (width, height)
- method (int): Resampling method (Resampling.LANCZOS, etc.)
- bleed (float): Remove a border around fitted image (0.0-0.5)
- centering (tuple): Crop centering as (horizontal, vertical) (0.0-1.0)
Returns:
Image: Fitted image
"""
def contain(image, size, method=3):
"""
Resize image to fit within size while maintaining aspect ratio.
Parameters:
- image (Image): Input image
- size (tuple): Maximum size as (width, height)
- method (int): Resampling method
Returns:
Image: Contained image
"""
def cover(image, size, method=3):
"""
Resize image to cover the entire size, cropping if necessary.
Parameters:
- image (Image): Input image
- size (tuple): Target size as (width, height)
- method (int): Resampling method
Returns:
Image: Covered image (may be cropped)
"""
def pad(image, size, method=3, color=None, centering=(0.5, 0.5)):
"""
Resize and pad image to exact size.
Parameters:
- image (Image): Input image
- size (tuple): Target size as (width, height)
- method (int): Resampling method
- color (int | tuple | str): Padding color
- centering (tuple): Image centering in padded area
Returns:
Image: Padded image
"""
def scale(image, factor, resample=3):
"""
Scale image by a factor.
Parameters:
- image (Image): Input image
- factor (float): Scale factor (>1 enlarges, <1 shrinks)
- resample (int): Resampling method
Returns:
Image: Scaled image
"""
def crop(image, border=0):
"""
Remove border pixels from image.
Parameters:
- image (Image): Input image
- border (int | tuple): Border size to remove
Returns:
Image: Cropped image
"""
def expand(image, border=0, fill=0):
"""
Add border pixels around image.
Parameters:
- image (Image): Input image
- border (int | tuple): Border size to add
- fill (int | tuple | str): Border color
Returns:
Image: Expanded image
"""Functions for color manipulation and tone adjustment.
def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoint=127):
"""
Colorize a grayscale image with specified colors.
Parameters:
- image (Image): Grayscale input image
- black (int | tuple | str): Color for black pixels
- white (int | tuple | str): Color for white pixels
- mid (int | tuple | str): Color for mid-tone pixels
- blackpoint (int): Pixel value mapped to black color (0-255)
- whitepoint (int): Pixel value mapped to white color (0-255)
- midpoint (int): Pixel value mapped to mid color (0-255)
Returns:
Image: Colorized image
"""
def grayscale(image):
"""
Convert image to grayscale using ITU-R 601-2 luma transform.
Parameters:
- image (Image): Input image
Returns:
Image: Grayscale image
"""
def invert(image):
"""
Invert image colors (negative effect).
Parameters:
- image (Image): Input image
Returns:
Image: Inverted image
"""Functions for rotating and flipping images.
def flip(image):
"""
Flip image vertically (top to bottom).
Parameters:
- image (Image): Input image
Returns:
Image: Vertically flipped image
"""
def mirror(image):
"""
Flip image horizontally (left to right).
Parameters:
- image (Image): Input image
Returns:
Image: Horizontally flipped image
"""
def exif_transpose(image, in_place=False):
"""
Transpose image according to EXIF orientation data.
Parameters:
- image (Image): Input image with EXIF data
- in_place (bool): Modify image in place if True
Returns:
Image | None: Transposed image (or None if in_place=True)
"""Functions for complex geometric transformations.
def deform(image, deformer, resample=1):
"""
Deform image using a deformer object.
Parameters:
- image (Image): Input image
- deformer: Deformer object implementing getmesh() method
- resample (int): Resampling method
Returns:
Image: Deformed image
"""from PIL import Image, ImageOps
def enhance_image_auto(image_path, save_path):
"""Automatically enhance an image using multiple operations."""
img = Image.open(image_path)
# Step 1: Auto contrast adjustment
enhanced = ImageOps.autocontrast(img, cutoff=2)
# Step 2: Equalize histogram for better distribution
enhanced = ImageOps.equalize(enhanced)
# Step 3: Convert to grayscale and back to remove color casts
if img.mode == "RGB":
gray = ImageOps.grayscale(enhanced)
# Apply colorization to remove color casts
enhanced = ImageOps.colorize(gray, black="black", white="white")
enhanced.save(save_path)
return enhanced
# Process an image
enhanced = enhance_image_auto("photo.jpg", "enhanced_photo.jpg")from PIL import Image, ImageOps
def resize_for_web(image_path, max_size=(800, 600), quality=85):
"""Resize image for web use with proper aspect ratio handling."""
img = Image.open(image_path)
# Method 1: Contain - fits within bounds
contained = ImageOps.contain(img, max_size, method=Image.Resampling.LANCZOS)
contained.save("web_contained.jpg", "JPEG", quality=quality, optimize=True)
# Method 2: Cover - fills entire area, may crop
covered = ImageOps.cover(img, max_size, method=Image.Resampling.LANCZOS)
covered.save("web_covered.jpg", "JPEG", quality=quality, optimize=True)
# Method 3: Fit with cropping control
fitted = ImageOps.fit(img, max_size, method=Image.Resampling.LANCZOS,
centering=(0.5, 0.4)) # Center horizontally, slightly above center vertically
fitted.save("web_fitted.jpg", "JPEG", quality=quality, optimize=True)
# Method 4: Pad to exact size
padded = ImageOps.pad(img, max_size, method=Image.Resampling.LANCZOS,
color="white", centering=(0.5, 0.5))
padded.save("web_padded.jpg", "JPEG", quality=quality, optimize=True)
resize_for_web("original_photo.jpg")from PIL import Image, ImageOps
def create_artistic_effects(image_path):
"""Create various artistic effects using ImageOps."""
img = Image.open(image_path)
# Convert to grayscale first for colorization effects
gray = ImageOps.grayscale(img)
# Sepia tone effect
sepia = ImageOps.colorize(gray, black="black", white="#FFF8DC", mid="#CD853F")
sepia.save("sepia_effect.jpg")
# Cool blue tone
cool = ImageOps.colorize(gray, black="#000080", white="#E6F3FF", mid="#4169E1")
cool.save("cool_effect.jpg")
# Warm orange tone
warm = ImageOps.colorize(gray, black="#2F1B14", white="#FFF8DC", mid="#FF8C00")
warm.save("warm_effect.jpg")
# High contrast black and white
high_contrast = ImageOps.autocontrast(gray, cutoff=5)
high_contrast.save("high_contrast.jpg")
# Posterized effect
posterized = ImageOps.posterize(img, bits=4)
posterized.save("posterized.jpg")
# Solarization effect
solarized = ImageOps.solarize(img, threshold=100)
solarized.save("solarized.jpg")
# Inverted colors (negative)
inverted = ImageOps.invert(img)
inverted.save("inverted.jpg")
create_artistic_effects("portrait.jpg")from PIL import Image, ImageOps
import os
import glob
def batch_process_images(input_dir, output_dir, target_size=(1024, 768)):
"""Batch process images in a directory."""
# Create output directory if it doesn't exist
os.makedirs(output_dir, exist_ok=True)
# Find all image files
image_extensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.tiff"]
image_files = []
for ext in image_extensions:
image_files.extend(glob.glob(os.path.join(input_dir, ext)))
image_files.extend(glob.glob(os.path.join(input_dir, ext.upper())))
for image_path in image_files:
try:
filename = os.path.basename(image_path)
name, ext = os.path.splitext(filename)
# Open and process image
img = Image.open(image_path)
# Fix orientation based on EXIF
if hasattr(img, '_getexif') and img._getexif():
img = ImageOps.exif_transpose(img)
# Resize while maintaining aspect ratio
processed = ImageOps.contain(img, target_size, method=Image.Resampling.LANCZOS)
# Auto-enhance
processed = ImageOps.autocontrast(processed, cutoff=1)
# Save processed image
output_path = os.path.join(output_dir, f"{name}_processed{ext}")
if ext.lower() in ['.jpg', '.jpeg']:
processed.save(output_path, "JPEG", quality=90, optimize=True)
else:
processed.save(output_path)
print(f"Processed: {filename}")
except Exception as e:
print(f"Error processing {filename}: {e}")
# Process all images in a directory
batch_process_images("input_photos", "output_photos", target_size=(800, 600))from PIL import Image, ImageOps
def add_decorative_border(image_path, border_size=20, border_color="white"):
"""Add decorative border to image."""
img = Image.open(image_path)
# Simple border
bordered = ImageOps.expand(img, border=border_size, fill=border_color)
bordered.save("simple_border.jpg")
# Fancy multi-layer border
# Inner border
bordered = ImageOps.expand(img, border=5, fill="black")
# Middle border
bordered = ImageOps.expand(bordered, border=15, fill="white")
# Outer border
bordered = ImageOps.expand(bordered, border=3, fill="gray")
bordered.save("fancy_border.jpg")
# Asymmetric border (different sizes for each side)
# Using crop with negative values adds border
width, height = img.size
expanded = Image.new(img.mode, (width + 100, height + 60), "lightgray")
expanded.paste(img, (50, 20)) # Offset creates border
expanded.save("asymmetric_border.jpg")
add_decorative_border("photo.jpg")from PIL import Image, ImageOps
def smart_crop_portraits(image_path):
"""Demonstrate different cropping strategies for portraits."""
img = Image.open(image_path)
# Square crop for profile pictures - center on upper portion
square_size = min(img.size)
square = ImageOps.fit(img, (square_size, square_size),
method=Image.Resampling.LANCZOS,
centering=(0.5, 0.3)) # Center horizontally, upper third vertically
square.save("profile_square.jpg")
# 16:9 crop for social media
aspect_16_9 = (1920, 1080)
social = ImageOps.fit(img, aspect_16_9,
method=Image.Resampling.LANCZOS,
centering=(0.5, 0.4))
# Scale down to reasonable size
social = ImageOps.contain(social, (800, 450), method=Image.Resampling.LANCZOS)
social.save("social_media.jpg")
# 4:5 crop for Instagram
instagram_ratio = (800, 1000)
instagram = ImageOps.fit(img, instagram_ratio,
method=Image.Resampling.LANCZOS,
centering=(0.5, 0.35))
instagram.save("instagram_crop.jpg")
smart_crop_portraits("portrait.jpg")from PIL import Image, ImageOps
def handle_image_orientation(image_path):
"""Properly handle image orientation using EXIF data."""
img = Image.open(image_path)
print(f"Original size: {img.size}")
print(f"Original mode: {img.mode}")
# Check for EXIF orientation data
if hasattr(img, '_getexif') and img._getexif():
exif = img._getexif()
if exif and 274 in exif: # 274 is the orientation tag
orientation = exif[274]
print(f"EXIF orientation: {orientation}")
# Fix orientation automatically
corrected = ImageOps.exif_transpose(img)
if corrected is not None:
print(f"Corrected size: {corrected.size}")
corrected.save("orientation_corrected.jpg")
else:
print("No orientation correction needed")
img.save("orientation_corrected.jpg")
# Manual orientation operations
flipped_v = ImageOps.flip(img) # Vertical flip
flipped_h = ImageOps.mirror(img) # Horizontal flip
flipped_v.save("flipped_vertical.jpg")
flipped_h.save("flipped_horizontal.jpg")
handle_image_orientation("photo_with_exif.jpg")from PIL import Image, ImageOps
def enhance_image_quality(image_path, output_path):
"""Professional image enhancement pipeline."""
img = Image.open(image_path)
# Step 1: Fix orientation
img = ImageOps.exif_transpose(img) or img
# Step 2: Crop unwanted borders (remove 1% from each edge)
width, height = img.size
crop_amount = min(width, height) * 0.01
img = ImageOps.crop(img, border=int(crop_amount))
# Step 3: Gentle auto-contrast (preserve most of the original look)
img = ImageOps.autocontrast(img, cutoff=0.5)
# Step 4: Subtle histogram equalization on luminance only
if img.mode == "RGB":
# Convert to HSV, equalize V channel, convert back
import colorsys
# This is a simplified approach - in practice you'd use proper color space conversion
img = ImageOps.autocontrast(img, cutoff=1.0)
# Step 5: Resize to target resolution if needed
max_dimension = 2048
if max(img.size) > max_dimension:
img = ImageOps.contain(img, (max_dimension, max_dimension),
method=Image.Resampling.LANCZOS)
# Save with optimal quality
img.save(output_path, "JPEG", quality=95, optimize=True)
return img
# Enhance image quality
enhanced = enhance_image_quality("raw_photo.jpg", "enhanced_photo.jpg")Install with Tessl CLI
npx tessl i tessl/pypi-pillow