Python 使用 Pillow 库批量处理图片的完整示例
Pillow 是 Python 中功能强大的图像处理库,下面我将展示如何使用它进行批量图片处理。
1. 安装 Pillow
首先确保已安装 Pillow:
pip install Pillow
2. 基本批量处理示例
2.1 批量调整图片尺寸
from PIL import Image
import os
def batch_resize_images(input_folder, output_folder, target_size=(800, 600)):
"""
批量调整图片尺寸
参数:
input_folder: 输入文件夹路径
output_folder: 输出文件夹路径
target_size: 目标尺寸 (宽, 高)
"""
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 支持的图片格式
supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif')
# 遍历输入文件夹
for filename in os.listdir(input_folder):
# 检查文件格式
if filename.lower().endswith(supported_formats):
try:
# 打开图片
img_path = os.path.join(input_folder, filename)
img = Image.open(img_path)
# 调整尺寸(保持宽高比)
img.thumbnail(target_size, Image.Resampling.LANCZOS)
# 保存图片
output_path = os.path.join(output_folder, filename)
img.save(output_path)
print(f"已处理: {filename}")
except Exception as e:
print(f"处理 {filename} 时出错: {e}")
print("批量调整尺寸完成!")
# 使用示例
if __name__ == "__main__":
input_dir = "./input_images" # 输入图片文件夹
output_dir = "./output_resized" # 输出图片文件夹
batch_resize_images(input_dir, output_dir, (800, 600))
2.2 批量转换图片格式
from PIL import Image
import os
def batch_convert_format(input_folder, output_folder, target_format='JPEG', quality=95):
"""
批量转换图片格式
参数:
input_folder: 输入文件夹路径
output_folder: 输出文件夹路径
target_format: 目标格式 ('JPEG', 'PNG', 'BMP', 'GIF'等)
quality: 图片质量(仅对JPEG有效,1-100)
"""
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 遍历输入文件夹
for filename in os.listdir(input_folder):
try:
# 打开图片
img_path = os.path.join(input_folder, filename)
img = Image.open(img_path)
# 生成输出文件名(修改扩展名)
name, ext = os.path.splitext(filename)
output_filename = f"{name}.{target_format.lower()}"
output_path = os.path.join(output_folder, output_filename)
# 转换并保存
if target_format.upper() == 'JPEG':
# JPEG格式需要转换RGBA模式
if img.mode in ('RGBA', 'LA', 'P'):
# 创建白色背景
rgb_img = Image.new('RGB', img.size, (255, 255, 255))
rgb_img.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
img = rgb_img
img.save(output_path, target_format, quality=quality)
else:
img.save(output_path, target_format)
print(f"已转换: {filename} -> {output_filename}")
except Exception as e:
print(f"处理 {filename} 时出错: {e}")
print("批量格式转换完成!")
# 使用示例
if __name__ == "__main__":
input_dir = "./input_images"
output_dir = "./output_converted"
batch_convert_format(input_dir, output_dir, 'PNG')
3. 高级批量处理功能
3.1 批量添加水印
from PIL import Image, ImageDraw, ImageFont
import os
def batch_add_watermark(input_folder, output_folder, watermark_text="Copyright",
position='bottom-right', opacity=0.5):
"""
批量添加文字水印
参数:
input_folder: 输入文件夹路径
output_folder: 输出文件夹路径
watermark_text: 水印文字
position: 水印位置 ('top-left', 'top-right', 'bottom-left', 'bottom-right', 'center')
opacity: 水印透明度 (0.0-1.0)
"""
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 支持的图片格式
supported_formats = ('.jpg', '.jpeg', '.png', '.bmp')
for filename in os.listdir(input_folder):
if filename.lower().endswith(supported_formats):
try:
# 打开图片
img_path = os.path.join(input_folder, filename)
img = Image.open(img_path).convert('RGBA')
# 创建水印图层
watermark = Image.new('RGBA', img.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(watermark)
# 设置字体(注意:需要字体文件,这里使用默认字体)
try:
font = ImageFont.truetype("arial.ttf", 40)
except:
font = ImageFont.load_default()
# 计算水印位置
text_bbox = draw.textbbox((0, 0), watermark_text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
if position == 'top-left':
position_coords = (10, 10)
elif position == 'top-right':
position_coords = (img.width - text_width - 10, 10)
elif position == 'bottom-left':
position_coords = (10, img.height - text_height - 10)
elif position == 'bottom-right':
position_coords = (img.width - text_width - 10, img.height - text_height - 10)
else: # center
position_coords = ((img.width - text_width) // 2, (img.height - text_height) // 2)
# 绘制半透明水印
draw.text(position_coords, watermark_text, font=font,
fill=(255, 255, 255, int(255 * opacity)))
# 合并图片和水印
watermarked = Image.alpha_composite(img, watermark)
# 保存图片
output_path = os.path.join(output_folder, filename)
watermarked.convert('RGB').save(output_path)
print(f"已添加水印: {filename}")
except Exception as e:
print(f"处理 {filename} 时出错: {e}")
print("批量添加水印完成!")
# 使用示例
if __name__ == "__main__":
input_dir = "./input_images"
output_dir = "./output_watermarked"
batch_add_watermark(input_dir, output_dir, "© 2023 MyCompany", 'bottom-right', 0.7)
3.2 批量调整亮度和对比度
from PIL import Image, ImageEnhance
import os
def batch_adjust_images(input_folder, output_folder, brightness=1.0,
contrast=1.0, saturation=1.0, sharpness=1.0):
"""
批量调整图片的亮度、对比度、饱和度和锐度
参数:
input_folder: 输入文件夹路径
output_folder: 输出文件夹路径
brightness: 亮度因子 (1.0为原始)
contrast: 对比度因子 (1.0为原始)
saturation: 饱和度因子 (1.0为原始)
sharpness: 锐度因子 (1.0为原始)
"""
if not os.path.exists(output_folder):
os.makedirs(output_folder)
supported_formats = ('.jpg', '.jpeg', '.png', '.bmp')
for filename in os.listdir(input_folder):
if filename.lower().endswith(supported_formats):
try:
img_path = os.path.join(input_folder, filename)
img = Image.open(img_path)
# 调整亮度
if brightness != 1.0:
enhancer = ImageEnhance.Brightness(img)
img = enhancer.enhance(brightness)
# 调整对比度
if contrast != 1.0:
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(contrast)
# 调整饱和度
if saturation != 1.0:
enhancer = ImageEnhance.Color(img)
img = enhancer.enhance(saturation)
# 调整锐度
if sharpness != 1.0:
enhancer = ImageEnhance.Sharpness(img)
img = enhancer.enhance(sharpness)
# 保存图片
output_path = os.path.join(output_folder, filename)
img.save(output_path)
print(f"已调整: {filename}")
except Exception as e:
print(f"处理 {filename} 时出错: {e}")
print("批量调整完成!")
# 使用示例
if __name__ == "__main__":
input_dir = "./input_images"
output_dir = "./output_adjusted"
# 增加亮度,增强对比度
batch_adjust_images(input_dir, output_dir, brightness=1.2, contrast=1.3)
4. 综合批量处理类
下面是一个更完整的批量处理类,包含多种功能:
from PIL import Image, ImageOps, ImageFilter, ImageEnhance, ImageDraw, ImageFont
import os
import time
class BatchImageProcessor:
"""批量图片处理器"""
def __init__(self, input_folder, output_folder):
self.input_folder = input_folder
self.output_folder = output_folder
self.supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff')
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
def get_image_files(self):
"""获取所有图片文件"""
image_files = []
for filename in os.listdir(self.input_folder):
if filename.lower().endswith(self.supported_formats):
image_files.append(filename)
return image_files
def batch_resize(self, target_size=(800, 600), keep_aspect=True):
"""批量调整尺寸"""
image_files = self.get_image_files()
for filename in image_files:
try:
img_path = os.path.join(self.input_folder, filename)
img = Image.open(img_path)
if keep_aspect:
# 保持宽高比
img.thumbnail(target_size, Image.Resampling.LANCZOS)
else:
# 强制调整到指定尺寸
img = img.resize(target_size, Image.Resampling.LANCZOS)
output_path = os.path.join(self.output_folder, filename)
img.save(output_path)
print(f"✓ 调整尺寸: {filename}")
except Exception as e:
print(f"✗ 处理 {filename} 时出错: {e}")
print(f"完成!共处理 {len(image_files)} 张图片")
def batch_convert(self, target_format='JPEG', quality=95):
"""批量转换格式"""
image_files = self.get_image_files()
for filename in image_files:
try:
img_path = os.path.join(self.input_folder, filename)
img = Image.open(img_path)
# 生成新文件名
name, ext = os.path.splitext(filename)
new_filename = f"{name}.{target_format.lower()}"
output_path = os.path.join(self.output_folder, new_filename)
# 保存为指定格式
img.save(output_path, target_format, quality=quality)
print(f"✓ 转换格式: {filename} -> {new_filename}")
except Exception as e:
print(f"✗ 处理 {filename} 时出错: {e}")
print(f"完成!共处理 {len(image_files)} 张图片")
def batch_rotate(self, angle=90):
"""批量旋转图片"""
image_files = self.get_image_files()
for filename in image_files:
try:
img_path = os.path.join(self.input_folder, filename)
img = Image.open(img_path)
# 旋转图片
rotated = img.rotate(angle, expand=True)
output_path = os.path.join(self.output_folder, filename)
rotated.save(output_path)
print(f"✓ 旋转图片: {filename} ({angle}度)")
except Exception as e:
print(f"✗ 处理 {filename} 时出错: {e}")
print(f"完成!共处理 {len(image_files)} 张图片")
def batch_grayscale(self):
"""批量转换为灰度图"""
image_files = self.get_image_files()
for filename in image_files:
try:
img_path = os.path.join(self.input_folder, filename)
img = Image.open(img_path)
# 转换为灰度
grayscale = ImageOps.grayscale(img)
output_path = os.path.join(self.output_folder, filename)
grayscale.save(output_path)
print(f"✓ 转换为灰度: {filename}")
except Exception as e:
print(f"✗ 处理 {filename} 时出错: {e}")
print(f"完成!共处理 {len(image_files)} 张图片")
def batch_apply_filter(self, filter_type='blur'):
"""批量应用滤镜"""
image_files = self.get_image_files()
filter_map = {
'blur': ImageFilter.BLUR,
'contour': ImageFilter.CONTOUR,
'detail': ImageFilter.DETAIL,
'edge_enhance': ImageFilter.EDGE_ENHANCE,
'emboss': ImageFilter.EMBOSS,
'sharpen': ImageFilter.SHARPEN,
'smooth': ImageFilter.SMOOTH,
}
if filter_type not in filter_map:
print(f"错误: 不支持的滤镜类型 '{filter_type}'")
return
for filename in image_files:
try:
img_path = os.path.join(self.input_folder, filename)
img = Image.open(img_path)
# 应用滤镜
filtered = img.filter(filter_map[filter_type])
output_path = os.path.join(self.output_folder, filename)
filtered.save(output_path)
print(f"✓ 应用滤镜: {filename} ({filter_type})")
except Exception as e:
print(f"✗ 处理 {filename} 时出错: {e}")
print(f"完成!共处理 {len(image_files)} 张图片")
def batch_add_border(self, border_size=10, border_color='white'):
"""批量添加边框"""
image_files = self.get_image_files()
# 将颜色名称转换为RGB值
color_map = {
'white': (255, 255, 255),
'black': (0, 0, 0),
'red': (255, 0, 0),
'green': (0, 255, 0),
'blue': (0, 0, 255),
'gray': (128, 128, 128),
}
if border_color in color_map:
border_rgb = color_map[border_color]
else:
# 如果不是预定义颜色,尝试解析
try:
border_rgb = tuple(int(border_color[i:i+2], 16) for i in (0, 2, 4))
except:
border_rgb = (255, 255, 255) # 默认白色
for filename in image_files:
try:
img_path = os.path.join(self.input_folder, filename)
img = Image.open(img_path)
# 添加边框
bordered = ImageOps.expand(img, border=border_size, fill=border_rgb)
output_path = os.path.join(self.output_folder, filename)
bordered.save(output_path)
print(f"✓ 添加边框: {filename}")
except Exception as e:
print(f"✗ 处理 {filename} 时出错: {e}")
print(f"完成!共处理 {len(image_files)} 张图片")
# 使用示例
if __name__ == "__main__":
# 创建处理器实例
processor = BatchImageProcessor(
input_folder="./input_images",
output_folder="./output_processed"
)
# 执行各种批量操作
print("=== 批量图片处理开始 ===")
# 1. 调整尺寸
print("\n1. 调整图片尺寸...")
processor.batch_resize(target_size=(1024, 768))
# 2. 转换为灰度图
print("\n2. 转换为灰度图...")
processor = BatchImageProcessor("./output_processed", "./output_grayscale")
processor.batch_grayscale()
# 3. 添加边框
print("\n3. 添加边框...")
processor = BatchImageProcessor("./output_grayscale", "./output_bordered")
processor.batch_add_border(border_size=20, border_color='black')
print("\n=== 批量图片处理完成 ===")
5. 使用示例脚本
创建一个完整的使用示例:
#!/usr/bin/env python3
"""
批量图片处理工具
使用: python batch_image_processor.py [操作] [输入文件夹] [输出文件夹] [参数...]
"""
import argparse
import sys
from PIL import Image, ImageOps
import os
def process_images(args):
"""根据参数处理图片"""
if not os.path.exists(args.input):
print(f"错误: 输入文件夹 '{args.input}' 不存在")
return
if not os.path.exists(args.output):
os.makedirs(args.output)
# 获取图片文件
image_files = []
supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif')
for filename in os.listdir(args.input):
if filename.lower().endswith(supported_formats):
image_files.append(filename)
if not image_files:
print("警告: 输入文件夹中没有支持的图片文件")
return
processed_count = 0
for filename in image_files:
try:
input_path = os.path.join(args.input, filename)
output_path = os.path.join(args.output, filename)
img = Image.open(input_path)
# 根据操作类型处理图片
if args.operation == 'resize':
img.thumbnail((args.width, args.height), Image.Resampling.LANCZOS)
elif args.operation == 'rotate':
img = img.rotate(args.angle, expand=True)
elif args.operation == 'grayscale':
img = ImageOps.grayscale(img)
elif args.operation == 'mirror':
if args.direction == 'horizontal':
img = ImageOps.mirror(img)
else: # vertical
img = ImageOps.flip(img)
elif args.operation == 'crop':
# 假设居中裁剪
width, height = img.size
left = (width - args.crop_width) // 2
top = (height - args.crop_height) // 2
right = left + args.crop_width
bottom = top + args.crop_height
img = img.crop((left, top, right, bottom))
# 保存图片
img.save(output_path)
processed_count += 1
print(f"已处理: {filename}")
except Exception as e:
print(f"处理 {filename} 时出错: {e}")
print(f"\n完成!成功处理 {processed_count}/{len(image_files)} 张图片")
def main():
parser = argparse.ArgumentParser(description='批量图片处理工具')
parser.add_argument('operation', choices=['resize', 'rotate', 'grayscale', 'mirror', 'crop'],
help='要执行的操作')
parser.add_argument('input', help='输入文件夹路径')
parser.add_argument('output', help='输出文件夹路径')
# 操作特定参数
parser.add_argument('--width', type=int, default=800, help='调整宽度 (用于resize操作)')
parser.add_argument('--height', type=int, default=600, help='调整高度 (用于resize操作)')
parser.add_argument('--angle', type=int, default=90, help='旋转角度 (用于rotate操作)')
parser.add_argument('--direction', choices=['horizontal', 'vertical'], default='horizontal',
help='镜像方向 (用于mirror操作)')
parser.add_argument('--crop-width', type=int, default=400, help='裁剪宽度 (用于crop操作)')
parser.add_argument('--crop-height', type=int, default=300, help='裁剪高度 (用于crop操作)')
args = parser.parse_args()
process_images(args)
if __name__ == "__main__":
main()
总结
Pillow 库为 Python 提供了强大的图像处理能力,通过上述示例,您可以:
批量调整图片尺寸 - 保持宽高比或强制调整
批量转换图片格式 - 在不同格式间转换
批量添加水印 - 添加文字或图片水印
批量调整图像属性 - 亮度、对比度、饱和度、锐度
批量应用滤镜 - 模糊、锐化、边缘检测等
批量执行其他操作 - 旋转、镜像、裁剪、添加边框等
这些示例可以根据您的具体需求进行调整和扩展,构建适合自己项目的批量图片处理工具。