图像教程#

关于使用 Matplotlib 绘制图像的简短教程。

启动命令#

首先,让我们启动 IPython。它是标准 Python 提示符的绝佳增强,并且与 Matplotlib 非常契合。直接在 shell 中或使用 Jupyter Notebook(其中 IPython 作为运行内核)启动 IPython。

启动 IPython 后,我们现在需要连接到 GUI 事件循环。这告诉 IPython 在哪里(以及如何)显示绘图。要连接到 GUI 循环,请在 IPython 提示符下执行 **%matplotlib** 魔法命令。有关此命令的更多详细信息,请参阅 IPython 文档中的 GUI 事件循环

如果您使用的是 Jupyter Notebook,则可以使用相同的命令,但人们通常会对 %matplotlib 魔法命令使用特定的参数

In [1]: %matplotlib inline

这将打开内联绘图,其中绘图图形将出现在您的笔记本中。这对交互性有重要影响。对于内联绘图,在输出绘图的单元格下方的单元格中的命令不会影响绘图。例如,无法从创建绘图的单元格下方的单元格中更改颜色图。但是,对于其他后端(例如 Qt)打开单独窗口的后端,在创建绘图的单元格下方的单元格将更改绘图 - 它是在内存中的实时对象。

本教程将使用 Matplotlib 的隐式绘图接口 pyplot。此接口维护全局状态,非常适合快速轻松地试验各种绘图设置。另一种方法是显式方法,更适合大型应用程序开发。有关隐式和显式接口之间权衡的解释,请参阅 Matplotlib 应用程序接口 (API)快速入门指南 以开始使用显式接口。现在,让我们继续进行隐式方法。

from PIL import Image

import matplotlib.pyplot as plt
import numpy as np

将图像数据导入 Numpy 数组#

Matplotlib 依赖于 Pillow 库来加载图像数据。

这是我们要使用的图像

../_images/stinkbug.png

它是一个 24 位 RGB PNG 图像(R、G、B 各 8 位)。根据您获取数据的位置,您最有可能遇到的其他类型的图像包括 RGBA 图像(允许透明度)或单通道灰度(亮度)图像。下载 stinkbug.png 到您的计算机,以便完成本教程的剩余部分。

我们使用 Pillow 打开图像(使用 PIL.Image.open),并立即将 PIL.Image.Image 对象转换为 8 位 (dtype=uint8) numpy 数组。

img = np.asarray(Image.open('../../doc/_static/stinkbug.png'))
print(repr(img))
array([[[104, 104, 104],
        [104, 104, 104],
        [104, 104, 104],
        ...,
        [109, 109, 109],
        [109, 109, 109],
        [109, 109, 109]],

       [[105, 105, 105],
        [105, 105, 105],
        [105, 105, 105],
        ...,
        [109, 109, 109],
        [109, 109, 109],
        [109, 109, 109]],

       [[107, 107, 107],
        [106, 106, 106],
        [106, 106, 106],
        ...,
        [110, 110, 110],
        [110, 110, 110],
        [110, 110, 110]],

       ...,

       [[112, 112, 112],
        [111, 111, 111],
        [110, 110, 110],
        ...,
        [116, 116, 116],
        [115, 115, 115],
        [115, 115, 115]],

       [[113, 113, 113],
        [113, 113, 113],
        [112, 112, 112],
        ...,
        [115, 115, 115],
        [114, 114, 114],
        [114, 114, 114]],

       [[113, 113, 113],
        [115, 115, 115],
        [115, 115, 115],
        ...,
        [114, 114, 114],
        [114, 114, 114],
        [113, 113, 113]]], dtype=uint8)

每个内部列表表示一个像素。在这里,对于 RGB 图像,有 3 个值。由于它是黑白图像,因此 R、G 和 B 都很相似。RGBA(其中 A 是 alpha 或透明度)每个内部列表有 4 个值,而简单的亮度图像只有一个值(因此它只是一个二维数组,而不是三维数组)。对于 RGB 和 RGBA 图像,Matplotlib 支持 float32 和 uint8 数据类型。对于灰度,Matplotlib 仅支持 float32。如果您的数组数据不符合这些描述之一,则需要对其进行重新缩放。

将 numpy 数组绘制为图像#

因此,您已将数据存储在 numpy 数组中(通过导入或生成)。让我们渲染它。在 Matplotlib 中,这是使用 imshow() 函数执行的。在这里,我们将获取绘图对象。此对象为您提供了一种从提示符操作绘图的简单方法。

images

您也可以绘制任何 NumPy 数组。

将伪彩色方案应用于图像图#

伪彩色可以是增强对比度和更轻松地可视化数据的有用工具。这在使用投影仪进行数据演示时尤其有用 - 它们的对比度通常很差。

伪彩色仅与单通道、灰度、亮度图像相关。我们目前有一个 RGB 图像。由于 R、G 和 B 都很相似(请自行查看上面的内容或您的数据),我们可以使用数组切片选择数据的其中一个通道(您可以在 NumPy 教程 中了解更多信息)。

lum_img = img[:, :, 0]
plt.imshow(lum_img)
images

现在,使用亮度(2D,无颜色)图像,将应用默认颜色图(也称为查找表,LUT)。默认值为 viridis。还有很多其他颜色图可供选择。

plt.imshow(lum_img, cmap="hot")
images

请注意,您还可以使用 set_cmap() 方法更改现有绘图对象上的颜色图。

images

注意

但是,请记住,在使用内联后端的 Jupyter Notebook 中,您无法对已渲染的绘图进行更改。如果您在此处在一个单元格中创建 imgplot,您无法在后面的单元格中对其调用 set_cmap() 并期望先前的绘图发生更改。确保您在一个单元格中一起输入这些命令。plt 命令不会更改来自先前单元格的绘图。

还有许多其他颜色图方案可用。请参阅 颜色图列表和图像

颜色比例参考#

了解颜色代表什么值很有帮助。我们可以通过在您的图形中添加颜色条来做到这一点。

images

检查特定数据范围#

有时您可能希望增强图像的对比度,或在特定区域扩展对比度,同时牺牲颜色变化不大的或不重要的细节。直方图是一个查找有趣区域的好工具。要创建图像数据的直方图,我们使用 hist() 函数。

plt.hist(lum_img.ravel(), bins=range(256), fc='k', ec='k')
images

大多数情况下,图像的“有趣”部分位于峰值附近,您可以通过裁剪峰值以上和/或以下的区域来获得额外的对比度。在我们的直方图中,看起来高端没有太多有用的信息(图像中没有太多白色物体)。让我们调整上限,以便我们有效地“放大”直方图的一部分。我们通过设置clim(颜色映射限制)来实现这一点。

这可以通过在调用 imshow 时传递clim关键字参数来完成。

plt.imshow(lum_img, clim=(0, 175))
images

这也可以通过调用返回的图像绘图对象的 set_clim() 方法来完成,但请确保在使用 Jupyter Notebook 时在与绘图命令相同的单元格中执行此操作 - 它不会更改早期单元格的绘图。

images

数组插值方案#

插值根据不同的数学方案计算像素的“应该”颜色或值。这在您调整图像大小时的常见情况。像素数量发生变化,但您希望保留相同的信息。由于像素是离散的,因此存在缺失空间。插值是您填充该空间的方式。这就是为什么当您放大图像时,有时图像会显得像素化。当原始图像和扩展图像之间的差异越大时,效果越明显。让我们以我们的图像为例,将其缩小。我们实际上是在丢弃像素,只保留少数像素。现在当我们绘制它时,这些数据会被放大到屏幕上的大小。旧的像素不再存在,计算机必须绘制像素来填充该空间。

我们将使用 Pillow 库来调整图像大小,我们之前也使用它来加载图像。

img = Image.open('../../doc/_static/stinkbug.png')
img.thumbnail((64, 64))  # resizes image in-place
imgplot = plt.imshow(img)
images

在这里,我们使用默认插值(“最近”),因为我们没有给 imshow() 任何插值参数。

让我们尝试其他一些。以下是“双线性”

imgplot = plt.imshow(img, interpolation="bilinear")
images

和双三次

imgplot = plt.imshow(img, interpolation="bicubic")
images

双三次插值通常用于放大照片 - 人们往往更喜欢模糊而不是像素化。

脚本的总运行时间:(0 分钟 9.407 秒)

由 Sphinx-Gallery 生成的图库