>>> from env_helper import info; info()
页面更新时间: 2022-03-22 11:55:03
运行环境:
    Linux发行版本: Debian GNU/Linux 11 (bullseye)
    操作系统内核: Linux-5.10.0-11-amd64-x86_64-with-glibc2.31
    Python版本: 3.9.2

1.3. 项目:添加徽标

假设你有一项无聊的工作,要调整数千张图片的大小, 并在每张图片的角上增加一个小徽标水印。使用基本的 图形程序,如 PaintbrushPaint ,完成 这项工作需要很长时间。像 Photoshop 这样神奇的 应用程序可以批量处理,但这个软件要花几百美元。 让我们写一个脚本来完成工作。

假定图17-11是要添加到每个图像右下角的标识: 带有白色边框的黑猫图标,图像的其余部分是透明的。

图17-11 添加到图像中的徽标

总的来说,程序应该完成下面的事:

  • 载入徽标图像。

  • 循环遍历工作目标中的所有.png和.jpg文件。

  • 检查图片是否宽于或高于300像素。

  • 如果是,将宽度或高度中较大的一个减小为300像素, 并按比例缩小的另一维度。

  • 在角上粘贴徽标图像。

  • 将改变的图像存入另一个文件夹。

这意味着代码需要做到以下几点:

  • 打开catlogo.png文件作为Image对象。

  • 循环遍历os.listdir('.')返回的字符串。

  • 通过size属性取得图像的宽度和高度。

  • 计算调整后图像的新高度和宽度。

  • 调用resize()方法来调整图像大小。

  • 调用paste()方法来粘贴徽标。

  • 调用 save()方法保存更改,使用原来的文件名。

1.3.1. 第1步:打开徽标图像

针对这个项目,打开一个新的文件编辑器窗口, 输入以下代码,并保存为 resizeAndAddLogo.py

#! python3
# resizeAndAddLogo.py - Resizes all images in current working directory to fit
# in a 300x300 square, and adds catlogo.png to the lower-right corner.
import os
from PIL import Image

SQUARE_FIT_SIZE = 300
L0G0_FILENAME ='catlogo.png'

logoIm = Image.open(L0G0_FILENAME)
logoWidth, logoHeight = logoIm.size

# T0D0: Loop over all files in the working directory.

# T0D0: Check if image needs to be resized.

# T0D0: Calculate the new width and height to resize to.

# T0D0: Resize the image.

# T0D0: Add the logo.

# T0D0: Save changes.

在程序开始时设置SQUARE_FIT_SIZEL0G0_FILENAME常量, 这让程序以后更容易修改。假定你要添加的徽标不是猫图标, 或者假定将输出图像的最大大小要减少的值不是300像素。 有了程序开始时定义的这些常量,你可以打开代码, 修改一下这些值,就大功告成了(或者你可以让这些常量的 值从命令行参数获得)。没有这些常数,就要在代码中寻找 所有的300和 'catlogo.png',将它们替换新项目的值。 总之,使用常量使程序更加通用。

徽标Image对象从Image.open()返回。为了增强可读性, logoWidthlogoHeight 被 赋予logoIm.size中的值。

该程序的其余部分目前是TODO注释,说明了程序的骨架。

1.3.2. 第2步:遍历所有文件并打开图像

现在,需要找到当前工作目录中的每个PNG文件和.jpg文件。 请注意,你不希望将徽标图像添加到徽标图像本身, 所以程序应该跳过所有像 L0G0_FILENAME 这样的图像文件名。在程序中添加以下代码:

#! python3
# resizeAndAddLogo.py - Resizes all images in current working directory to fit
# in a 300x300 square, and adds catlogo.png to the lower-right corner.

import os
from PIL import Image

SQUARE_FIT_SIZE = 300
L0G0_FILENAME ='catlogo.png'

logoIm = Image.open(L0G0_FILENAME)
logoWidth, logoHeight = logoIm.size
os.makedirs('withLogo', exist_ok=True)
# Loop over all files in the working directory.

for filename in os.listdir('.'):
    if not (filename.endswith('.png') or filename.endswith('.jpg')) or filename == L0G0_FILENAME:
        continue    # skip non-image files and the logo file itself

    im = Image.open(filename)
    width, height = im.size

首先, os.makedirs() 调用创建了一个文件夹 withLogo ,用于保存完成的、带有徽标的图像, 而不是覆盖原始图像文件。关键字参数 exist_ok=True 将防止 os.makedirs()withLogo 己存在时抛出异常。在用 os.listdir('.')遍历工作目录中的所有文件时, 较长的 if 语句检查每个 filename 是否 以.png.jpg结束。如果不是,或者该文件是 徽标图像本身,循环就跳过它,使用 continue 去处理下一个文件。如果 filename确实以'. png''.jpg’结束(而且不是徽标文件),可以将 它打开为一个 Image 对象, 并设置 widthheight

1.3.3. 第3步:调整图像的大小

只在有宽或高超过 SQUARE_FIT_SIZE 时(在这个 例子中,是300像素),该程序才应该调整图像的大小, 所以将所有大小调整的代码放在一个检查 widthheight 变量的 if 语句内。在程序中 添加以下代码:

#! python3
# resizeAndAddLogo.py - Resizes all images in current working directory to fit
# in a 300x300 square, and adds catlogo.png to the lower-right corner.

import os
from PIL import Image

SQUARE_FIT_SIZE = 300
L0G0_FILENAME ='catlogo.png'

logoIm = Image.open(L0G0_FILENAME)
logoWidth, logoHeight = logoIm.size
os.makedirs('withLogo', exist_ok=True)
# Loop over all files in the working directory.

for filename in os.listdir('.'):
    if not (filename.endswith('.png') or filename.endswith('.jpg')) or filename == L0G0_FILENAME:
        continue    # skip non-image files and the logo file itself

    im = Image.open(filename)
    width, height = im.size

# Check if image needs to be resized.

    if width > SQUARE_FIT_SIZE and height > SQUARE_FIT_SIZE:

        # Calculate the new width and height to resize to.
        if width > height:
            height = int((SQUARE_FIT_SIZE / width) * height)
            width = SQUARE_FIT_SIZE
        else:
            width = int((SQUARE_FIT_SIZE / height) * width)
            height = SQUARE_FIT_SIZE

        # Resize the image.
        print('Resizing %s...' % (filename))
        im = im.resize((width, height))

如果图像确实需要调整大小,就需要弄清楚它是太宽还是太高。 如果 width 大于 height ,则高度应该根据宽度同 比例减小。这个比例是当前宽度除以 SQUARE_FIT_SIZE 的值。新的高度值是这个比例乘以当前高度值。由于除法 运算符返回一个浮点值,而 resize() 要求的尺寸是 整数,所以要记得将结果用 int() 函数转换成整数。 最后,新的 width 值就设置为 SQUARE_FIT_SIZE

如果 height 大于或等于 width(这两种情况都在 else 子句中处理),那么进行同样的计算,只是交换 heightwidth 变量的位置。

widthheight 包含新图像尺寸后,将它们 传入 resize() 方法,并返回的Image对象保存 在 im 中。

1.3.4. 第4步:添加徽标,并保存更改

不论图像是否调整大小,徽标仍应粘贴到右下角。 徽标粘贴的确切位置取决于图像的大小和徽标的大小。 图17-12展示了如何计算粘贴的位置。粘贴徽标的 左坐标将是图像宽度减去徽标宽度,顶坐标将是 图像高度减去徽标高度。

图 17-12 在右下角放置徽标的左坐标和顶坐标, 应该是图像的宽度/高度减去徽标宽度/高度

代码将徽标粘贴到图像中后,应保存修改后的 Image 对象。完整代码如下:

>>> #! python3
>>> # resizeAndAddLogo.py - Resizes all images in current working directory to fit
>>> # in a 300x300 square, and adds catlogo.png to the lower-right corner.
>>>
>>> import os
>>> from PIL import Image
>>>
>>> SQUARE_FIT_SIZE = 300
>>> LOGO_FILENAME = 'catlogo.png'
>>>
>>> logoIm = Image.open(LOGO_FILENAME)
>>> logoWidth, logoHeight = logoIm.size
>>>
>>> os.makedirs('withLogo', exist_ok=True)
>>> # Loop over all files in the working directory.
>>> for filename in os.listdir('.'):
>>>     if not (filename.endswith('.png') or filename.endswith('.jpg')) \
>>>        or filename == LOGO_FILENAME:
>>>         continue # skip non-image files and the logo file itself
>>>
>>>     im = Image.open(filename)
>>>     width, height = im.size
>>>
>>>     # Check if image needs to be resized.
>>>     if width > SQUARE_FIT_SIZE and height > SQUARE_FIT_SIZE:
>>>         # Calculate the new width and height to resize to.
>>>         if width > height:
>>>             height = int((SQUARE_FIT_SIZE / width) * height)
>>>             width = SQUARE_FIT_SIZE
>>>         else:
>>>             width = int((SQUARE_FIT_SIZE / height) * width)
>>>             height = SQUARE_FIT_SIZE
>>>
>>>         # Resize the image.
>>>         print('Resizing %s...' % (filename))
>>>         im = im.resize((width, height))
>>>
>>>     # Add logo.
>>>     print('Adding logo to %s...' % (filename))
>>>     im.paste(logoIm, (width - logoWidth, height - logoHeight), logoIm)
>>>
>>>     # Save changes.
>>>     im.save(os.path.join('withLogo', filename))
Resizing vertical_flip.png...
Adding logo to vertical_flip.png...
Adding logo to purplelmage.png...
Adding logo to putPixel.png...
Resizing pasted.png...
Adding logo to pasted.png...
Resizing rotated6_expanded.png...
Adding logo to rotated6_expanded.png...
Adding logo to cropped.png...
Adding logo to text.png...
Resizing rotated90.png...
Adding logo to rotated90.png...
Resizing zophie.png...
Adding logo to zophie.png...
Resizing horizontal_flip.png...
Adding logo to horizontal_flip.png...
Resizing rotated180.png...
Adding logo to rotated180.png...
Resizing quartersized.png...
Adding logo to quartersized.png...
Resizing rotated6.png...
Adding logo to rotated6.png...
Adding logo to drawing.png...
Resizing svelte.png...
Adding logo to svelte.png...
Resizing rotated270.png...
Adding logo to rotated270.png...
Adding logo to transparentlmage.png...
Resizing tiled.png...
Adding logo to tiled.png...
Resizing zophie.jpg...
Adding logo to zophie.jpg...

新的代码输出一条消息,告诉用户徽标已被加入 print('Resizing %s...' % (filename)), 将logoIm粘贴到im中计算的坐标处, 并将变更保存到withLogo目录的filename中。 如果运行这个程序,zophie.png文件是工作 目录中唯一的图像,输出会是这样:

Resizing zophie.png...
Adding logo to zophie.png...

图像zophie.png将变成225X300像素的图像,如图17-13所示。 请记住,如果没有传入logoIm作为第三个参数,paste() 方法不会粘贴透明的像素。这个程序可以在短短几分钟内自动 调整几百幅图像,并“加上徽标”。

图 17-13 图像zophie.png调整了大小并加上了徽标(左)。 如果忘记了第三个参数,徽标中透明的像素将被复制为不透明的 白色像素(右)

1.3.5. 第5步:类似程序的想法

能够批量合成图像或修改图像大小,在许多应用中都有用。 可以编写类似的程序,完成以下任务:

  • 为图像添加文字或网站URL。

  • 为图像添加时间戳。

  • 根据图像的大小,将图像复制或移动到不同的文件夹中。

  • 为图像添加一个几乎透明的水印,防止他人复制。