苦于不知如何高效添加图片水印?Pillow 加 Tkinter 帮你轻松搞定!
运行展示
经过测试,功能正常。
一、引言
在日常工作和生活中,我们经常需要对图片添加水印来保护版权或者标识信息。Python 作为一门功能强大且易于上手的编程语言,提供了丰富的库来实现图片处理功能,其中 Pillow
库就是一个非常实用的图片处理库。本文将详细介绍如何使用 Python 结合 Pillow
库实现图片文字水印的添加,并将添加水印后的图片保存到指定文件夹。
二、实现思路
我们的目标是开发一个具有图形用户界面(GUI)的程序,用户可以通过界面选择图片或文件夹,输入水印文字、字体大小和透明度,还能指定输出文件夹,程序会自动为图片添加文字水印并保存到指定位置。具体实现步骤如下:
- 输入验证:检查用户输入的字体大小和透明度是否为有效的数字。
- 水印添加:定义添加文字水印的函数,处理单张图片和批量图片添加水印的情况。
- GUI 设计:使用
tkinter
库创建一个简单易用的图形用户界面,方便用户操作。 - 输出保存:允许用户选择输出文件夹,将添加水印后的图片保存到该文件夹。
三、代码实现
1. 导入必要的库
from PIL import Image, ImageDraw, ImageFont
import os
import tkinter as tk
from tkinter import filedialog, messagebox
这里导入了 Pillow
库用于图片处理,os
库用于文件和文件夹操作,tkinter
库用于创建 GUI 界面,filedialog
和 messagebox
用于文件选择和消息提示。
2. 输入验证函数
# 检查输入是否为有效的数字
def is_valid_number(input_str):
try:
float(input_str)
return True
except ValueError:
return False
该函数用于检查用户输入的字符串是否可以转换为浮点数,如果可以则返回 True
,否则返回 False
。
3. 生成输出文件路径函数
# 生成输出文件路径
def generate_output_path(image_path, output_folder):
file_name = os.path.splitext(os.path.basename(image_path))[0]
file_extension = os.path.splitext(image_path)[1]
return os.path.join(output_folder, file_name + "_watermarked" + file_extension)
该函数根据输入图片的路径和输出文件夹路径,生成添加水印后图片的保存路径。
4. 添加文字水印函数
# 添加文字水印函数
def add_text_watermark(image_path, output_folder, watermark_text, font_size, opacity):
try:
img = Image.open(image_path).convert("RGBA")
# 尝试使用支持中文的字体文件
try:
font = ImageFont.truetype("simhei.ttf", font_size)
except OSError:
try:
# 尝试使用系统默认字体
font = ImageFont.load_default()
except Exception as e:
messagebox.showerror("错误", f"无法加载字体: {str(e)}")
return
# 修改文字颜色为黑色
text_color = (0, 0, 0, int(255 * opacity))
draw = ImageDraw.Draw(img)
# 使用 textbbox 方法计算文本大小
bbox = draw.textbbox((0, 0), watermark_text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
img_width, img_height = img.size
position = (img_width - text_width - 10, img_height - text_height - 10) # 右下角位置
text_layer = Image.new('RGBA', (text_width, text_height), (255, 255, 255, 0))
draw_text = ImageDraw.Draw(text_layer)
draw_text.text((0, 0), watermark_text, font=font, fill=text_color)
img.paste(text_layer, position, text_layer)
img = img.convert("RGB")
output_path = generate_output_path(image_path, output_folder)
img.save(output_path)
except Exception as e:
messagebox.showerror("错误", f"添加文字水印失败: {str(e)}")
该函数用于为单张图片添加文字水印。首先打开图片并转换为 RGBA
模式,然后尝试加载支持中文的字体文件 simhei.ttf
,如果加载失败则使用系统默认字体。接着计算水印文字的大小和位置,创建一个透明的文本图层并绘制水印文字,最后将文本图层粘贴到图片上并保存到指定的输出文件夹。
5. 批量添加水印函数
# 批量添加水印函数
def batch_add_watermark(folder_path, output_folder, watermark_text, font_size, opacity):
for root, dirs, files in os.walk(folder_path):
for file in files:
file_extension = os.path.splitext(file)[1].lower()
if file_extension in [".png", ".jpg", ".jpeg", ".bmp", ".gif"]:
image_path = os.path.join(root, file)
add_text_watermark(image_path, output_folder, watermark_text, font_size, opacity)
该函数用于批量处理指定文件夹中的图片。遍历文件夹中的所有文件,筛选出图片文件,然后调用 add_text_watermark
函数为每张图片添加水印。
6. GUI 界面类
# GUI界面
class WatermarkApp:
def __init__(self, root):
self.root = root
self.root.title("文字水印处理工具")
self.image_path = ""
self.folder_path = ""
self.output_folder = ""
self.create_widgets()
def create_widgets(self):
# 添加水印部分
self.add_watermark_frame = tk.LabelFrame(self.root, text="添加文字水印")
self.add_watermark_frame.grid(row=0, column=0, padx=10, pady=10)
tk.Label(self.add_watermark_frame, text="选择图片:").grid(row=0, column=0)
self.image_button = tk.Button(self.add_watermark_frame, text="选择图片", command=self.select_image)
self.image_button.grid(row=0, column=1)
self.image_label = tk.Label(self.add_watermark_frame, text="")
self.image_label.grid(row=0, column=2)
tk.Label(self.add_watermark_frame, text="选择输出文件夹:").grid(row=1, column=0)
self.output_folder_button = tk.Button(self.add_watermark_frame, text="选择输出文件夹",
command=self.select_output_folder)
self.output_folder_button.grid(row=1, column=1)
self.output_folder_label = tk.Label(self.add_watermark_frame, text="")
self.output_folder_label.grid(row=1, column=2)
tk.Label(self.add_watermark_frame, text="输入水印文字:").grid(row=2, column=0)
self.watermark_text = tk.Entry(self.add_watermark_frame)
self.watermark_text.grid(row=2, column=1)
tk.Label(self.add_watermark_frame, text="字体大小:").grid(row=3, column=0)
self.font_size = tk.Entry(self.add_watermark_frame)
self.font_size.grid(row=3, column=1)
tk.Label(self.add_watermark_frame, text="透明度 (0 - 1):").grid(row=4, column=0)
self.opacity = tk.Entry(self.add_watermark_frame)
self.opacity.grid(row=4, column=1)
self.add_watermark_button = tk.Button(self.add_watermark_frame, text="添加水印", command=self.add_watermark)
self.add_watermark_button.grid(row=5, column=1)
# 批量添加水印部分
self.batch_add_watermark_frame = tk.LabelFrame(self.root, text="批量添加文字水印")
self.batch_add_watermark_frame.grid(row=0, column=1, padx=10, pady=10)
tk.Label(self.batch_add_watermark_frame, text="选择文件夹:").grid(row=0, column=0)
self.batch_folder_button = tk.Button(self.batch_add_watermark_frame, text="选择文件夹",
command=self.select_batch_folder)
self.batch_folder_button.grid(row=0, column=1)
self.batch_folder_label = tk.Label(self.batch_add_watermark_frame, text="")
self.batch_folder_label.grid(row=0, column=2)
tk.Label(self.batch_add_watermark_frame, text="选择输出文件夹:").grid(row=1, column=0)
self.batch_output_folder_button = tk.Button(self.batch_add_watermark_frame, text="选择输出文件夹",
command=self.select_batch_output_folder)
self.batch_output_folder_button.grid(row=1, column=1)
self.batch_output_folder_label = tk.Label(self.batch_add_watermark_frame, text="")
self.batch_output_folder_label.grid(row=1, column=2)
tk.Label(self.batch_add_watermark_frame, text="输入水印文字:").grid(row=2, column=0)
self.batch_watermark_text = tk.Entry(self.batch_add_watermark_frame)
self.batch_watermark_text.grid(row=2, column=1)
tk.Label(self.batch_add_watermark_frame, text="字体大小:").grid(row=3, column=0)
self.batch_font_size = tk.Entry(self.batch_add_watermark_frame)
self.batch_font_size.grid(row=3, column=1)
tk.Label(self.batch_add_watermark_frame, text="透明度 (0 - 1):").grid(row=4, column=0)
self.batch_opacity = tk.Entry(self.batch_add_watermark_frame)
self.batch_opacity.grid(row=4, column=1)
self.batch_add_watermark_button = tk.Button(self.batch_add_watermark_frame, text="批量添加水印",
command=self.batch_add_watermark_action)
self.batch_add_watermark_button.grid(row=5, column=1)
def select_image(self):
self.image_path = filedialog.askopenfilename(title="选择图片",
filetypes=[("Image files", "*.png *.jpg *.jpeg *.bmp *.gif")])
self.image_label.config(text=f"已选择图片: {self.image_path}")
def select_output_folder(self):
self.output_folder = filedialog.askdirectory(title="选择输出文件夹")
self.output_folder_label.config(text=f"已选择输出文件夹: {self.output_folder}")
def add_watermark(self):
watermark_text = self.watermark_text.get()
font_size_str = self.font_size.get()
opacity_str = self.opacity.get()
if not is_valid_number(font_size_str) or not is_valid_number(opacity_str):
messagebox.showerror("错误", "字体大小和透明度必须为有效的数字!")
return
font_size = int(font_size_str)
opacity = float(opacity_str)
if self.image_path and self.output_folder:
add_text_watermark(self.image_path, self.output_folder, watermark_text, font_size, opacity)
messagebox.showinfo("提示", "水印添加成功!")
else:
messagebox.showerror("错误", "请先选择图片和输出文件夹!")
def select_batch_folder(self):
self.folder_path = filedialog.askdirectory(title="选择文件夹")
self.batch_folder_label.config(text=f"已选择文件夹: {self.folder_path}")
def select_batch_output_folder(self):
self.output_folder = filedialog.askdirectory(title="选择输出文件夹")
self.batch_output_folder_label.config(text=f"已选择输出文件夹: {self.output_folder}")
def batch_add_watermark_action(self):
watermark_text = self.batch_watermark_text.get()
font_size_str = self.batch_font_size.get()
opacity_str = self.batch_opacity.get()
if not is_valid_number(font_size_str) or not is_valid_number(opacity_str):
messagebox.showerror("错误", "字体大小和透明度必须为有效的数字!")
return
font_size = int(font_size_str)
opacity = float(opacity_str)
if self.folder_path and self.output_folder:
batch_add_watermark(self.folder_path, self.output_folder, watermark_text, font_size, opacity)
messagebox.showinfo("提示", "批量水印添加成功!")
else:
messagebox.showerror("错误", "请先选择文件夹和输出文件夹!")
该类使用 tkinter
库创建了一个 GUI 界面,包含添加单张图片水印和批量添加水印两个部分。用户可以通过按钮选择图片、文件夹和输出文件夹,输入水印文字、字体大小和透明度,然后点击相应的按钮进行操作。
7. 主程序
if __name__ == "__main__":
root = tk.Tk()
app = WatermarkApp(root)
root.mainloop()
在主程序中,创建 tkinter
的主窗口,实例化 WatermarkApp
类并启动主循环,使 GUI 界面保持运行状态。
四、使用方法
- 运行代码,弹出 GUI 界面
- 添加单张图片水印:
- 点击“选择图片”按钮,选择要添加水印的图片。
- 点击“选择输出文件夹”按钮,选择保存添加水印后图片的文件夹。
- 输入水印文字、字体大小和透明度。
- 点击“添加水印”按钮,程序会自动为图片添加水印并保存到指定文件夹。
- 批量添加水印:
- 点击“选择文件夹”按钮,选择包含要添加水印图片的文件夹。
- 点击“选择输出文件夹”按钮,选择保存添加水印后图片的文件夹。
- 输入水印文字、字体大小和透明度。
- 点击“批量添加水印”按钮,程序会遍历指定文件夹中的所有图片,为每张图片添加水印并保存到指定文件夹。
五、注意事项
- 代码设置添加的水印文字颜色是黑色,如果要处理的图片背景颜色是黑色或者灰色之类,水印文字看不清,这时候就要更改水印文字的颜色。
- 要确保你的 Python 脚本所在目录下有
simhei.ttf
字体文件,或者你可以根据实际情况修改字体文件的路径。 - 不同操作系统的字体文件存放位置可能不同,若遇到字体文件找不到的问题,可以手动将字体文件复制到脚本所在目录,或者使用绝对路径指定字体文件。
(主要看能否正常显示中文水印,如果能正常显示就不用特意添加)
六、总结
通过本文的介绍,我们学习了如何使用 Python 的 Pillow
库和 tkinter
库实现图片文字水印的添加,并将添加水印后的图片保存到指定文件夹。这个程序不仅可以帮助我们保护图片版权,还可以方便地对大量图片进行批量处理。希望本文对你有所帮助,你可以根据自己的需求对代码进行扩展和优化。