HOWTO · Matplotlib

如何在 Matplotlib 中不显示图形而将其保存为图像文件

使用非交互式后端、savefig() 以及先保存后关闭的正确顺序,在不打开窗口的情况下保存 Matplotlib 图形。

本页内容

要在不打开图形窗口的情况下将完整的 Matplotlib 绘图保存为图像,请省略 show(),使用 savefig() 保存指定的 Figure,并在写入文件后再关闭它。在真正没有显示器的环境中,请在导入 pyplot 前选择非交互式后端 Agg

在不显示图形的情况下保存 Matplotlib 绘图

要在不打开 GUI 窗口的情况下保存完整的 Matplotlib 图形,请省略 show(),对指定图形调用 savefig(),并且只在文件写入后调用 close()。如果脚本运行在服务器、CI 任务、容器或其他明确需要无头后端的环境中,请在导入 matplotlib.pyplot 之前选择静态 Agg 后端。

因此,关键顺序是:选择后端、创建图形、保存图形,然后关闭图形。保留 Figure 对象的引用可以明确保存目标,避免依赖 pyplot 当前认定的活动图形。

当脚本必须在没有显示服务器的计算机上稳定运行时,显式选择 Agg 最有用。脚本要保存文件并不意味着必须这样做,因为 Matplotlib 可能已经自动选择了合适的非交互式后端。如果命令本身无法修改,也可以通过 MPLBACKEND=Agg 环境变量选择后端。不要无故混用多种后端选择方式,因为 Matplotlib 会按照优先级处理,最后生效的配置将覆盖前面的配置。

使用无头 Agg 后端保存图形

下面经过测试的示例在不调用 show() 的情况下,将正弦波绘图保存为 sine-wave.pngAgg 是非交互式后端,它生成栅格输出,因此不需要屏幕窗口。

from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt
import numpy as np

output = Path("sine-wave.png")
x = np.linspace(0, 2 * np.pi, 200)

fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(x, np.sin(x), color="#2166ac", linewidth=2)
ax.set(title="Sine wave", xlabel="x", ylabel="sin(x)")
ax.grid(alpha=0.25)

fig.savefig(output, dpi=150, bbox_inches="tight")
plt.close(fig)

saved = plt.imread(output)
assert output.is_file() and saved.size > 0
print("backend:", matplotlib.get_backend())
print("saved:", output.name)
print("valid PNG:", True)

验证后的输出如下:

backend: Agg
saved: sine-wave.png
valid PNG: True

Matplotlib 保存的正弦波图形,其中包含带标签的坐标轴和网格。

图像路径相对于进程的当前工作目录。若调度器或服务可能从其他目录启动脚本,使用绝对 Path,或基于已知项目目录构造路径会更安全。

断言会重新打开生成的 PNG,并确认文件存在且包含图像数据。这可以发现输出缺失或不可读取的问题;上方的渲染预览则确认了预期的曲线、标签和网格。在生产环境中,如果空文件或位置错误的导出会造成损失,也应采用同样明确的验证方式。

选择图像格式和 savefig() 选项

Figure.savefig() 通常根据文件扩展名推断格式。PNG 适合兼容性广泛的栅格输出;SVG 适合可缩放的网页图形;PDF 适合矢量文档。如果文件名没有合适的扩展名,请显式传入 format 参数。

对于栅格文件,dpi 控制输出分辨率,但不会让 SVG 或 PDF 中的矢量路径更清晰。bbox_inches="tight" 会裁掉周围多余的空白;除非另行覆盖颜色,transparent=True 会让图形和坐标轴背景透明。这些选项影响导出的文件,而不决定是否显示窗口。实际可用的格式还可能取决于已安装的后端和可选库。

应根据使用场景选择格式。PNG 适合嵌入文档或网页的图表;浏览器缩放线条和文字时,SVG 仍然清晰;PDF 便于面向打印的工作流。较高的 dpi 会增加栅格图像的尺寸和文件大小,因此应根据实际显示或打印需求设置,而不是一味采用最大值。

理解 ioff()show() 和保存顺序

plt.ioff() 会关闭 pyplot 的交互模式,但它不是通用的无头开关。尤其是笔记本前端,即使交互模式已关闭,也可能自动显示单元格中的最后一个图形。需要非 GUI 渲染器时应使用 Agg;在笔记本中,还应抑制最后的图形值,或在保存后显式关闭图形。

反过来,调用 savefig() 并不需要交互模式或 show()show() 用于通过交互式后端展示图形;对于只导出文件的脚本,通常应该省略它。因此,关闭交互模式和选择文件渲染后端解决的是两个相关但不同的问题。

请先保存,再调用 close(fig)。如果先关闭,pyplot 会失去该图形的引用,后续有状态的 plt.savefig() 可能保存另一个图形或新建的图形。同样,show() 文档提醒,阻塞式 show() 结束后再保存可能得到空图;应先保存,或者保留图形对象并调用它的 savefig() 方法。

在循环中释放图形并保存到内存

pyplot 会保留通过其接口创建的图形引用。批处理任务若创建许多绘图,应在每次成功保存后调用 close(fig),以便释放这些图形及其占用的内存。如果后续处理可能失败,请使用 try/finally 清理模式。

如果其他 API 需要图像字节而无需磁盘文件,可以把 io.BytesIO 对象而不是文件系统路径传给 fig.savefig()。读取或上传前,使用 seek(0) 将缓冲区位置重置到开头;保存后仍应关闭图形。

处理输出目录不存在的情况

savefig() 会创建图像文件,但不会创建缺失的父目录。下面经过验证的边界示例会捕获该错误,并在任何情况下关闭图形:

from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt

output = Path("missing") / "plot.png"
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [2, 4, 3])

try:
    fig.savefig(output)
except FileNotFoundError:
    print("Create the parent directory before savefig().")
finally:
    plt.close(fig)
Create the parent directory before savefig().

实际导出时,先用 output.parent.mkdir(parents=True, exist_ok=True) 创建目录,并确认进程拥有该目录的写入权限。

使用 imsave() 保存数值数组

当输入是二维或 RGB(A) 数值数组,而不是包含坐标轴、标签、图例和其他 Artist 的绘制图形时,应使用 matplotlib.pyplot.imsave()。它把数组值映射为图像像素,不能替代用于保存完整绘图的 Figure.savefig()

from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt
import numpy as np

pixels = np.array([[0.0, 0.5, 1.0], [1.0, 0.5, 0.0]])
output = Path("array-image.png")

plt.imsave(output, pixels, cmap="gray", vmin=0, vmax=1)

saved = plt.imread(output)
assert output.is_file() and saved.shape[:2] == pixels.shape
print("saved array:", output.name)
print("pixel grid:", saved.shape[0], "x", saved.shape[1])
saved array: array-image.png
pixel grid: 2 x 3