Matplotlib,savefig() 的替代品以提高保存到 CString 对象时的性能?
Posted
技术标签:
【中文标题】Matplotlib,savefig() 的替代品以提高保存到 CString 对象时的性能?【英文标题】:Matplotlib, alternatives to savefig() to improve performance when saving into a CString object? 【发布时间】:2011-07-20 10:44:00 【问题描述】:我正在尝试加快将图表保存为图像的过程。现在我正在创建一个 cString 对象,我使用 savefig 将图表保存到其中;但我真的非常感谢任何有助于改进这种保存图像的方法。这个操作我要做几十次,savefig命令非常非常慢;必须有更好的方法来做到这一点。我读了一些关于将其保存为未压缩的原始图像的内容,但我不知道该怎么做。如果我也可以切换到另一个更快的后端,我真的不在乎 agg。
即:
RAM = cStringIO.StringIO()
CHART = plt.figure(....
**code for creating my chart**
CHART.savefig(RAM, format='png')
我一直在使用带有 FigureCanvasAgg 后端的 matplotlib。
谢谢!
【问题讨论】:
我对此了解不多。但是您可以查看以下帮助:format='raw'
或 format='rgba'
。看起来它们产生了相同的输出。
您是否尝试过分析代码以查看 savefig 大部分时间花在哪里?您是否尝试过降低分辨率(dpi 参数)或其他图像类型(jpeg、gif、tif,如果支持)?
【参考方案1】:
如果你只想要一个原始缓冲区,试试fig.canvas.print_rgb
、fig.canvas.print_raw
等(两者的区别在于raw
是rgba,而rgb
是rgb。还有print_png
、@987654328 @等)
这将使用fig.dpi
而不是savefig
的默认dpi 值(100 dpi)。尽管如此,即使比较fig.canvas.print_raw(f)
和fig.savefig(f, format='raw', dpi=fig.dpi)
,print_canvas
版本也略快快得微不足道,因为它不需要像savefig
那样重置轴补丁的颜色等默认情况下。
尽管如此,以原始格式保存图形所花费的大部分时间只是绘制图形,这是无法绕过的。
无论如何,作为一个毫无意义但有趣的例子,请考虑以下几点:
import matplotlib.pyplot as plt
import numpy as np
import cStringIO
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)
for i in xrange(1000):
xy = np.random.random(2*num).reshape(num,2) - 0.5
offsets = scat.get_offsets() + 0.3 * xy
offsets.clip(0, max_dim, offsets)
scat.set_offsets(offsets)
scat._sizes += 30 * (np.random.random(num) - 0.5)
scat._sizes.clip(1, 300, scat._sizes)
fig.canvas.draw()
如果我们查看原始绘制时间:
import matplotlib.pyplot as plt
import numpy as np
import cStringIO
fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)
for i in xrange(1000):
xy = np.random.random(2*num).reshape(num,2) - 0.5
offsets = scat.get_offsets() + 0.3 * xy
offsets.clip(0, max_dim, offsets)
scat.set_offsets(offsets)
scat._sizes += 30 * (np.random.random(num) - 0.5)
scat._sizes.clip(1, 300, scat._sizes)
fig.canvas.draw()
这在我的机器上大约需要 25 秒。
如果我们改为将原始 RGBA 缓冲区转储到 cStringIO 缓冲区,它实际上会稍微快一些,大约为 22 秒(这只是因为我使用的是交互式后端!否则它将是等效的。):
import matplotlib.pyplot as plt
import numpy as np
import cStringIO
fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)
for i in xrange(1000):
xy = np.random.random(2*num).reshape(num,2) - 0.5
offsets = scat.get_offsets() + 0.3 * xy
offsets.clip(0, max_dim, offsets)
scat.set_offsets(offsets)
scat._sizes += 30 * (np.random.random(num) - 0.5)
scat._sizes.clip(1, 300, scat._sizes)
ram = cStringIO.StringIO()
fig.canvas.print_raw(ram)
ram.close()
如果我们将此与使用 savefig
进行比较,并设置可比较的 dpi:
import matplotlib.pyplot as plt
import numpy as np
import cStringIO
fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)
for i in xrange(1000):
xy = np.random.random(2*num).reshape(num,2) - 0.5
offsets = scat.get_offsets() + 0.3 * xy
offsets.clip(0, max_dim, offsets)
scat.set_offsets(offsets)
scat._sizes += 30 * (np.random.random(num) - 0.5)
scat._sizes.clip(1, 300, scat._sizes)
ram = cStringIO.StringIO()
fig.savefig(ram, format='raw', dpi=fig.dpi)
ram.close()
这需要大约 23.5 秒。基本上savefig
只是设置了一些默认参数,调用print_raw
,在这种情况下,差别很小。
现在,如果我们将原始图像格式与压缩图像格式 (png) 进行比较,我们会看到更显着的差异:
import matplotlib.pyplot as plt
import numpy as np
import cStringIO
fig = plt.figure()
ax = fig.add_subplot(111)
num = 50
max_dim = 10
x = max_dim / 2 * np.ones(num)
s, c = 100 * np.random.random(num), np.random.random(num)
scat = ax.scatter(x,x,s,c)
ax.axis([0,max_dim,0,max_dim])
ax.set_autoscale_on(False)
for i in xrange(1000):
xy = np.random.random(2*num).reshape(num,2) - 0.5
offsets = scat.get_offsets() + 0.3 * xy
offsets.clip(0, max_dim, offsets)
scat.set_offsets(offsets)
scat._sizes += 30 * (np.random.random(num) - 0.5)
scat._sizes.clip(1, 300, scat._sizes)
ram = cStringIO.StringIO()
fig.canvas.print_png(ram)
ram.close()
这需要大约 52 秒!显然,压缩图像有很多开销。
无论如何,这可能是一个不必要的复杂示例...我想我只是想避免实际工作...
【讨论】:
很好的例子乔,即使它可能是矫枉过正。我想知道您是否将每次迭代绘制的帧保存在磁盘上,然后将它们离线编译成动画 gif,或者是否有某种方式将绘制的帧“in-stream”编译成动画 gif?我不是说使用 $animation$ 模块,因为我想保存由交互式(鼠标事件驱动)绘图产生的动画。 好吧,做了一些搜索,我想你的建议可能是这里显示的:***.com/a/14986894/467522,对吧? 实际上,这个特殊的 gif 是通过保存每个迭代并离线编译它们制作的(使用 imagemagick 的convert
)。 (我认为这个例子早于带有 animation
模块的 matplotlib 版本的发布。)无论如何,应该可以使用 ffmpeg
创建动画 gif,但如果我没记错的话,使用保存为 gif animation
模块无法正常工作。 (我可能记错了,无论如何,它现在可能已经修复了。我已经有一段时间没有尝试过了。)
意识到这是一个旧线程,但想知道是否有办法避免 cStringIO。任何纯 Matplotlib 解决方案?【参考方案2】:
我还需要快速生成大量图。我发现多处理通过可用的内核数量提高了绘图速度。例如,如果 100 个绘图在一个进程中需要 10 秒,那么将任务拆分到 4 个内核时需要大约 3 秒。
【讨论】:
以上是关于Matplotlib,savefig() 的替代品以提高保存到 CString 对象时的性能?的主要内容,如果未能解决你的问题,请参考以下文章
matplotlib savefig() 绘图不同于 show()
Matplotlib.savefig 忽略轴并在图像周围绘制黑色边框