引言
在日常办公、数据处理、网站开发和图像处理学习中,经常会遇到图片格式转换需求,例如:
- 将 jpg 图片批量转换为 png
- 将 png 图片批量转换为 jpg
- 压缩图片体积,方便上传或传输
- 批量调整图片尺寸
- 处理整个目录下的图片文件
如果图片数量很少,可以使用图像编辑软件手动转换。但当图片数量达到几十张、几百张甚至更多时,手动操作就非常低效。python 的 pillow 库可以很好地解决这个问题。
本文面向 python 图像处理入门者,使用 pillow 实现一个简易的图片批量格式转换工具。
一、pillow 是什么
pillow 是 python 中最常用的图像处理库之一,它是 pil 的现代维护版本。
pil 的全称是 python imaging library,但原始 pil 已经停止维护。现在实际项目中通常安装和使用的是 pillow,不过导入时仍然使用:
from pil import image
pillow 支持常见图像处理操作,例如:
- 打开图片
- 保存图片
- 图片格式转换
- 图片压缩
- 图片缩放
- 图片裁剪
- 图片旋转
- 添加水印
- 获取图片尺寸和模式
对于 python 图像处理入门者来说,pillow 是非常适合练手的库。
二、pillow 安装
使用 pip 安装:
pip install pillow
如果使用的是国内网络环境,也可以指定镜像源:
pip install pillow -i https://pypi.tuna.tsinghua.edu.cn/simple
验证是否安装成功:
from pil import image print(image.__version__)
如果可以正常输出版本号,说明 pillow 安装成功。
三、打开和查看图片信息
在做格式转换之前,先看一下如何读取图片基本信息。
from pil import image
image_path = "demo.jpg"
with image.open(image_path) as img:
print(f"图片格式:{img.format}")
print(f"图片尺寸:{img.size}")
print(f"图片模式:{img.mode}")
常见的图片模式包括:
rgb:常见彩色 图片模式rgba:带透明通道的图片模式l:灰度图模式p:调色板模式
理解图片模式很重要,尤其是在 png 转 jpg 时。因为 jpg 不支持透明通道,如果直接保存 rgba 图片为 jpg,可能会报错。
四、jpg 转 png
jpg 转 png 的核心逻辑非常简单:打开 jpg 文件,然后保存为 png 文件。
from pil import image
input_path = "demo.jpg"
output_path = "demo.png"
with image.open(input_path) as img:
img.save(output_path, "png")
print("jpg 转 png 完成")
如果希望统一转换为 rgb 模式,可以写成:
from pil import image
with image.open("demo.jpg") as img:
img = img.convert("rgb")
img.save("demo.png", "png")
png 是无损格式,适合保存截图、图标、透明背景图片等。但 png 文件体积通常比 jpg 更大。
五、png 转 jpg
png 转 jpg 时,需要特别注意透明通道。
jpg 不支持透明背景,如果 png 是 rgba 模式,需要先转换为 rgb。
from pil import image
input_path = "demo.png"
output_path = "demo.jpg"
with image.open(input_path) as img:
img = img.convert("rgb")
img.save(output_path, "jpeg")
print("png 转 jpg 完成")
如果 png 图片带透明背景,直接 convert("rgb") 可能会把透明区域变成黑色。更稳妥的方式是先创建一个白色背景,再把 png 贴上去。
from pil import image
input_path = "logo.png"
output_path = "logo.jpg"
with image.open(input_path) as img:
if img.mode in ("rgba", "la"):
background = image.new("rgb", img.size, (255, 255, 255))
background.paste(img, mask=img.getchannel("a"))
background.save(output_path, "jpeg")
else:
img.convert("rgb").save(output_path, "jpeg")
print("png 转 jpg 完成")
这种处理方式更适合批量转换,因为它能兼容透明 png。
六、图片压缩
图片压缩通常用于减小文件体积。对于 jpg 图片,可以通过 quality 参数控制质量。
from pil import image
input_path = "demo.jpg"
output_path = "demo_compressed.jpg"
with image.open(input_path) as img:
img = img.convert("rgb")
img.save(output_path, "jpeg", quality=70, optimize=true)
print("图片压缩完成")
参数说明:
quality:图片质量,取值一般为 1 到 95,数值越大质量越高,文件越大optimize=true:让 pillow 尽量优化保存结果
常用建议:
- 文章配图可以使用
quality=75 - 缩略图可以使用
quality=60 - 高清存档可以使用
quality=90
png 也可以压缩:
from pil import image
with image.open("demo.png") as img:
img.save("demo_compressed.png", "png", optimize=true)
需要注意的是,png 是无损格式,单纯使用 pillow 压缩时,体积下降不一定明显。如果对 png 压缩要求很高,可以结合专门的压缩工具。
七、图片尺寸调整
调整图片尺寸可以使用 resize()。
from pil import image
input_path = "demo.jpg"
output_path = "demo_resized.jpg"
with image.open(input_path) as img:
resized = img.resize((800, 600))
resized.save(output_path)
print("图片尺寸调整完成")
不过直接指定固定宽高可能导致图片变形。实际项目中更常见的是按比例缩放。
from pil import image
def resize_keep_ratio(img, max_width, max_height):
img_copy = img.copy()
img_copy.thumbnail((max_width, max_height))
return img_copy
with image.open("demo.jpg") as img:
resized = resize_keep_ratio(img, 800, 800)
resized.save("demo_resized.jpg")
thumbnail() 会保持原图比例,并确保图片不会超过指定的最大宽高。
例如,原图是 3000 x 2000,最大尺寸设置为 800 x 800,缩放后会变成 800 x 533,不会被拉伸变形。
八、批量处理整个目录
批量处理目录的核心思路:
- 遍历输入目录下的文件
- 判断文件后缀是否为图片格式
- 使用 pillow 打开图片
- 根据目标格式转换并保存
- 遇到异常时跳过并打印错误信息
可以先写一个简单版本:
from pathlib import path
from pil import image
input_dir = path("input_images")
output_dir = path("output_images")
output_dir.mkdir(parents=true, exist_ok=true)
for image_path in input_dir.iterdir():
if image_path.suffix.lower() not in [".jpg", ".jpeg", ".png", ".bmp", ".webp"]:
continue
output_path = output_dir / f"{image_path.stem}.png"
with image.open(image_path) as img:
img.save(output_path, "png")
print(f"已转换:{image_path.name} -> {output_path.name}")
这个版本可以完成基本批量转换,但还不够灵活。下面我们会封装成一个完整工具。
九、完整代码案例:图片批量格式转换工具
下面的完整代码支持:
- jpg、png、bmp、webp 等常见图片格式输入
- 输出为 jpg、png、webp
- 可选图片压缩质量
- 可选最大宽高,保持比例缩放
- 批量处理整个目录
- 自动创建输出目录
- 兼容 png 透明通道转 jpg 的情况
保存为 batch_image_converter.py。
import argparse
from pathlib import path
from pil import image
supported_extensions = {".jpg", ".jpeg", ".png", ".bmp", ".webp", ".tif", ".tiff"}
supported_output_formats = {"jpg", "jpeg", "png", "webp"}
def normalize_format(output_format):
output_format = output_format.lower().strip(".")
if output_format not in supported_output_formats:
raise valueerror(f"不支持的输出格式:{output_format}")
return "jpg" if output_format == "jpeg" else output_format
def get_output_suffix(output_format):
return ".jpg" if output_format in ("jpg", "jpeg") else f".{output_format}"
def prepare_image_for_jpg(img, background_color=(255, 255, 255)):
if img.mode in ("rgba", "la"):
background = image.new("rgb", img.size, background_color)
alpha = img.getchannel("a")
background.paste(img, mask=alpha)
return background
if img.mode == "p":
img = img.convert("rgba")
if "a" in img.getbands():
background = image.new("rgb", img.size, background_color)
background.paste(img, mask=img.getchannel("a"))
return background
return img.convert("rgb")
def resize_keep_ratio(img, max_width=none, max_height=none):
if not max_width and not max_height:
return img
width, height = img.size
target_width = max_width or width
target_height = max_height or height
resized = img.copy()
resized.thumbnail((target_width, target_height))
return resized
def save_image(img, output_path, output_format, quality):
if output_format == "jpg":
img = prepare_image_for_jpg(img)
img.save(output_path, "jpeg", quality=quality, optimize=true)
return
if output_format == "png":
img.save(output_path, "png", optimize=true)
return
if output_format == "webp":
img.save(output_path, "webp", quality=quality, method=6)
return
raise valueerror(f"不支持的输出格式:{output_format}")
def convert_one_image(image_path, output_dir, output_format, quality, max_width, max_height):
output_suffix = get_output_suffix(output_format)
output_path = output_dir / f"{image_path.stem}{output_suffix}"
with image.open(image_path) as img:
img = resize_keep_ratio(img, max_width=max_width, max_height=max_height)
save_image(img, output_path, output_format, quality)
return output_path
def batch_convert(input_dir, output_dir, output_format, quality=80, max_width=none, max_height=none):
input_dir = path(input_dir)
output_dir = path(output_dir)
output_format = normalize_format(output_format)
if not input_dir.exists():
raise filenotfounderror(f"输入目录不存在:{input_dir}")
if not input_dir.is_dir():
raise notadirectoryerror(f"输入路径不是目录:{input_dir}")
output_dir.mkdir(parents=true, exist_ok=true)
total = 0
success = 0
failed = 0
for image_path in input_dir.rglob("*"):
if not image_path.is_file():
continue
if image_path.suffix.lower() not in supported_extensions:
continue
total += 1
try:
output_path = convert_one_image(
image_path=image_path,
output_dir=output_dir,
output_format=output_format,
quality=quality,
max_width=max_width,
max_height=max_height,
)
success += 1
print(f"[成功] {image_path} -> {output_path}")
except exception as exc:
failed += 1
print(f"[失败] {image_path},原因:{exc}")
print("-" * 60)
print(f"处理完成:总数={total},成功={success},失败={failed}")
def parse_args():
parser = argparse.argumentparser(description="python pillow 图片批量格式转换工具")
parser.add_argument("-i", "--input", required=true, help="输入图片目录")
parser.add_argument("-o", "--output", required=true, help="输出图片目录")
parser.add_argument(
"-f",
"--format",
default="jpg",
choices=["jpg", "jpeg", "png", "webp"],
help="输出图片格式,默认 jpg",
)
parser.add_argument(
"-q",
"--quality",
type=int,
default=80,
help="图片质量,主要用于 jpg 和 webp,默认 80",
)
parser.add_argument("--max-width", type=int, default=none, help="最大宽度,保持比例缩放")
parser.add_argument("--max-height", type=int, default=none, help="最大高度,保持比例缩放")
return parser.parse_args()
def main():
args = parse_args()
batch_convert(
input_dir=args.input,
output_dir=args.output,
output_format=args.format,
quality=args.quality,
max_width=args.max_width,
max_height=args.max_height,
)
if __name__ == "__main__":
main()
十、运行示例
将 input_images 目录下的图片批量转换为 jpg:
python batch_image_converter.py -i input_images -o output_images -f jpg
转换为 png:
python batch_image_converter.py -i input_images -o output_images -f png
转换为 webp,并设置压缩质量:
python batch_image_converter.py -i input_images -o output_images -f webp -q 75
批量压缩 jpg:
python batch_image_converter.py -i input_images -o compressed_images -f jpg -q 60
批量调整尺寸,最大宽度和高度不超过 800:
python batch_image_converter.py -i input_images -o resized_images -f jpg --max-width 800 --max-height 800
批量转换并压缩:
python batch_image_converter.py -i input_images -o result_images -f jpg -q 70 --max-width 1200 --max-height 1200
十一、代码设计说明
这个工具把功能拆成了几个函数:
normalize_format():统一处理输出格式prepare_image_for_jpg():解决透明 png 转 jpg 的问题resize_keep_ratio():保持比例调整尺寸save_image():根据目标格式保存图片convert_one_image():处理单张图片batch_convert():批量处理整个目录parse_args():解析命令行参数
这种拆分方式比把所有逻辑写在一个循环里更容易维护。后续要增加水印、重命名、跳过已存在文件等功能,也可以继续扩展。
十二、gui 简单扩展思路
当前工具是命令行版本,适合脚本自动化和批处理。如果要做成图形界面,可以使用 tkinter、pyqt 或 pyside。
一个简单 gui 可以包含这些控件:
- 输入目录选择按钮
- 输出目录选择按钮
- 输出格式下拉框
- 图片质量滑块
- 最大宽度和最大高度输入框
- 开始转换按钮
- 转换日志显示区域
- 进度条
如果使用 tkinter,基本结构可以这样设计:
import tkinter as tk
from tkinter import filedialog, messagebox
def choose_input_dir():
path = filedialog.askdirectory()
input_var.set(path)
def choose_output_dir():
path = filedialog.askdirectory()
output_var.set(path)
def start_convert():
input_dir = input_var.get()
output_dir = output_var.get()
output_format = format_var.get()
if not input_dir or not output_dir:
messagebox.showwarning("提示", "请选择输入目录和输出目录")
return
# 这里可以调用前面的 batch_convert() 函数
messagebox.showinfo("完成", f"开始转换为 {output_format}")
root = tk.tk()
root.title("图片批量格式转换工具")
root.geometry("500x260")
input_var = tk.stringvar()
output_var = tk.stringvar()
format_var = tk.stringvar(value="jpg")
tk.label(root, text="输入目录").pack(anchor="w", padx=20, pady=5)
tk.entry(root, textvariable=input_var, width=50).pack(anchor="w", padx=20)
tk.button(root, text="选择输入目录", command=choose_input_dir).pack(anchor="w", padx=20, pady=5)
tk.label(root, text="输出目录").pack(anchor="w", padx=20, pady=5)
tk.entry(root, textvariable=output_var, width=50).pack(anchor="w", padx=20)
tk.button(root, text="选择输出目录", command=choose_output_dir).pack(anchor="w", padx=20, pady=5)
tk.label(root, text="输出格式").pack(anchor="w", padx=20, pady=5)
tk.optionmenu(root, format_var, "jpg", "png", "webp").pack(anchor="w", padx=20)
tk.button(root, text="开始转换", command=start_convert).pack(pady=15)
root.mainloop()
实际开发时,可以直接复用命令行版本中的 batch_convert() 函数。这样核心处理逻辑不用重写,只需要把 gui 控件中的参数传给函数即可。
如果图片数量很多,gui 中还需要注意一点:不要在主线程里直接执行批量转换,否则界面可能卡住。可以使用 threading.thread 把转换任务放到子线程中执行。
十三、常见问题
1. 为什么 png 转 jpg 报错
常见原因是 png 图片包含透明通道,而 jpg 不支持透明通道。解决方式是先转换为 rgb,或者先合成白色背景。
2. 为什么压缩后图片还是很大
可能原因包括:
- 原图尺寸太大
quality设置过高- png 本身不适合用 pillow 大幅压缩
- 图片内容复杂,压缩空间有限
可以同时使用降低质量和调整尺寸两种方式。
3. 为什么批量处理时有些图片失败
可能原因包括:
- 文件扩展名是图片,但文件内容已经损坏
- 文件权限不足
- 路径中存在特殊权限问题
- 输出目录不可写
所以批量脚本中要使用 try except,避免一张图片失败导致整个任务中断。
十四、总结
本文使用 python + pillow 实现了一个图片批量格式转换工具,覆盖了图像处理入门中非常常见的几个需求:
- 安装 pillow
- jpg 转 png
- png 转 jpg
- 图片压缩
- 图片尺寸调整
- 批量处理整个目录
- 命令行完整代码案例
- gui 图形界面扩展思路
这个案例非常适合作为 python 图像处理入门练习。掌握这些基础之后,可以继续扩展水印添加、图片裁剪、缩略图生成、图片重命名、exif 信息读取等功能,逐步做成一个更完整的图片处理工具箱。
以上就是使用python+pillow开发一个图片批量格式转换工具的详细内容,更多关于python pillow图片格式转换工具的资料请关注代码网其它相关文章!
发表评论