geekdoc-python-zh/docs/pythonlibrary/python-101-how-to-work-with...

17 KiB
Raw Permalink Blame History

Python 101 -如何处理图像

原文:https://www.blog.pythonlibrary.org/2021/09/14/python-101-how-to-work-with-images/

Python 图像库 (PIL)是一个第三方 Python 包,为您的 Python 解释器增加了图像处理能力。它允许你处理照片和做许多常见的图像文件操作。这个软件的当前版本在里,是原 PIL 支持 Python 3 的一个分叉。其他几个 Python 包,如 wxPython 和 ReportLab使用 Pillow 来支持加载许多不同的图像文件类型。您可以在以下几种情况下使用 Pillow:

  • 图像处理
  • 图像存档
  • 成批处理
  • 通过 Tkinter 显示图像

在这篇文章中,你将学习如何用枕头做以下事情:

  • 打开图像
  • 裁剪图像
  • 使用过滤器
  • 添加边框
  • 调整图像大小

如你所见Pillow 可以用于多种类型的图像处理。本文中使用的图片是作者自己拍摄的一些图片。它们包含在 Github 上的代码示例中。详见简介。

现在让我们开始安装枕头吧!

安装枕头

使用pip可以轻松安装枕头。打开终端或控制台窗口后,您可以这样做:

python -m pip install pillow

现在枕头安装好了,你可以开始使用它了!

打开图像

枕头让你打开和查看许多不同的文件类型。有关 Pillow 支持的图像文件类型的完整列表,请参见以下内容:

您可以使用 Pillow 打开并查看上面链接中“完全支持的格式”部分提到的任何文件类型。查看器是用 Tkinter 制作的,在显示图形时,其工作方式与 Matplotlib 非常相似。

要了解这是如何工作的,创建一个名为open_image.py的新文件,并输入以下代码:

# open_image.py

from PIL import Image

image = Image.open('jellyfish.jpg')
image.show()

在这里,您从PIL包中导入Image。然后你用Image.open()打开一个图像。这将返回一个PIL.JpegImagePlugin.JpegImageFile对象,您可以使用它来了解关于您的图像的更多信息。当您运行此代码时,您将看到一个类似如下的窗口:

Loading an Image with Pillow

这非常方便,因为现在您可以使用 Python 查看图像,而无需编写完整的图形用户界面。您也可以使用 Pillow 来了解更多关于图像的信息。创建一个名为get_image_info.py的新文件,并将以下代码添加到其中:

# get_image_info.py

from PIL import Image

def get_image_info(path):
    image = Image.open(path)
    print(f'This image is {image.width} x {image.height}')
    exif = image._getexif()
    print(exif)

if __name__ == '__main__':
    get_image_info('ducks.jpg')

这里你使用image对象得到图像的宽度和高度。然后使用_getexif()方法获取关于图像的元数据。EXIF 代表“可交换图像文件格式”,是一种指定数码相机使用的图像、声音和辅助标签格式的标准。输出非常详细,但您可以从数据中了解到,这张特定的照片是用索尼 6300 相机拍摄的,设置如下:“E 18-200mm F3.5-6.3 OSS LE”。照片的时间戳也在 Exif 信息中。

但是如果您使用照片编辑软件进行裁剪、应用滤镜或进行其他类型的图像处理Exif 数据可能会被更改。这可以删除部分或全部 Exif 数据。试着在你自己的一些照片上运行这个功能,看看你能提取出什么样的信息!

您可以从图像中提取的另一个有趣信息是直方图数据。图像的直方图是其色调值的图形表示。它将照片的亮度显示为一个数值列表,您可以用图形表示出来。让我们以这张图片为例:

Butterfly

要从该图像中获得直方图,您将使用图像的histogram()方法。然后你会用 Matplotlib 把它画出来。要查看实现这一点的一种方法,创建一个名为get_histrogram.py的新文件,并向其中添加以下代码:

# get_histrogram.py

import matplotlib.pyplot as plt

from PIL import Image

def get_image_histrogram(path):
    image = Image.open(path)
    histogram = image.histogram()
    plt.hist(histogram, bins=len(histogram))
    plt.xlabel('Histogram')
    plt.show()

if __name__ == '__main__':
    get_image_histrogram('butterfly.jpg')

当运行这段代码时,像以前一样打开图像。然后从中提取直方图,并将值列表传递给 Matplotlib 对象,在该对象中调用hist()函数。hist()函数接受值列表以及值范围内的等宽仓的数量。

运行此代码时,您将看到下图:

Butterfly histogram

这个图表显示了之前提到的图像中的色调值。您可以尝试传入 Github 上包含的一些其他图像来查看不同的图表,或者换入一些您自己的图像来查看它们的直方图。

现在让我们来看看如何使用 Pillow 来裁剪图像!

裁剪图像

当你拍照时,照片的主体经常会移动,或者你没有放大足够远。这导致照片中图像的焦点并不在正面中央。要解决此问题,您可以将图像裁剪到您想要突出显示的图像部分。

枕头内置了这种功能。要了解它是如何工作的,创建一个名为cropping.py的文件,并向其中添加以下代码:

# cropping.py

from PIL import Image

def crop_image(path, cropped_path):
    image = Image.open(path)
    cropped = image.crop((40, 590, 979, 1500))
    cropped.save(cropped_path)

if __name__ == '__main__':
    crop_image('ducks.jpg', 'ducks_cropped.jpg')

crop_image()函数接受您希望裁剪的文件的路径以及新裁剪文件的路径。然后你像以前一样open()这个文件并打电话给crop()。此方法采用您用来裁剪的起始和结束 x/y 坐标。您正在创建一个用于裁剪的框。

让我们拍摄这张有趣的鸭子照片,并尝试使用上面的代码进行裁剪:

Ducklings

现在,当您对此运行代码时,您将得到以下裁剪后的图像:

Cropped ducklings

您用来裁剪的坐标会因照片而异。事实上,您可能应该更改这段代码,以便它接受裁剪坐标作为参数。你可以自己做一点功课。找出要使用的裁剪边界框需要一些反复试验。你可以使用 Gimp 这样的工具来帮助你,用 Gimp 画一个边界框,并记下它给你的坐标,用 Pillow 试试。

现在让我们继续学习如何将滤镜应用到你的图片中!

使用过滤器

枕头包有几个过滤器,您可以应用到您的图像。以下是当前支持的过滤器:

  • 虚化
  • 轮廓
  • 详述
  • 边缘增强
  • 边缘 _ 增强 _ 更多
  • 装饰
  • 查找 _ 边
  • 尖锐
  • 光滑的
  • 平滑 _ 更多

让我们使用之前的蝴蝶图像来测试这些过滤器。以下是您将使用的图像:

Butterfly

现在您已经有了一个可以使用的图像,接下来创建一个名为blur.py的新文件,并将这段代码添加到其中,以试用 Pillow 的BLUR过滤器:

# blur.py

from PIL import Image
from PIL import ImageFilter

def blur(path, modified_photo):
    image = Image.open(path)
    blurred_image = image.filter(ImageFilter.BLUR)
    blurred_image.save(modified_photo)

if __name__ == '__main__':
    blur('butterfly.jpg', 'butterfly_blurred.jpg')

要在 Pillow 中实际使用滤镜,需要导入ImageFilter。然后将您想要使用的特定过滤器传递给filter()方法。当你调用filter()时,它会返回一个新的图像对象。然后将文件保存到磁盘。

这是您运行代码时将得到的图像:

Blurred butterfly

这看起来有点模糊,所以你可以认为这是一个成功!如果你想让它更模糊,你可以通过你的脚本第二次运行模糊的照片。

当然,有时候你拍的照片有点模糊,你想把它们锐化一点。枕头包括你可以应用的过滤器。创建一个名为sharpen.py的新文件,并添加以下代码:

# sharpen.py

from PIL import Image
from PIL import ImageFilter

def sharpen(path, modified_photo):
    image = Image.open(path)
    sharpened_image = image.filter(ImageFilter.SHARPEN)
    sharpened_image.save(modified_photo)

if __name__ == '__main__':
    sharpen('butterfly.jpg', 'butterfly_sharper.jpg')

在这里,你要拍摄原始的蝴蝶照片,并在保存之前应用SHARPEN滤镜。当您运行此代码时,您的结果将如下所示:

Sharpened butterfly

根据您的视力和显示器的质量,您可能会看到也可能看不到太大的差异。不过,你可以放心,它稍微锋利一点。

现在让我们来看看如何给你的图片添加边框!

添加边框

让照片看起来更专业的一个方法是给它们添加边框。Pillow 通过他们的ImageOps模块让这变得非常容易。但是在你做任何边界之前,你需要一个图像。下面是您将使用的一个:

Grey butterfly

现在您已经有了一个很好的图像,接下来创建一个名为border.py的文件,并将以下代码放入其中:

# border.py

from PIL import Image, ImageOps

def add_border(input_image, output_image, border):
    img = Image.open(input_image)

    if isinstance(border, int) or isinstance(border, tuple):
        bimg = ImageOps.expand(img, border=border)
    else:
        raise RuntimeError('Border is not an integer or tuple!')

    bimg.save(output_image)

if __name__ == '__main__':
    in_img = 'butterfly_grey.jpg'

    add_border(in_img, output_image='butterfly_border.jpg',
             border=100)

add_border()函数接受 3 个参数:

  • input_image -要添加边框的图像
  • output_image -应用了新边框的图像
  • border -以像素为单位应用的边框数量

在这段代码中,您告诉 Pillow您想要为传入的照片添加一个 100 像素的边框。当您传入一个整数时,该整数将用于所有四条边的边框。边框的默认颜色是黑色。这里的关键方法是expand(),它接受图像对象和边框量。

当您运行这段代码时,您将得到这个可爱的结果:

Butterfly with black border

您可以传入一组值来使边框具有不同的宽度。例如,如果你传入(10, 50),它会在图片的左右两边添加一个 10 像素的边框,在顶部和底部添加一个 50 像素的边框。试着用上面的代码做这件事,然后重新运行。如果这样做,您将得到以下内容:

Butterfly with altered border

这不是很好吗?如果你想变得更漂亮,你可以为图像的四个边传递不同的值。但是可能没有太多有意义的用例。

有一个黑色的边框是很好的,但有时你会想给你的图片添加一点活力。您可以通过将参数fill传递给expand()来更改边框颜色。该参数采用命名颜色或 RGB 颜色。

创建一个名为colored_border.py的新文件,并将以下代码添加到其中:

# colored_border.py

from PIL import Image, ImageOps

def add_border(input_image, output_image, border, color=0):
    img = Image.open(input_image)

    if isinstance(border, int) or isinstance(
        border, tuple):
        bimg = ImageOps.expand(img,
                               border=border, 
                               fill=color)
    else:
        msg = 'Border is not an integer or tuple!'
        raise RuntimeError(msg)

    bimg.save(output_image)

if __name__ == '__main__':
    in_img = 'butterfly_grey.jpg'

    add_border(in_img,
               output_image='butterfly_border_red.jpg',
               border=100,
               color='indianred')

现在你的add_border()函数接受一个color参数,你把它传递给expand()方法。当您运行这段代码时,您会看到这样的结果:

Butterfly with color border

看起来很不错。你可以尝试不同的颜色,或者用自己喜欢的颜色作为边框。

枕头之旅的下一个项目是学习如何调整图像大小!

调整图像大小

使用 Pillow 调整图像大小相当简单。您将使用resize()方法,该方法接受一个整数元组,用于调整图像的大小。为了了解这是如何工作的,你将使用这张可爱的蜥蜴照片:

Lizard

现在您已经有了一张照片,继续创建一个名为resize_image.py的新文件,并将以下代码放入其中:

# resize_image.py

from PIL import Image

def resize_image(input_image_path, output_image_path, size):
    original_image = Image.open(input_image_path)
    width, height = original_image.size
    print(f'The original image size is {width} wide x {height} '
          f'high')

    resized_image = original_image.resize(size)
    width, height = resized_image.size
    print(f'The resized image size is {width} wide x {height} '
          f'high')
    resized_image.show()
    resized_image.save(output_image_path)

if __name__ == '__main__':
    resize_image(
            input_image_path='lizard.jpg',
            output_image_path='lizard_small.jpg',
            size=(800, 400),
            )

这里,您传入蜥蜴照片,并告诉 Pillow 将其大小调整为 600 x 400。当您运行这段代码时输出会告诉您原始照片在为您调整大小之前是 1191 x 1141 像素。

运行这段代码的结果如下所示:

Resized lizard

这看起来有点奇怪Pillow 在调整图像大小时实际上不做任何缩放。相反,枕头会拉伸或扭曲你的图像,以适应你告诉它使用的值。

你要做的是缩放图像。要做到这一点,您需要创建一个名为scale_image.py的新文件,并向其中添加一些新代码。以下是您需要的代码:

# scale_image.py

from PIL import Image

def scale_image(
            input_image_path,
            output_image_path,
            width=None,
            height=None
    ):
    original_image = Image.open(input_image_path)
    w, h = original_image.size
    print(f'The original image size is {w} wide x {h} '
          'high')

    if width and height:
        max_size = (width, height)
    elif width:
        max_size = (width, h)
    elif height:
        max_size = (w, height)
    else:
        # No width or height specified
        raise ValueError('Width or height required!')

    original_image.thumbnail(max_size, Image.ANTIALIAS)
    original_image.save(output_image_path)

    scaled_image = Image.open(output_image_path)
    width, height = scaled_image.size
    print(f'The scaled image size is {width} wide x {height} '
          'high')

if __name__ == '__main__':
    scale_image(
            input_image_path='lizard.jpg',
            output_image_path='lizard_scaled.jpg',
            width=800,
            )

这一次,您让用户指定宽度和高度。如果用户指定了宽度和/或高度,那么条件语句使用这些信息创建一个max_size。一旦计算出了max_size的值,就将它传递给thumbnail()并保存结果。如果用户指定了这两个值,thumbnail()将在调整大小时正确保持纵横比。

当您运行这段代码时,您会发现结果是原始图像的一个较小版本,并且它现在保持了它的纵横比。

包扎

Pillow 对于使用 Python 处理图像非常有用。在本文中,您学习了如何执行以下操作:

  • 打开图像
  • 裁剪图像
  • 使用过滤器
  • 添加边框
  • 调整图像大小

你可以用枕头做比这里展示的更多的事情。例如,您可以进行各种图像增强,如更改图像的对比度或亮度。或者你可以将多张图片合成在一起。枕头还有许多其他用途。您应该检查一下这个包,并阅读它的文档以了解更多信息。

相关阅读

这篇文章基于 Python 101 第二版中的一章,你可以在 Leanpub亚马逊上购买。

如果你想学习更多的 Python那么看看这些教程: