ImageSequence 模块
下面给你一个 Pillow(PIL Fork)中 ImageSequence 模块 的完整入门指南,包含 GIF/APNG 动图解析、帧提取、帧率控制、帧合并、动图优化、转视频、帧动画生成等高级应用。
(基于 Pillow ≥ 9.0,Python 3.8+)
1. 安装 Pillow
pip install --upgrade pillow
2. 基本导入
from PIL import Image, ImageSequence
3. ImageSequence 模块概述
ImageSequence 提供对 多帧图像(如 GIF、APNG、TIFF) 的帧级访问,支持:
- 逐帧遍历
- 获取每帧的
info(持续时间、处置方式) - 帧提取、合并、编辑
- 动图优化(减少颜色、压缩)
- 转为视频(配合
imageio或opencv)
适用场景:
- 动图拆帧做数据集
- 提取表情包帧
- 优化 GIF 体积
- 动图转视频/倒放/加速
- 帧级特效处理
4. 核心功能:ImageSequence.Iterator
img = Image.open("animation.gif") # 自动识别多帧
# 遍历所有帧
for i, frame in enumerate(ImageSequence.Iterator(img)):
frame.save(f"frame_{i:03d}.png")
print(f"第 {i} 帧: {frame.size}, 模式: {frame.mode}")
5. 获取帧信息(info 字典)
| 键 | 说明 |
|---|---|
duration | 每帧显示时间(毫秒) |
disposal | 处置方式(0=无,1=不处置,2=恢复背景,3=恢复前一帧) |
loop | 循环次数(0=无限) |
background | 背景色索引 |
img = Image.open("animation.gif")
print(f"总帧数: {img.n_frames}")
print(f"是否动图: {img.is_animated}")
print(f"循环次数: {img.info.get('loop', '无限')}")
for i, frame in enumerate(ImageSequence.Iterator(img)):
duration = frame.info.get('duration', 100) # 默认 100ms
print(f"帧 {i}: 持续 {duration}ms")
6. 帧提取 + 优化保存
def extract_frames(gif_path, output_dir="frames"):
import os
os.makedirs(output_dir, exist_ok=True)
img = Image.open(gif_path)
palette = img.getpalette() # 保留调色板
for i, frame in enumerate(ImageSequence.Iterator(img)):
# 转换为 RGB 避免调色板问题
frame_rgb = frame.convert("RGB")
frame_rgb.save(f"{output_dir}/frame_{i:03d}.png")
print(f"提取完成: {img.n_frames} 帧")
extract_frames("dance.gif")
7. 高级应用
7.1 动图倒放
def reverse_gif(input_path, output_path):
img = Image.open(input_path)
frames = [frame.copy() for frame in ImageSequence.Iterator(img)]
frames.reverse()
# 保持第一帧的调色板
frames[0].save(
output_path,
save_all=True,
append_images=frames[1:],
duration=img.info.get('duration', 100),
loop=img.info.get('loop', 0),
disposal=2
)
print(f"倒放完成: {output_path}")
reverse_gif("forward.gif", "reverse.gif")
7.2 调整播放速度(加速/减慢)
def change_speed(gif_path, output_path, speed_factor=2.0):
# speed_factor >1 加速,<1 减慢
img = Image.open(gif_path)
original_duration = img.info.get('duration', 100)
new_duration = max(1, int(original_duration / speed_factor)) # 最小 1ms
frames = list(ImageSequence.Iterator(img))
frames[0].save(
output_path,
save_all=True,
append_images=frames[1:],
duration=new_duration,
loop=img.info.get('loop', 0)
)
print(f"速度调整完成: {speed_factor}x")
change_speed("normal.gif", "fast.gif", 3.0) # 3倍速
7.3 动图转视频(MP4)
pip install imageio[ffmpeg]
import imageio
from PIL import ImageSequence
def gif_to_mp4(gif_path, mp4_path, fps=10):
img = Image.open(gif_path)
writer = imageio.get_writer(mp4_path, fps=fps, codec='libx264', quality=8)
for frame in ImageSequence.Iterator(img):
writer.append_data(np.array(frame.convert("RGB")))
writer.close()
print(f"转为视频: {mp4_path}")
gif_to_mp4("anim.gif", "anim.mp4", fps=15)
7.4 帧级滤镜(每帧锐化)
from PIL import ImageFilter
def apply_filter_to_frames(gif_path, output_path, filter_obj=ImageFilter.SHARPEN):
img = Image.open(gif_path)
frames = []
for frame in ImageSequence.Iterator(img):
filtered = frame.convert("RGB").filter(filter_obj)
frames.append(filtered)
frames[0].save(
output_path,
save_all=True,
append_images=frames[1:],
duration=img.info.get('duration', 100),
loop=0
)
apply_filter_to_frames("blurry.gif", "sharp.gif")
7.5 动图优化(减小体积)
def optimize_gif(input_path, output_path, optimize=True):
img = Image.open(input_path)
# 减少颜色(量化)
quantized = img.quantize(colors=64, method=2) # 64 色
quantized.save(
output_path,
optimize=optimize,
quality=85
)
print(f"优化完成: {output_path}")
optimize_gif("large.gif", "small.gif")
7.6 从图片序列生成 GIF
def images_to_gif(image_paths, output_path, duration=200):
images = [Image.open(p).convert("RGB") for p in image_paths]
images[0].save(
output_path,
save_all=True,
append_images=images[1:],
duration=duration,
loop=0
)
print(f"合成 GIF: {output_path}")
# 使用
images_to_gif([f"frame_{i:03d}.png" for i in range(30)], "new.gif", 100)
8. 完整示例:动图处理工具箱
def gif_toolbox(input_gif, output_dir="output"):
import os
os.makedirs(output_dir, exist_ok=True)
img = Image.open(input_gif)
print(f"动图信息: {img.n_frames} 帧, {img.size}, 循环: {img.info.get('loop', '无限')}")
# 1. 提取帧
for i, frame in enumerate(ImageSequence.Iterator(img)):
frame.convert("RGB").save(f"{output_dir}/frame_{i:03d}.png")
# 2. 生成倒放
frames = [f.copy() for f in ImageSequence.Iterator(img)]
frames.reverse()
frames[0].save(f"{output_dir}/reverse.gif", save_all=True, append_images=frames[1:], duration=img.info.get('duration', 100))
# 3. 加速 2x
frames = list(ImageSequence.Iterator(img))
frames[0].save(f"{output_dir}/fast.gif", save_all=True, append_images=frames[1:], duration=max(1, img.info.get('duration', 100)//2))
print(f"工具箱处理完成: {output_dir}/")
gif_toolbox("input.gif")
9. 常见问题 & 解决方案
| 问题 | 原因 | 解决 |
|---|---|---|
n_frames 为 1 | 不是动图 | 检查 img.is_animated |
| 帧颜色失真 | 调色板丢失 | 使用 frame.convert("RGB") |
duration 为 0 | 部分 GIF 未设置 | 设默认值 100 |
| 合成后闪烁 | disposal 未处理 | 手动处理背景恢复 |
| 体积未减小 | 未 optimize | save(optimize=True) |
10. 官方文档
- https://pillow.readthedocs.io/en/stable/reference/ImageSequence.html
Image.save动图参数:https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif
一键命令行工具:giftool.py
#!/usr/bin/env python3
import argparse
from PIL import Image, ImageSequence
import os
def main():
parser = argparse.ArgumentParser(description="GIF 动图处理工具")
parser.add_argument("input")
parser.add_argument("--extract", action="store_true", help="提取帧")
parser.add_argument("--reverse", action="store_true", help="倒放")
parser.add_argument("--speed", type=float, default=1.0, help="速度倍率")
parser.add_argument("-o", "--output", help="输出目录/文件")
args = parser.parse_args()
output = args.output or "gif_output"
os.makedirs(output, exist_ok=True)
img = Image.open(args.input)
if args.extract:
for i, frame in enumerate(ImageSequence.Iterator(img)):
frame.convert("RGB").save(f"{output}/frame_{i:03d}.png")
print(f"帧已提取到 {output}/")
if args.reverse:
frames = [f.copy() for f in ImageSequence.Iterator(img)][::-1]
frames[0].save(f"{output}/reverse.gif", save_all=True, append_images=frames[1:], duration=img.info.get('duration', 100))
print(f"倒放保存: {output}/reverse.gif")
if args.speed != 1.0:
duration = max(1, int((img.info.get('duration', 100)) / args.speed))
frames = list(ImageSequence.Iterator(img))
frames[0].save(f"{output}/speed_{args.speed}x.gif", save_all=True, append_images=frames[1:], duration=duration)
print(f"速度调整: {args.speed}x")
if __name__ == "__main__":
main()
使用示例:
python giftool.py dance.gif --extract --reverse --speed 2.0 -o result
需要我帮你实现 表情包批量处理、动图加水印、帧差视频、动图转 WebP、帧级 AI 上色 等功能吗?直接说需求,我给你完整代码!