注意
转到末尾以下载完整的示例代码。
图像教程#
关于使用 Matplotlib 绘制图像的简短教程。
启动命令#
首先,让我们启动 IPython。它是标准 Python 提示符的绝佳增强,与 Matplotlib 结合得特别好。可以直接在 shell 中启动 IPython,也可以通过 Jupyter Notebook(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 库来加载图像数据。
这是我们将要使用的图像

这是一个 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]]], shape=(375, 500, 3), dtype=uint8)
每个内部列表代表一个像素。这里,对于 RGB 图像,有 3 个值。由于它是黑白图像,R、G 和 B 都相似。RGBA(其中 A 是 alpha,即透明度)每个内部列表有 4 个值,而简单的亮度图像只有一个值(因此它只是一个二维数组,而不是三维数组)。对于 RGB 和 RGBA 图像,Matplotlib 支持 float32 和 uint8 数据类型。对于灰度图像,Matplotlib 仅支持 float32。如果您的数组数据不符合这些描述之一,则需要对其进行重新缩放。
将 numpy 数组绘制为图像#
因此,您的数据已存在于 numpy 数组中(无论是通过导入还是生成)。现在让我们来渲染它。在 Matplotlib 中,这是使用 imshow()
函数执行的。这里我们将获取绘图对象。此对象提供了一种从提示符轻松操作绘图的方法。
imgplot = plt.imshow(img)

您也可以绘制任何 numpy 数组。
将伪彩色方案应用于图像绘图#
伪彩色是增强对比度并更轻松地可视化数据的有用工具。这在使用投影仪演示数据时尤其有用——投影仪的对比度通常很差。
伪彩色仅适用于单通道、灰度、亮度图像。我们当前有一个 RGB 图像。由于 R、G 和 B 都相似(您可以在上方或您自己的数据中查看),我们可以使用数组切片选择数据的一个通道(您可以在 Numpy 教程 中阅读更多内容)
lum_img = img[:, :, 0]
plt.imshow(lum_img)

现在,对于亮度(二维,无颜色)图像,将应用默认颜色映射(也称为查找表,LUT)。默认名称为 viridis。还有许多其他可供选择。
plt.imshow(lum_img, cmap="hot")

请注意,您也可以使用 set_cmap()
方法更改现有绘图对象的颜色映射
imgplot = plt.imshow(lum_img)
imgplot.set_cmap('nipy_spectral')

注意
但是,请记住,在带有行内后端的 Jupyter Notebook 中,您无法更改已渲染的图表。如果您在一个单元格中创建 imgplot,您不能在后续单元格中对其调用 set_cmap() 并期望先前的图表会发生变化。请确保将这些命令一起输入到一个单元格中。plt 命令不会更改先前单元格中的图表。
还有许多其他颜色映射方案可用。请参阅 颜色映射列表和图像。
颜色比例参考#
了解颜色所代表的值是很有帮助的。我们可以通过在图表中添加颜色条来做到这一点

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

通常,图像中“有趣”的部分位于峰值附近,您可以通过裁剪峰值上方和/或下方的区域来获得额外的对比度。在我们的直方图中,看起来高端(图像中没有太多白色物体)没有太多有用的信息。让我们调整上限,以便我们有效地“放大”直方图的一部分。我们通过设置 *clim*,即颜色映射限制来实现这一点。
这可以通过在调用 imshow
时传递 *clim* 关键字参数来完成。
plt.imshow(lum_img, clim=(0, 175))

这也可以通过调用返回的图像绘图对象的 set_clim()
方法来完成,但在使用 Jupyter Notebook 时,请确保在与您的绘图命令相同的单元格中执行此操作——它不会更改先前单元格中的绘图。
imgplot = plt.imshow(lum_img)
imgplot.set_clim(0, 175)

数组插值方案#
插值根据不同的数学方案计算像素的颜色或值“应该”是什么。一个常见的情况是当您调整图像大小时。像素数量会改变,但您希望保留相同的信息。由于像素是离散的,因此存在缺失空间。插值就是您填充该空间的方式。这就是为什么当您放大图像时,有时会看起来像素化的原因。当原始图像和放大图像之间的差异越大时,这种效果越明显。让我们将图像缩小。我们实际上是在丢弃像素,只保留少数几个。现在当我们绘制它时,该数据会放大到屏幕上的尺寸。旧像素不再存在,计算机必须绘制像素来填充该空间。
我们将使用我们加载图像的 Pillow 库来调整图像大小。
img = Image.open('../../doc/_static/stinkbug.png')
img.thumbnail((64, 64)) # resizes image in-place
imgplot = plt.imshow(img)

这里我们使用默认插值(“nearest”),因为我们没有给 imshow()
任何插值参数。
让我们尝试其他一些。这是“bilinear”
imgplot = plt.imshow(img, interpolation="bilinear")

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

双三次插值常用于放大照片——人们倾向于选择模糊而非像素化。
脚本总运行时间: (0 分钟 8.955 秒)