Pillow 教程

Pillow 教程

Pillow(PIL Fork)是 Python 最流行的图像处理库,支持图像打开、转换、裁剪、滤镜、绘图、保存等操作,是 Python 图像处理的入门与主力工具。

Pillow 核心能力

  • 图像读写:PNG、JPEG、GIF、BMP、WebP 等格式
  • 图像处理:裁剪、缩放、旋转、翻转、滤镜
  • 绘图:绘制线条、图形、文字(ImageDraw)
  • 像素操作:获取/修改像素,通道处理
  • 格式转换:RGB、RGBA、L(灰度)、P 等模式

安装

pip install Pillow

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

第一个程序

from PIL import Image

# 打开图像
img = Image.open("photo.jpg")
print(img.size, img.mode)   # (1920, 1080) RGB

# 显示
img.show()

# 保存副本
img.save("copy.png")

与 OpenCV 的区别

Pillow: 轻量,图像处理/绘图友好,易上手
OpenCV: 重量级,计算机视觉算法(检测/识别)
两者可配合: Pillow 处理 + OpenCV 分析

Pillow 简介

Pillow 简介

Pillow 是 PIL(Python Imaging Library)的继承者,PIL 停止维护后由 Pillow 继续发展,API 兼容并持续更新。

发展历史

1995  PIL 发布(Fredrik Lundh)
2009  PIL 停止维护
2010  Pillow 分支启动(兼容 PIL)
现在  Pillow 10.x,持续更新

# 安装注意
pip install Pillow
# 不是 pip install PIL(老包名)

能做什么

1. 图片格式转换(jpg/png/webp)
2. 缩略图与尺寸调整
3. 裁剪与拼接
4. 滤镜(模糊/锐化/浮雕)
5. 绘图与文字水印
6. 验证码/图片验证码生成
7. 批量图片处理脚本
8. 图表/海报简单绘制

基本结构

from PIL import Image

img = Image.open("photo.jpg")   # 打开
# 核心对象: Image 图像对象

常用模块:
Image        图像主模块
ImageDraw    绘图
ImageFont    字体
ImageFilter  滤镜
ImageEnhance 增强(亮度/对比度)
ImageOps     操作(翻转/缩放)
ImageChops   通道运算
ImageGrab    屏幕截图
ImageStat    统计

图像模式

1     1 位黑白
L     8 位灰度
P     8 位调色板
RGB   24 位真彩
RGBA  32 位(含透明度)
CMYK  打印
HSV   色相饱和度明度

# 转换
img.convert("L")      # 转灰度
img.convert("RGBA")   # 转 RGBA

Pillow 安装

Pillow 安装

Pillow 安装非常简单,支持 pip 与源码编译。

1. pip 安装

# 基本安装
pip install Pillow

# 指定版本
pip install Pillow==10.2.0

# 更新
pip install -U Pillow

2. 验证安装

python -c "from PIL import Image; print(Image.__version__)"
# 输出如: 10.2.0

# 支持格式查看
python -c "from PIL import features; print(features.get_supported())"

3. 完整安装(全部格式支持)

# Pillow 依赖可选库增强格式支持
# Linux 安装依赖
sudo apt install libjpeg-dev zlib1g-dev libpng-dev
sudo apt install libfreetype6-dev liblcms2-dev libwebp-dev
sudo apt install libtiff5-dev libopenjp2-7-dev

# 编译安装(带全部特性)
pip install Pillow --no-binary Pillow

# 验证特性
python -c "
from PIL import features
for f in features.get_supported():
    print(f)"

4. 常见问题

1. ImportError: No module named PIL
   → 安装 Pillow
2. 格式不支持(如 webp)
   → 安装 webp 依赖后重装
3. 中文乱码 → 用 ImageFont 加载中文字体
4. 权限 → pip 加 --user 或用虚拟环境

Pillow 入门实例

Pillow 入门实例

通过几个小实例快速上手 Pillow 常用操作。

1. 打开、显示、保存

from PIL import Image

img = Image.open("photo.jpg")
print(img.size)          # (1920, 1080)
print(img.mode)          # RGB
print(img.format)        # JPEG

img.show()               # 系统默认查看器
img.save("output.png")   # 格式转换

2. 缩放与裁剪

from PIL import Image

img = Image.open("photo.jpg")

# 缩放到指定尺寸
resized = img.resize((400, 300))
resized.save("resized.jpg")

# 等比缩略图
img.thumbnail((400, 400))   # 保持比例
img.save("thumb.jpg")

# 裁剪 (left, top, right, bottom)
crop = img.crop((100, 100, 500, 400))
crop.save("crop.jpg")

3. 旋转与翻转

img = Image.open("photo.jpg")

# 旋转(扩展画布)
rotated = img.rotate(45, expand=True)

# 90 度旋转
img.rotate(90)

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

# 保存
rotated.save("rotated.jpg")

4. 滤镜与增强

from PIL import Image, ImageFilter, ImageEnhance

img = Image.open("photo.jpg")

# 滤镜
blur = img.filter(ImageFilter.BLUR)          # 模糊
edge = img.filter(ImageFilter.FIND_EDGES)    # 边缘
contour = img.filter(ImageFilter.CONTOUR)    # 轮廓

# 增强
enhancer = ImageEnhance.Brightness(img)
brighter = enhancer.enhance(1.5)             # 提亮

contrast = ImageEnhance.Contrast(img).enhance(2.0)
color = ImageEnhance.Color(img).enhance(1.5)

5. 文字与水印

from PIL import Image, ImageDraw, ImageFont

img = Image.open("photo.jpg")
draw = ImageDraw.Draw(img)

# 文字
font = ImageFont.truetype("arial.ttf", 40)
draw.text((50, 50), "Hello", fill="white", font=font)

# 水印(半透明)
watermark = Image.new("RGBA", img.size, (0, 0, 0, 0))
wd = ImageDraw.Draw(watermark)
font2 = ImageFont.truetype("arial.ttf", 60)
wd.text((img.width-400, img.height-100), "©2026", fill=(255, 255, 255, 128), font=font2)
img = Image.alpha_composite(img.convert("RGBA"), watermark)
img.save("watermark.png")

Pillow 图像基本操作

Pillow 图像基本操作

掌握 Pillow 图像的核心操作:像素访问、通道、裁剪拼接。

1. 像素访问

from PIL import Image

img = Image.open("photo.jpg")

# 获取单个像素
pixel = img.getpixel((100, 50))    # (r, g, b)

# 修改像素
img.putpixel((100, 50), (255, 0, 0))

# 批量获取(性能好)
pixels = img.load()
for y in range(50, 60):
    for x in range(50, 60):
        pixels[x, y] = (0, 255, 0)

# 转为像素数组
pixel_data = list(img.getdata())

2. 通道操作

# 分离通道
r, g, b = img.split()

# 合并
from PIL import Image
img2 = Image.merge("RGB", (r, g, b))

# 单通道灰度
gray = img.convert("L")

# 点运算(亮度)
img.point(lambda i: i * 1.2).show()

# 反色
from PIL import ImageOps
inverted = ImageOps.invert(img.convert("L"))

3. 图像拼接

from PIL import Image

img1 = Image.open("a.jpg")
img2 = Image.open("b.jpg")

# 水平拼接
w = img1.width + img2.width
h = max(img1.height, img2.height)
canvas = Image.new("RGB", (w, h), (255, 255, 255))
canvas.paste(img1, (0, 0))
canvas.paste(img2, (img1.width, 0))
canvas.save("hconcat.jpg")

# 垂直拼接
w = max(img1.width, img2.width)
h = img1.height + img2.height
canvas = Image.new("RGB", (w, h))
canvas.paste(img1, (0, 0))
canvas.paste(img2, (0, img1.height))
canvas.save("vconcat.jpg")

4. 空白图像创建

from PIL import Image

# 纯色
Image.new("RGB", (500, 300), "red")
Image.new("RGB", (500, 300), (0, 128, 255))
Image.new("RGBA", (500, 300), (0, 0, 0, 0))   # 透明
Image.new("L", (200, 200), 128)              # 灰度

# 渐变
w, h = 256, 256
img = Image.new("L", (w, h))
for y in range(h):
    for x in range(w):
        img.putpixel((x, y), x)   # 水平渐变
img.save("gradient.png")

Pillow 绘图中文乱码

Pillow 绘图中文乱码解决

Pillow 默认字体不支持中文,绘制中文会显示乱码或方块,需要加载中文字体。

1. 问题现象

from PIL import Image, ImageDraw, ImageFont

img = Image.new("RGB", (400, 200), "white")
draw = ImageDraw.Draw(img)
font = ImageFont.load_default()   # 默认字体不支持中文
draw.text((50, 50), "你好世界", fill="black", font=font)
img.save("test.png")
# 显示为乱码/方块

2. 加载系统中文字体

from PIL import Image, ImageDraw, ImageFont

# 常见中文字体路径
# Windows: C:/Windows/Fonts/msyh.ttc(微软雅黑)
#          C:/Windows/Fonts/simhei.ttf(黑体)
# macOS:   /System/Library/Fonts/PingFang.ttc
#          /System/Library/Fonts/STHeiti Light.ttc
# Linux:   /usr/share/fonts/truetype/wqy/wqy-zenhei.ttc

font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 40)
draw.text((50, 50), "你好世界", fill="black", font=font)
img.save("chinese.png")   # 正常显示

3. 跨平台字体查找

import os

def find_chinese_font():
    candidates = [
        "C:/Windows/Fonts/msyh.ttc",          # Windows 雅黑
        "C:/Windows/Fonts/simhei.ttf",        # Windows 黑体
        "/System/Library/Fonts/PingFang.ttc", # macOS 苹方
        "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",  # Linux 文泉驿
        "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
    ]
    for path in candidates:
        if os.path.exists(path):
            return path
    return None

font_path = find_chinese_font()
font = ImageFont.truetype(font_path, 40)

4. 文本换行与居中

from PIL import Image, ImageDraw, ImageFont

def draw_multiline(draw, text, x, y, font, fill, max_width):
    lines = []
    current = ""
    for ch in text:
        if draw.textlength(current + ch, font=font) <= max_width:
            current += ch
        else:
            lines.append(current)
            current = ch
    lines.append(current)
    for i, line in enumerate(lines):
        draw.text((x, y + i * font.size), line, fill=fill, font=font)

img = Image.new("RGB", (500, 300), "white")
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(font_path, 24)
draw_multiline(draw, "这是一段很长的中文文本,需要自动换行显示。", 30, 30, font, "black", 440)
img.save("multiline.png")

5. 常见问题

1. 找不到字体文件: 检查路径,或用 find_chinese_font()
2. 字体文件损坏: 重新下载
3. 缩放模糊: 用大字号 + resize
4. 某些 ttc 加载失败: 用 truetype 指定 index
   ImageFont.truetype("msyh.ttc", 40, index=1)

Pillow 常用模块与方法

Pillow 常用模块与方法

Pillow 模块体系概览,常用类与方法速查。

1. Image 模块(核心)

Image.open(path)           打开图像
Image.new(mode, size, color) 新建
Image.merge(mode, bands)   合并通道
Image.composite(a, b, mask) 合成

img.resize((w, h))         缩放
img.thumbnail(size)        缩略图(等比)
img.crop(box)              裁剪
img.rotate(angle)          旋转
img.transpose(method)      翻转
img.filter(filter)         滤镜
img.convert(mode)          模式转换
img.save(path)             保存
img.show()                 显示
img.getpixel / putpixel    像素
img.split()                分离通道
img.paste(image, box)      粘贴
img.copy()                 复制
img.crop / img.resize / ...

2. ImageDraw(绘图)

from PIL import ImageDraw

draw = ImageDraw.Draw(img)

draw.point((x, y), fill)
draw.line([(x1,y1), (x2,y2)], fill, width)
draw.rectangle([x1,y1,x2,y2], fill, outline, width)
draw.ellipse([x1,y1,x2,y2], fill, outline)
draw.polygon([(x1,y1),...], fill)
draw.arc / draw.chord / draw.pieslice
draw.text((x, y), "text", fill, font)
draw.textlength(text, font)    # 文本宽度
draw.multiline_text((x,y), text, fill, font)

3. 其他常用模块

ImageFont:  字体
  ImageFont.truetype(path, size)
  ImageFont.load_default()

ImageFilter: 滤镜
  BLUR / CONTOUR / DETAIL / EDGE_ENHANCE
  EMBOSS / FIND_EDGES / SHARPEN / SMOOTH
  GaussianBlur(radius) / UnsharpMask

ImageEnhance: 增强
  Brightness / Contrast / Color / Sharpness

ImageOps: 操作
  invert / mirror / flip / grayscale
  autocontrast / equalize / expand

ImageChops: 通道运算
  add / subtract / multiply / difference
  screen / overlay / blend

ImageGrab: 截屏
  ImageGrab.grab() / grab(bbox)

ImageStat: 统计
  stat = ImageStat.Stat(img); stat.mean

Pillow 高级图像处理

Pillow 高级图像处理

高级技巧:批量处理、合成、特效与图像分析。

1. 批量处理(缩略图)

from PIL import Image
import os, glob

os.makedirs("thumbs", exist_ok=True)

for path in glob.glob("photos/*.jpg"):
    img = Image.open(path)
    img.thumbnail((200, 200))
    out = os.path.join("thumbs", os.path.basename(path))
    img.save(out)
    print("已处理:", path)

2. 批量加水印

from PIL import Image, ImageDraw, ImageFont

def add_watermark(img_path, out_path, text="©2026"):
    img = Image.open(img_path).convert("RGBA")
    layer = Image.new("RGBA", img.size, (0,0,0,0))
    draw = ImageDraw.Draw(layer)
    font = ImageFont.truetype("msyh.ttc", 50)
    w = draw.textlength(text, font=font)
    x = img.width - w - 30
    y = img.height - 70
    draw.text((x, y), text, fill=(255,255,255,120), font=font)
    result = Image.alpha_composite(img, layer)
    result.convert("RGB").save(out)

import glob
for f in glob.glob("photos/*.jpg"):
    add_watermark(f, "wm_" + os.path.basename(f))

3. 图像合成(带透明)

from PIL import Image

bg = Image.open("background.jpg").convert("RGBA")
logo = Image.open("logo.png").convert("RGBA")

# 缩放 logo
logo.thumbnail((200, 200))

# 粘贴(支持透明)
pos = (bg.width - logo.width - 20, 20)
bg.alpha_composite(logo, pos)
bg.convert("RGB").save("composite.jpg")

4. 图片拼接九宫格

from PIL import Image

# 3x3 网格拼接
tile_w, tile_h = 200, 200
cols, rows = 3, 3
canvas = Image.new("RGB", (cols*tile_w, rows*tile_h), "white")

for i in range(9):
    img = Image.new("RGB", (tile_w, tile_h), (i*25 % 255, 100, 200))
    x = (i % cols) * tile_w
    y = (i // cols) * tile_h
    canvas.paste(img, (x, y))

canvas.save("grid.jpg")

5. 图像统计与分析

from PIL import Image, ImageStat

img = Image.open("photo.jpg").convert("RGB")
stat = ImageStat.Stat(img)

print("均值:", stat.mean)      # 每通道均值
print("标准差:", stat.stddev)
print("极值:", stat.extrema)

# 亮度判断
gray = img.convert("L")
avg_brightness = ImageStat.Stat(gray).mean[0]
print("平均亮度:", avg_brightness)

Pillow Image 模块

Pillow Image 模块

Image 是 Pillow 的核心类,几乎一切操作都围绕它展开。

1. 打开与创建

from PIL import Image

# 打开
img = Image.open("photo.jpg")          # 惰性: 未加载全部
img.load()                             # 强制加载

# 从字节
with open("photo.jpg", "rb") as f:
    data = f.read()
img = Image.open(__import__("io").BytesIO(data))

# 新建
Image.new("RGB", (200, 100), "white")
Image.new("RGB", (200, 100), (255, 0, 0))
Image.new("RGBA", (200, 100))          # 透明黑

2. 属性

img.size        # (宽, 高)
img.width / img.height
img.mode        # RGB / L / RGBA
img.format      # JPEG / PNG
img.info        # 元信息(EXIF 等)
img.filename    # 文件名
img.getbands()  # ('R', 'G', 'B') 通道名

3. 几何变换

# 缩放
img.resize((800, 600))
img.resize((400, 300), Image.LANCZOS)   # 高质量

# 缩略图(等比)
img.thumbnail((300, 300))

# 裁剪
img.crop((left, top, right, bottom))

# 旋转
img.rotate(45)                    # 裁剪边缘
img.rotate(45, expand=True)       # 保留全部
img.rotate(90, resample=Image.BICUBIC)

# 翻转
img.transpose(Image.FLIP_LEFT_RIGHT)
img.transpose(Image.FLIP_TOP_BOTTOM)
img.transpose(Image.ROTATE_90)

# 平移(transform)
img.transform((w, h), Image.AFFINE, (1, 0, 10, 0, 1, 20))

4. 颜色与模式

img.convert("L")         # 灰度
img.convert("RGBA")      # 加透明通道
img.convert("P")         # 调色板
img.convert("1")         # 黑白二值

# 获取调色板
pal = img.convert("P").getpalette()

# 透明处理
img.putalpha(128)        # 整体半透明

5. 保存与格式

img.save("out.jpg", quality=85)       # JPEG 质量
img.save("out.png")
img.save("out.webp", quality=85)
img.save("out.gif")

# 参数
img.save("out.jpg", optimize=True)    # 优化
img.save("out.png", compress_level=9)
img.save("out.gif", save_all=True, append_images=[f2, f3])  # 动图

Pillow ImageDraw 模块

Pillow ImageDraw 模块

ImageDraw 提供矢量绘图能力:点、线、矩形、圆、多边形与文字。

1. 基础图形

from PIL import Image, ImageDraw

img = Image.new("RGB", (400, 400), "white")
draw = ImageDraw.Draw(img)

# 点
draw.point((200, 200), fill="black")

# 线
draw.line([(10, 10), (390, 10)], fill="red", width=3)

# 矩形
draw.rectangle([50, 50, 150, 150], fill="blue", outline="black", width=2)

# 圆/椭圆
draw.ellipse([200, 50, 350, 200], fill="green", outline="black")

# 圆角矩形
draw.rounded_rectangle([50, 220, 200, 350], radius=20, fill="orange")

# 多边形
draw.polygon([(250, 220), (350, 220), (300, 350)], fill="purple")

# 弧线/扇形
draw.arc([50, 50, 200, 200], start=0, end=180, fill="black", width=3)
draw.pieslice([250, 250, 390, 390], start=90, end=180, fill="cyan")

img.save("shapes.png")

2. 文字绘制

from PIL import Image, ImageDraw, ImageFont

img = Image.new("RGB", (500, 200), "white")
draw = ImageDraw.Draw(img)

font = ImageFont.truetype("msyh.ttc", 36)
draw.text((20, 20), "Hello Pillow", fill="black", font=font)

# 居中
text = "居中文字"
w = draw.textlength(text, font=font)
x = (500 - w) / 2
y = 100
draw.text((x, y), text, fill="red", font=font)

# 多行
draw.multiline_text((20, 150), "第一行\n第二行", fill="blue", font=font, spacing=10)

img.save("text.png")

3. 绘制验证码

import random
from PIL import Image, ImageDraw, ImageFont

def gen_captcha(text="AB12"):
    img = Image.new("RGB", (150, 60), (245, 245, 245))
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype("msyh.ttc", 36)

    # 背景干扰线
    for _ in range(5):
        draw.line([(random.randint(0,150), random.randint(0,60)),
                   (random.randint(0,150), random.randint(0,60))],
                  fill=(random.randint(150,255),)*3, width=1)

    # 字符
    x = 15
    for ch in text:
        color = (random.randint(0,100), random.randint(0,100), random.randint(0,100))
        draw.text((x, 10), ch, fill=color, font=font)
        x += 35

    # 噪点
    for _ in range(100):
        draw.point((random.randint(0,150), random.randint(0,60)),
                   fill=(random.randint(0,255),)*3)
    return img

gen_captcha("A8K2").save("captcha.png")

4. 坐标辅助

draw.textlength(text, font)     # 文本宽度
draw.textbbox((x,y), text, font=font)  # 文本边界框
draw.multiline_textbbox(...)

# 圆心椭圆
def draw_circle(draw, center, r, fill):
    cx, cy = center
    draw.ellipse([cx-r, cy-r, cx+r, cy+r], fill=fill)

draw_circle(draw, (200, 200), 50, "red")

Pillow ImageFont 模块

Pillow ImageFont 模块

ImageFont 管理字体,用于绘制文字时指定字体样式。

1. 加载字体

from PIL import ImageFont

# 默认字体
font = ImageFont.load_default()

# TTF/TTC 字体
font = ImageFont.truetype("arial.ttf", 48)
font = ImageFont.truetype("msyh.ttc", 36)

# 从字节加载
with open("font.ttf", "rb") as f:
    font = ImageFont.truetype(__import__("io").BytesIO(f.read()), 40)

# 获取可用系统字体
from PIL import ImageFont
print(ImageFont.getsize?) # 见下
# 或列出目录
import os
os.listdir("C:/Windows/Fonts/")

2. 字体属性与方法

font.size             # 字号
font.getname()        # 字体名
font.getmetrics()     # (ascent, descent)
font.getlength("text")  # 文本宽度
font.getbbox("text")  # 边界框

# 测量文本
from PIL import ImageDraw
draw = ImageDraw.Draw(img)
w = draw.textlength("Hello", font=font)
h = font.size

# 计算文本框用于布局
text = "示例文字"
bbox = font.getbbox(text)
text_w = bbox[2] - bbox[0]
text_h = bbox[3] - bbox[1]

3. 中文字体

# 常见中文字体
# Windows
微软雅黑: msyh.ttc / msyhbd.ttc(粗)
黑体:     simhei.ttf
宋体:     simsun.ttc
楷体:     simkai.ttf

# macOS
苹方: PingFang.ttc
冬青黑体: Hiragino Sans GB.ttc
宋体: STSong.ttc

# Linux
文泉驿: wqy-zenhei.ttc
Noto:  NotoSansCJK-Regular.ttc

# 加载
font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 32)

4. 文字阴影与描边

from PIL import Image, ImageDraw, ImageFont

img = Image.new("RGB", (400, 150), "white")
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("msyh.ttc", 48)

# 阴影
draw.text((35, 35), "阴影文字", fill="gray", font=font)
draw.text((30, 30), "阴影文字", fill="black", font=font)

# 描边
draw.text((230, 30), "描边文字", fill="blue",
          font=font, stroke_width=2, stroke_fill="black")

img.save("style_text.png")

5. 常见问题

1. OSError: cannot open resource: 字体路径错误
2. 中文乱码: 用支持中文的字体
3. ttc 多字体: truetype(path, size, index=n)
4. 字体版权: 商用注意授权
5. 部署服务器: 打包字体文件

Pillow ImageFilter 模块

Pillow ImageFilter 模块

ImageFilter 提供丰富的图像滤镜:模糊、锐化、边缘检测、浮雕等。

1. 内置滤镜

from PIL import Image, ImageFilter

img = Image.open("photo.jpg")

# 基础滤镜
img.filter(ImageFilter.BLUR)          # 模糊
img.filter(ImageFilter.SMOOTH)        # 平滑
img.filter(ImageFilter.SHARPEN)       # 锐化
img.filter(ImageFilter.CONTOUR)       # 轮廓
img.filter(ImageFilter.EDGE_ENHANCE)  # 边缘增强
img.filter(ImageFilter.EMBOSS)        # 浮雕
img.filter(ImageFilter.FIND_EDGES)    # 查找边缘
img.filter(ImageFilter.DETAIL)        # 细节增强

# 保存示例
img.filter(ImageFilter.FIND_EDGES).save("edges.png")

2. 高斯模糊与参数

# 高斯模糊(可调半径)
gauss = img.filter(ImageFilter.GaussianBlur(radius=5))
gauss.save("gauss5.png")

# 半径越大越模糊
img.filter(ImageFilter.GaussianBlur(1))
img.filter(ImageFilter.GaussianBlur(10))

3. 锐化与细节

# UnsharpMask 锐化(精细控制)
sharp = img.filter(ImageFilter.UnsharpMask(
    radius=2,      # 半径
    percent=150,   # 强度
    threshold=3    # 阈值
))

# 普通锐化
img.filter(ImageFilter.SHARPEN)

4. 模糊特效应用

# 背景模糊(证件照/头像效果)
from PIL import Image, ImageFilter

img = Image.open("photo.jpg")
# 整图模糊
bg = img.filter(ImageFilter.GaussianBlur(20))

# 中心清晰(简单版本)
bg.paste(img.crop((100, 100, 500, 500)), (100, 100))
bg.save("blur_bg.jpg")

5. 自定义卷积核

from PIL import ImageFilter

# 自定义 3x3 卷积核
# 锐化核
sharp_kernel = ImageFilter.Kernel((3, 3),
    (0, -1, 0,
     -1, 5, -1,
     0, -1, 0), scale=1)

img.filter(sharp_kernel).save("custom_sharp.png")

# 边缘检测核
edge_kernel = ImageFilter.Kernel((3, 3),
    (-1, -1, -1,
     -1, 8, -1,
     -1, -1, -1), scale=1, offset=128)

6. 组合滤镜

# 先锐化再浮雕等
from PIL import ImageFilter

# 处理链
result = (img
    .filter(ImageFilter.SHARPEN)
    .filter(ImageFilter.CONTOUR))

# 多次高斯(模拟弥散)
result = img
for _ in range(3):
    result = result.filter(ImageFilter.GaussianBlur(3))

Pillow ImageEnhance 模块

Pillow ImageEnhance 模块

ImageEnhance 用于调节图像的亮度、对比度、色彩饱和度与锐度。

1. 亮度调节

from PIL import Image, ImageEnhance

img = Image.open("photo.jpg")

enhancer = ImageEnhance.Brightness(img)

brighter = enhancer.enhance(1.5)   # 变亮(>1)
darker = enhancer.enhance(0.5)     # 变暗(<1)
normal = enhancer.enhance(1.0)     # 不变

brighter.save("brighter.jpg")

2. 对比度

enhancer = ImageEnhance.Contrast(img)

high = enhancer.enhance(2.0)   # 高对比
low = enhancer.enhance(0.5)    # 低对比

high.save("high_contrast.jpg")

3. 色彩饱和度

enhancer = ImageEnhance.Color(img)

vivid = enhancer.enhance(2.0)    # 鲜艳
grayish = enhancer.enhance(0.0)  # 变灰(去色)
mild = enhancer.enhance(0.5)

vivid.save("vivid.jpg")

4. 锐度

enhancer = ImageEnhance.Sharpness(img)

sharp = enhancer.enhance(3.0)    # 更锐利
soft = enhancer.enhance(0.5)     # 柔化
blur = enhancer.enhance(0.0)     # 完全模糊

sharp.save("sharp.jpg")

5. 综合调色(一键美化)

from PIL import Image, ImageEnhance

def beautify(img, brightness=1.1, contrast=1.2,
             color=1.3, sharpness=1.5):
    img = ImageEnhance.Brightness(img).enhance(brightness)
    img = ImageEnhance.Contrast(img).enhance(contrast)
    img = ImageEnhance.Color(img).enhance(color)
    img = ImageEnhance.Sharpness(img).enhance(sharpness)
    return img

img = Image.open("photo.jpg")
beautify(img).save("beautified.jpg")

6. 批量处理

import glob, os
from PIL import Image, ImageEnhance

os.makedirs("enhanced", exist_ok=True)

for path in glob.glob("photos/*.jpg"):
    img = Image.open(path)
    img = ImageEnhance.Brightness(img).enhance(1.1)
    img = ImageEnhance.Contrast(img).enhance(1.1)
    out = os.path.join("enhanced", os.path.basename(path))
    img.save(out, quality=90)

Pillow ImageOps 模块

Pillow ImageOps 模块

ImageOps 提供常用的图像操作:翻转、缩放、自动对比度、均衡化等。

1. 翻转与镜像

from PIL import Image, ImageOps

img = Image.open("photo.jpg")

# 水平镜像
mirror = ImageOps.mirror(img)
# 垂直翻转
flip = ImageOps.flip(img)

mirror.save("mirror.jpg")

2. 灰度与反色

# 灰度(等效 convert("L"))
gray = ImageOps.grayscale(img)

# 反色(负片效果)
inverted = ImageOps.invert(gray)
inverted_color = ImageOps.invert(img.convert("RGB"))

inverted.save("negative.jpg")

3. 自动对比度与均衡化

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

# 直方图均衡化(增强暗图)
equalized = ImageOps.equalize(img)

auto.save("autocontrast.jpg")
equalized.save("equalized.jpg")

4. 缩放与填充

# 精确尺寸(可能变形)
resized = ImageOps.fit(img, (200, 200))
# 等比居中裁剪适配
fit = ImageOps.fit(img, (400, 300), centering=(0.5, 0.5))

# 扩展画布(加边)
expanded = ImageOps.expand(img, border=20, fill="black")

fit.save("fit.jpg")

5. 添加边框

from PIL import ImageOps

# 简单边框
img2 = ImageOps.expand(img, border=10, fill="white")

# 渐变边框效果(多层)
current = img
for i in range(3):
    current = ImageOps.expand(current, border=5,
                              fill=(200-i*40, 200-i*40, 200-i*40))
current.save("frame.jpg")

6. 其他操作

# 色彩通道
ImageOps.grayscale(img)

# 调色板处理
ImageOps.posterize(img, bits=3)    # 色调分离(海报化)

# solarize 负感
ImageOps.solarize(img, threshold=128)

# 示例: 海报化
poster = ImageOps.posterize(img, 4)
poster.save("poster.jpg")

Pillow ImageChops 模块

Pillow ImageChops 模块

ImageChops 提供图像通道级运算:相加、相减、混合、差异检测等。

1. 算术运算

from PIL import Image, ImageChops

img1 = Image.open("a.jpg")
img2 = Image.open("b.jpg")

# 相加(提亮)
add = ImageChops.add(img1, img2)
add = ImageChops.add(img1, 50)      # 加常数

# 相减
sub = ImageChops.subtract(img1, img2)

# 乘法
mult = ImageChops.multiply(img1, img2)

# 屏幕(变亮)
screen = ImageChops.screen(img1, img2)

add.save("add.jpg")

2. 差异检测

from PIL import Image, ImageChops, ImageStat

img1 = Image.open("v1.jpg")
img2 = Image.open("v2.jpg")

# 差异
diff = ImageChops.difference(img1, img2)
diff.save("diff.jpg")

# 差异统计
stat = ImageStat.Stat(diff)
print("平均差异:", stat.mean)

# 差异放大
diff_enhanced = ImageChops.add(diff, diff)
diff_enhanced = ImageChops.multiply(diff, 5)

3. 混合与叠加

# 叠加(overlay)
overlay = ImageChops.overlay(img1, img2)

# 柔光
soft = ImageChops.soft_light(img1, img2)

# 硬光
hard = ImageChops.hard_light(img1, img2)

# 线性混合
blend = Image.blend(img1, img2, alpha=0.5)

4. 其他操作

# 取亮/取暗
light = ImageChops.lighter(img1, img2)
dark = ImageChops.darker(img1, img2)

# 偏移
offset = ImageChops.offset(img1, 50, 30)

# 常数填充
const = ImageChops.constant(img1, 128)

# 逻辑运算(黑白图)
logical = ImageChops.logical_and(img1.convert("1"), img2.convert("1"))

5. 实际应用: 水印检测

from PIL import Image, ImageChops, ImageStat

def detect_difference(before, after, threshold=10):
    """比较两张图片是否变化"""
    b = Image.open(before).convert("RGB")
    a = Image.open(after).convert("RGB")
    diff = ImageChops.difference(b, a)
    stat = ImageStat.Stat(diff)
    avg_diff = sum(stat.mean) / 3
    return avg_diff > threshold, avg_diff

changed, diff = detect_difference("before.jpg", "after.jpg")
print(f"发生变化: {changed}, 差异: {diff:.2f}")

6. 注意事项

1. 图像尺寸需一致(或先 resize)
2. 模式建议统一(RGB)
3. 结果可能超出范围,注意截断
4. add/subtract 有 scale 与 offset 参数
5. 性能: 大图运算较慢,先缩略

Pillow ImageColor 模块

Pillow ImageColor 模块

ImageColor 用于解析各种颜色格式字符串,转换为 RGB/RGBA 值。

1. 颜色字符串解析

from PIL import ImageColor

# 颜色名
ImageColor.getrgb("red")          # (255, 0, 0)
ImageColor.getrgb("white")        # (255, 255, 255)
ImageColor.getrgb("black")        # (0, 0, 0)

# 十六进制
ImageColor.getrgb("#ff0000")      # (255, 0, 0)
ImageColor.getrgb("#f00")         # 简写 (255, 0, 0)
ImageColor.getrgb("#ff000080")    # 带透明度

# rgb() 格式
ImageColor.getrgb("rgb(255, 0, 0)")
ImageColor.getrgb("rgb(100%, 0%, 0%)")

# 百分比/HSL
ImageColor.getrgb("hsl(0, 100%, 50%)")   # (255, 0, 0)

2. 获取 RGBA

from PIL import ImageColor

# 带透明度
ImageColor.getcolor("red", "RGBA")        # (255, 0, 0, 255)
ImageColor.getcolor("#ff000080", "RGBA")  # (255, 0, 0, 128)
ImageColor.getcolor("white", "RGB")

# 常用: 得到元组用于绘图
from PIL import Image, ImageDraw
img = Image.new("RGB", (100, 100))
draw = ImageDraw.Draw(img)
draw.rectangle([0, 0, 100, 100], fill=ImageColor.getrgb("steelblue"))

3. 颜色名列表

# 标准颜色名(W3C)
from PIL import ImageColor
names = ImageColor.colormap.keys()
print(len(names))          # 148 个
print(list(names)[:10])

# 常用
# 基本: red green blue black white gray
# 系统: orange purple brown pink gold
# 扩展: steelblue tomato gold skyblue
# 安全色: #fff #000 #f00 ...

4. 实际应用

from PIL import Image, ImageDraw, ImageColor

# 从配置字符串解析颜色
config = {
    "bg": "linear-gradient"  # 简化示例
}
bg = ImageColor.getrgb("#f5f5f5")
text_color = ImageColor.getrgb("rgb(50, 50, 50)")

img = Image.new("RGB", (400, 200), bg)
draw = ImageDraw.Draw(img)
draw.text((20, 80), "Hello", fill=text_color)
img.save("color_app.png")

5. 颜色转换工具

# 转十六进制
def to_hex(rgb):
    return "#{:02x}{:02x}{:02x}".format(*rgb)

to_hex((255, 0, 0))    # '#ff0000'

# 解析 + 转格式
rgb = ImageColor.getrgb("steelblue")
print(to_hex(rgb))

Pillow ImageGrab 模块

Pillow ImageGrab 模块

ImageGrab 用于屏幕截图,可以截取全屏或指定区域。

1. 全屏截图

from PIL import ImageGrab

# 全屏
img = ImageGrab.grab()
img.save("fullscreen.png")

# 抓取指定区域 (left, top, right, bottom)
region = ImageGrab.grab(bbox=(100, 100, 800, 600))
region.save("region.png")

2. 定时截图

import time
from PIL import ImageGrab

def delayed_screenshot(seconds=3, path="shot.png"):
    print(f"{seconds} 秒后截图...")
    time.sleep(seconds)
    img = ImageGrab.grab()
    img.save(path)
    print("已保存:", path)

delayed_screenshot(5)

3. 连续截图(录屏基础)

from PIL import ImageGrab
import time, os

os.makedirs("frames", exist_ok=True)

for i in range(10):   # 截 10 帧
    img = ImageGrab.grab()
    img.save(f"frames/frame_{i:03d}.png")
    time.sleep(1)
    print(f"帧 {i}")

# 合并为 GIF
from PIL import Image
frames = [Image.open(f"frames/f_{i:03d}.png") for i in range(10)]
frames[0].save("screencast.gif",
               save_all=True, append_images=frames[1:],
               duration=1000, loop=0)

4. 窗口/区域截图

from PIL import ImageGrab

# 多显示器(Windows)
all_screens = ImageGrab.grab(all_screens=True)

# 获取鼠标位置截图(区域跟随)
import pyautogui  # 需要安装
# x, y = pyautogui.position()
# ImageGrab.grab(bbox=(x-100, y-100, x+100, y+100))

5. 与图像处理结合

from PIL import ImageGrab, ImageEnhance

# 截图 + 增强
img = ImageGrab.grab()
enhanced = ImageEnhance.Sharpness(img).enhance(1.5)

# 截图 + OCR 前的预处理
img = ImageGrab.grab()
gray = img.convert("L")
# 二值化
gray = gray.point(lambda x: 255 if x > 128 else 0)
gray.save("ocr_input.png")

# 截图查找文字(配合 OCR)
# pip install pytesseract
# import pytesseract
# text = pytesseract.image_to_string(img)

6. 注意事项

1. Linux 需要 xgrab 依赖: sudo apt install x11-apps
2. 远程桌面/无头环境不支持
3. 截图速度: 大屏较慢,可降分辨率
4. 权限: macOS 需要屏幕录制权限
5. 隐私: 注意截图内容合规

Pillow ImageStat 模块

Pillow ImageStat 模块

ImageStat 计算图像的统计信息:均值、方差、极值等,用于图像分析。

1. 基本统计

from PIL import Image, ImageStat

img = Image.open("photo.jpg")
stat = ImageStat.Stat(img)

print("均值:", stat.mean)        # 每通道平均
print("标准差:", stat.stddev)
print("中位数:", stat.median)
print("极值:", stat.extrema)     # (min, max)
print("和:", stat.sum)
print("平方和:", stat.sum2)

# RGB 示例
# mean: [123.4, 110.2, 98.7]  # R G B 平均

2. 亮度与对比度分析

from PIL import Image, ImageStat

def analyze(img):
    gray = img.convert("L")
    stat = ImageStat.Stat(gray)

    brightness = stat.mean[0]     # 0-255
    contrast = stat.stddev[0]     # 越大对比越强

    if brightness < 85:
        level = "偏暗"
    elif brightness > 170:
        level = "偏亮"
    else:
        level = "适中"
    return brightness, contrast, level

img = Image.open("photo.jpg")
b, c, level = analyze(img)
print(f"亮度 {b:.0f}({level}), 对比度 {c:.0f}")

3. 图像质量检测

from PIL import Image, ImageStat

def check_blur(img):
    """模糊检测: 边缘强度低=可能模糊"""
    from PIL import ImageFilter
    edges = img.convert("L").filter(ImageFilter.FIND_EDGES)
    stat = ImageStat.Stat(edges)
    return stat.stddev[0]   # 越小越模糊

img = Image.open("photo.jpg")
blur_score = check_blur(img)
print("模糊分数:", blur_score)
print("可能模糊" if blur_score < 20 else "清晰")

4. 颜色分析

from PIL import Image, ImageStat

img = Image.open("photo.jpg").convert("RGB")
stat = ImageStat.Stat(img)

r, g, b = stat.mean
print(f"R: {r:.0f} G: {g:.0f} B: {b:.0f}")

# 冷暖判断
if r > b:
    print("偏暖色调")
else:
    print("偏冷色调")

# 饱和度
from PIL import ImageEnhance
sat_stat = ImageStat.Stat(ImageEnhance.Color(img).enhance(1.0))

5. 批量统计

import glob
from PIL import Image, ImageStat

for path in glob.glob("photos/*.jpg"):
    img = Image.open(path).convert("L")
    stat = ImageStat.Stat(img)
    print(f"{path}: 亮度 {stat.mean[0]:.0f}, "
          f"对比 {stat.stddev[0]:.1f}")

Pillow ImagePalette 模块

Pillow ImagePalette 模块

ImagePalette 管理调色板(P 模式图像的颜色表)。

1. 什么是调色板

P 模式: 每个像素存索引,颜色在调色板中查
优点: 文件小(256 色),适合 GIF 等

# 查看调色板
from PIL import Image

img = Image.open("gif_image.gif")
print(img.mode)          # P
palette = img.getpalette()
print(len(palette))      # 768(256色×3)
print(palette[:9])       # 前 3 个颜色

2. 创建与设置调色板

from PIL import Image

# 创建 P 模式图像
img = Image.new("P", (100, 100))

# 设置调色板(768 长度 RGB 序列)
palette = []
# 渐变调色板
for i in range(256):
    palette += [i, 255 - i, 128]   # R,G,B
img.putpalette(palette)

# 或设置单个索引颜色
img.putpalette([255, 0, 0], rawmode="RGB", start=0)  # 索引0=红

3. RGB 转 P(量化)

from PIL import Image

img = Image.open("photo.jpg")

# 转调色板模式(自动量化)
p_img = img.convert("P")
p_img.save("quantized.png")

# 指定调色板大小(颜色数)
p_img = img.quantize(colors=64)
p_img.save("quant64.png")

# 获取实际使用的颜色
used = p_img.getcolors(maxcolors=256)
print(len(used))       # 使用颜色数

4. 调色板应用

from PIL import Image

# 转 P 再转回 RGB(降色)
img = Image.open("photo.jpg")
quantized = img.quantize(32).convert("RGB")
quantized.save("art32.png")

# 获取像素索引
p_img = img.convert("P")
pixel_index = p_img.getpixel((10, 10))
print("调色板索引:", pixel_index)

# 取对应颜色
palette = p_img.getpalette()
idx = pixel_index * 3
color = palette[idx:idx+3]
print("颜色:", color)

5. GIF 支持

from PIL import Image

# GIF 读取(每帧)
gif = Image.open("anim.gif")
print(gif.mode)          # P
print(gif.n_frames)      # 帧数

# 遍历帧
for i in range(gif.n_frames):
    gif.seek(i)
    frame = gif.convert("RGB")
    frame.save(f"frame_{i}.png")

# 保存 GIF(需 P 模式)
frames = [Image.open(f"frame_{i}.png").convert("P") for i in range(3)]
frames[0].save("out.gif", save_all=True, append_images=frames[1:],
               duration=200, loop=0)

Pillow ImagePath 模块

Pillow ImagePath 模块

ImagePath 用于创建与操作路径(点序列),与 ImageDraw 配合绘制复杂图形。

1. 创建路径

from PIL import ImagePath

# 从点序列创建
path = ImagePath.Path([(0, 0), (100, 0), (100, 100), (0, 100)])

# 从字符串(可含曲线命令)
path2 = ImagePath.Path("M 0 0 L 100 0 L 100 100 Z")

# 属性
print(path.points)       # 点序列
print(len(path))         # 点数

2. 路径操作

from PIL import ImagePath

path = ImagePath.Path([(0, 0), (100, 0), (100, 100)])

# 变换
scaled = path.transform((2, 0, 0, 0, 2, 0))   # 缩放 2 倍
translated = path.transform((1, 0, 50, 0, 1, 50))  # 平移

# 转列表
pts = list(path)

# 边界框
print(path.getbbox())    # (0, 0, 100, 100)

# 压缩/近似
simplified = path.tolist(flat=0)

3. 与 ImageDraw 配合

from PIL import Image, ImageDraw, ImagePath

img = Image.new("RGB", (400, 400), "white")
draw = ImageDraw.Draw(img)

# 绘制路径
path = ImagePath.Path([(50, 50), (200, 50), (200, 150)])
draw.line(path.points, fill="red", width=3)

# 多边形
poly = ImagePath.Path([(50, 200), (150, 200), (100, 300)])
draw.polygon(poly.points, fill="blue")

# 描边多边形
draw.line(poly.points + [poly.points[0]], fill="black", width=2)

img.save("path_draw.png")

4. 应用场景

1. 绘制不规则形状(地图区域)
2. 路径动画帧
3. 多边形裁剪区域
4. 数据可视化连线

# 简单图表连线
from PIL import Image, ImageDraw, ImagePath

data = [(i*40, 300 - v*3) for i, v in enumerate([10, 30, 20, 50, 40, 70])]
img = Image.new("RGB", (300, 350), "white")
draw = ImageDraw.Draw(img)
path = ImagePath.Path(data)
draw.line(path.points, fill="blue", width=2)
# 数据点
for x, y in path.points:
    draw.ellipse([x-3, y-3, x+3, y+3], fill="red")
img.save("line_chart.png")

ImageSequence 模块

ImageSequence 模块

ImageSequence 用于处理序列图像(多帧图像,如 GIF、TIFF、APNG)。

1. 遍历帧

from PIL import Image, ImageSequence

gif = Image.open("animation.gif")

# 遍历所有帧
for frame in ImageSequence.Iterator(gif):
    # frame 是图像对象,可处理
    print(frame.size, frame.seek)   # seek 是帧索引

# 保存所有帧
for i, frame in enumerate(ImageSequence.Iterator(gif)):
    frame.convert("RGB").save(f"frame_{i:02d}.png")

2. 帧处理

from PIL import Image, ImageSequence, ImageFilter

gif = Image.open("animation.gif")

# 逐帧处理(如加模糊)后重新合成
frames = []
for frame in ImageSequence.Iterator(gif):
    f = frame.convert("RGB").filter(ImageFilter.GaussianBlur(1))
    frames.append(f)

frames[0].save("blur_anim.gif",
               save_all=True,
               append_images=frames[1:],
               duration=gif.info.get("duration", 100),
               loop=0)

3. 常用操作

# 查看帧数
from PIL import Image
img = Image.open("anim.gif")
print(getattr(img, "n_frames", 1))

# 跳转帧
img.seek(5)
print(img.tell())    # 当前帧

# 第一帧作为封面
img.seek(0)
img.convert("RGB").save("cover.png")

# 取最后一帧
img.seek(img.n_frames - 1)
img.convert("RGB").save("last.png")

4. 创建动图

from PIL import Image
import math

# 生成简单动画帧
frames = []
for i in range(10):
    img = Image.new("RGB", (200, 200), "white")
    from PIL import ImageDraw
    draw = ImageDraw.Draw(img)
    r = 20 + i * 8
    draw.ellipse([100-r, 100-r, 100+r, 100+r], fill="blue")
    frames.append(img)

# 保存为 GIF
frames[0].save("growing.gif",
               save_all=True,
               append_images=frames[1:],
               duration=100, loop=0)

print("动图已创建")

5. 性能与注意

1. 大 GIF 帧数多,内存占用大
2. 处理大量帧用流式(逐帧处理)
3. duration 单位毫秒
4. GIF 调色板限制 256 色
5. 透明帧处理需注意 disposal 模式

Pillow ImageMorph 模块

Pillow ImageMorph 模块

ImageMorph 实现形态学操作(基于像素邻域模式匹配),用于二值图像处理。

1. 基本概念

形态学操作: 基于形状的处理(腐蚀/膨胀/骨架化)
Pillow ImageMorph: 通过模式匹配实现

适用: 二值图像(黑白)、线条处理

# 创建形态学操作器
from PIL import ImageMorph

lut = ImageMorph.LutBuilder2().build_lut()
morph = ImageMorph.MorphOp(lut=lut)

2. 使用示例

from PIL import Image, ImageMorph

img = Image.open("binary.png").convert("L")

# 创建操作
builder = ImageMorph.LutBuilder2()
# 模式: 十字形(可选)
builder.setOpName("dilation4")
lut = builder.build_lut()
morph = ImageMorph.MorphOp(lut=lut)

# 应用
result, mesh = morph.apply(img)
result.save("morphed.png")

# 或直接
morph = ImageMorph.MorphOp()
result, mesh = morph.apply(img)

3. 腐蚀与膨胀

from PIL import Image, ImageMorph

img = Image.open("binary.png").convert("L")

# 膨胀(扩大白色区域)
b = ImageMorph.LutBuilder2()
b.setOpName("dilation4")     # 4 邻域膨胀
morph = ImageMorph.MorphOp(lut=b.build_lut())
dilated, _ = morph.apply(img)

# 腐蚀(缩小白色区域)
b2 = ImageMorph.LutBuilder2()
b2.setOpName("erosion4")
morph2 = ImageMorph.MorphOp(lut=b2.build_lut())
eroded, _ = morph2.apply(img)

dilated.save("dilated.png")
eroded.save("eroded.png")

4. 可用操作名

# LutBuilder2 内置操作
dilation4 / dilation8    膨胀
erosion4 / erosion8      腐蚀

# 骨架化等需要自定义模式
# setOpName 不匹配时用 setOp 自定义

# 查看支持
b = ImageMorph.LutBuilder2()
print(b.getOpNames())   # 列出可用操作

5. 应用场景

1. 文字图像清理(去毛刺)
2. 线条骨架提取
3. 区域填充与分离
4. 图像预处理(配合 OCR)

# 简单处理管线
img = Image.open("text.png").convert("L")
# 二值化
img = img.point(lambda x: 255 if x > 128 else 0)
# 腐蚀去噪
b = ImageMorph.LutBuilder2()
b.setOpName("erosion4")
result, _ = ImageMorph.MorphOp(lut=b.build_lut()).apply(img)
result.save("clean_text.png")

6. 注意事项

1. 仅支持 1 位/灰度二值图像
2. 操作基于 3x3 邻域
3. 比 OpenCV 形态学功能弱,复杂场景用 OpenCV
4. 结果需查看是否符合预期

Pillow ImageShow 模块

Pillow ImageShow 模块

ImageShow 提供跨平台的图像显示支持,调用系统默认查看器。

1. 基本显示

from PIL import Image, ImageShow

img = Image.open("photo.jpg")

# 显示(系统默认查看器)
img.show()
# 等价
ImageShow.show(img)

# 指定查看器
ImageShow.show(img, "xv")   # Unix

2. 查看器注册

from PIL import ImageShow

# 查看支持的命令
print(ImageShow.Viewer)
# 查看器列表
print([v.name for v in ImageShow._viewers])

# 自定义查看器
class MyViewer(ImageShow.Viewer):
    format = "PNG"
    def get_command(self, file, **options):
        return f"myviewer {file}"

ImageShow.register(MyViewer(), 1)   # 注册,位置 1

3. 显示已保存文件

from PIL import ImageShow

# 保存后显示
img.save("out.png")
ImageShow.show("out.png")   # 传路径

# 或
ImageShow.show(img)   # 内部临时保存后显示

4. 无头环境处理

# 服务器无 GUI 时 show 无效果
# 替代方案:
img.save("out.png")              # 保存文件
# 或转 base64(Web 显示)
import io, base64
buf = io.BytesIO()
img.save(buf, format="PNG")
b64 = base64.b64encode(buf.getvalue()).decode()
print(f"data:image/png;base64,{b64[:50]}...")

5. 与 Jupyter 配合

# Jupyter Notebook 显示
from PIL import Image
img = Image.open("photo.jpg")
img.show()   # 或直接显示对象

# 内嵌显示
from IPython.display import display
display(img)

Pillow ImageTk 模块

Pillow ImageTk 模块

ImageTk 让 Pillow 图像与 Tkinter GUI 无缝集成,是 Tk 界面显示图片的桥梁。

1. 基本用法

import tkinter as tk
from PIL import Image, ImageTk

# 创建窗口
root = tk.Tk()
root.title("Pillow + Tkinter")

# 打开并转换
img = Image.open("photo.jpg")
photo = ImageTk.PhotoImage(img)

# 显示
label = tk.Label(root, image=photo)
label.pack()

root.mainloop()

2. 缩放显示

import tkinter as tk
from PIL import Image, ImageTk

root = tk.Tk()

img = Image.open("photo.jpg")
img.thumbnail((400, 300))     # 缩放到窗口大小
photo = ImageTk.PhotoImage(img)

label = tk.Label(root, image=photo)
label.pack()
root.mainloop()

3. 动态更新图像

import tkinter as tk
from PIL import Image, ImageTk, ImageEnhance

root = tk.Tk()

img = Image.open("photo.jpg")
photo = ImageTk.PhotoImage(img)
label = tk.Label(root, image=photo)
label.pack()

def brighten():
    global photo
    img2 = ImageEnhance.Brightness(img).enhance(1.2)
    photo = ImageTk.PhotoImage(img2)
    label.config(image=photo)

btn = tk.Button(root, text="提亮", command=brighten)
btn.pack()

root.mainloop()

4. 注意事项

1. PhotoImage 对象需保持引用(全局/属性)
   否则被垃圾回收,图像消失
2. 每次更新图像创建新的 PhotoImage
3. 大图先缩略,避免卡顿
4. 与 canvas 结合: canvas.create_image(x, y, image=photo)

Pillow ImageQt 模块

Pillow ImageQt 模块

ImageQt 提供 Pillow 与 Qt(PyQt/PySide)的互转,方便在 Qt 界面显示图像。

1. Pillow → QImage/QPixmap

# 安装 PyQt
# pip install PyQt6 或 PySide6

from PIL import Image, ImageQt

# 转换
img = Image.open("photo.jpg")
qimg = ImageQt.ImageQt(img)

# 在 QLabel 显示
from PyQt6.QtWidgets import QApplication, QLabel
from PyQt6.QtGui import QPixmap
import sys

app = QApplication(sys.argv)
label = QLabel()
pixmap = QPixmap.fromImage(qimg)
label.setPixmap(pixmap)
label.show()
sys.exit(app.exec())

2. QImage → Pillow

from PIL import Image, ImageQt
from PyQt6.QtGui import QImage

# 假设已有 qimage
qimage = QImage("photo.jpg")

# 转 Pillow
img = ImageQt.fromqimage(qimage)   # 或 Image.fromqimage(qimage)
print(img.size)

3. 实时处理显示

from PIL import Image, ImageQt, ImageEnhance
from PyQt6.QtWidgets import QApplication, QLabel
from PyQt6.QtGui import QPixmap

def show_pillow(window, img):
    qimg = ImageQt.ImageQt(img)
    pixmap = QPixmap.fromImage(qimg)
    window.setPixmap(pixmap)

# 处理链
img = Image.open("photo.jpg")
img = ImageEnhance.Contrast(img).enhance(1.5)
show_pillow(label, img)

4. 注意事项

1. ImageQt 依赖 PyQt/PySide(需单独安装)
2. 保持 qimg/pixmap 引用
3. 大图缩放后再显示
4. 兼容: 检查 Qt 版本与 Pillow 版本
5. 也支持 PySide2/6

Pillow ExifTags 模块

Pillow ExifTags 模块

ExifTags 提供 EXIF 元数据的标签定义,可读取照片的拍摄信息。

1. 读取 EXIF

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS

img = Image.open("photo.jpg")

# 获取 EXIF
exif = img.getexif()
print(exif)

# 用标签名读取
for tag_id, value in exif.items():
    tag = TAGS.get(tag_id, tag_id)
    print(f"{tag}: {value}")

2. 常用 EXIF 信息

from PIL import Image
from PIL.ExifTags import TAGS

img = Image.open("photo.jpg")
exif = img.getexif()

def get_exif(img, tag_name):
    exif = img.getexif()
    for tag_id, value in exif.items():
        if TAGS.get(tag_id) == tag_name:
            return value
    return None

# 常用标签
make = get_exif(img, "Make")          # 厂商
model = get_exif(img, "Model")        # 型号
datetime = get_exif(img, "DateTime")  # 拍摄时间
fnum = get_exif(img, "FNumber")       # 光圈
exposure = get_exif(img, "ExposureTime")  # 快门
iso = get_exif(img, "ISOSpeedRatings")    # ISO

print(f"{make} {model}, {datetime}, ISO {iso}")

3. GPS 信息

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS

img = Image.open("photo.jpg")
exif = img.getexif()

gps_info = exif.get_ifd(0x8825)   # GPS IFD
# 或
# gps_info = exif.get(0x8825)

if gps_info:
    for tag_id, value in gps_info.items():
        tag = GPSTAGS.get(tag_id, tag_id)
        print(f"{tag}: {value}")

# 转换度分秒为十进制
def dms_to_decimal(dms):
    d, m, s = dms
    return d + m/60 + s/3600

# lat = dms_to_decimal(gps_info[2])   # GPSLatitude
# lon = dms_to_decimal(gps_info[4])

4. 写入 EXIF(部分支持)

from PIL import Image

img = Image.open("photo.jpg")
exif = img.getexif()
exif[270] = "拍摄说明"    # ImageDescription
# exif 需保存回图像
img.save("with_exif.jpg", exif=exif)

5. 常见用途

1. 照片管理(按时间/设备分类)
2. GPS 轨迹(图游)
3. 摄影参数统计(光圈/ISO 使用分布)
4. 隐私: 分享前清除 EXIF
   # 清除 EXIF 保存
   img = Image.open("photo.jpg")
   data = list(img.getdata())
   img2 = Image.new(img.mode, img.size)
   img2.putdata(data)
   img2.save("clean.jpg")

Pillow PSDraw 模块

Pillow PSDraw 模块

PSDraw 用于生成 PostScript 绘图文件,适合打印与 PostScript 设备输出。

1. 基本概念

PostScript: 页面描述语言(打印/出版)
PSDraw: 把绘图命令输出为 PostScript

适用: 打印文档、出版流程、古老打印机
现代用途: 较少,了解即可

# 简单示例
from PIL import PSDraw

ps = PSDraw.PSDraw("output.ps")
ps.begin_document()
# ... 绘制 ...
ps.end_document()

2. 绘制文本与图形

from PIL import PSDraw

ps = PSDraw.PSDraw("doc.ps")
ps.begin_document("示例文档")

# 页面尺寸(单位: 英寸)
ps.setpage(8.5, 11)   # A4/Letter

# 文本
ps.text((1, 10), "Hello PostScript")
ps.text((1, 9.5), "第二行")

# 线
ps.line((1, 1), (7, 1))     # 线段
ps.box((1, 2), (7, 3))      # 矩形框

# 图像
from PIL import Image
img = Image.open("photo.jpg")
ps.image((1, 4), (4, 8), img)   # 位置与尺寸

ps.end_document()

3. 打印图像

from PIL import Image, PSDraw

img = Image.open("photo.jpg")
ps = PSDraw.PSDraw("print.ps")
ps.begin_document()

# A4 页面 (8.27 x 11.69 in)
ps.setpage(8.27, 11.69)

# 打印图片(按英寸定位)
# 位置 (x1, y1) 到 (x2, y2)
ps.image((1, 6), (7, 10), img)

ps.end_document()

# 转换/查看
# ps2pdf print.ps print.pdf

4. 注意事项

1. 现代打印建议用 Pillow 直接 save PDF:
   img.save("out.pdf", "PDF", resolution=100)
2. PSDraw 是较老接口,功能有限
3. 坐标单位英寸
4. 输出 .ps 可被 Ghostscript/ps2pdf 处理

Pillow TiffTags 模块

Pillow TiffTags 模块

TiffTags 提供 TIFF 文件的标签定义,用于读取 TIFF 元数据与多页 TIFF 处理。

1. TIFF 简介

TIFF: 高质量图像格式(扫描/印刷/摄影)
特点:
- 无损(可选压缩 LZW/Deflate)
- 支持多页(扫描文档)
- 16 位色深
- 元数据丰富(TIFF tags)

# 读取 TIFF
from PIL import Image
img = Image.open("scan.tiff")
print(img.mode, img.size)
print(img.info)

2. 读取标签

from PIL import Image
from PIL.TiffTags import TAGS

img = Image.open("scan.tiff")

# 打印所有标签
tags = img.tag_v2
for tag_id, value in tags.items():
    name = TAGS.get(tag_id, tag_id)
    print(f"{name}: {value}")

# 常用标签
print(tags.get(256))   # ImageWidth
print(tags.get(257))   # ImageLength
print(tags.get(258))   # BitsPerSample
print(tags.get(262))   # PhotometricInterpretation

3. 多页 TIFF

from PIL import Image

tiff = Image.open("multipage.tiff")
print("页数:", tiff.n_frames)

# 逐页处理
for i in range(tiff.n_frames):
    tiff.seek(i)
    page = tiff.convert("RGB")
    page.save(f"page_{i}.png")

# 保存多页 TIFF
pages = [Image.open(f"page_{i}.png").convert("RGB") for i in range(3)]
pages[0].save("multi.tiff", save_all=True, append_images=pages[1:])

4. 保存选项

from PIL import Image

img = Image.open("photo.jpg")

# TIFF 保存
img.save("out.tiff")
img.save("out_lzw.tiff", compression="tiff_lzw")    # LZW 压缩
img.save("out_def.tiff", compression="tiff_adobe_deflate")

# 保真(适合印刷)
img.save("print.tiff", compression=None)

5. 常见用途

1. 扫描文档存档(多页 TIFF)
2. 摄影后期无损编辑
3. 印刷文件交付
4. 科研图像(16 位)处理
5. OCR 前处理

# 16 位图像支持
img16 = Image.open("16bit.tiff")
print(img16.mode)    # I;16
第 1 / 28 节