Python自动化图片处理:批量压缩、转换、编辑一条龙
·
图片处理是日常工作的常见需求:压缩体积、格式转换、批量添加水印…每次手动操作繁琐又容易出错。今天教你用Python实现图片批量处理的自动化!
实战场景
- 批量压缩图片体积
- 图片格式互转(PNG/JPG/WebP)
- 批量添加水印或Logo
- 图片批量裁剪和缩放
核心实现
准备工作
pip install Pillow python-dotenv
基础版本:图片格式转换和压缩
from PIL import Image
import os
from pathlib import Path
from datetime import datetime
class ImageProcessor:
"""图片处理器"""
def __init__(self, input_dir='./images_input', output_dir='./images_output'):
"""
初始化处理器
Args:
input_dir: 输入目录
output_dir: 输出目录
"""
self.input_dir = input_dir
self.output_dir = output_dir
self._ensure_dir(output_dir)
def _ensure_dir(self, directory):
"""确保目录存在"""
if not os.path.exists(directory):
os.makedirs(directory)
def convert_format(self, input_file, output_format='JPEG', quality=85,
output_name=None):
"""
转换图片格式
Args:
input_file: 输入文件路径
output_format: 输出格式(JPEG/PNG/WebP等)
quality: 图片质量(1-100)
output_name: 输出文件名
Returns:
输出文件路径
"""
img = Image.open(input_file)
# 转换RGBA为RGB(JPEG不支持透明通道)
if output_format.upper() == 'JPEG' and img.mode == 'RGBA':
img = img.convert('RGB')
# 生成输出文件名
if not output_name:
name = Path(input_file).stem
output_name = f"{name}.{output_format.lower()}"
output_path = os.path.join(self.output_dir, output_name)
# 保存
save_kwargs = {'quality': quality}
if output_format.upper() == 'PNG':
save_kwargs.pop('quality', None)
save_kwargs['optimize'] = True
img.save(output_path, output_format.upper(), **save_kwargs)
print(f"转换完成: {output_name}")
return output_path
def batch_convert_format(self, input_dir=None, output_format='JPEG',
quality=85):
"""
批量转换格式
Args:
input_dir: 输入目录
output_format: 输出格式
quality: 图片质量
Returns:
转换后的文件列表
"""
input_dir = input_dir or self.input_dir
converted_files = []
extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp']
for filename in os.listdir(input_dir):
ext = Path(filename).suffix.lower()
if ext in extensions:
input_path = os.path.join(input_dir, filename)
try:
output_path = self.convert_format(
input_path,
output_format,
quality
)
converted_files.append(output_path)
except Exception as e:
print(f"转换失败: {filename}, 错误: {e}")
print(f"批量转换完成,共 {len(converted_files)} 个文件")
return converted_files
def compress_image(self, input_file, max_size_kb=500, quality=85,
output_name=None):
"""
压缩图片
Args:
input_file: 输入文件路径
max_size_kb: 最大文件大小(KB)
quality: 初始质量
output_name: 输出文件名
Returns:
输出文件路径
"""
img = Image.open(input_file)
# 生成输出文件名
if not output_name:
name = Path(input_file).stem
output_name = f"{name}_compressed.jpg"
output_path = os.path.join(self.output_dir, output_name)
# 逐步降低质量直到满足大小要求
current_quality = quality
while current_quality > 10:
img.save(output_path, 'JPEG', quality=current_quality)
file_size = os.path.getsize(output_path) / 1024
if file_size <= max_size_kb:
print(f"压缩完成: {output_name} (质量={current_quality}, 大小={file_size:.1f}KB)")
return output_path
current_quality -= 10
print(f"警告: 未能压缩到 {max_size_kb}KB,当前大小 {file_size:.1f}KB")
return output_path
def resize_image(self, input_file, width=None, height=None,
keep_aspect_ratio=True, output_name=None):
"""
调整图片尺寸
Args:
input_file: 输入文件路径
width: 目标宽度
height: 目标高度
keep_aspect_ratio: 是否保持宽高比
output_name: 输出文件名
Returns:
输出文件路径
"""
img = Image.open(input_file)
original_width, original_height = img.size
# 计算新尺寸
if keep_aspect_ratio:
if width and not height:
height = int(original_height * width / original_width)
elif height and not width:
width = int(original_width * height / original_height)
elif width and height:
ratio_w = width / original_width
ratio_h = height / original_height
ratio = min(ratio_w, ratio_h)
width = int(original_width * ratio)
height = int(original_height * ratio)
else:
width = width or original_width
height = height or original_height
# 调整尺寸
resized_img = img.resize((width, height), Image.Resampling.LANCZOS)
# 生成输出文件名
if not output_name:
name = Path(input_file).stem
output_name = f"{name}_{width}x{height}.jpg"
output_path = os.path.join(self.output_dir, output_name)
resized_img.save(output_path, 'JPEG', quality=90)
print(f"调整尺寸完成: {output_name} ({width}x{height})")
return output_path
进阶版本:图片编辑和批处理
from PIL import Image, ImageDraw, ImageFont, ImageFilter
class ImageEditor:
"""图片编辑器"""
def __init__(self, output_dir='./images_output'):
"""
初始化编辑器
Args:
output_dir: 输出目录
"""
self.output_dir = output_dir
os.makedirs(output_dir, exist_ok=True)
def add_watermark_text(self, input_file, text, position='bottom_right',
font_size=36, color=(255, 255, 255, 128)):
"""
添加文字水印
Args:
input_file: 输入文件路径
text: 水印文字
position: 位置(bottom_right/bottom_left/top_right/top_left/center)
font_size: 字体大小
color: 颜色
Returns:
输出文件路径
"""
img = Image.open(input_file).convert('RGBA')
# 创建水印层
txt_layer = Image.new('RGBA', img.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(txt_layer)
# 尝试加载字体
try:
font = ImageFont.truetype("arial.ttf", font_size)
except:
font = ImageFont.load_default()
# 计算文字位置
bbox = draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
padding = 20
img_width, img_height = img.size
positions = {
'bottom_right': (img_width - text_width - padding,
img_height - text_height - padding),
'bottom_left': (padding, img_height - text_height - padding),
'top_right': (img_width - text_width - padding, padding),
'top_left': (padding, padding),
'center': ((img_width - text_width) // 2,
(img_height - text_height) // 2)
}
pos = positions.get(position, positions['bottom_right'])
# 绘制阴影
shadow_offset = 2
draw.text((pos[0] + shadow_offset, pos[1] + shadow_offset),
text, font=font, fill=(0, 0, 0, 64))
# 绘制文字
draw.text(pos, text, font=font, fill=color)
# 合并图层
watermarked = Image.alpha_composite(img, txt_layer)
output_name = f"{Path(input_file).stem}_watermarked.png"
output_path = os.path.join(self.output_dir, output_name)
watermarked.save(output_path)
print(f"添加水印完成: {output_name}")
return output_path
def add_watermark_image(self, input_file, watermark_file,
position='bottom_right', opacity=0.5):
"""
添加图片水印
Args:
input_file: 输入文件路径
watermark_file: 水印图片路径
position: 位置
opacity: 透明度
Returns:
输出文件路径
"""
img = Image.open(input_file).convert('RGBA')
watermark = Image.open(watermark_file).convert('RGBA')
# 调整水印大小(为原图的20%)
wm_scale = min(img.width, img.height) * 0.2 / max(watermark.size)
new_size = (int(watermark.width * wm_scale),
int(watermark.height * wm_scale))
watermark = watermark.resize(new_size, Image.Resampling.LANCZOS)
# 调整透明度
watermark = Image.new('RGBA', watermark.size)
watermark.putalpha(int(255 * opacity))
# 计算位置
padding = 20
positions = {
'bottom_right': (img.width - watermark.width - padding,
img.height - watermark.height - padding),
'bottom_left': (padding, img.height - watermark.height - padding),
'top_right': (img.width - watermark.width - padding, padding),
'top_left': (padding, padding),
'center': ((img.width - watermark.width) // 2,
(img.height - watermark.height) // 2)
}
pos = positions.get(position, positions['bottom_right'])
# 创建带水印的图片
result = Image.new('RGBA', img.size)
result.paste(img, (0, 0))
result.paste(watermark, pos, watermark)
output_name = f"{Path(input_file).stem}_logo.png"
output_path = os.path.join(self.output_dir, output_name)
result.save(output_path)
print(f"添加Logo水印完成: {output_name}")
return output_path
def create_thumbnail(self, input_file, size=(200, 200)):
"""
创建缩略图
Args:
input_file: 输入文件路径
size: 缩略图尺寸
Returns:
输出文件路径
"""
img = Image.open(input_file)
# 使用thumbnail保持比例
img.thumbnail(size, Image.Resampling.LANCZOS)
output_name = f"{Path(input_file).stem}_thumb.jpg"
output_path = os.path.join(self.output_dir, output_name)
img.save(output_path, 'JPEG', quality=85)
print(f"缩略图创建完成: {output_name}")
return output_path
def apply_filter(self, input_file, filter_type='blur'):
"""
应用滤镜
Args:
input_file: 输入文件路径
filter_type: 滤镜类型(blur/sharpen/edge/emboss)
Returns:
输出文件路径
"""
img = Image.open(input_file)
filters = {
'blur': ImageFilter.BLUR,
'sharpen': ImageFilter.SHARPEN,
'edge': ImageFilter.FIND_EDGES,
'emboss': ImageFilter.EMBOSS,
'smooth': ImageFilter.SMOOTH,
}
filtered_img = img.filter(filters.get(filter_type, ImageFilter.BLUR))
output_name = f"{Path(input_file).stem}_{filter_type}.jpg"
output_path = os.path.join(self.output_dir, output_name)
filtered_img.save(output_path)
print(f"滤镜应用完成: {output_name}")
return output_path
实战:自动化图片处理工作流
class BatchImageProcessor:
"""批量图片处理工作流"""
def __init__(self, input_dir, output_dir):
"""
初始化处理器
Args:
input_dir: 输入目录
output_dir: 输出目录
"""
self.input_dir = input_dir
self.output_dir = output_dir
self.processor = ImageProcessor(input_dir, output_dir)
self.editor = ImageEditor(output_dir)
def process_product_images(self, compress=True, resize_to=(800, 800),
add_watermark=True, watermark_text="@店铺名"):
"""
处理商品图片工作流
Args:
compress: 是否压缩
resize_to: 调整尺寸
add_watermark: 是否添加水印
watermark_text: 水印文字
Returns:
处理完成的文件列表
"""
processed_files = []
extensions = ['.jpg', '.jpeg', '.png', '.webp']
for filename in os.listdir(self.input_dir):
ext = Path(filename).suffix.lower()
if ext not in extensions:
continue
input_path = os.path.join(self.input_dir, filename)
temp_path = input_path
try:
# 1. 调整尺寸
if resize_to:
temp_path = self.processor.resize_image(
input_path,
width=resize_to[0],
height=resize_to[1],
output_name=f"_temp_{filename}"
)
# 2. 压缩
if compress:
temp_path = self.processor.compress_image(
temp_path,
max_size_kb=500,
output_name=f"_temp_comp_{filename}"
)
# 3. 添加水印
if add_watermark:
temp_path = self.editor.add_watermark_text(
temp_path,
watermark_text,
position='bottom_right',
font_size=24
)
processed_files.append(temp_path)
print(f"处理完成: {filename}")
except Exception as e:
print(f"处理失败: {filename}, 错误: {e}")
print(f"\n工作流处理完成,共 {len(processed_files)} 个文件")
return processed_files
def generate_image_report(self, processed_files):
"""
生成处理报告
Args:
processed_files: 处理后的文件列表
"""
report_path = os.path.join(self.output_dir, '处理报告.txt')
total_size = sum(os.path.getsize(f) for f in processed_files)
with open(report_path, 'w', encoding='utf-8') as f:
f.write(f"图片处理报告\n")
f.write(f"=" * 50 + "\n")
f.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"处理文件数: {len(processed_files)}\n")
f.write(f"总大小: {total_size / 1024:.1f} KB\n")
f.write(f"平均大小: {total_size / len(processed_files) / 1024:.1f} KB\n")
f.write(f"\n文件列表:\n")
for idx, filepath in enumerate(processed_files, 1):
size = os.path.getsize(filepath)
f.write(f"{idx}. {Path(filepath).name} ({size / 1024:.1f} KB)\n")
print(f"报告已生成: {report_path}")
return report_path
# 使用示例
def main():
# 格式转换
processor = ImageProcessor()
processor.convert_format('photo.png', 'JPEG', quality=85)
# 批量压缩
processor.batch_convert_format(
input_dir='./photos',
output_format='JPEG',
quality=80
)
# 批量处理工作流
workflow = BatchImageProcessor(
input_dir='./商品图片',
output_dir='./处理后图片'
)
processed = workflow.process_product_images(
compress=True,
resize_to=(1200, 1200),
add_watermark=True,
watermark_text="@扣扣优品"
)
# 生成报告
workflow.generate_image_report(processed)
if __name__ == '__main__':
main()
注意事项
- 保持原图:重要图片先备份再处理
- 质量平衡:压缩率太高会影响画质
- 透明通道:PNG转JPEG需要处理透明通道
- 内存占用:大图片处理注意内存管理
总结
本文介绍了Python图片处理的完整方案,从基础格式转换、压缩、调整尺寸,到进阶的添加水印、滤镜应用,再到自动化的批量处理工作流。掌握这些技能后,你可以轻松实现各种图片处理需求!
更多推荐


所有评论(0)