日常开发或办公中,经常需要批量处理图片:统一尺寸、添加水印、格式转换或压缩大小。Python的Pillow库(PIL的升级版)可以高效完成这些重复操作,无需逐张手动处理。本文从基础操作到完整工作流,提供可直接复用的脚本代码,并针对常见报错给出解决方案。
一、安装与导入
Pillow可通过pip安装:在脚本中导入核心模块:- from PIL import Image, ImageDraw, ImageFont, ImageFilter
复制代码
二、基础操作
打开图片后,可通过属性获取格式、尺寸和颜色模式:- img = Image.open("照片.jpg")
- print(f"格式: {img.format}")
- print(f"尺寸: {img.size}")
- print(f"模式: {img.mode}")
复制代码
调整尺寸有多种方式:- # 等比例缩放(指定宽度,高度自动计算)
- def resize_by_width(img, target_width=800):
- w, h = img.size
- ratio = target_width / w
- new_h = int(h * ratio)
- return img.resize((target_width, new_h), Image.LANCZOS)
- # 裁剪为正方形(从中心切)
- def crop_square(img):
- w, h = img.size
- min_side = min(w, h)
- left = (w - min_side) // 2
- top = (h - min_side) // 2
- right = left + min_side
- bottom = top + min_side
- return img.crop((left, top, right, bottom))
- # 强制缩放到指定尺寸(可能变形)
- img_resized = img.resize((1920, 1080), Image.LANCZOS)
- # 直接调用thumbnail方法,原图被修改,最长边不超过给定值
- img.thumbnail((800, 600))
复制代码
旋转与翻转:- # rotation (expand=True可防止裁剪)
- rotated = img.rotate(90, expand=True)
- # horizontal flip
- flipped_h = img.transpose(Image.FLIP_LEFT_RIGHT)
- # vertical flip
- flipped_v = img.transpose(Image.FLIP_TOP_BOTTOM)
复制代码
三、实战案例:批量处理婚纱照
以下PhotoProcessor类封装了三个常用功能:压缩到微信分享尺寸、添加文字水印、创建拼图。可直接用于影楼或个人的批量处理。- from PIL import Image, ImageDraw, ImageFont
- import os
- class PhotoProcessor:
- def __init__(self, input_dir, output_dir):
- self.input_dir = input_dir
- self.output_dir = output_dir
- os.makedirs(output_dir, exist_ok=True)
- def resize_for_wechat(self, max_width=1080):
- """批量压缩至适合微信分享的尺寸(最长边不超过max_width)"""
- for f in os.listdir(self.input_dir):
- if not f.lower().endswith((".jpg", ".jpeg", ".png")):
- continue
- img = Image.open(os.path.join(self.input_dir, f))
- w, h = img.size
- if max(w, h) > max_width:
- ratio = max_width / max(w, h)
- new_size = (int(w * ratio), int(h * ratio))
- img = img.resize(new_size, Image.LANCZOS)
- output_path = os.path.join(self.output_dir, f"wechat_{f}")
- img.save(output_path, quality=85, optimize=True)
- print(f"已处理: {f}")
- def add_watermark(self, text="张政 & 爱妻", position="右下角"):
- """批量添加半透明水印,支持位置参数"""
- for f in os.listdir(self.input_dir):
- if not f.lower().endswith((".jpg", ".jpeg", ".png")):
- continue
- img = Image.open(os.path.join(self.input_dir, f)).convert("RGBA")
- watermark = Image.new("RGBA", img.size, (0, 0, 0, 0))
- draw = ImageDraw.Draw(watermark)
- font_size = max(img.size) // 30
- try:
- font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", font_size)
- except:
- font = ImageFont.load_default()
- bbox = draw.textbbox((0, 0), text, font=font)
- text_w, text_h = bbox[2] - bbox[0], bbox[3] - bbox[1]
- margin = 20
- if position == "右下角":
- x, y = img.width - text_w - margin, img.height - text_h - margin
- elif position == "左下角":
- x, y = margin, img.height - text_h - margin
- elif position == "居中":
- x, y = (img.width - text_w) // 2, (img.height - text_h) // 2
- draw.text((x, y), text, font=font, fill=(255, 255, 255, 100))
- result = Image.alpha_composite(img, watermark).convert("RGB")
- output_path = os.path.join(self.output_dir, f"wm_{f}")
- result.save(output_path, quality=95)
- print(f"已添加水印: {f}")
- def create_collage(self, photos_per_row=3):
- """创建照片拼图,适合朋友圈九宫格预览"""
- photos = [f for f in os.listdir(self.input_dir)
- if f.lower().endswith((".jpg", ".jpeg", ".png"))]
- if not photos:
- return
- thumb_size = 300
- rows = (len(photos) + photos_per_row - 1) // photos_per_row
- canvas = Image.new("RGB", (photos_per_row * thumb_size, rows * thumb_size), (255, 255, 255))
- for i, f in enumerate(photos):
- img = Image.open(os.path.join(self.input_dir, f))
- img.thumbnail((thumb_size - 10, thumb_size - 10))
- x = (i % photos_per_row) * thumb_size + 5
- y = (i // photos_per_row) * thumb_size + 5
- canvas.paste(img, (x, y))
- output_path = os.path.join(self.output_dir, "collage.jpg")
- canvas.save(output_path, quality=95)
- print(f"拼图已生成")
- # 使用示例
- processor = PhotoProcessor("原始照片", "输出照片")
- processor.resize_for_wechat(max_width=1080)
- # processor.add_watermark("张政 & 爱妻")
- # processor.create_collage(photos_per_row=3)
复制代码
四、其他常用场景
批量格式转换:- def batch_convert(input_dir, output_dir, target_format="png"):
- os.makedirs(output_dir, exist_ok=True)
- for f in os.listdir(input_dir):
- name, ext = os.path.splitext(f)
- if ext.lower() not in (".jpg", ".jpeg", ".png", ".bmp", ".webp"):
- continue
- img = Image.open(os.path.join(input_dir, f))
- img.save(os.path.join(output_dir, f"{name}.{target_format}"))
- print(f"已转换: {f}")
复制代码
批量压缩(可控制quality参数,减少文件大小):- def compress_images(input_dir, output_dir, quality=60):
- os.makedirs(output_dir, exist_ok=True)
- for f in os.listdir(input_dir):
- if not f.lower().endswith((".jpg", ".jpeg", ".png")):
- continue
- img = Image.open(os.path.join(input_dir, f))
- output_path = os.path.join(output_dir, f"compressed_{f}")
- img.save(output_path, quality=quality, optimize=True)
- orig_size = os.path.getsize(os.path.join(input_dir, f))
- new_size = os.path.getsize(output_path)
- ratio = (1 - new_size / orig_size) * 100
- print(f"{f}: 压缩 {ratio:.0f}%")
复制代码
添加滤镜效果和亮度/对比度调整:- from PIL import ImageEnhance
- img = Image.open("照片.jpg")
- # 滤镜
- blur = img.filter(ImageFilter.BLUR)
- contour = img.filter(ImageFilter.CONTOUR)
- emboss = img.filter(ImageFilter.EMBOSS)
- sharpen = img.filter(ImageFilter.SHARPEN)
- smooth = img.filter(ImageFilter.SMOOTH)
- # 转灰度
- gray = img.convert("L")
- # 增强亮度
- enhancer = ImageEnhance.Brightness(img)
- brighter = enhancer.enhance(1.3) # 增加30%
- # 增强对比度
- enhancer = ImageEnhance.Contrast(img)
- higher_contrast = enhancer.enhance(1.2)
复制代码
批量重命名(可自定义前缀和起始编号):- def batch_rename(directory, prefix="IMG_", start=1):
- files = [f for f in os.listdir(directory)
- if f.lower().endswith((".jpg", ".jpeg", ".png", ".webp"))]
- files.sort()
- for i, f in enumerate(files):
- ext = os.path.splitext(f)[1]
- new_name = f"{prefix}{start + i:03d}{ext}"
- os.rename(os.path.join(directory, f), os.path.join(directory, new_name))
- print(f"{f} → {new_name}")
复制代码
五、实际工作流
现实中可将上述函数串联:①从设备导出照片 → ②批量重命名 → ③调整尺寸/压缩 → ④添加水印 → ⑤挑选部分做滤镜/拼图 → ⑥发朋友圈或上传。每个步骤独立成函数,参数可调,不满意只需修改参数重新运行,无需手动逐张操作。
六、常见问题与解决
1. OSError: cannot identify image file
原因:文件扩展名与实际格式不一致或文件损坏。处理时用try包裹并验证:- try:
- img = Image.open(file)
- img.verify()
- except:
- print(f"跳过损坏文件: {file}")
复制代码
2. 中文文字显示为方框
原因:Pillow默认字体不支持中文。需要指定中文字体路径(Windows常见字体):- font = ImageFont.truetype("C:/Windows/Fonts/simhei.ttf", 36) # 黑体
- # 也可用 msyh.ttc (微软雅黑), simsun.ttc (宋体)
复制代码
3. 图片太大导致内存不足
处理超大图片前先缩小尺寸:- img = Image.open("超大图片.jpg")
- img.thumbnail((4000, 4000)) # 限制最长边4000像素
复制代码
总结:Pillow几乎覆盖日常所有图片处理需求。将以上代码保存为脚本,下次只需修改文件路径和参数即可批量执行,省去90%的手动操作时间。最常用的三个场景:批量改尺寸(发朋友圈/上传网站)、批量加水印(保护版权)、批量压缩(节省存储空间)。 |