注意
转到结尾 下载完整的示例代码。
图像教程#
关于使用 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 库来加载图像数据。
这是我们要使用的图像
它是一个 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()
函数执行的。在这里,我们将获取绘图对象。此对象为您提供了一种从提示符操作绘图的简单方法。
imgplot = plt.imshow(img)
您也可以绘制任何 NumPy 数组。
将伪彩色方案应用于图像图#
伪彩色可以是增强对比度和更轻松地可视化数据的有用工具。这在使用投影仪进行数据演示时尤其有用 - 它们的对比度通常很差。
伪彩色仅与单通道、灰度、亮度图像相关。我们目前有一个 RGB 图像。由于 R、G 和 B 都很相似(请自行查看上面的内容或您的数据),我们可以使用数组切片选择数据的其中一个通道(您可以在 NumPy 教程 中了解更多信息)。
lum_img = img[:, :, 0]
plt.imshow(lum_img)
现在,使用亮度(2D,无颜色)图像,将应用默认颜色图(也称为查找表,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)
在这里,我们使用默认插值(“最近”),因为我们没有给 imshow()
任何插值参数。
让我们尝试其他一些。以下是“双线性”
imgplot = plt.imshow(img, interpolation="bilinear")
和双三次
imgplot = plt.imshow(img, interpolation="bicubic")
双三次插值通常用于放大照片 - 人们往往更喜欢模糊而不是像素化。
脚本的总运行时间:(0 分钟 9.407 秒)