本文分享如何实现 批量图片转 GIF 动图:
关键内容
- ✅ 支持 PNG/JPG/BMP 等主流图片格式,自动过滤非图片文件;
- ✅ 参数灵活配置输入目录、输出路径、GIF 帧率(FPS)、图像名称顺序和循环次数;
- 🎨 自动处理图片格式兼容问题,并包含完善的异常提示。
- 🌟 多维度轻量化压缩,支持 0.1-1.0 缩放因子调整图片大小。
运行效果:

1、简洁版
首先分享的是简洁版,一个批量图片转GIF动图的Python脚本,基于PIL库实现。
代码的参数配置:
| 阶段 | 关键操作 | 说明 |
|---|---|---|
| 参数解析 | argparse |
支持输入目录、输出路径、FPS、循环次数、排序方式 |
| 文件筛选 | os.listdir + 后缀过滤 |
仅保留支持的5种图片格式 |
| 智能排序 | re.search(r'\d+') |
提取文件名中的数字进行排序,处理如 frame_001.png |
| 图像处理 | Image.open → convert('RGB') |
统一转RGB,解决透明通道兼容问题 |
| GIF生成 | images[0].save(save_all=True) |
首帧保存,其余帧append |
| 目录处理 | os.makedirs(exist_ok=True) |
自动创建不存在的输出目录 |
思路流程:

源代码:
python
import os
import argparse
import re
from PIL import Image
def create_gif(image_folder, output_path, fps=10, loop=0, sort_order='asc'):
"""
从指定目录的图片创建GIF动图
参数:
image_folder (str): 图片目录路径
output_path (str): GIF输出路径
fps (int/float): 每秒帧数,默认0.5
loop (int): 循环次数(0=无限),默认0
sort_order (str): 排序方式(asc=升序/desc=降序),默认asc
"""
# 支持的图片格式
IMAGE_EXTS = ('.png', '.jpg', '.jpeg', '.bmp', '.webp')
# 筛选目录内的图片文件
image_files = [
os.path.join(image_folder, f)
for f in os.listdir(image_folder)
if f.lower().endswith(IMAGE_EXTS)
]
if not image_files:
raise ValueError(f"目录 '{image_folder}' 未找到图片文件")
# 提取文件名中的数字作为排序依据
def sort_key(filename):
basename = os.path.basename(filename)
match = re.search(r'\d+', basename)
return int(match.group()) if match else basename
# 根据参数切换升/降序排序
reverse_flag = True if sort_order == 'desc' else False
image_files.sort(key=sort_key, reverse=reverse_flag)
# 打印排序后的图片列表
print(f"找到 {len(image_files)} 张图片,排序方式: {sort_order}")
for i, f in enumerate(image_files):
print(f" {i+1}. {os.path.basename(f)}")
# 打开并处理图片(转RGB兼容透明通道)
images = []
for img_path in image_files:
try:
img = Image.open(img_path)
img = img.convert('RGB') # 统一转为RGB模式
images.append(img)
except Exception as e:
print(f"警告: 无法打开图片 {img_path} - {e}")
if not images:
raise ValueError("无可用图片文件")
# 计算每帧持续时间(毫秒)
duration = 1000 / fps
# 保存GIF
images[0].save(
output_path,
save_all=True,
append_images=images[1:],
duration=duration,
loop=loop
)
# 打印生成结果
print(f"\nGIF已保存至: {output_path}")
print(f"帧率: {fps} FPS | 图片数量: {len(images)} | 循环次数: {'无限' if loop == 0 else loop}")
def main():
# 命令行参数解析
parser = argparse.ArgumentParser(description='批量图片转GIF动图工具')
parser.add_argument('--input_dir', default='./coco_test/', help='输入图片目录(默认: ./coco_test/)')
parser.add_argument('--output_file', default='Output_GIF/coco_test-202601.gif', help='输出GIF文件路径')
parser.add_argument('-f', '--fps', type=float, default=0.5, help='每秒帧数(默认: 0.5)')
parser.add_argument('-l', '--loop', type=int, default=0, help='循环次数(0=无限,默认: 0)')
parser.add_argument('-s', '--sort_order', choices=['asc', 'desc'], default='asc', help='排序方式(asc=升序/desc=降序,默认: asc)')
args = parser.parse_args()
# 校验输入目录是否存在
if not os.path.isdir(args.input_dir):
print(f"错误: 输入目录 '{args.input_dir}' 不存在")
return
# 自动补全GIF扩展名
if not args.output_file.lower().endswith('.gif'):
args.output_file += '.gif'
# 自动创建输出目录(如果不存在)
output_dir = os.path.dirname(args.output_file)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
print(f"提示: 自动创建输出目录 '{output_dir}'")
# 生成GIF
try:
create_gif(args.input_dir, args.output_file, args.fps, args.loop, args.sort_order)
except Exception as e:
print(f"错误: {e}")
if __name__ == "__main__":
main()
打印信息:
找到 5 张图片,排序方式: asc
000000000030.jpg
000000000034.jpg
000000000192.jpg
000000000247.jpg
000000000307.jpg
GIF已保存至: Output_GIF/coco_test-202601.gif
帧率: 0.5 FPS | 图片数量: 5 | 循环次数: 无限
运行效果:

2、专业版
这是一个增强版批量图片转GIF工具 ,相比之前的基础版本,新增了压缩优化、尺寸缩放、颜色量化等专业功能。
核心功能对比:
| 特性 | 基础版 | 增强版(本代码) |
|---|---|---|
| 图片转GIF | ✅ | ✅ |
| 智能数字排序 | ✅ | ✅ |
| 透明通道处理 | ❌(直接转RGB) | ✅(白色背景填充) |
| 尺寸缩放 | ❌ | ✅(LANCZOS重采样) |
| 颜色量化 | ❌ | ✅(MedianCut算法,1-256色) |
| 帧差异优化 | ❌ | ✅(optimize参数) |
| 压缩级别控制 | ❌ | ✅(0-9级) |
| 调色板质量 | ❌ | ✅(1-100) |
| 尺寸一致性校验 | ❌ | ✅(打印警告) |
🌟这是一款功能全面、高度可定制的批量图片转 GIF 工具,覆盖从图片预处理到 GIF 优化的全流程,核心能力如下:
-
✅ 核心生成:灵活可控的 GIF 创建
- 读取指定目录下 PNG/JPG/BMP/WebP 等主流格式图片,支持通过
-s/--sort_order参数一键切换文件名升序 / 降序排序; - 自定义帧率(FPS)、循环次数(0 = 无限循环),满足不同播放节奏需求。
- 读取指定目录下 PNG/JPG/BMP/WebP 等主流格式图片,支持通过
-
🎨 预处理:智能兼容 & 规整图片
- 自动处理 RGBA 透明图片:将透明背景转为白色,避免 GIF 背景异常;
- 校验所有图片尺寸一致性,尺寸不符时给出明确警告,保障动图显示效果;
- 支持 0.1-1.0 缩放因子调整图片大小,平衡 GIF 清晰度与文件体积。
-
🗜️ 优化:多维度轻量化压缩
- 可选颜色数精简:通过中值切割算法限制最大颜色数(1-256),大幅降低 GIF 体积;
- 帧优化 + 多级压缩:启用帧差异编码、自定义压缩级别(0-9)、调色板质量(1-100),兼顾画质与压缩率;
- 内置 Floyd-Steinberg 抖动算法,减少颜色精简后的画面失真。

源代码:
python
import os
import argparse
import re
from PIL import Image
import numpy as np
def create_gif(image_folder, output_path, fps=10, loop=0,
reduce_colors=True, max_colors=256,
optimize_frames=True, resize_factor=1.0,
compress_level=6, quality=85, sort_order='asc'):
"""
从指定目录图片创建GIF动图(支持压缩/缩放/调色)
参数:
image_folder (str): 图片目录路径
output_path (str): GIF输出路径
fps (int/float): 每秒帧数,默认10
loop (int): 循环次数(0=无限),默认0
reduce_colors (bool): 是否减少颜色数,默认True
max_colors (int): 最大颜色数(1-256),默认256
optimize_frames (bool): 是否优化帧(差异编码),默认True
resize_factor (float): 缩放因子(0.1-1.0),默认1.0(不缩放)
compress_level (int): 压缩级别(0-9),默认6
quality (int): 调色板质量(1-100),默认85
sort_order (str): 排序方式(asc=升序/desc=降序),默认asc
"""
# 支持的图片格式
IMAGE_EXTS = ('.png', '.jpg', '.jpeg', '.bmp', '.webp')
# 筛选目录内的图片文件
image_files = [
os.path.join(image_folder, f)
for f in os.listdir(image_folder)
if f.lower().endswith(IMAGE_EXTS)
]
if not image_files:
raise ValueError(f"目录 '{image_folder}' 未找到图片文件")
# 提取文件名中的数字作为排序依据
def sort_key(filename):
basename = os.path.basename(filename)
match = re.search(r'\d+', basename)
return int(match.group()) if match else basename
# 根据参数切换升/降序排序
reverse = True if sort_order == 'desc' else False
image_files.sort(key=sort_key, reverse=reverse)
# 打印排序结果
print(f"找到 {len(image_files)} 张图片,排序方式: {sort_order}")
for i, f in enumerate(image_files):
print(f" {i+1}. {os.path.basename(f)}")
# 加载并统一图片格式/尺寸
images = []
first_size = None
for img_path in image_files:
try:
img = Image.open(img_path)
# 校验图片尺寸一致性
if first_size is None:
first_size = img.size
print(f"\n基准尺寸: {first_size[0]}x{first_size[1]}")
elif img.size != first_size:
print(f"警告: 图片 {os.path.basename(img_path)} 尺寸 {img.size} 与基准不一致")
# 处理透明通道(转为白色背景)
if img.mode == 'RGBA':
background = Image.new('RGB', img.size, (255, 255, 255))
background.paste(img, mask=img.split()[-1])
img = background
else:
img = img.convert('RGB')
images.append(img)
except Exception as e:
print(f"警告: 无法打开图片 {img_path} - {e}")
if not images:
raise ValueError("无可用图片文件")
print(f"\n成功加载 {len(images)} 张图片")
# 图片缩放(按需)
if resize_factor != 1.0 and 0.1 <= resize_factor <= 1.0:
new_size = (int(first_size[0] * resize_factor),
int(first_size[1] * resize_factor))
print(f"缩放图片: {first_size} → {new_size} (缩放因子: {resize_factor})")
for i in range(len(images)):
images[i] = images[i].resize(new_size, Image.Resampling.LANCZOS)
# 减少颜色数(按需)
if reduce_colors and max_colors < 256:
print(f"减少颜色数至 {max_colors} 色")
for i in range(len(images)):
images[i] = images[i].quantize(colors=max_colors,
method=Image.Quantize.MEDIANCUT)
images[i] = images[i].convert('RGB')
# 计算每帧时长(毫秒)
duration = 1000 / fps
# 自动创建输出目录(不存在时)
output_dir = os.path.dirname(os.path.abspath(output_path))
if output_dir:
os.makedirs(output_dir, exist_ok=True)
print(f"\n输出目录已确保存在: {output_dir}")
# 生成并保存GIF(带优化参数)
print("正在生成GIF文件...")
images[0].save(
output_path,
save_all=True,
append_images=images[1:],
duration=duration,
loop=loop,
optimize=optimize_frames,
compress_level=compress_level,
quality=quality,
disposal=2,
dither=Image.Dither.FLOYDSTEINBERG
)
# 输出生成结果
file_size = os.path.getsize(output_path) / (1024 * 1024)
print(f"\n✅ GIF已保存至: {output_path}")
print(f"📦 文件大小: {file_size:.2f} MB | 🎞️ 帧率: {fps} FPS (每帧 {duration:.1f}ms)")
print(f"🖼️ 图片数量: {len(images)} | 🔁 循环次数: {'无限' if loop == 0 else loop}")
if resize_factor != 1.0:
print(f"🔍 缩放因子: {resize_factor}")
if reduce_colors:
print(f"🎨 颜色数限制: {max_colors}")
print(f"🗜️ 压缩级别: {compress_level} | 📊 质量设置: {quality}")
def main():
# 命令行参数解析
parser = argparse.ArgumentParser(description='GIF生成工具(支持压缩/缩放/调色/排序)')
parser.add_argument('--input_dir', default='./THUD_Robot_data/', help='输入图片目录(默认: ./Gif_bev_Global_Graph_Range/)')
parser.add_argument('--output_file', default='Output_GIF/THUD_Robot_data-test.gif', help='输出GIF文件路径')
parser.add_argument('-f', '--fps', type=float, default=0.5, help='每秒帧数(默认: 0.5)')
parser.add_argument('-l', '--loop', type=int, default=0, help='循环次数(0=无限,默认: 0)')
parser.add_argument('-s', '--sort_order', choices=['asc', 'desc'], default='asc',help='排序方式(asc=升序/desc=降序,默认: asc)')
# 压缩/优化相关参数
parser.add_argument('--no-reduce-colors', action='store_false', default=True, dest='reduce_colors',help='不减少颜色数')
parser.add_argument('--max-colors', type=int, default=256,help='最大颜色数(1-256),默认256')
parser.add_argument('--resize', type=float, default=0.6,help='缩放因子(0.1-1.0),默认0.6')
parser.add_argument('--compress', type=int, default=3, choices=range(0, 10),help='压缩级别(0-9),默认3')
parser.add_argument('--quality', type=int, default=95,help='调色板质量(1-100),默认95')
args = parser.parse_args()
# 校验输入目录
if not os.path.isdir(args.input_dir):
print(f"❌ 错误: 输入目录 '{args.input_dir}' 不存在")
return
# 自动补全GIF扩展名
if not args.output_file.lower().endswith('.gif'):
args.output_file += '.gif'
# 执行GIF生成
try:
create_gif(
args.input_dir,
args.output_file,
args.fps,
args.loop,
reduce_colors=args.reduce_colors,
max_colors=args.max_colors,
resize_factor=args.resize,
compress_level=args.compress,
quality=args.quality,
sort_order=args.sort_order
)
except Exception as e:
print(f"\n❌ 错误: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
-
🔧 配置:零代码便捷适配
- 全参数通过命令行配置(输入 / 输出路径、缩放、压缩、排序等),无需修改代码;
- 自动补全
.gif扩展名,避免格式错误; - 输出生成后详细信息(文件大小、帧率、颜色数等),直观掌握结果。
-
🛡️ 鲁棒性:防错 & 易调试
- 自动创建不存在的输出目录,无需手动建文件夹;
- 校验输入目录有效性,捕获异常并打印错误堆栈,快速定位问题;
- 跳过无法打开的异常图片,不中断整体生成流程。
可视化结果:

运行信息:
找到 7 张图片,排序方式: asc
frame-000010.color.png
frame-000019.color.png
frame-000039.color.png
frame-000046.color.png
frame-000066.color.png
frame-000086.color.png
frame-000109.color.png
基准尺寸: 960x540
成功加载 7 张图片
缩放图片: (960, 540) → (768, 432) (缩放因子: 0.8)
输出目录已确保存在: /home/user/lgp_dev/01_Gif_picture/Output_GIF
正在生成GIF文件...
✅ GIF已保存至: Output_GIF/THUD_Robot_data-test.gif
📦 文件大小: 1.14 MB | 🎞️ 帧率: 0.5 FPS (每帧 2000.0ms)
🖼️ 图片数量: 7 | 🔁 循环次数: 无限
🔍 缩放因子: 0.8
🎨 颜色数限制: 256
🗜️ 压缩级别: 3 | 📊 质量设置: 95
分析完成~