logo

Python PIL批量图片文字水印添加全攻略

作者:暴富20212025.10.10 17:03浏览量:3

简介:本文详细介绍如何使用Python的PIL库批量为图片添加文字水印,包括基础实现、样式优化、批量处理技巧及性能优化策略,适合开发者快速掌握高效水印添加方法。

PIL如何批量给图片添加文字水印?

在图像处理领域,为图片添加文字水印是保护版权、标注信息的重要手段。Python的PIL(Pillow)库因其简单易用、功能强大,成为批量处理图片水印的首选工具。本文将从基础实现到进阶优化,系统讲解如何使用PIL高效完成批量文字水印添加。

一、PIL基础:文字水印的核心实现

1.1 安装与导入

PIL的安装通过pip即可完成:

  1. pip install pillow

导入库后,需创建ImageImageDraw对象:

  1. from PIL import Image, ImageDraw, ImageFont

1.2 单张图片水印添加

核心步骤分为三步:

  1. 打开图片:使用Image.open()加载图片
  2. 创建绘图对象:通过ImageDraw.Draw()获取绘图上下文
  3. 绘制文字:指定位置、字体、颜色后调用text()方法

示例代码:

  1. def add_watermark(input_path, output_path, text):
  2. # 打开图片
  3. img = Image.open(input_path)
  4. draw = ImageDraw.Draw(img)
  5. # 设置字体(需指定字体文件路径)
  6. try:
  7. font = ImageFont.truetype("arial.ttf", 40)
  8. except IOError:
  9. font = ImageFont.load_default()
  10. # 计算文字位置(右下角为例)
  11. text_width, text_height = draw.textsize(text, font=font)
  12. position = (img.width - text_width - 10, img.height - text_height - 10)
  13. # 添加半透明效果
  14. from PIL import ImageColor
  15. transparent_color = ImageColor.getrgb("white") + (128,) # RGBA格式
  16. # 创建透明图层(需转换为RGBA模式)
  17. if img.mode != 'RGBA':
  18. img = img.convert('RGBA')
  19. # 绘制带透明度的文字
  20. tmp = Image.new('RGBA', img.size, (0, 0, 0, 0))
  21. draw_tmp = ImageDraw.Draw(tmp)
  22. draw_tmp.text(position, text, fill=transparent_color, font=font)
  23. # 合并图层
  24. img = Image.alpha_composite(img, tmp)
  25. img = img.convert('RGB') # 转换回RGB模式保存
  26. img.save(output_path)

1.3 关键参数说明

  • 字体设置:推荐使用truetype()加载系统字体,默认字体可能在不同平台表现不一致
  • 位置计算:通过textsize()获取文字尺寸,结合图片宽高实现精准定位
  • 透明度控制:RGBA模式中的A值(0-255)控制透明度,128表示50%透明

二、批量处理:从单张到批量的技术跃迁

2.1 基础批量实现

使用os.listdir()遍历文件夹:

  1. import os
  2. def batch_watermark(input_dir, output_dir, text):
  3. if not os.path.exists(output_dir):
  4. os.makedirs(output_dir)
  5. for filename in os.listdir(input_dir):
  6. if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
  7. input_path = os.path.join(input_dir, filename)
  8. output_path = os.path.join(output_dir, filename)
  9. add_watermark(input_path, output_path, text)

2.2 多线程优化

对于大量图片,使用concurrent.futures提升处理速度:

  1. from concurrent.futures import ThreadPoolExecutor
  2. def parallel_watermark(input_dir, output_dir, text, max_workers=4):
  3. if not os.path.exists(output_dir):
  4. os.makedirs(output_dir)
  5. def process_file(filename):
  6. input_path = os.path.join(input_dir, filename)
  7. output_path = os.path.join(output_dir, filename)
  8. add_watermark(input_path, output_path, text)
  9. filenames = [f for f in os.listdir(input_dir)
  10. if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
  11. with ThreadPoolExecutor(max_workers=max_workers) as executor:
  12. executor.map(process_file, filenames)

2.3 进度显示

添加tqdm库显示处理进度:

  1. from tqdm import tqdm
  2. def batch_with_progress(input_dir, output_dir, text):
  3. if not os.path.exists(output_dir):
  4. os.makedirs(output_dir)
  5. filenames = [f for f in os.listdir(input_dir)
  6. if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
  7. for filename in tqdm(filenames, desc="Processing"):
  8. input_path = os.path.join(input_dir, filename)
  9. output_path = os.path.join(output_dir, filename)
  10. add_watermark(input_path, output_path, text)

三、进阶技巧:提升水印质量与效率

3.1 文字样式优化

  • 描边效果:通过绘制两次文字实现

    1. def add_outlined_text(draw, position, text, font, fill_color, outline_color, outline_width=1):
    2. # 先绘制描边
    3. for x in range(-outline_width, outline_width+1):
    4. for y in range(-outline_width, outline_width+1):
    5. if x != 0 or y != 0: # 中心点不绘制
    6. draw.text((position[0]+x, position[1]+y), text, font=font, fill=outline_color)
    7. # 再绘制填充文字
    8. draw.text(position, text, font=font, fill=fill_color)
  • 阴影效果:类似描边但使用半透明颜色

3.2 动态位置计算

根据图片尺寸自动调整文字大小:

  1. def auto_adjust_font(draw, text, max_width, max_height, min_size=10, max_size=100):
  2. for size in range(max_size, min_size-1, -1):
  3. try:
  4. font = ImageFont.truetype("arial.ttf", size)
  5. w, h = draw.textsize(text, font=font)
  6. if w <= max_width and h <= max_height:
  7. return font
  8. except:
  9. continue
  10. return ImageFont.load_default()

3.3 性能优化策略

  1. 字体缓存:避免重复加载字体文件
    ```python
    font_cache = {}

def get_cached_font(font_path, size):
key = (font_path, size)
if key not in font_cache:
try:
font_cache[key] = ImageFont.truetype(font_path, size)
except:
font_cache[key] = ImageFont.load_default()
return font_cache[key]

  1. 2. **内存管理**:及时关闭图片对象
  2. ```python
  3. with Image.open(input_path) as img:
  4. draw = ImageDraw.Draw(img)
  5. # 处理逻辑
  6. img.save(output_path)

四、完整实现示例

  1. import os
  2. from PIL import Image, ImageDraw, ImageFont
  3. from concurrent.futures import ThreadPoolExecutor
  4. from tqdm import tqdm
  5. class WatermarkBatchProcessor:
  6. def __init__(self, font_path="arial.ttf"):
  7. self.font_path = font_path
  8. self.font_cache = {}
  9. def get_font(self, size):
  10. key = (self.font_path, size)
  11. if key not in self.font_cache:
  12. try:
  13. self.font_cache[key] = ImageFont.truetype(self.font_path, size)
  14. except IOError:
  15. self.font_cache[key] = ImageFont.load_default()
  16. return self.font_cache[key]
  17. def add_watermark(self, img_path, output_path, text, position="bottom_right",
  18. font_size=40, opacity=128, color="white", outline_color=None, outline_width=1):
  19. with Image.open(img_path) as img:
  20. draw = ImageDraw.Draw(img)
  21. font = self.get_font(font_size)
  22. # 计算文字尺寸
  23. text_width, text_height = draw.textsize(text, font=font)
  24. # 确定位置
  25. if position == "bottom_right":
  26. x = img.width - text_width - 10
  27. y = img.height - text_height - 10
  28. elif position == "top_left":
  29. x, y = 10, 10
  30. # 可扩展其他位置
  31. # 创建透明图层
  32. if img.mode != 'RGBA':
  33. img = img.convert('RGBA')
  34. tmp = Image.new('RGBA', img.size, (0, 0, 0, 0))
  35. draw_tmp = ImageDraw.Draw(tmp)
  36. # 设置颜色(带透明度)
  37. r, g, b = ImageColor.getrgb(color)
  38. fill_color = (r, g, b, opacity)
  39. # 添加描边(如果需要)
  40. if outline_color:
  41. o_r, o_g, o_b = ImageColor.getrgb(outline_color)
  42. outline_color = (o_r, o_g, o_b, opacity)
  43. # 实现描边逻辑(简化版)
  44. pass # 实际实现参考3.1节
  45. # 绘制文字
  46. draw_tmp.text((x, y), text, fill=fill_color, font=font)
  47. # 合并图层
  48. img = Image.alpha_composite(img, tmp)
  49. img = img.convert('RGB')
  50. img.save(output_path)
  51. def process_batch(self, input_dir, output_dir, text, max_workers=4, **kwargs):
  52. if not os.path.exists(output_dir):
  53. os.makedirs(output_dir)
  54. filenames = [f for f in os.listdir(input_dir)
  55. if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
  56. def process_file(filename):
  57. input_path = os.path.join(input_dir, filename)
  58. output_path = os.path.join(output_dir, filename)
  59. self.add_watermark(input_path, output_path, text, **kwargs)
  60. with ThreadPoolExecutor(max_workers=max_workers) as executor:
  61. list(tqdm(executor.map(process_file, filenames),
  62. total=len(filenames), desc="Processing"))
  63. # 使用示例
  64. if __name__ == "__main__":
  65. processor = WatermarkBatchProcessor()
  66. processor.process_batch(
  67. input_dir="input_images",
  68. output_dir="output_images",
  69. text="Sample Watermark",
  70. position="bottom_right",
  71. font_size=36,
  72. opacity=150,
  73. color="red",
  74. max_workers=8
  75. )

五、常见问题解决方案

  1. 中文显示问题

    • 确保使用支持中文的字体文件(如simhei.ttf
    • 错误处理:
      1. try:
      2. font = ImageFont.truetype("simhei.ttf", 40)
      3. except IOError:
      4. print("警告:无法加载中文字体,将使用默认字体")
      5. font = ImageFont.load_default()
  2. 图片模式兼容性

    • 处理前统一转换为RGBA模式
    • 保存时根据需要转换回RGB
  3. 性能瓶颈分析

    • 使用cProfile分析耗时操作
    • 典型瓶颈:字体加载、磁盘I/O

六、最佳实践建议

  1. 字体管理

    • 将常用字体放在项目目录下
    • 使用相对路径加载字体
  2. 配置分离

    • 将水印参数(位置、颜色等)提取为配置文件
    • 示例配置:
      1. {
      2. "text": "Copyright 2023",
      3. "font": "fonts/arial.ttf",
      4. "position": "bottom_right",
      5. "color": "#FFFFFF",
      6. "opacity": 128,
      7. "font_size": 36
      8. }
  3. 异常处理

    • 捕获并记录处理失败的图片
    • 实现重试机制

通过本文介绍的完整方案,开发者可以高效实现PIL批量图片文字水印添加,兼顾功能完整性与性能优化。实际项目中,建议根据具体需求调整参数,并通过单元测试验证处理效果。

相关文章推荐

发表评论

活动