注意
跳转至页面底部下载完整示例代码。
图像教程#
本教程将使用 Matplotlib 的隐式绘图接口 pyplot。该接口维护全局状态,对于快速简便地试验各种绘图设置非常有用。另一种选择是显式接口,它更适合大型应用程序开发。有关隐式和显式接口之间权衡的解释,请参阅 Matplotlib 应用程序接口 (APIs) 以及 快速入门指南 以开始使用显式接口。现在,让我们继续采用隐式方法。
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')

还有许多其他可用的配色方案。请查看 颜色映射列表和图像。
颜色标度参考#
了解颜色所代表的数值很有帮助。我们可以通过在图中添加颜色条来实现这一点。

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

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

也可以通过调用返回的图像绘图对象的 set_clim() 方法来完成。
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 分 6.391 秒)