Pillow

技术栈
工具链
pillowpython图像处理PIL图片缩略图

概览

Pillow

Pillow 是 Python 最流行的图像处理库,是 PIL(Python Imaging Library)的友好分支。由 Alex Clark 和社区维护,它提供了全面的图像操作能力。

解决什么问题

  • 打开、编辑、保存各种格式的图像文件(JPEG、PNG、GIF、BMP、TIFF、WebP 等)
  • 批量处理:缩略图、裁剪、旋转、滤镜、水印
  • 在 Django/Flask 等 Web 框架中处理用户上传的图片

关键特性

  • 支持 30+ 图像格式
  • ImageFilter 模块提供模糊、锐化、边缘检测等滤镜
  • ImageDraw 模块支持在图片上绘制文字和图形
  • 像素级访问和 NumPy 数组互转
  • ImageOps 提供自动对比度、反转、灰度化等操作
  • 支持 EXIF 元数据读写

安装

环境准备

  • 操作系统: Windows / macOS / Linux 均可
  • Python 版本: 3.8 及以上(推荐 3.10+)
  • 系统依赖: libjpeg、zlib、libtiff、libwebp(pip 安装时自动处理)

安装命令

# 基础安装
pip install Pillow

# 如需要处理更多格式
pip install Pillow[avif]     # AVIF 格式支持
pip install Pillow[heif]     # HEIF/HEIC 格式支持

# 验证安装
python -c "from PIL import Image; print(Image.__version__)"

常见安装问题

Q: Linux 上 JPEG 支持缺失

# Ubuntu/Debian
sudo apt install libjpeg-dev zlib1g-dev libtiff5-dev libwebp-dev

# 重新安装
pip uninstall Pillow
pip install --no-cache-dir Pillow

Q: macOS 安装后图像显示乱码或颜色不对

pip install Pillow --no-binary Pillow

Q: 提示 "decoder jpeg not available"
确保安装 Pillow 前已有 libjpeg,或使用 conda:

conda install pillow

Q: 运行时 FreeType 字体警告

# macOS
brew install freetype

# Ubuntu
sudo apt install libfreetype6-dev

示例

Pillow Hello World:缩略图与滤镜

目标

打开一张图片,生成缩略图,添加文字水印,应用滤镜,并保存结果。

完整代码

from PIL import Image, ImageFilter, ImageDraw, ImageFont, ImageOps

# === 1. 打开图片 ===
img = Image.open("input.jpg")
print(f"原图尺寸: {img.size}, 模式: {img.mode}")

# === 2. 生成缩略图(保持比例) ===
thumb = img.copy()
thumb.thumbnail((300, 300))
thumb.save("output_thumb.jpg", quality=85)
print(f"缩略图尺寸: {thumb.size}")

# === 3. 裁剪 + 旋转 ===
# 中心裁剪 500x500
w, h = img.size
crop = img.crop(((w-500)//2, (h-500)//2, (w+500)//2, (h+500)//2))
rotated = crop.rotate(15, expand=True, fillcolor="white")
rotated.save("output_rotated.jpg")

# === 4. 滤镜链 ===
filtered = img.copy()
filtered = filtered.filter(ImageFilter.BLUR)      # 先模糊
filtered = filtered.filter(ImageFilter.CONTOUR)   # 再轮廓
filtered.save("output_filtered.jpg")

# === 5. 添加文字水印 ===
watermarked = img.copy()
draw = ImageDraw.Draw(watermarked)
text = "© 2024 My Company"
# 使用默认字体(如无字体文件可省略 font 参数)
try:
    font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 36)
except OSError:
    font = ImageFont.load_default()

# 右下角水印
text_bbox = draw.textbbox((0, 0), text, font=font)
text_w, text_h = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]
margin = 20
draw.text(
    (img.width - text_w - margin, img.height - text_h - margin),
    text, fill=(255, 255, 255, 128), font=font
)
watermarked.save("output_watermarked.jpg")

# === 6. 批量灰度化与反转 ===
gray = ImageOps.grayscale(img)
inverted = ImageOps.invert(gray)
inverted.save("output_inverted.jpg")

# === 7. 与 NumPy 互转 ===
import numpy as np
arr = np.array(img)
print(f"NumPy 数组形状: {arr.shape}, dtype: {arr.dtype}")

# 修改像素:将图片上半部分调暗
arr_top = arr.copy()
arr_top[:arr_top.shape[0]//2] = (arr_top[:arr_top.shape[0]//2] * 0.5).astype(np.uint8)
darkened = Image.fromarray(arr_top)
darkened.save("output_darkened.jpg")

print("\n所有输出已生成!")

运行步骤

pip install Pillow numpy
python hello_pillow.py

预期输出

原图尺寸: (1920, 1080), 模式: RGB
缩略图尺寸: (300, 169)
NumPy 数组形状: (1080, 1920, 3), dtype: uint8

所有输出已生成!

教程

Pillow 图像处理完全指南

背景

从用户头像裁剪到批量水印添加,从验证码生成到图像格式转换,Pillow 几乎覆盖了 Web 开发中所有图像处理需求。它也是 scikit-image、torchvision 等更高级库的图像读写基础。


第 1 章:图像基本操作

from PIL import Image

# 打开与属性
img = Image.open("photo.jpg")
print(img.format)      # JPEG
print(img.size)        # (1920, 1080)
print(img.mode)        # RGB
print(img.info)        # 元数据字典

# 格式转换
img.save("photo.png", "PNG")
img.save("photo.webp", "WEBP", quality=80)

# 图像模式转换
gray = img.convert("L")       # 灰度
rgba = img.convert("RGBA")    # 透明通道
cmyk = img.convert("CMYK")    # 印刷色

第 2 章:ImageDraw 绘图

from PIL import Image, ImageDraw, ImageFont

img = Image.new("RGB", (800, 600), color=(30, 30, 30))
draw = ImageDraw.Draw(img)

# 基本形状
draw.rectangle([50, 50, 300, 200], fill=(60, 120, 200), outline="white", width=3)
draw.ellipse([400, 300, 700, 500], fill=(200, 60, 60))
draw.line([(50, 400), (750, 400)], fill="yellow", width=5)
draw.polygon([(400, 100), (550, 50), (600, 150)], fill="green")

# 文字
font = ImageFont.truetype("arial.ttf", size=48)
draw.text((250, 550), "Hello, Pillow!", fill="white", font=font)

img.save("drawing.png")

第 3 章:像素级操作

# 逐像素访问(慢,适用于小图)
pixels = img.load()
for y in range(img.height):
    for x in range(img.width):
        r, g, b = pixels[x, y]
        # 替换所有接近白色的像素为透明
        if r > 240 and g > 240 and b > 240:
            pixels[x, y] = (0, 0, 0, 0)

# 批量像素操作(快,使用 NumPy)
import numpy as np
arr = np.array(img)
arr[arr > 200] = 255  # 高通阈值
result = Image.fromarray(arr)

第 4 章:批量处理实战

import os
from PIL import Image, ImageOps
from pathlib import Path

def batch_process(input_dir, output_dir, size=(800, 800)):
    """批量缩略图 + 灰度 + 增强对比度"""
    output_dir = Path(output_dir)
    output_dir.mkdir(parents=True, exist_ok=True)

    for filepath in Path(input_dir).glob("*.*"):
        if filepath.suffix.lower() not in {".jpg", ".jpeg", ".png", ".webp"}:
            continue

        with Image.open(filepath) as img:
            # 转换为 RGB(统一格式)
            if img.mode in ("RGBA", "P"):
                img = img.convert("RGB")

            # 缩略图
            img.thumbnail(size)

            # 自动对比度
            img = ImageOps.autocontrast(img)

            # 保存
            out_path = output_dir / f"processed_{filepath.stem}.jpg"
            img.save(out_path, "JPEG", quality=85, optimize=True)
            print(f"✓ {out_path}")

batch_process("./raw_photos", "./processed_photos")

第 5 章:与 Web 框架集成

# Flask 示例:上传并生成头像缩略图
from flask import Flask, request
from PIL import Image
from io import BytesIO

@app.route("/upload-avatar", methods=["POST"])
def upload_avatar():
    file = request.files["avatar"]
    img = Image.open(file.stream)

    # 裁剪为正方形中心
    w, h = img.size
    size = min(w, h)
    left = (w - size) // 2
    top = (h - size) // 2
    img = img.crop((left, top, left + size, top + size))

    # 三档缩略图
    sizes = {"lg": 512, "md": 128, "sm": 64}
    urls = {}
    for name, px in sizes.items():
        thumb = img.resize((px, px), Image.LANCZOS)
        buf = BytesIO()
        thumb.save(buf, "JPEG", quality=85)
        buf.seek(0)
        # 上传到 S3 / 保存到磁盘...
        urls[name] = f"/avatars/{name}_{user_id}.jpg"

    return {"urls": urls}

思考题

  1. Image.thumbnail()Image.resize() 有什么区别?何时用哪个?
  2. 如何在 Pillow 中实现图片的颜色直方图均衡化?
  3. 处理超大图片(>10000x10000)时,Pillow 有哪些内存优化技巧?