Pillow ImagePath 模块
下面给你一个 Pillow(PIL Fork)中 ImagePath 模块 的完整入门指南,包含路径绘制、矢量图形生成、多边形填充、坐标变换、SVG 路径解析、性能优化及高级应用。
(基于 Pillow ≥ 9.0,Python 3.8+)
1. 安装 Pillow
pip install --upgrade pillow
2. 基本导入
from PIL import Image, ImageDraw, ImagePath
3. ImagePath 模块概述
ImagePath.Path 是 Pillow 中用于矢量路径操作的底层模块,主要用于:
- 创建任意多边形路径
- 填充或描边复杂形状
- 坐标变换(平移、缩放、旋转)
- 与
ImageDraw配合绘制矢量图形 - 解析 SVG 路径字符串(
d属性)
核心用途:
- 生成验证码背景
- 艺术字路径
- 地图多边形填充
- 自定义图形特效
4. 核心类:ImagePath.Path
4.1 创建路径
# 方法1:从坐标列表创建
coords = [(50, 50), (150, 50), (100, 150), (50, 50)] # 闭合三角形
path = ImagePath.Path(coords)
# 方法2:空路径 + 追加
path = ImagePath.Path()
path.moveto(100, 100)
path.lineto(200, 100)
path.lineto(150, 200)
path.closepath() # 闭合
5. 路径方法详解
| 方法 | 作用 |
|---|---|
moveto(x, y) | 移动到点(不绘制) |
lineto(x, y) | 绘制直线到点 |
curveto(x1,y1, x2,y2, x3,y3) | 三次贝塞尔曲线 |
closepath() | 闭合路径 |
getbbox() | 返回路径边界 (left, top, right, bottom) |
tolist(flat=False) | 转为坐标列表 |
compact() | 移除冗余点 |
transform(matrix) | 应用 2D 变换矩阵 |
6. 与 ImageDraw 配合使用
img = Image.new("RGB", (300, 300), "white")
draw = ImageDraw.Draw(img)
# 创建五角星路径
import math
def star_path(cx, cy, r, points=5):
path = ImagePath.Path()
for i in range(points * 2):
angle = i * math.pi / points
radius = r if i % 2 == 0 else r * 0.4
x = cx + radius * math.cos(angle - math.pi/2)
y = cy + radius * math.sin(angle - math.pi/2)
if i == 0:
path.moveto(x, y)
else:
path.lineto(x, y)
path.closepath()
return path
star = star_path(150, 150, 80)
draw.polygon(star, fill="gold", outline="black", width=3)
img.save("star.png")
7. 路径变换(平移、缩放、旋转)
from PIL import ImagePath
import math
path = ImagePath.Path([(0,0), (100,0), (50,100), (0,0)])
# 平移
path.transform((1, 0, 0, 1, 50, 30)) # [a,b,c,d,tx,ty]
# 缩放
path.transform((2, 0, 0, 2, 0, 0)) # 2倍放大
# 旋转 45°(围绕原点)
angle = math.radians(45)
c, s = math.cos(angle), math.sin(angle)
path.transform((c, s, -s, c, 0, 0))
# 绘制
img = Image.new("RGB", (300, 300), "white")
draw = ImageDraw.Draw(img)
draw.polygon(path, fill="lightblue")
img.save("transformed.png")
8. 解析 SVG 路径字符串
def svg_path_to_pil(d, scale=1.0):
"""将 SVG path 的 d 属性转为 ImagePath.Path"""
import re
path = ImagePath.Path()
commands = re.findall(r'[MLHVCSQTAZmlhvcsqtaz][^MLHVCSQTAZmlhvcsqtaz]*', d)
x = y = 0
for cmd in commands:
cmd_type = cmd[0]
nums = [float(n) for n in re.findall(r'-?\d+\.?\d*', cmd)]
i = 0
while i < len(nums):
if cmd_type in 'Mm':
x, y = nums[i], nums[i+1]
path.moveto(x * scale, y * scale) if cmd_type.isupper() else path.lineto(x * scale, y * scale)
i += 2
elif cmd_type in 'Ll':
x, y = nums[i], nums[i+1]
path.lineto(x * scale, y * scale)
i += 2
# 更多命令可扩展...
return path
# 示例:SVG 心形
svg_d = "M10,30 A20,20 0,0,1 50,30 A20,20 0,0,1 90,30 Q90,60 50,90 Q10,60 10,30 z"
heart = svg_path_to_pil(svg_d, scale=3)
img = Image.new("RGB", (300, 300), "white")
draw = ImageDraw.Draw(img)
draw.polygon(heart, fill="red")
img.save("heart.svg_path.png")
9. 高级应用
9.1 扭曲文字路径(艺术字)
from PIL import Image, ImageDraw, ImagePath, ImageFont
import random
def wavy_text(text, font_path, size=(600, 200)):
img = Image.new("RGB", size, "white")
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(font_path, 72)
# 获取文字边界
left, top, right, bottom = draw.textbbox((0,0), text, font=font)
w, h = right - left, bottom - top
# 创建波浪路径
path = ImagePath.Path()
y_base = size[1] // 2
for x in range(left, right + 1):
offset = 20 * math.sin(x * 0.05)
y = y_base + offset
if x == left:
path.moveto(x, y)
else:
path.lineto(x, y)
# 绘制文字沿路径(近似)
for i, ch in enumerate(text):
char_img = Image.new("RGBA", (w//len(text)+10, h+40), (0,0,0,0))
char_draw = ImageDraw.Draw(char_img)
char_draw.text((5, 20), ch, font=font, fill="black")
# 粘贴到波浪位置
x_pos = left + i * (w//len(text))
y_offset = 20 * math.sin(x_pos * 0.05)
img.paste(char_img, (x_pos, int(y_base + y_offset - h//2)), char_img)
return img
wavy_text("WAVE", "arial.ttf").save("wavy_text.png")
9.2 生成验证码背景(随机多边形)
def captcha_background(size=(300, 100)):
img = Image.new("RGB", size, "white")
draw = ImageDraw.Draw(img)
for _ in range(5):
points = [(random.randint(0, size[0]), random.randint(0, size[1])) for _ in range(4)]
path = ImagePath.Path(points)
path.closepath()
color = (random.randint(100, 255), random.randint(100, 255), random.randint(100, 255))
draw.polygon(path, fill=color, outline=None)
return img
captcha_background().save("captcha_bg.png")
10. 性能优化
| 场景 | 建议 |
|---|---|
| 大量路径 | 使用 path.compact() 压缩 |
| 重复变换 | 缓存变换矩阵 |
| 复杂 SVG | 预解析 + 缓存路径 |
| 高分辨率 | 先低分辨率绘制,再 resize |
11. 常见问题
| 问题 | 解决 |
|---|---|
| 路径不闭合 | 调用 path.closepath() |
| 坐标超出画布 | 使用 getbbox() 检查并平移 |
| 变换后变形 | 确保矩阵顺序正确 |
| SVG 解析失败 | 仅支持 M L Z 等基本命令 |
12. 官方文档
- https://pillow.readthedocs.io/en/stable/reference/ImagePath.html
一键工具脚本:path_tool.py
#!/usr/bin/env python3
import argparse
from PIL import Image, ImageDraw, ImagePath
import math
def main():
parser = argparse.ArgumentParser(description="ImagePath 路径生成器")
parser.add_argument("-t", "--type", choices=["star", "heart", "wave"], default="star")
parser.add_argument("-o", "--output", default="path.png")
args = parser.parse_args()
img = Image.new("RGB", (400, 400), "white")
draw = ImageDraw.Draw(img)
if args.type == "star":
path = ImagePath.Path()
cx, cy, r = 200, 200, 100
for i in range(10):
angle = i * math.pi / 5
radius = r if i % 2 == 0 else r * 0.4
x = cx + radius * math.cos(angle - math.pi/2)
y = cy + radius * math.sin(angle - math.pi/2)
(path.moveto if i == 0 else path.lineto)(x, y)
path.closepath()
draw.polygon(path, fill="gold", outline="black")
img.save(args.output)
print(f"已生成: {args.output}")
if __name__ == "__main__":
main()
使用:
python path_tool.py -t star -o my_star.png
需要我帮你实现 SVG 转 PNG 批量工具、路径动画帧生成、地图行政区填充、自定义字体路径 等功能吗?直接说需求,我给你完整代码!