Python 自动化之图片批量处理——Pillow 实战

日常办公和开发中,图片处理的需求非常多------批量改尺寸、加水印、格式转换、压缩大小。用 Python 的 Pillow(PIL 的升级版)可以一键搞定几百张图片的重复操作。

一、安装

bash 复制代码
pip install Pillow
python 复制代码
from PIL import Image, ImageDraw, ImageFont, ImageFilter

二、基础操作

1. 打开与显示

python 复制代码
from PIL import Image

# 打开图片
img = Image.open("照片.jpg")
print(f"格式: {img.format}")
print(f"尺寸: {img.size}")       # (宽度, 高度)
print(f"模式: {img.mode}")       # RGB、RGBA、L(灰度)等

# 显示图片(会弹出默认图片查看器)
img.show()

# 保存为其他格式
img.save("照片.png")

2. 调整尺寸

python 复制代码
# 等比例缩放(指定宽度,高度自动计算)
def resize_by_width(img, target_width=800):
    w, h = img.size
    ratio = target_width / w
    new_h = int(h * ratio)
    return img.resize((target_width, new_h), Image.LANCZOS)

# 裁剪为正方形(从中心切)
def crop_square(img):
    w, h = img.size
    min_side = min(w, h)
    left = (w - min_side) // 2
    top = (h - min_side) // 2
    right = left + min_side
    bottom = top + min_side
    return img.crop((left, top, right, bottom))

# 强制缩放到指定尺寸(会变形)
img_resized = img.resize((1920, 1080), Image.LANCZOS)

# 按比例缩放
img.thumbnail((800, 600))  # 原图会被修改,最长边不超过800或600

3. 旋转与翻转

python 复制代码
# 旋转(expand=True 防止裁剪)
rotated = img.rotate(90, expand=True)

# 翻转
flipped_h = img.transpose(Image.FLIP_LEFT_RIGHT)   # 水平翻转
flipped_v = img.transpose(Image.FLIP_TOP_BOTTOM)    # 垂直翻转

三、实战案例:批量处理婚纱照

你刚拍了婚纱照,可以用脚本批量处理:

python 复制代码
from PIL import Image, ImageFilter
import os

class PhotoProcessor:
    """婚纱照批量处理器"""

    def __init__(self, input_dir, output_dir):
        self.input_dir = input_dir
        self.output_dir = output_dir
        os.makedirs(output_dir, exist_ok=True)

    def resize_for_wechat(self, max_width=1080):
        """
        批量压缩到适合微信分享的尺寸
        微信发送原图有限制,先统一压缩
        """
        for f in os.listdir(self.input_dir):
            if not f.lower().endswith((".jpg", ".jpeg", ".png")):
                continue

            img = Image.open(os.path.join(self.input_dir, f))

            # 等比例缩放到最长边不超过 max_width
            w, h = img.size
            if max(w, h) > max_width:
                ratio = max_width / max(w, h)
                new_size = (int(w * ratio), int(h * ratio))
                img = img.resize(new_size, Image.LANCZOS)

            # 保存(压缩质量 85,既清晰又小)
            output_path = os.path.join(self.output_dir, f"wechat_{f}")
            img.save(output_path, quality=85, optimize=True)
            print(f"已处理: {f} → {output_path}")

    def add_watermark(self, text="张政 & 爱妻", position="右下角"):
        """
        批量添加水印(保护版权)
        """
        for f in os.listdir(self.input_dir):
            if not f.lower().endswith((".jpg", ".jpeg", ".png")):
                continue

            img = Image.open(os.path.join(self.input_dir, f)).convert("RGBA")

            # 创建水印层
            watermark = Image.new("RGBA", img.size, (0, 0, 0, 0))
            draw = ImageDraw.Draw(watermark)

            # 字体(Windows 系统字体路径)
            font_size = max(img.size) // 30
            try:
                font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", font_size)
            except:
                font = ImageFont.load_default()

            # 计算文字宽高
            bbox = draw.textbbox((0, 0), text, font=font)
            text_w = bbox[2] - bbox[0]
            text_h = bbox[3] - bbox[1]

            # 定位
            margin = 20
            if position == "右下角":
                x = img.width - text_w - margin
                y = img.height - text_h - margin
            elif position == "左下角":
                x = margin
                y = img.height - text_h - margin
            elif position == "居中":
                x = (img.width - text_w) // 2
                y = (img.height - text_h) // 2

            # 绘制半透明文字
            draw.text((x, y), text, font=font, fill=(255, 255, 255, 100))

            # 合并
            result = Image.alpha_composite(img, watermark)
            result = result.convert("RGB")

            output_path = os.path.join(self.output_dir, f"wm_{f}")
            result.save(output_path, quality=95)
            print(f"已添加水印: {f}")

    def create_collage(self, photos_per_row=3):
        """
        创建照片拼图(适合发朋友圈九宫格预览)
        """
        photos = [f for f in os.listdir(self.input_dir)
                  if f.lower().endswith((".jpg", ".jpeg", ".png"))]

        if not photos:
            return

        # 设定每张小图尺寸
        thumb_size = 300
        rows = (len(photos) + photos_per_row - 1) // photos_per_row

        canvas = Image.new("RGB",
            (photos_per_row * thumb_size, rows * thumb_size),
            (255, 255, 255)  # 白色背景
        )

        for i, f in enumerate(photos):
            img = Image.open(os.path.join(self.input_dir, f))
            img.thumbnail((thumb_size - 10, thumb_size - 10))
            x = (i % photos_per_row) * thumb_size + 5
            y = (i // photos_per_row) * thumb_size + 5
            canvas.paste(img, (x, y))

        output_path = os.path.join(self.output_dir, "collage.jpg")
        canvas.save(output_path, quality=95)
        print(f"拼图已生成: {output_path}")

# 使用
processor = PhotoProcessor("原始照片", "输出照片")
processor.resize_for_wechat(max_width=1080)
# processor.add_watermark("张政 & 爱妻")
# processor.create_collage(photos_per_row=3)

四、其他常用场景

1. 批量格式转换

python 复制代码
def batch_convert(input_dir, output_dir, target_format="png"):
    """批量转换图片格式"""
    os.makedirs(output_dir, exist_ok=True)
    for f in os.listdir(input_dir):
        name, ext = os.path.splitext(f)
        if ext.lower() not in (".jpg", ".jpeg", ".png", ".bmp", ".webp"):
            continue

        img = Image.open(os.path.join(input_dir, f))
        output_path = os.path.join(output_dir, f"{name}.{target_format}")
        img.save(output_path)
        print(f"已转换: {f} → {output_path}")

# 把所有 jpg 转成 png
batch_convert("原始图片", "输出图片", "png")

2. 图片压缩

python 复制代码
def compress_images(input_dir, output_dir, quality=60):
    """批量压缩图片(适合上传到网站)"""
    os.makedirs(output_dir, exist_ok=True)
    for f in os.listdir(input_dir):
        if not f.lower().endswith((".jpg", ".jpeg", ".png")):
            continue

        img = Image.open(os.path.join(input_dir, f))
        output_path = os.path.join(output_dir, f"compressed_{f}")
        img.save(output_path, quality=quality, optimize=True)

        original_size = os.path.getsize(os.path.join(input_dir, f))
        compressed_size = os.path.getsize(output_path)
        ratio = (1 - compressed_size / original_size) * 100
        print(f"{f}: {original_size//1024}KB → {compressed_size//1024}KB (压缩 {ratio:.0f}%)")

# 压缩到 60% 质量
compress_images("原始图片", "压缩图片", quality=60)

3. 添加滤镜效果

python 复制代码
def apply_filters():
    img = Image.open("照片.jpg")

    # 各种滤镜效果
    img.filter(ImageFilter.BLUR)              # 模糊
    img.filter(ImageFilter.CONTOUR)           # 轮廓
    img.filter(ImageFilter.EMBOSS)            # 浮雕
    img.filter(ImageFilter.SHARPEN)           # 锐化
    img.filter(ImageFilter.SMOOTH)            # 平滑

    # 转换为灰度
    gray = img.convert("L")

    # 增强亮度/对比度
    from PIL import ImageEnhance
    enhancer = ImageEnhance.Brightness(img)
    brighter = enhancer.enhance(1.3)  # 亮度增加 30%

    enhancer = ImageEnhance.Contrast(img)
    higher_contrast = enhancer.enhance(1.2)

4. 批量重命名

python 复制代码
import os

def batch_rename(directory, prefix="IMG_", start=1):
    """批量重命名图片"""
    files = [f for f in os.listdir(directory)
             if f.lower().endswith((".jpg", ".jpeg", ".png", ".webp"))]
    files.sort()

    for i, f in enumerate(files):
        ext = os.path.splitext(f)[1]
        new_name = f"{prefix}{start + i:03d}{ext}"
        os.rename(
            os.path.join(directory, f),
            os.path.join(directory, new_name)
        )
        print(f"{f} → {new_name}")

# 使用:重命名为 IMG_001.jpg, IMG_002.jpg ...
batch_rename("婚纱照原片", prefix="wedding_", start=1)

五、实际工作流

复制代码
① 从相机/手机导出照片 → ② 批量重命名
③ 批量调整尺寸/压缩 → ④ 添加水印
⑤ 挑选部分做滤镜/拼图 → ⑥ 发朋友圈/上传

每一步都写成一个函数,中间环节不满意就调整参数重新运行,不用一张一张手动处理

六、常见问题

1. OSError: cannot identify image file

python 复制代码
# 原因:文件扩展名和实际格式不一致
# 解决:用 try 跳过损坏的图片
try:
    img = Image.open(file)
    img.verify()  # 验证图片是否损坏
except:
    print(f"跳过损坏文件: {file}")

2. 中文文字显示为方框

python 复制代码
# 原因:Pillow 默认字体不支持中文
# 解决:指定中文字体
font = ImageFont.truetype("C:/Windows/Fonts/simhei.ttf", 36)
# 常见的 Windows 中文字体路径:
# 微软雅黑: msyh.ttc
# 宋体: simsun.ttc
# 黑体: simhei.ttf

3. 图片太大导致内存不足

python 复制代码
# 处理超大图片时,先缩小再操作
img = Image.open("超大图片.jpg")
img.thumbnail((4000, 4000))  # 限制最长边 4000 像素

总结

Pillow 几乎可以搞定日常所有的图片处理需求。把常用的几个功能写成脚本,下次处理图片时直接运行就行,比一张张手动操作省下 90% 的时间。

最常用的三个场景:

  • 批量改尺寸(发朋友圈/上传网站)
  • 批量加水印(保护版权)
  • 批量压缩(节省空间)

💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。