在网站图片优化、客户交付或系统图标上传等场景中,经常需要把图片在 WebP、PNG、JPG 之间互相转换,同时兼顾体积压缩。手动用画图工具逐张处理效率太低,用 Python 结合 Pillow 可以快速实现批量转换与压缩。
Pillow 是 Python 最常用的图像处理库,它可以把任意常见格式的图片读取为统一的 Image 对象,再按指定格式保存。转换时主要涉及有损/无损格式差异、透明通道处理和压缩质量参数。JPG 使用有损压缩且不支持透明;PNG 使用无损压缩且支持透明;WebP 是 Google 推出的网页图片格式,体积通常更小;BMP 无压缩,TIFF 支持多层,适合存档。
环境准备:
pip install Pillow
代码实现分为三个工具函数。
一、单张格式转换
convert_image(input_file, output_format='jpg', quality=85, max_size=None) 接收输入路径、目标格式、质量值和最大尺寸。打开图片后,若指定 max_size 则用 thumbnail 等比缩放;若输出格式为 jpg/jpeg/bmp 且原图是 RGBA,则先用白色背景合成,去掉透明通道;输出文件名自动替换扩展名;保存时按格式设置不同参数:jpg/jpeg 用 quality+optimize,webp 用 quality+method=6,png 用 optimize。最后打印原图与新图体积差。
- import os
- from pathlib import Path
- from PIL import Image
- SUPPORTED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.webp', '.bmp', '.tiff', '.gif'}
- def convert_image(input_file, output_format='jpg', quality=85, max_size=None):
- input_path = Path(input_file)
- img = Image.open(input_path)
- if max_size:
- img.thumbnail(max_size, Image.LANCZOS)
- if output_format in ('jpg', 'jpeg', 'bmp') and img.mode == 'RGBA':
- background = Image.new('RGB', img.size, (255, 255, 255))
- background.paste(img, mask=img.split()[3])
- img = background
- output_file = input_path.with_suffix(f'.{output_format}')
- save_kwargs = {}
- if output_format in ('jpg', 'jpeg'):
- save_kwargs = {'quality': quality, 'optimize': True}
- elif output_format == 'webp':
- save_kwargs = {'quality': quality, 'method': 6}
- elif output_format == 'png':
- save_kwargs = {'optimize': True}
- img.save(output_file, **save_kwargs)
- original_size = input_path.stat().st_size
- new_size = output_file.stat().st_size
- ratio = (1 - new_size / original_size) * 100
- print(f"{input_path.name} ({original_size / 1024:.1f}KB) → "
- f"{output_file.name} ({new_size / 1024:.1f}KB) "
- f"[{'+' if ratio < 0 else '-'}{abs(ratio):.1f}%]")
- return output_file
复制代码
二、批量转换文件夹
batch_convert_folder(input_dir, output_dir, output_format='jpg', quality=85) 遍历输入目录中的所有支持格式文件,输出到指定目录。对 RGBA 透明图同样做白色背景处理;jpg/jpeg 使用 quality+optimize,webp 使用 quality+method=6。最后统计转换张数和节省空间。
- def batch_convert_folder(input_dir, output_dir, output_format='jpg', quality=85):
- input_path = Path(input_dir)
- output_path = Path(output_dir)
- output_path.mkdir(parents=True, exist_ok=True)
- count = 0
- total_saved = 0
- for file_path in input_path.iterdir():
- if file_path.is_file() and file_path.suffix.lower() in SUPPORTED_EXTENSIONS:
- output_file = output_path / f"{file_path.stem}.{output_format}"
- img = Image.open(file_path)
- original_size = file_path.stat().st_size
- if output_format in ('jpg', 'jpeg', 'bmp') and img.mode == 'RGBA':
- background = Image.new('RGB', img.size, (255, 255, 255))
- background.paste(img, mask=img.split()[3])
- img = background
- save_kwargs = {}
- if output_format in ('jpg', 'jpeg'):
- save_kwargs = {'quality': quality, 'optimize': True}
- elif output_format == 'webp':
- save_kwargs = {'quality': quality, 'method': 6}
- img.save(output_file, **save_kwargs)
- new_size = output_file.stat().st_size
- total_saved += original_size - new_size
- count += 1
- print(f"\n批量转换完成: {count} 张图片")
- print(f"节省空间: {total_saved / 1024 / 1024:.2f} MB")
复制代码
三、批量压缩(保持原格式)
batch_compress_images(input_dir, output_dir, quality=75, max_width=1920, max_height=1080) 遍历 jpg/jpeg/png/webp 文件,先将图片等比缩放到不超过 max_width×max_height,再按质量参数保存。注意:原代码中 save_kwargs 只在 jpg/jpeg 分支定义,若处理 png/webp 会报错;实际使用时需要为 png/webp 补充对应的保存参数。这一点在原作者提供的示例中也未覆盖,读者需留意。
- def batch_compress_images(input_dir, output_dir, quality=75, max_width=1920, max_height=1080):
- input_path = Path(input_dir)
- output_path = Path(output_dir)
- output_path.mkdir(parents=True, exist_ok=True)
- count = 0
- total_saved = 0
- for file_path in input_path.iterdir():
- if file_path.is_file() and file_path.suffix.lower() in {'.jpg', '.jpeg', '.png', '.webp'}:
- img = Image.open(file_path)
- if max_width and max_height:
- img.thumbnail((max_width, max_height), Image.LANCZOS)
- save_kwargs = {}
- if file_path.suffix.lower() in ('.jpg', '.jpeg'):
- save_kwargs = {'quality': quality, 'optimize': True}
- # 注意:原代码没有处理 png/webp 的保存参数
- output_file = output_path / file_path.name
- img.save(output_file, **save_kwargs)
- original_size = file_path.stat().st_size
- new_size = output_file.stat().st_size
- total_saved += original_size - new_size
- count += 1
- print(f"批量压缩完成: {count} 张图片")
- print(f"节省空间: {total_saved / 1024 / 1024:.2f} MB")
复制代码
四、使用示例
主入口示范了批量转 WebP 的调用方式。单张转换和批量压缩的调用已注释,按需放开即可。
- if __name__ == "__main__":
- # convert_image("photo.png", output_format='jpg', quality=85)
- batch_convert_folder("产品图片", "产品图片_WebP", output_format='webp', quality=80)
- # batch_compress_images("原始照片", "压缩照片", quality=75, max_width=1920)
复制代码
常见问题:
Q1: GIF 动图转换后不动了?
Pillow 默认只读取 GIF 的第一帧,所以动画会丢失。要保留动画,可以安装 imageio:pip install imageio,然后用 imageio.mimread 读取所有帧,再用 imageio.mimwrite 写出 WebP(duration=0.1 控制帧间隔)。
Q2: JPG 转 PNG 后体积变大很多?
因为 JPG 是有损压缩,PNG 是无损压缩,JPG 转 PNG 会把原本丢失的信息重新“放大”保存,导致体积增加。如果目标是减小体积,反向从 PNG 转 JPG 更合适。
总结:
- 格式转换:img.save('output.jpg') 即可自动按扩展名转换。
- 压缩质量:quality=75 左右能在体积和画质间取得平衡。
- 尺寸缩放:img.thumbnail() 按比例缩小,避免拉伸变形。
- 透明处理:JPG 不支持 alpha 通道,RGBA 转 RGB 时先用白色背景合成。
这样一套脚本可以覆盖日常图片处理需求,在批量转换和压缩场景下替代人工操作,节省大量时间。 |