1.5 保存图像 本节摘要:savefig 是 Matplotlib 把内存里的 Figure 固化成图像文件的出口函数,它通过文件名后缀或 format 参数决定格式,通过 dpi 决定分辨率,还能用 transparent、bboxinches 等参数控制背景透明和空白裁剪。保存不是"存一下"这么简单——格式选错,图会糊或有伪影;DPI 选错,要么文件大得离谱,要么印刷出来全是锯齿。本节讲清格式怎么选、分辨率怎么定、透明和裁剪各有什么用,让你存出来的图能在正确的场合被正确使用。
本节摘要:savefig 是 Matplotlib 把内存里的 Figure 固化成图像文件的出口函数,它通过文件名后缀或 format 参数决定格式,通过 dpi 决定分辨率,还能用 transparent、bbox_inches 等参数控制背景透明和空白裁剪。保存不是"存一下"这么简单——格式选错,图会糊或有伪影;DPI 选错,要么文件大得离谱,要么印刷出来全是锯齿。本节讲清格式怎么选、分辨率怎么定、透明和裁剪各有什么用,让你存出来的图能在正确的场合被正确使用。
阅读完本节,你应当能够:
很多人学画图,最后一步随手 savefig 一下就算完事,结果交出去才发现:放在网页上模糊、印在纸上发虚、或者文件大得邮件都发不出去。问题往往不在画图,而在保存时没想清楚两件事:存成什么格式、存成多清晰。
保存这一步,本质上是在回答"这张图最终要去哪"。去向不同,选择就完全不同:
把这些"去向"想清楚,下面讲的格式和 DPI 就有了判断的锚点。否则你只是在碰运气地填参数。
💡 关键直觉:保存之前先问一句"这张图最终出现在哪",答案决定了格式和分辨率。想不清去向,再高的 DPI、再贵的格式,都可能是错的。
Matplotlib 最核心的保存函数是 savefig。它有个很省心的特性——根据文件名的后缀自动判断格式。你写 savefig("chart.png"),它存成 PNG;写 savefig("chart.pdf"),它存成 PDF。如果想更明确,也可以用 format 参数显式指定,两者效果一样。
import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1, 2, 3], [1, 4, 2]) plt.savefig("chart.png") # 靠后缀判断,存成 PNG plt.savefig("chart.pdf", format="pdf") # 显式指定,存成 PDF
格式的差别,核心在"栅格"和"矢量"之分。栅格格式(PNG、JPG)把图存成一格一格的像素点,放大到一定程度就露马赛克;矢量格式(SVG、PDF)把图存成"线条和文字的数学描述",放大多少倍都清晰。这个区分是选格式的底层逻辑。
| 格式 | 类型 | 特点 | 适用场合 |
|---|---|---|---|
| PNG | 栅格,无损压缩 | 清晰、支持透明背景、文件适中 | 大多数图表的默认选择 |
| SVG | 矢量 | 无损缩放、可编辑、文件小 | 网页、需要缩放的图 |
| 矢量 | 可嵌文字、多页、适合排版 | 报告、论文、文档 | |
| JPG | 栅格,有损压缩 | 文件小但可能出伪影 | 照片类,图表慎用 |
这里要特别提一句 JPG。它靠丢弃细节换小体积,对照片这种复杂图像合适,但用在折线图、柱状图这类"线条加纯色块"的图表上,往往会在边缘产生一圈模糊的伪影,图越简单越明显。所以画图表,能不用 JPG 就别用,PNG 和 SVG 是更稳的选择。
⚠️ 常见坑:别拿 JPG 存图表。它为了压缩体积丢掉细节,会在线条和色块边缘留下伪影,图越简单越明显。图表优先 PNG(要透明或日常用)或 SVG、PDF(要缩放或印刷)。
DPI(每英寸点数)是控制分辨率的核心参数。它表示"每英寸里画多少个像素点",值越高,图越清晰,但文件也越大、渲染越慢。
理解 DPI,要把它和 1.2 节的 figsize 连起来看:最终图片的像素尺寸 = figsize 的英寸数 × dpi。 一张 8×6 英寸的画布,用 100 DPI 存出来是 800×600 像素,用 300 DPI 存出来就是 2400×1800 像素。所以想让图"更大更清",要么加 figsize,要么加 dpi,两者是乘法关系。
import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(8, 6)) ax.plot([1, 2, 3], [1, 4, 2]) plt.savefig("low.png", dpi=72) # 低分辨率,适合网页 plt.savefig("high.png", dpi=300) # 高分辨率,适合印刷
DPI 的选择可以按"去向"分三档:
💡 关键直觉:DPI 不是越高越好,而是"够用就行"。给网页存一张 600 DPI 的图,除了让文件膨胀、加载变慢,读者在屏幕上根本看不出差别。先定去向,再定 DPI,别盲目拉满。
除了格式和 DPI,还有两个参数能显著影响图的实际观感:transparent 和 bbox_inches。
transparent=True 让背景变透明。默认背景是不透明的白色,透明背景的图可以直接叠在彩色网页、深色幻灯片或其他图片上,不会出现一个难看的白方块。它主要对 PNG 和 PDF 有效——JPG 不支持透明。
plt.savefig("chart_transparent.png", transparent=True)
bbox_inches="tight" 则解决另一个问题:图周围的空白太大。 默认保存时,Matplotlib 会把整个画布都存下来,包括内容之外的一大圈留白。加了这个参数,它会自动计算内容实际占用的区域,把多余的空白裁掉,让图更紧凑。
plt.savefig("chart_tight.png", bbox_inches="tight")
这个参数还有一个隐藏价值:当你的图例或标题不小心超出了画布范围,默认保存会把超出的部分切掉,而 bbox_inches="tight" 会反过来把边界框扩大到正好包住这些元素,保证它们完整落进图里。所以凡是图例放到了框外,记得加这个参数。
⚠️ 常见坑:图例或标题被切掉一半,几乎都是因为没加
bbox_inches="tight"。只要内容有溢出画布的可能,就顺手加上这个参数,成本极低,却能避免返工。
把前面讲的参数串起来,一次保存就能覆盖绝大多数交付场景。下面是一个综合示例:画一张带两条线、有图例和网格的图,然后分别存成适合网页的 SVG 和适合印刷的高清 PNG。
import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 10, 200) fig, ax = plt.subplots(figsize=(8, 6)) ax.plot(x, np.sin(x), label="正弦", linewidth=2) ax.plot(x, np.cos(x), label="余弦", linestyle="--") ax.set_title("正弦与余弦曲线") ax.set_xlabel("横轴") ax.set_ylabel("纵轴") ax.legend() ax.grid(True, linestyle=":", alpha=0.5) # 网页用:矢量、可缩放、背景透明 plt.savefig("curve.svg", format="svg", bbox_inches="tight", transparent=True) # 印刷用:栅格、高分辨率、裁掉多余空白 plt.savefig("curve.png", format="png", dpi=300, bbox_inches="tight")
这段代码里每个参数都有明确的目的:bbox_inches="tight" 裁掉空白并保住可能溢出的元素,transparent=True 让网页图能融入任意背景,dpi=300 保证印刷清晰。它们不是随手堆上去的,而是各自对应一个"去向"带来的需求。
到这里,你已经走完了从建画布到存文件的完整链路。但"保存"还有更细的控制——比如 PDF 的页面方向、元数据嵌入、JPEG 的压缩质量等,这些属于进阶内容,等你真正需要交付特定格式时再查官方文档即可。现在把基础这套吃透,已经能应付绝大多数日常场景。
格式、DPI、透明、裁剪之外,还有两个控制画布外观的参数:facecolor(背景色)和 edgecolor(边缘色)。它们作用在整张 Figure 上,影响存出来的图片"底色"和"外框"。
import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1, 2, 3], [1, 4, 2]) plt.savefig("styled.png", facecolor="lightgray", edgecolor="black")
默认背景是白色。facecolor 能把它换成浅灰、米白,甚至深色——深色背景配浅色线条,就是常说的"夜间模式"图表,在深色主题的幻灯片或网页里很协调。edgecolor 控制画布边缘那圈线的颜色,平时用得少,但做白底图嵌进白色网页时,加一圈浅色边缘能避免图"融"进背景里。
这两个参数和 transparent 有交集,但分工不同:transparent=True 是让背景彻底透明,facecolor 是给背景一个具体颜色。要叠加到彩色背景上,用 transparent=True;要一个固定的深色或浅色底,用 facecolor。二者别混着用——设了透明,facecolor 的颜色就等于白设。
savefig 还有几个参数不常用,但特定场景下能省很多事,值得知道它们存在:
orientation(仅 PDF):设页面方向,"landscape" 横向、"portrait" 纵向。存报告用的 PDF 时,横向更适合宽幅图表。papertype(仅 PDF):设纸张类型,比如 "a4"、"letter",决定 PDF 页面按哪种纸的尺寸排。metadata:往图像文件里嵌入元数据,比如作者、标题。这些信息能被一些图片查看器和文档工具读出来,方便归档和溯源。pil_kwargs:把额外的参数透传给底层的图像处理库,比如存 JPG 时指定压缩质量。plt.savefig("report.pdf", format="pdf", orientation="landscape", papertype="a4", metadata={"Author": "数据分析组", "Title": "销售趋势"})
这些参数不必现在就背,但要知道"有这回事"。真到需要交付特定格式、满足特定要求时,你至少知道该往哪个方向去找,而不是抓瞎。
把这一节串起来,其实就一句话:保存的所有参数,都是"去向"决定的。 图要去网页,就选 SVG、透明背景、低 DPI;图要去印刷,就选 PNG 或 PDF、高 DPI、裁掉空白;图要嵌进深色幻灯片,就配深色背景或透明背景。
很多人保存出问题,不是不会用参数,而是没先想清楚去向,就随手点保存。结果要么图糊了,要么文件大得离谱,要么格式根本用不上。反过来,只要在保存前花十秒钟问一句"这张图最终出现在哪",后面的格式、DPI、透明、裁剪,全都有了判断依据。
💡 关键直觉:把"去向"当成保存的第一参数。它不写在代码里,却决定了代码里每一个保存参数该怎么填。
这是个新手高频踩坑:代码里明明画了图,存出来的文件却是一片空白。十有八九,是把 savefig 写在了 show 之后。
原因在于,show 在显示完图之后,可能会把 Figure 从内存里释放掉;这时再调 savefig,它保存的是一个已经被清空、或者已经进入不一致状态的画布,自然就是空白。正确的顺序是先 savefig 保存,再 show 显示——先固化到文件,再展示到屏幕,两者不冲突。
import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1, 2, 3], [1, 4, 2]) plt.savefig("chart.png") # 先保存 plt.show() # 后显示
⚠️ 记住这个顺序:先存后显。凡是"存出来的图是空白"的报障,先检查 savefig 是不是被放在了 show 之后,这是最常见、也最好修的原因。
中文保存后显示成方框或乱码,是 Matplotlib 很常见的现象,根因是它默认使用的字体里没有中文字形。这不是你代码写错了,而是字体配置的问题——需要给 Matplotlib 指定一个支持中文的字体。这个话题牵扯到字体查找、文本渲染的细节,第 4 章"文本渲染与字体"会给出完整解法。这里先给你一个心理预期,避免遇到方框时误以为是保存参数出问题。
到这里,第 1 章"基础入门"的五节就全部走完了。你已经能从零建画布、画四种基础图、调参数、存文件。下一章我们将在这个地基上盖楼——多子图、颜色映射、样式美化、特殊图形,把"画得出来"升级成"画得好"。