查看: 111|回复: 3

Python Pillow批量处理图片:自动调整尺寸、添加水印和压缩的实战脚本

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
日常开发或办公中,经常需要批量处理图片:统一尺寸、添加水印、格式转换或压缩大小。Python的Pillow库(PIL的升级版)可以高效完成这些重复操作,无需逐张手动处理。本文从基础操作到完整工作流,提供可直接复用的脚本代码,并针对常见报错给出解决方案。

一、安装与导入

Pillow可通过pip安装:
  1. pip install Pillow
复制代码
在脚本中导入核心模块:
  1. from PIL import Image, ImageDraw, ImageFont, ImageFilter
复制代码

二、基础操作

打开图片后,可通过属性获取格式、尺寸和颜色模式:
  1. img = Image.open("照片.jpg")
  2. print(f"格式: {img.format}")
  3. print(f"尺寸: {img.size}")
  4. print(f"模式: {img.mode}")
复制代码

调整尺寸有多种方式:
  1. # 等比例缩放(指定宽度,高度自动计算)
  2. def resize_by_width(img, target_width=800):
  3.     w, h = img.size
  4.     ratio = target_width / w
  5.     new_h = int(h * ratio)
  6.     return img.resize((target_width, new_h), Image.LANCZOS)
  7. # 裁剪为正方形(从中心切)
  8. def crop_square(img):
  9.     w, h = img.size
  10.     min_side = min(w, h)
  11.     left = (w - min_side) // 2
  12.     top = (h - min_side) // 2
  13.     right = left + min_side
  14.     bottom = top + min_side
  15.     return img.crop((left, top, right, bottom))
  16. # 强制缩放到指定尺寸(可能变形)
  17. img_resized = img.resize((1920, 1080), Image.LANCZOS)
  18. # 直接调用thumbnail方法,原图被修改,最长边不超过给定值
  19. img.thumbnail((800, 600))
复制代码

旋转与翻转:
  1. # rotation (expand=True可防止裁剪)
  2. rotated = img.rotate(90, expand=True)
  3. # horizontal flip
  4. flipped_h = img.transpose(Image.FLIP_LEFT_RIGHT)
  5. # vertical flip
  6. flipped_v = img.transpose(Image.FLIP_TOP_BOTTOM)
复制代码

三、实战案例:批量处理婚纱照

以下PhotoProcessor类封装了三个常用功能:压缩到微信分享尺寸、添加文字水印、创建拼图。可直接用于影楼或个人的批量处理。
  1. from PIL import Image, ImageDraw, ImageFont
  2. import os
  3. class PhotoProcessor:
  4.     def __init__(self, input_dir, output_dir):
  5.         self.input_dir = input_dir
  6.         self.output_dir = output_dir
  7.         os.makedirs(output_dir, exist_ok=True)
  8.     def resize_for_wechat(self, max_width=1080):
  9.         """批量压缩至适合微信分享的尺寸(最长边不超过max_width)"""
  10.         for f in os.listdir(self.input_dir):
  11.             if not f.lower().endswith((".jpg", ".jpeg", ".png")):
  12.                 continue
  13.             img = Image.open(os.path.join(self.input_dir, f))
  14.             w, h = img.size
  15.             if max(w, h) > max_width:
  16.                 ratio = max_width / max(w, h)
  17.                 new_size = (int(w * ratio), int(h * ratio))
  18.                 img = img.resize(new_size, Image.LANCZOS)
  19.             output_path = os.path.join(self.output_dir, f"wechat_{f}")
  20.             img.save(output_path, quality=85, optimize=True)
  21.             print(f"已处理: {f}")
  22.     def add_watermark(self, text="张政 & 爱妻", position="右下角"):
  23.         """批量添加半透明水印,支持位置参数"""
  24.         for f in os.listdir(self.input_dir):
  25.             if not f.lower().endswith((".jpg", ".jpeg", ".png")):
  26.                 continue
  27.             img = Image.open(os.path.join(self.input_dir, f)).convert("RGBA")
  28.             watermark = Image.new("RGBA", img.size, (0, 0, 0, 0))
  29.             draw = ImageDraw.Draw(watermark)
  30.             font_size = max(img.size) // 30
  31.             try:
  32.                 font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", font_size)
  33.             except:
  34.                 font = ImageFont.load_default()
  35.             bbox = draw.textbbox((0, 0), text, font=font)
  36.             text_w, text_h = bbox[2] - bbox[0], bbox[3] - bbox[1]
  37.             margin = 20
  38.             if position == "右下角":
  39.                 x, y = img.width - text_w - margin, img.height - text_h - margin
  40.             elif position == "左下角":
  41.                 x, y = margin, img.height - text_h - margin
  42.             elif position == "居中":
  43.                 x, y = (img.width - text_w) // 2, (img.height - text_h) // 2
  44.             draw.text((x, y), text, font=font, fill=(255, 255, 255, 100))
  45.             result = Image.alpha_composite(img, watermark).convert("RGB")
  46.             output_path = os.path.join(self.output_dir, f"wm_{f}")
  47.             result.save(output_path, quality=95)
  48.             print(f"已添加水印: {f}")
  49.     def create_collage(self, photos_per_row=3):
  50.         """创建照片拼图,适合朋友圈九宫格预览"""
  51.         photos = [f for f in os.listdir(self.input_dir)
  52.                   if f.lower().endswith((".jpg", ".jpeg", ".png"))]
  53.         if not photos:
  54.             return
  55.         thumb_size = 300
  56.         rows = (len(photos) + photos_per_row - 1) // photos_per_row
  57.         canvas = Image.new("RGB", (photos_per_row * thumb_size, rows * thumb_size), (255, 255, 255))
  58.         for i, f in enumerate(photos):
  59.             img = Image.open(os.path.join(self.input_dir, f))
  60.             img.thumbnail((thumb_size - 10, thumb_size - 10))
  61.             x = (i % photos_per_row) * thumb_size + 5
  62.             y = (i // photos_per_row) * thumb_size + 5
  63.             canvas.paste(img, (x, y))
  64.         output_path = os.path.join(self.output_dir, "collage.jpg")
  65.         canvas.save(output_path, quality=95)
  66.         print(f"拼图已生成")
  67. # 使用示例
  68. processor = PhotoProcessor("原始照片", "输出照片")
  69. processor.resize_for_wechat(max_width=1080)
  70. # processor.add_watermark("张政 & 爱妻")
  71. # processor.create_collage(photos_per_row=3)
复制代码

四、其他常用场景

批量格式转换:
  1. def batch_convert(input_dir, output_dir, target_format="png"):
  2.     os.makedirs(output_dir, exist_ok=True)
  3.     for f in os.listdir(input_dir):
  4.         name, ext = os.path.splitext(f)
  5.         if ext.lower() not in (".jpg", ".jpeg", ".png", ".bmp", ".webp"):
  6.             continue
  7.         img = Image.open(os.path.join(input_dir, f))
  8.         img.save(os.path.join(output_dir, f"{name}.{target_format}"))
  9.         print(f"已转换: {f}")
复制代码

批量压缩(可控制quality参数,减少文件大小):
  1. def compress_images(input_dir, output_dir, quality=60):
  2.     os.makedirs(output_dir, exist_ok=True)
  3.     for f in os.listdir(input_dir):
  4.         if not f.lower().endswith((".jpg", ".jpeg", ".png")):
  5.             continue
  6.         img = Image.open(os.path.join(input_dir, f))
  7.         output_path = os.path.join(output_dir, f"compressed_{f}")
  8.         img.save(output_path, quality=quality, optimize=True)
  9.         orig_size = os.path.getsize(os.path.join(input_dir, f))
  10.         new_size = os.path.getsize(output_path)
  11.         ratio = (1 - new_size / orig_size) * 100
  12.         print(f"{f}: 压缩 {ratio:.0f}%")
复制代码

添加滤镜效果和亮度/对比度调整:
  1. from PIL import ImageEnhance
  2. img = Image.open("照片.jpg")
  3. # 滤镜
  4. blur = img.filter(ImageFilter.BLUR)
  5. contour = img.filter(ImageFilter.CONTOUR)
  6. emboss = img.filter(ImageFilter.EMBOSS)
  7. sharpen = img.filter(ImageFilter.SHARPEN)
  8. smooth = img.filter(ImageFilter.SMOOTH)
  9. # 转灰度
  10. gray = img.convert("L")
  11. # 增强亮度
  12. enhancer = ImageEnhance.Brightness(img)
  13. brighter = enhancer.enhance(1.3)  # 增加30%
  14. # 增强对比度
  15. enhancer = ImageEnhance.Contrast(img)
  16. higher_contrast = enhancer.enhance(1.2)
复制代码

批量重命名(可自定义前缀和起始编号):
  1. def batch_rename(directory, prefix="IMG_", start=1):
  2.     files = [f for f in os.listdir(directory)
  3.              if f.lower().endswith((".jpg", ".jpeg", ".png", ".webp"))]
  4.     files.sort()
  5.     for i, f in enumerate(files):
  6.         ext = os.path.splitext(f)[1]
  7.         new_name = f"{prefix}{start + i:03d}{ext}"
  8.         os.rename(os.path.join(directory, f), os.path.join(directory, new_name))
  9.         print(f"{f} → {new_name}")
复制代码

五、实际工作流

现实中可将上述函数串联:①从设备导出照片 → ②批量重命名 → ③调整尺寸/压缩 → ④添加水印 → ⑤挑选部分做滤镜/拼图 → ⑥发朋友圈或上传。每个步骤独立成函数,参数可调,不满意只需修改参数重新运行,无需手动逐张操作。

六、常见问题与解决

1. OSError: cannot identify image file
原因:文件扩展名与实际格式不一致或文件损坏。处理时用try包裹并验证:
  1. try:
  2.     img = Image.open(file)
  3.     img.verify()
  4. except:
  5.     print(f"跳过损坏文件: {file}")
复制代码

2. 中文文字显示为方框
原因:Pillow默认字体不支持中文。需要指定中文字体路径(Windows常见字体):
  1. font = ImageFont.truetype("C:/Windows/Fonts/simhei.ttf", 36)  # 黑体
  2. # 也可用 msyh.ttc (微软雅黑), simsun.ttc (宋体)
复制代码

3. 图片太大导致内存不足
处理超大图片前先缩小尺寸:
  1. img = Image.open("超大图片.jpg")
  2. img.thumbnail((4000, 4000))  # 限制最长边4000像素
复制代码

总结:Pillow几乎覆盖日常所有图片处理需求。将以上代码保存为脚本,下次只需修改文件路径和参数即可批量执行,省去90%的手动操作时间。最常用的三个场景:批量改尺寸(发朋友圈/上传网站)、批量加水印(保护版权)、批量压缩(节省存储空间)。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python Pillow批量处理图片:自动调整尺寸、添加水印和压缩的实战脚本

楼主的分享非常实用,正好最近在整理婚纱照,这个 `PhotoProcessor` 类直接拿来改改就能用。提一个小建议:`add_watermark` 方法里 `font` 加载那里如果系统没有中文字体可能会报错,可以在代码里加个 `ImageFont.load_default()` 作为 fallback,或者指定一个常见中文字体路径(比如 Windows 下的 `msyh.ttc`),这样更省心。另外裁剪正方形的逻辑很赞,下次需要头像裁剪直接调用了。感谢楼主的辛勤整理!
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python Pillow批量处理图片:自动调整尺寸、添加水印和压缩的实战脚本

感谢分享!这个批量处理脚本很实用,代码结构清晰,特别是`PhotoProcessor`类封装得方便复用。想请教一下,在`add_watermark`方法里,如果指定的字体文件不存在(比如系统没装对应中文字体),有什么推荐的兜底处理吗?另外注意到最后的`compress`函数好像没贴完,是打算后续再补充还是漏掉了?期待完整版,先收藏学习!
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Python Pillow批量处理图片:自动调整尺寸、添加水印和压缩的实战脚本

非常实用的脚本,感谢分享!特别是`PhotoProcessor`类的设计简洁易用,直接就能套用到自己的工作流里。有个小建议:`add_watermark`里字体路径是硬编码的,实际部署时最好加个`font_path`参数或者自动检测系统字体,不然Windows和Mac上容易报FileNotFoundError。另外压缩时如果能保留原图目录结构就更完美了,方便子文件夹批量处理。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-30 10:55 , Processed in 0.040477 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部