引言

你是否遇到过这样的场景?想给200张旅行照片批量添加水印却不想用PS;想把正方形自拍切成九宫格发朋友圈却找不到顺手的工具;想给老照片上色或制作漫画风特效却被专业软件的复杂操作劝退?Python的Pillow库(PIL的升级版)正是解决这些问题的“图像处理瑞士军刀”——它不仅能完成基础的裁剪、旋转,还支持滤镜特效、批量处理,甚至能为计算机视觉模型预处理数据。本文将带你解锁Pillow的高级玩法,从实用技巧到创意特效,让你用代码轻松搞定所有图像处理需求。


一、几何变换:从精准裁剪到任意角度旋转

1.1 裁剪:从坐标定位到智能区域提取

Pillow的crop()方法支持通过坐标(左、上、右、下)裁剪图像,但实际应用中我们可能需要:

  • 按比例裁剪(如将16:9视频帧转为9:16竖屏)
  • 智能识别区域(如裁剪身份证的人像部分,需结合坐标计算)

示例1:按比例裁剪竖屏图像

from PIL import Image

def crop_to_ratio(img_path, target_ratio=9/16):
    """将图像裁剪为目标宽高比(如9:16竖屏)"""
    with Image.open(img_path) as img:
        width, height = img.size
        current_ratio = width / height
        
        if current_ratio > target_ratio:  # 原图更宽,需裁剪左右两侧
            new_width = int(height * target_ratio)
            left = (width - new_width) // 2
            right = left + new_width
            cropped = img.crop((left, 0, right, height))
        else:  # 原图更高,需裁剪上下两侧
            new_height = int(width / target_ratio)
            top = (height - new_height) // 2
            bottom = top + new_height
            cropped = img.crop((0, top, width, bottom))
        
        return cropped

# 使用示例:将16:9的风景图转为9:16竖屏
cropped_img = crop_to_ratio("scenery_16_9.jpg")
cropped_img.save("scenery_9_16.jpg")

1.2 旋转:避免内容丢失的“扩展画布”技巧

默认的rotate()方法会裁剪超出原画布的内容,而通过expand=True参数可以自动扩展画布,保留完整旋转后的图像(背景默认黑色,可自定义填充色)。

示例2:30度旋转并扩展画布

from PIL import Image

with Image.open("cat.jpg") as img:
    # 旋转30度,扩展画布,用白色填充背景
    rotated = img.rotate(
        30,          # 旋转角度(顺时针)
        expand=True, # 扩展画布
        fillcolor=(255, 255, 255)  # RGB白色
    )
    rotated.save("cat_rotated.jpg")

1.3 翻转与镜像:水平/垂直翻转的艺术

transpose()方法支持多种翻转模式,适合制作对称效果(如镜面倒影)。

示例3:制作水面倒影效果

from PIL import Image

with Image.open("mountain.jpg") as img:
    # 垂直翻转图像(上下颠倒)
    flipped = img.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
    # 合并原图与翻转图(上下拼接)
    total_height = img.height * 2
    combined = Image.new("RGB", (img.width, total_height))
    combined.paste(img, (0, 0))          # 粘贴原图在上半部分
    combined.paste(flipped, (0, img.height))  # 粘贴翻转图在下半部分
    # 添加渐变透明度(让倒影更自然)
    for y in range(img.height):
        alpha = int(255 * (1 - y / img.height))  # 从255渐变到0
        for x in range(img.width):
            r, g, b = flipped.getpixel((x, y))
            flipped.putpixel((x, y), (r, g, b, alpha))  # 需转换为RGBA模式
    combined.save("mountain_reflection.png")

二、滤镜与色彩魔法:从内置效果到自定义调整

2.1 内置滤镜:一键生成艺术效果

Pillow的ImageFilter模块提供了20+种内置滤镜,适合快速生成模糊、轮廓、浮雕等效果。

示例4:常用滤镜对比

from PIL import Image, ImageFilter

with Image.open("portrait.jpg") as img:
    # 高斯模糊(背景虚化)
    blurred = img.filter(ImageFilter.GaussianBlur(radius=5))
    # 轮廓提取(漫画线稿)
    contour = img.filter(ImageFilter.CONTOUR)
    # 浮雕效果(3D质感)
    emboss = img.filter(ImageFilter.EMBOSS)
    # 锐化(增强细节)
    sharpened = img.filter(ImageFilter.UnsharpMask(radius=2, percent=150))
    
    # 拼接结果图
    result = Image.new("RGB", (img.width*2, img.height*2))
    result.paste(img, (0, 0))          # 原图(左上)
    result.paste(blurred, (img.width, 0))  # 模糊(右上)
    result.paste(contour, (0, img.height))  # 轮廓(左下)
    result.paste(emboss, (img.width, img.height))  # 浮雕(右下)
    result.save("filter_comparison.jpg")

2.2 色彩调整:从亮度到HSL的精准控制

通过ImageEnhance模块可以调整图像的亮度、对比度、色彩平衡和锐度,甚至自定义HSL(色相、饱和度、亮度)变换。

示例5:老照片色调还原

from PIL import Image, ImageEnhance

def old_photo_effect(img_path):
    with Image.open(img_path) as img:
        # 转为RGB模式(避免PNG透明通道干扰)
        img = img.convert("RGB")
        # 降低饱和度(模拟褪色)
        enhancer = ImageEnhance.Color(img)
        desaturated = enhancer.enhance(0.3)  # 0=灰度,1=原图,>1更鲜艳
        # 增加暖色调(红色和绿色通道)
        width, height = desaturated.size
        for y in range(height):
            for x in range(width):
                r, g, b = desaturated.getpixel((x, y))
                # 红色+10%,绿色+5%,蓝色-10%(范围0-255)
                new_r = min(r + 25, 255)
                new_g = min(g + 15, 255)
                new_b = max(b - 25, 0)
                desaturated.putpixel((x, y), (new_r, new_g, new_b))
        # 添加颗粒感(模拟胶片噪点)
        enhancer = ImageEnhance.Contrast(desaturated)
        grainy = enhancer.enhance(1.2)
        return grainy

# 使用示例:将现代照片转为老照片风格
old_photo = old_photo_effect("modern_photo.jpg")
old_photo.save("old_photo_effect.jpg")

三、批量处理:自动化处理百张图像的秘诀

3.1 遍历文件夹:用os模块实现批量操作

结合os.walk()遍历文件夹,可对所有图片(如JPG、PNG)执行统一操作(如添加水印、调整尺寸)。

示例6:批量添加文字水印

import os
from PIL import Image, ImageDraw, ImageFont

def add_watermark(img_path, text="版权所有", output_dir="watermarked"):
    """给单张图像添加文字水印"""
    with Image.open(img_path) as img:
        draw = ImageDraw.Draw(img)
        # 选择字体(需下载中文字体文件,如simhei.ttf)
        font = ImageFont.truetype("simhei.ttf", size=int(img.width*0.03))
        # 计算水印位置(右下角,边距10%)
        text_width, text_height = draw.textsize(text, font=font)
        x = img.width - text_width - int(img.width*0.02)
        y = img.height - text_height - int(img.height*0.02)
        # 添加半透明水印(RGBA模式)
        img = img.convert("RGBA")
        watermark = Image.new("RGBA", img.size, (255, 255, 255, 0))
        draw = ImageDraw.Draw(watermark)
        draw.text((x, y), text, font=font, fill=(255, 255, 255, 128))  # 透明度50%
        # 合并原图与水印
        combined = Image.alpha_composite(img, watermark).convert("RGB")
        # 保存到输出目录
        os.makedirs(output_dir, exist_ok=True)
        output_path = os.path.join(output_dir, os.path.basename(img_path))
        combined.save(output_path)

# 批量处理当前文件夹下的所有JPG图片
for root, dirs, files in os.walk("."):
    for file in files:
        if file.lower().endswith((".jpg", ".jpeg")):
            img_path = os.path.join(root, file)
            add_watermark(img_path)

3.2 性能优化:多线程加速批量处理

处理大量图像时,单线程效率低下,可通过concurrent.futures.ThreadPoolExecutor实现多线程并行处理。

示例7:多线程批量压缩图片

import os
from PIL import Image
from concurrent.futures import ThreadPoolExecutor

def compress_image(img_path, max_size=1024*1024, quality=80):
    """压缩图像到指定文件大小(默认1MB)"""
    with Image.open(img_path) as img:
        # 调整尺寸(保持宽高比,最长边不超过1920像素)
        width, height = img.size
        if width > 1920 or height > 1920:
            ratio = min(1920/width, 1920/height)
            img = img.resize((int(width*ratio), int(height*ratio)))
        # 保存并调整质量直到文件大小达标
        output_path = os.path.splitext(img_path)[0] + "_compressed.jpg"
        img.save(output_path, quality=quality)
        # 若文件仍过大,递归降低质量
        if os.path.getsize(output_path) > max_size and quality > 10:
            compress_image(output_path, max_size, quality-10)

# 多线程处理(假设要处理images文件夹下的所有图片)
image_dir = "images"
image_paths = [os.path.join(image_dir, f) for f in os.listdir(image_dir) 
               if f.lower().endswith((".jpg", ".jpeg", ".png"))]

with ThreadPoolExecutor(max_workers=4) as executor:
    executor.map(compress_image, image_paths)

四、创意特效:从九宫格拼图到漫画风生成

4.1 九宫格拼图:朋友圈的“仪式感神器”

将一张正方形图片切成9小块,适合分9天发布或拼成网格图。

示例8:生成九宫格拼图

from PIL import Image

def split_into_nine(img_path):
    with Image.open(img_path) as img:
        # 确保图像是正方形(否则先裁剪)
        width, height = img.size
        if width != height:
            min_side = min(width, height)
            img = img.crop((0, 0, min_side, min_side))
        # 每块的尺寸
        block_size = img.width // 3
        # 生成9块子图
        for i in range(3):
            for j in range(3):
                left = j * block_size
                top = i * block_size
                right = left + block_size
                bottom = top + block_size
                block = img.crop((left, top, right, bottom))
                block.save(f"nine_grid_{i}_{j}.jpg")

# 使用示例:将自拍切成九宫格
split_into_nine("selfie.jpg")

4.2 漫画风特效:边缘检测+色彩量化

通过轮廓提取和色彩量化,可将照片转为漫画风格(类似手机APP的“漫画滤镜”)。

示例9:照片转漫画风

from PIL import Image, ImageFilter, ImageOps

def photo_to_cartoon(img_path):
    with Image.open(img_path) as img:
        # 步骤1:提取轮廓(边缘检测)
        edges = img.filter(ImageFilter.FIND_EDGES)
        edges = ImageOps.invert(edges)  # 反转颜色(黑边变白)
        # 步骤2:色彩量化(减少颜色数量)
        quantized = img.quantize(colors=32).convert("RGB")
        # 步骤3:合并轮廓与量化图(轮廓覆盖在量化图上)
        cartoon = Image.composite(quantized, edges, edges.convert("L"))
        return cartoon

# 使用示例:将风景照转为漫画风格
cartoon_img = photo_to_cartoon("landscape.jpg")
cartoon_img.save("cartoon_style.jpg")

五、Pillow在计算机视觉中的应用:预处理与数据增强

5.1 图像预处理:为模型输入准备数据

深度学习模型(如CNN)通常需要固定尺寸、标准化的输入,Pillow可轻松完成这些操作。

示例10:为ResNet模型预处理图像

from PIL import Image
import numpy as np

def preprocess_for_resnet(img_path):
    with Image.open(img_path) as img:
        # 调整尺寸到224x224(ResNet输入要求)
        img = img.resize((224, 224))
        # 转为numpy数组并标准化(均值[0.485, 0.456, 0.406],标准差[0.229, 0.224, 0.225])
        img_array = np.array(img).astype(np.float32) / 255.0
        mean = np.array([0.485, 0.456, 0.406])
        std = np.array([0.229, 0.224, 0.225])
        img_array = (img_array - mean) / std
        # 增加批次维度(模型输入为[batch, height, width, channels])
        return np.expand_dims(img_array, axis=0)

# 使用示例:预处理图像用于模型推理
input_tensor = preprocess_for_resnet("test_image.jpg")

5.2 数据增强:提升模型泛化能力

在训练阶段,通过随机旋转、翻转、裁剪等操作生成更多样化的训练数据,Pillow可轻松实现这些增强。

示例11:随机数据增强函数

import random
from PIL import Image

def random_augment(img):
    """随机应用旋转、翻转、亮度调整"""
    # 随机旋转(-15°到+15°)
    angle = random.uniform(-15, 15)
    img = img.rotate(angle, expand=True, fillcolor=(255, 255, 255))
    # 随机水平翻转(50%概率)
    if random.random() > 0.5:
        img = img.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
    # 随机调整亮度(0.8到1.2倍)
    enhancer = ImageEnhance.Brightness(img)
    img = enhancer.enhance(random.uniform(0.8, 1.2))
    return img

# 使用示例:对训练图像进行增强
with Image.open("train_image.jpg") as img:
    augmented_img = random_augment(img)
    augmented_img.save("augmented_train_image.jpg")

结语

Pillow库的强大之处在于“简单问题简单解决,复杂问题灵活扩展”:从日常的九宫格拼图、批量水印,到高级的漫画特效、数据增强,它用简洁的API覆盖了90%的图像处理需求。当你需要更复杂的功能(如目标检测、语义分割)时,Pillow也能作为预处理工具,与OpenCV、PyTorch等库无缝衔接。

你用Pillow实现过哪些有趣的功能?是给宠物照片加猫耳贴纸,还是为博客文章批量生成封面图?欢迎在评论区分享你的“Pillow创意项目”——你的经验,可能启发更多人用代码解锁图像处理的无限可能!

Logo

GitCode AI社区是一款由 GitCode 团队打造的智能助手,AI大模型社区、提供国内外头部大模型及数据集服务。

更多推荐