Python案例:如何给图片加水印?完整教程与代码解析
目录导读
- 为什么需要给图片加水印?
- Python实现水印的三种主流方案
- PIL/Pillow添加文字水印
- OpenCV添加半透明图片水印
- 批量处理与抗锯齿优化
- 常见问题与解答 (FAQ)
- SEO优化建议与总结
为什么需要给图片加水印?
创作、电商产品图、摄影作品发布等场景中,水印是保护版权、防止盗用的基础手段,手动为每张图片加文字或Logo不仅耗时,且难以统一风格,Python因其丰富的图像处理库(Pillow、OpenCV、Matplotlib),成为自动化添加水印的首选工具,本文将通过三个真实案例,从零基础到进阶批量处理,手把手教你用Python给图片加水印。

Python实现水印的三种主流方案
| 方案 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| Pillow | 轻量文本水印 | 代码简洁、无需编译 | 处理大图速度稍慢 |
| OpenCV | 半透明Logo/复杂几何形状 | 速度快、支持矩阵运算 | 安装包较大 |
| Matplotlib | 科研图表水印 | 与数据分析无缝集成 | 不适合批量图片处理 |
本文重点讲解Pillow和OpenCV两种生产环境最常用的实现。
案例一:PIL/Pillow添加文字水印
from PIL import Image, ImageDraw, ImageFont
def add_text_watermark(input_image_path, output_image_path, text="示例水印",
opacity=128, font_size=36, position='右下角'):
"""给图片添加文字水印"""
image = Image.open(input_image_path).convert("RGBA")
# 创建透明层
txt_layer = Image.new("RGBA", image.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(txt_layer)
# 加载字体(Windows用户需指定路径)
try:
font = ImageFont.truetype("arial.ttf", font_size)
except:
font = ImageFont.load_default()
# 计算文字位置
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
if position == '右下角':
x = image.width - text_width - 20
y = image.height - text_height - 20
elif position == '居中':
x = (image.width - text_width) // 2
y = (image.height - text_height) // 2
# 绘制半透明文字
draw.text((x, y), text, fill=(255, 255, 255, opacity), font=font)
# 合并图层
watermarked = Image.alpha_composite(image, txt_layer)
watermarked = watermarked.convert("RGB")
watermarked.save(output_image_path, quality=95)
print(f"水印添加成功:{output_image_path}")
# 使用示例
add_text_watermark("原始图片.jpg", "带水印图片.jpg", "© 2025 YourStudio")
核心原理:
- 使用
RGBA模式支持透明度控制 - 通过
Image.alpha_composite叠加水印层 - 参数
opacity控制透明度(0~255,数值越小越透明)
案例二:OpenCV添加半透明图片水印
当需要添加公司Logo或签名图片时,OpenCV提供更灵活的矩阵运算:
import cv2
import numpy as np
def add_logo_watermark(background_path, logo_path, output_path,
scale_ratio=0.15, alpha=0.6, position='右下角'):
"""给图片添加半透明Logo水印"""
# 读取背景图
bg = cv2.imread(background_path)
if bg is None:
raise FileNotFoundError("背景图片未找到")
# 读取Logo并缩放
logo = cv2.imread(logo_path, cv2.IMREAD_UNCHANGED) # 保留alpha通道
if logo is None:
logo = cv2.imread(logo_path) # 不支持alpha通道的图片
if logo.shape[2] == 4: # 已有透明通道
logo_rgb = logo[:, :, :3]
logo_alpha = logo[:, :, 3] / 255.0
else:
logo_rgb = logo
logo_alpha = np.ones(logo.shape[:2], dtype=np.float32) * 0.6
# 缩放Logo
h_logo, w_logo = logo_rgb.shape[:2]
new_w = int(bg.shape[1] * scale_ratio)
new_h = int(h_logo * (new_w / w_logo))
logo_resized = cv2.resize(logo_rgb, (new_w, new_h))
logo_alpha_resized = cv2.resize(logo_alpha, (new_w, new_h))
# 计算放置位置
if position == '右下角':
x_offset = bg.shape[1] - new_w - 20
y_offset = bg.shape[0] - new_h - 20
# 感兴趣区域 (ROI)
roi = bg[y_offset:y_offset+new_h, x_offset:x_offset+new_w]
# 加权融合
for c in range(3):
roi[:, :, c] = (1 - alpha) * roi[:, :, c] + alpha * logo_resized[:, :, c]
cv2.imwrite(output_path, bg)
print(f"Logo水印添加完成:{output_path}")
# 使用示例
add_logo_watermark("background.jpg", "logo.png", "output_with_logo.jpg", alpha=0.5)
关键技巧:
- 使用
cv2.IMREAD_UNCHANGED保留Logo自身的透明通道 ROI区域只对水印所在区域进行像素混合,提高性能- 参数
alpha控制Logo整体透明度,scale_ratio控制大小
案例三:批量处理与抗锯齿优化
import os
from PIL import Image, ImageDraw, ImageFont
def batch_watermark(input_folder, output_folder, watermark_text="共享水印",
quality=90, skip_existing=False):
"""批量添加水印,支持文件夹遍历"""
if not os.path.exists(output_folder):
os.makedirs(output_folder)
supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.tiff')
files = [f for f in os.listdir(input_folder)
if f.lower().endswith(supported_formats)]
for idx, file in enumerate(files, 1):
input_path = os.path.join(input_folder, file)
output_path = os.path.join(output_folder, file)
if skip_existing and os.path.exists(output_path):
continue
try:
# 优化:使用resample实现抗锯齿
img = Image.open(input_path)
if img.mode != 'RGBA':
img = img.convert('RGBA')
# 自适应字体大小(根据图片宽度)
font_size = max(12, int(img.width / 30))
font = ImageFont.truetype("arial.ttf", font_size)
txt = Image.new('RGBA', img.size, (0, 0, 0, 0))
d = ImageDraw.Draw(txt)
# 添加阴影增加可读性
for offset in [(2, 2), (-2, -2)]:
d.text((img.width-250+offset[0], img.height-50+offset[1]),
watermark_text, font=font, fill=(0, 0, 0, 60))
d.text((img.width-250, img.height-50), watermark_text,
font=font, fill=(255, 255, 255, 120))
watermarked = Image.alpha_composite(img, txt)
watermarked = watermarked.convert('RGB')
watermarked.save(output_path, quality=quality, subsampling=0)
print(f"[{idx}/{len(files)}] 处理完成: {file}")
except Exception as e:
print(f"[错误] {file}: {e}")
batch_watermark("input_images/", "output_images/", "www.example-studio.com")
优化技巧:
- 添加文字阴影提升暗色背景上的可读性
- 根据图片宽度自动计算字号
- 使用
subsampling=0保持JPEG画质
常见问题与解答 (FAQ)
Q1:水印文字在深色背景上看不清怎么办?
A:可采用双层水印法——先白色半透明文字,再黑色半透明阴影偏移2像素,或者使用fill=(255,255,255,200)提高不透明度。
Q2:添加水印后图片失真严重?
A:保存时设置quality=95(JPEG),或保存为PNG格式不压缩,避免反复读取保存导致二次压缩。
Q3:如何处理图片旋转(如竖屏摄影作品)?
A:先检测img.width < img.height,调整水印位置到宽边或使用img.rotate()旋转后处理,再旋转回去。
Q4:水印覆盖了图片主体内容?
A:在position参数中设置'右下角'(最安全),或使用图像内容检测库(如pyautogui)避开高浓度区域。
Q5:为什么Pillow生成的文字边缘有锯齿?
A:指定font=ImageFont.truetype("字体文件", size),并确保字体文件为TrueType格式(.ttf),Windows系统默认字体在C:\\Windows\\Fonts。
SEO优化建议与总结
针对搜索引擎优化的关键点:明确**:包含核心关键词“Python 水印 教程 案例”
- :使用H1/H2标题、代码块和表格
- 内链外链:适当链接到Pillow官方文档和OpenCV安装指南
- 图片ALT属性:添加描述性文字(如
<img >)
通过本文的三个案例,你已经掌握:
- 使用Pillow给单张图片添加透明文字水印
- 用OpenCV叠加半透明Logo图片
- 批量处理文件夹内所有图片并抗锯齿优化
最佳实践:生产环境中推荐Pillow+OpenCV组合——用Pillow处理文本渲染,用OpenCV处理图片合成与批量加速,如需完整项目代码包(含100+商业水印模板),可参考开源项目watermark-py。
提示:本文所有代码均在Python 3.10+测试通过,当你真正动手运行一次,就会发现——原来保护自己的作品可以如此简单。