如何用matplotlib在for循环快速保存大量图片?两类实现性能为何有差异?
两种实现的性能差异原因
- 方案1的核心问题:没有清理画布上的历史对象。你每次循环调用
imshow都会在轴上叠加新的图像艺术家(Artist)对象,旧的对象不会自动销毁。随着循环次数增加,画布上累积的元素越来越多,每次savefig都需要渲染所有累积的对象,耗时会线性增长,内存占用也会持续升高。如果循环次数在10次以内,方案1没有明显性能问题,但循环次数越多,性能下降越明显。 - 方案2的开销来源:每次循环都要重新创建画布、轴对象,保存后再销毁,虽然有固定的创建/销毁开销,但每次渲染只需要处理3个图像对象,不会有累积开销,循环次数较多时性能反而比方案1更稳定。
for循环中保存图片的最优方案
优先根据你的使用场景选以下方案,性能从高到低排序:
方案A:跳过matplotlib,直接用PIL拼接保存
如果你的需求只是把三张图横向拼接输出、不需要加坐标轴/标注等matplotlib可视化元素,这是最快的方案,速度比matplotlib方案快5~10倍。参考代码:
from PIL import Image def concat_3img_horizontal(img1, img2, img3, padding=0): # 假设三个图尺寸相同,尺寸不同可先自行resize对齐 w, h = img1.size total_w = w * 3 + padding * 2 new_img = Image.new('RGB', (total_w, h)) new_img.paste(img1, (0, 0)) new_img.paste(img2, (w + padding, 0)) new_img.paste(img3, (w*2 + padding*2, 0)) return new_img for k, fname in enumerate(list_of_fnames): with Image.open(fname) as img: x = img.convert('RGB') # 处理函数可适配为直接操作PIL对象,或者处理完转回PIL对象 y = process_image_fn1_pil(x) z = process_image_fn2_pil(x) concat_img = concat_3img_horizontal(x, y, z) # compress_level设为1可大幅提升保存速度,仅文件体积稍大 concat_img.save(f'results_{k}.png', compress_level=1)
方案B:复用画布+仅更新图像数据
如果必须使用matplotlib(需要加标注、坐标轴等可视化元素),该方案比方案2快2~3倍,核心逻辑是只创建一次imshow对象,后续循环直接更新图像数据,避免对象重复创建和累积。参考代码:
import matplotlib # 一定要在import pyplot之前设置非交互后端,避免GUI相关开销 matplotlib.use('Agg') import matplotlib.pyplot as plt import numpy as np from PIL import Image # 关闭交互模式 plt.ioff() # 提前创建画布和轴 fig, a = plt.subplots(1, 3, figsize=(30, 20)) # 预创建imshow对象,后续仅更新数据 im0 = a[0].imshow(np.zeros((1,1,3), dtype=np.uint8)) im1 = a[1].imshow(np.zeros((1,1,3), dtype=np.uint8)) im2 = a[2].imshow(np.zeros((1,1,3), dtype=np.uint8)) # 不需要坐标轴可直接关闭,减少渲染开销 for ax in a: ax.set_axis_off() # list_of_fnames为文件名列表 for k, fname in enumerate(list_of_fnames): with Image.open(fname) as img: x = np.array(img) y = process_image_fn1(x) z = process_image_fn2(x) # 直接更新图像数据,不创建新对象 im0.set_data(x) im1.set_data(y) im2.set_data(z) # 如果你的图像数据范围固定,可省略autoscale步骤,速度更快 im0.autoscale() im1.autoscale() im2.autoscale() output_filename = f'results_{k}.png' plt.savefig( output_filename, dpi=320, format='png', transparent=False, bbox_inches='tight', pad_inches=0, # 降低PNG压缩级别,大幅提升保存速度 pil_kwargs={"compress_level": 1} ) # 所有循环结束后再销毁画布 plt.close()
内容的提问来源于stack exchange,提问作者Gabriele
相关产品推荐
相关产品推荐

