看看效果图:
import os
import math
from PIL import Image, ImageDraw, ImageFont
def add_watermark(image_path, output_path, watermark_text, opacity=50):
"""
给图片添加重复文字水印
参数:
image_path -- 输入图片路径
output_path -- 输出图片路径
watermark_text -- 水印文字
opacity -- 水印透明度 (0-255)
"""
try:
# 打开原始图片
with Image.open(image_path) as img:
# 创建一个与原图同样大小的透明图层用于绘制水印
watermark = Image.new('RGBA', img.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(watermark)
# 尝试加载字体,如果失败使用默认字体
try:
# 加载字体,根据需要更改字体大小
font_size = min(img.width, img.height) // 20 # 动态调整字体大小
font = ImageFont.truetype("arial.ttf", font_size)
except IOError:
# 如果无法加载指定字体,使用默认字体
font = ImageFont.load_default()
# 计算水印文字的大小 - 修复textsize问题
# 在新版本PIL中,textsize已被替换为textbbox或textlength
if hasattr(draw, "textlength"):
text_width = draw.textlength(watermark_text, font=font)
# 估算高度
text_height = font_size
elif hasattr(draw, "textbbox"):
bbox = draw.textbbox((0, 0), watermark_text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
else:
# 如果都不支持,给予估计值
text_width = font_size * len(watermark_text) * 0.6
text_height = font_size
# 根据图片大小,计算水印排列的行列数
spacing = min(img.width, img.height) // 5 # 水印之间的间距
# 确定水印的行列数
rows = int(img.height / spacing) + 2
cols = int(img.width / spacing) + 2
# 添加偏移量使水印交错排列
offset_x = spacing // 3
offset_y = spacing // 3
# 绘制重复的水印
for row in range(rows):
for col in range(cols):
# 计算每个水印的位置,使其交错分布
pos_x = col * spacing
# 偶数行偏移
if row % 2 == 0:
pos_x += offset_x
pos_y = row * spacing
# 偶数列偏移
if col % 2 == 0:
pos_y += offset_y
# 在不同位置绘制透明水印
draw.text((pos_x, pos_y), watermark_text,
fill=(0, 0, 0, opacity), font=font)
# 如果原图是RGBA模式,直接使用,否则转换为RGBA
if img.mode != 'RGBA':
img = img.convert('RGBA')
# 将水印图层合并到原图
result = Image.alpha_composite(img, watermark)
# 保存结果
if output_path.lower().endswith('.jpg') or output_path.lower().endswith('.jpeg'):
# JPEG不支持透明度,将其转换为RGB并保存
result = result.convert('RGB')
result.save(output_path)
print(f"已添加水印: {os.path.basename(image_path)} -> {os.path.basename(output_path)}")
return True
except Exception as e:
print(f"处理图片 {image_path} 时出错: {e}")
return False
def batch_add_watermark(input_folder, output_folder, watermark_text):
"""
批量给文件夹内的图片添加水印
参数:
input_folder -- 输入图片文件夹
output_folder -- 输出图片文件夹
watermark_text -- 水印文字
"""
# 确保输出文件夹存在
os.makedirs(output_folder, exist_ok=True)
# 支持的图片扩展名
supported_formats = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']
# 成功和失败计数
success_count = 0
fail_count = 0
# 检查输入文件夹是否存在
if not os.path.exists(input_folder):
print(f"错误: 输入文件夹 '{input_folder}' 不存在!")
return
# 打印当前工作目录和寻找的文件夹
print(f"当前工作目录: {os.getcwd()}")
print(f"查找图片的文件夹: {input_folder}")
# 获取文件夹中的所有文件
files = []
try:
files = os.listdir(input_folder)
print(f"在文件夹中找到 {len(files)} 个文件")
except Exception as e:
print(f"列出文件夹内容时出错: {e}")
return
# 遍历输入文件夹中的所有文件
for filename in files:
# 获取文件完整路径
input_path = os.path.join(input_folder, filename)
# 忽略子目录
if os.path.isdir(input_path):
print(f"跳过子目录: {filename}")
continue
# 获取文件扩展名
file_ext = os.path.splitext(filename)[1].lower()
# 检查是否为支持的图片格式
if file_ext in supported_formats:
output_path = os.path.join(output_folder, filename)
# 如果是JPEG格式,确保正确处理
if file_ext in ['.jpg', '.jpeg']:
# JPEG保存为JPG格式
output_path = os.path.splitext(output_path)[0] + '.jpg'
print(f"处理图片: {input_path}")
# 添加水印
if add_watermark(input_path, output_path, watermark_text):
success_count += 1
else:
fail_count += 1
else:
print(f"跳过不支持的文件格式: {filename}")
print(f"\n处理完成! 成功: {success_count}, 失败: {fail_count}")
if __name__ == "__main__":
# 可自定义的水印文本
watermark_text = "TEST " # 在这里修改水印文字
# 指定输入和输出文件夹
input_folder = "img" # 输入图片文件夹
output_folder = "result" # 输出图片文件夹
print("=== 图片批量水印添加工具 ===")
print(f"水印文字: '{watermark_text}'")
print(f"输入文件夹: '{input_folder}'")
print(f"输出文件夹: '{output_folder}'")
# 开始批量添加水印
batch_add_watermark(input_folder, output_folder, watermark_text)