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

1 简单引入

  • 平常我们想给某些图片添加文字水印,方法有很多,也有很多的工具可以方便的进行;
  • 今天主要是对PIL库的应用,结合Python语言批量对图片添加水印;
  • 这里需要注意的是图片的格式,不能为JPG或JPEG,因为这两种格式的图片不支持透明度设置。

2 关于PIL

之前有的文章已经详细介绍过,这里不再赘述了。

  • PIL是Python的一个图像处理库,支持多种文件格式;
  • PIL提供强大的图像处理和图形处理能力;
  • 可完成对图像的缩放、裁剪、叠加以及图像添加线条、图像和文字等操作。
  • 安装的话,使用以下命令:
python 复制代码
pip install Pillow

3 本文涉及的PIL的几个类

模块或类 说明
image模块 用于对图像就行处理
ImageDraw 2D图像对象
ImageFont 存储字体
ImageEnhance 图像增强

4 实现原理

本文主要目的是批量对某个文件夹下的图片进行添加水印,原理如下:

  • 对水印内容设置;
  • 使用Image对象的open()方法打开原始图片;
  • 使用Image对象的new()方法新建存储水印图片对象;
  • 使用Image.Draw.Draw对象的text()绘制水印文字;
  • 使用ImageEnhance中的Brightness中的enhance()设置水印透明度。

5 实现过程

5.1 原始图片

  • 我们设置一个原始图片的存储目录,比如:
python 复制代码
F:\python_study\image\image01
  • 这个文件夹中存放了多个png格式的图片:
  • 这里的一个原始图片为:

5.2 导入相关模块

  • 导入需要的几个PIL中的模块或类:
python 复制代码
from PIL import Image, ImageDraw, ImageFont, ImageEnhance
import os

5.3 初始化数据

  • 我们这里预想的是,通过用户自己手动输入相关信息,比如图片的存储路径,需要设置的水印文字,通过输入的方式设置水印的位置,设置水印的透明度等;
python 复制代码
class TestText():
    def __init__(self):
        super(TestText, self).__init__()
        self.pic_path = input('图片路径:')
        self.pic_text = input('水印文字:')
        self.p_flag = int(input('水印位置(1:左上角,2:左下角,3:右上角,4:右下角,5:居中):'))
        self.a = float(input('水印透明度(0---1之间的1位小数):'))

5.4 水印字体设置

  • 这里我们先看下ImageFont.truetype源码:
python 复制代码
def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
    """
    Load a TrueType or OpenType font from a file or file-like object,
    and create a font object.
    This function loads a font object from the given file or file-like
    object, and creates a font object for a font of the given size.

    Pillow uses FreeType to open font files. On Windows, be aware that FreeType
    will keep the file open as long as the FreeTypeFont object exists. Windows
    limits the number of files that can be open in C at once to 512, so if many
    fonts are opened simultaneously and that limit is approached, an
    ``OSError`` may be thrown, reporting that FreeType "cannot open resource".
    A workaround would be to copy the file(s) into memory, and open that instead.

    This function requires the _imagingft service.

    :param font: A filename or file-like object containing a TrueType font.
                 If the file is not found in this filename, the loader may also
                 search in other directories, such as the :file:`fonts/`
                 directory on Windows or :file:`/Library/Fonts/`,
                 :file:`/System/Library/Fonts/` and :file:`~/Library/Fonts/` on
                 macOS.

    :param size: The requested size, in pixels.
    :param index: Which font face to load (default is first available face).
    :param encoding: Which font encoding to use (default is Unicode). Possible
                     encodings include (see the FreeType documentation for more
                     information):
  • 它的大意就是从系统中的字体库中读取字体;
  • 那我们的系统字体在哪里呢?
  • 系统字体在
python 复制代码
C:\Windows\Fonts
  • 如下:
  • 我们随便选一个自己喜欢的字体,复制下名字即可:
python 复制代码
self.font = ImageFont.truetype("cambriab.ttf", size=35)

5.5 打开原始图片并新建存储对象

  • 打开原始图片,并转换为RGB:
python 复制代码
image = Image.open(img).convert('RGBA') 
  • 创建绘制对象:
python 复制代码
new_img = Image.new('RGBA', image.size, (255, 255, 255, 0))
image_draw = ImageDraw.Draw(new_img)

5.6 计算图片和水印的大小

  • 图片大小:
python 复制代码
w, h = image.size  
  • 文字大小:
python 复制代码
w1 = self.font.getsize(self.pic_text)[0]  # 获取字体宽度
h1 = self.font.getsize(self.pic_text)[1]  # 获取字体高度

5.7 选择性设置水印文字

  • 通过if语句来实现:
python 复制代码
 if self.p_flag == 1:  # 左上角
      location = (0, 0)
 elif self.p_flag == 2:  # 左下角
      location = (0, h - h1)
 elif self.p_flag == 3:  # 右上角
      location = (w - w1, 0)
 elif self.p_flag == 4:  # 右下角
      location = (w - w1, h - h1)
 elif self.p_flag == 5:  # 居中
      location = (h/2, h/2)

5.8 绘制文字并设置透明度

  • 绘制文字:
python 复制代码
image_draw.text(location, self.pic_text, font=self.font, fill="blue")
  • 设置透明度
python 复制代码
 transparent = new_img.split()[3]
 transparent = ImageEnhance.Brightness(transparent).enhance(self.a)
 new_img.putalpha(transparent)

 Image.alpha_composite(image, new_img).save(img)

5.9 遍历获取图片文件并调用绘制方法

python 复制代码
 text_pic = TestText()
    try:
        file_list = os.listdir(text_pic.pic_path)
        for i in range(0, len(file_list)):
            filepath = os.path.join(text_pic.pic_path, file_list[i])  
            if os.path.isfile(filepath): 
                filetype = os.path.splitext(filepath)[1] 
                if filetype == '.png': 
                    text_pic.test_text(filepath) 
                else:
                    print("图片格式有误,无法添加水印,请使用png格式图片")
        print('批量添加水印完成')
    except:
        print('输入的文件路径有误,请检查~~')

6 完整源码

python 复制代码
# -*- coding:utf-8 -*-
# 作者:虫无涯
# 日期:2023/11/21 
# 文件名称:test_text_pic.py
# 作用:批量给图片添加文字水印和图片水印
# 联系:VX(NoamaNelson)
# 博客:https://blog.csdn.net/NoamaNelson


from PIL import Image, ImageDraw, ImageFont, ImageEnhance
import os


class TestText():
    def __init__(self):
        super(TestText, self).__init__()
        self.pic_path = input('图片路径:')
        self.pic_text = input('水印文字:')
        self.p_flag = int(input('水印位置(1:左上角,2:左下角,3:右上角,4:右下角,5:居中):'))
        self.a = float(input('水印透明度(0---1之间的1位小数):'))

        # 设置字体
        self.font = ImageFont.truetype("cambriab.ttf", size=35)

    # 文字水印
    def test_text(self, img):
        global location
        image = Image.open(img).convert('RGBA') 
        new_img = Image.new('RGBA', image.size, (255, 255, 255, 0)) 
        image_draw = ImageDraw.Draw(new_img) 
        w, h = image.size  # 图片大小
        w1 = self.font.getsize(self.pic_text)[0]  # 字体宽度
        h1 = self.font.getsize(self.pic_text)[1]  # 字体高度

        # 设置水印文字位置
        if self.p_flag == 1:  # 左上角
            location = (0, 0)
        elif self.p_flag == 2:  # 左下角
            location = (0, h - h1)
        elif self.p_flag == 3:  # 右上角
            location = (w - w1, 0)
        elif self.p_flag == 4:  # 右下角
            location = (w - w1, h - h1)
        elif self.p_flag == 5:  # 居中
            location = (h/2, h/2)
        # 绘制文字
        image_draw.text(location, self.pic_text, font=self.font, fill="blue")

        # 设置透明度
        transparent = new_img.split()[3]
        transparent = ImageEnhance.Brightness(transparent).enhance(self.a)
        new_img.putalpha(transparent)

        Image.alpha_composite(image, new_img).save(img)


if __name__ == "__main__":
    text_pic = TestText()
    try:
        file_list = os.listdir(text_pic.pic_path) 
        for i in range(0, len(file_list)): 
            filepath = os.path.join(text_pic.pic_path, file_list[i])
            if os.path.isfile(filepath): 
                filetype = os.path.splitext(filepath)[1] 
                if filetype == '.png': 
                    text_pic.test_text(filepath) 
        print('批量添加水印完成')
    except:
        print('输入的文件路径有误,请检查~~')

7 显示效果

  • 运行过程:
python 复制代码
D:\Python37\python.exe F:/python_study/python_project/test_text_pic.py
图片路径:F:\python_study\image\image01
水印文字:@虫无涯
水印位置(1:左上角,2:左下角,3:右上角,4:右下角,5:居中):1
水印透明度(0---1之间的1位小数):0.5
F:/python_study/python_project/test_text_pic.py:32: DeprecationWarning: getsize is deprecated and will be removed in Pillow 10 (2023-07-01). Use getbbox or getlength instead.
  w1 = self.font.getsize(self.pic_text)[0]  # 获取字体宽度
F:/python_study/python_project/test_text_pic.py:33: DeprecationWarning: getsize is deprecated and will be removed in Pillow 10 (2023-07-01). Use getbbox or getlength instead.
  h1 = self.font.getsize(self.pic_text)[1]  # 获取字体高度
批量添加水印完成
  • 不同位置的水印效果:
  • 居中效果:
相关推荐
全栈弄潮儿2 小时前
Python实战第7期:字典与集合
后端·python·编程语言
2603_965148112 小时前
电商选品调研:通过关键词搜索API获取市场热销商品,结合商品详情API分析竞品卖点与定价
开发语言·数据库·人工智能·python·信息可视化
xixiaoyunya3 小时前
Python 异步编程入门:用 asyncio 轻松实现并发任务
开发语言·python·php
把我格子衫拿来3 小时前
Dice 很高就够了吗?分割评价中的 IoU、表面距离与 HD95
python·图像分割·模型评估·医学影像·dice
曲径海盗3 小时前
【Python 学习第 31 天】复习:变量与数据类型 等 3 题
python·学习笔记·编程入门
Data_Journal3 小时前
如何绕过 Cloudflare:网页抓取的最佳方法
网络·python
我命由我123454 小时前
智能助手通用问答边界
人工智能·python·学习·ai·学习方法·ai编程·智能体
Angeldream1234 小时前
第 4 章 医学影像预处理
图像处理·人工智能·python·医学影像·医学影像处理
Web3&Basketball4 小时前
实测Gemini4Argon:ERQA 87%登顶长程任务榜
数据库·python·大模型·多模态·gemini·长上下文·推理优化
Data analyse4564 小时前
rnweb 回放数据量太大怎么办?分片存储与断点续传的实现思路
前端·网络·算法·数据分析