Matplotlib 的快速实时绘图/PyPlot

多年来,我一直努力在 matplotlib 中获得高效的实时绘图,直到今天我仍然不满意。

我需要一个 redraw_figure函数,它可以更新图形“ live”(代码运行时) ,并且如果我在断点处停止,它将显示最新的图形。

下面是一些演示代码:

import time
from matplotlib import pyplot as plt
import numpy as np


def live_update_demo():


plt.subplot(2, 1, 1)
h1 = plt.imshow(np.random.randn(30, 30))
redraw_figure()
plt.subplot(2, 1, 2)
h2, = plt.plot(np.random.randn(50))
redraw_figure()


t_start = time.time()
for i in xrange(1000):
h1.set_data(np.random.randn(30, 30))
redraw_figure()
h2.set_ydata(np.random.randn(50))
redraw_figure()
print 'Mean Frame Rate: %.3gFPS' % ((i+1) / (time.time() - t_start))


def redraw_figure():
plt.draw()
plt.pause(0.00001)


live_update_demo()

当代码运行时,图应该实时更新,我们应该在 redraw_figure()之后的任何断点处停止时看到最新的数据。问题是如何最好地实现 redraw_figure()

在上面的实现(plt.draw(); plt.pause(0.00001))中,它可以工作,但是非常慢(约3.7 FPS)

我可以把它实现为:

def redraw_figure():
plt.gcf().canvas.flush_events()
plt.show(block=False)

它运行速度更快(大约11 FPS) ,但是当您在断点处停止时,绘图不是最新的(例如,如果我在 t_start = ...行上放置了一个断点,则不会出现第二个绘图)。

奇怪的是,真正起作用的是节目被叫了两次:

def redraw_figure():
plt.gcf().canvas.flush_events()
plt.show(block=False)
plt.show(block=False)

它给出 ~ 11 FPS,并保持图最新数据,如果你在任何行上的突破。

现在我听说“ block”关键字是不推荐的。而且两次调用同一个函数似乎是一种奇怪的,可能是不可移植的黑客行为。

那么,我可以在这个函数中放入什么,使它能够以合理的帧速率绘图,而不是一个巨大的组件,并且最好能够跨后端和系统工作?

一些注意事项:

  • 我在 OSX 上,并使用 TkAgg后端,但任何后端/系统的解决方案都是受欢迎的
  • 交互模式“打开”将无法工作,因为它不会实时更新。当解释器等待用户输入时,它只在 Python 控制台中进行更新。
  • 博客建议实施:

    Def repull _ figure () : Fig = plt.gcf () (图片) Flash _ events ()

但至少在我的系统中,这根本不会重新绘制情节。

所以,如果有人有答案,你会直接让我和其他成千上万的人非常高兴。他们的幸福可能会慢慢传递给他们的朋友和亲戚,以及他们的朋友和亲戚,等等,这样你就有可能改善数十亿人的生活。

结论

Import anceOfBeingErnest 展示了如何使用 blit 来实现更快的绘图,但这并不像在 redraw_figure函数中放入不同的内容那么简单(您需要跟踪要重绘的内容)。

89436 次浏览

首先,问题中提交的代码在我的机器上运行,速度为7 fps,后端是 QT4Agg。

现在,正如在许多文章中所建议的,像 给你给你,使用 blit可能是一种选择。虽然 这篇文章提到 blit 会导致很强的内存泄漏,但是我没有注意到这一点。

我已经修改了你的代码一点点,并比较了帧速率和没有使用位点。下面的代码给出

  • 没有闪光灯的情况下,每秒28格
  • 每秒175格

密码:

import time
from matplotlib import pyplot as plt
import numpy as np




def live_update_demo(blit = False):
x = np.linspace(0,50., num=100)
X,Y = np.meshgrid(x,x)
fig = plt.figure()
ax1 = fig.add_subplot(2, 1, 1)
ax2 = fig.add_subplot(2, 1, 2)


img = ax1.imshow(X, vmin=-1, vmax=1, interpolation="None", cmap="RdBu")




line, = ax2.plot([], lw=3)
text = ax2.text(0.8,0.5, "")


ax2.set_xlim(x.min(), x.max())
ax2.set_ylim([-1.1, 1.1])


fig.canvas.draw()   # note that the first draw comes before setting data




if blit:
# cache the background
axbackground = fig.canvas.copy_from_bbox(ax1.bbox)
ax2background = fig.canvas.copy_from_bbox(ax2.bbox)


plt.show(block=False)




t_start = time.time()
k=0.


for i in np.arange(1000):
img.set_data(np.sin(X/3.+k)*np.cos(Y/3.+k))
line.set_data(x, np.sin(x/3.+k))
tx = 'Mean Frame Rate:\n {fps:.3f}FPS'.format(fps= ((i+1) / (time.time() - t_start)) )
text.set_text(tx)
#print tx
k+=0.11
if blit:
# restore background
fig.canvas.restore_region(axbackground)
fig.canvas.restore_region(ax2background)


# redraw just the points
ax1.draw_artist(img)
ax2.draw_artist(line)
ax2.draw_artist(text)


# fill in the axes rectangle
fig.canvas.blit(ax1.bbox)
fig.canvas.blit(ax2.bbox)


# in this post http://bastibe.de/2013-05-30-speeding-up-matplotlib.html
# it is mentionned that blit causes strong memory leakage.
# however, I did not observe that.


else:
# redraw everything
fig.canvas.draw()


fig.canvas.flush_events()
#alternatively you could use
#plt.pause(0.000000000001)
# however plt.pause calls canvas.draw(), as can be read here:
#http://bastibe.de/2013-05-30-speeding-up-matplotlib.html




live_update_demo(True)   # 175 fps
#live_update_demo(False) # 28 fps

更新:
为了更快地绘图,可以考虑使用 图表
正如 Pyqtgraph 文档所说: “对于绘图,pyqtgraph 不像 matplotlib 那样完整/成熟,但运行速度要快得多。”

我将上面的示例移植到 pyqtgraph。虽然它看起来有点丑,但是它在我的机器上可以运行250fps。

总结一下,

  • Matplotlib (无 blitting) : 28 fps
  • Matplotlib (带 blitting) : 175 fps
  • 图片来源: 250帧/秒

Pyqtgraph 代码:

import sys
import time
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
import pyqtgraph as pg




class App(QtGui.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent)


#### Create Gui Elements ###########
self.mainbox = QtGui.QWidget()
self.setCentralWidget(self.mainbox)
self.mainbox.setLayout(QtGui.QVBoxLayout())


self.canvas = pg.GraphicsLayoutWidget()
self.mainbox.layout().addWidget(self.canvas)


self.label = QtGui.QLabel()
self.mainbox.layout().addWidget(self.label)


self.view = self.canvas.addViewBox()
self.view.setAspectLocked(True)
self.view.setRange(QtCore.QRectF(0,0, 100, 100))


#  image plot
self.img = pg.ImageItem(border='w')
self.view.addItem(self.img)


self.canvas.nextRow()
#  line plot
self.otherplot = self.canvas.addPlot()
self.h2 = self.otherplot.plot(pen='y')




#### Set Data  #####################


self.x = np.linspace(0,50., num=100)
self.X,self.Y = np.meshgrid(self.x,self.x)


self.counter = 0
self.fps = 0.
self.lastupdate = time.time()


#### Start  #####################
self._update()


def _update(self):


self.data = np.sin(self.X/3.+self.counter/9.)*np.cos(self.Y/3.+self.counter/9.)
self.ydata = np.sin(self.x/3.+ self.counter/9.)


self.img.setImage(self.data)
self.h2.setData(self.ydata)


now = time.time()
dt = (now-self.lastupdate)
if dt <= 0:
dt = 0.000000000001
fps2 = 1.0 / dt
self.lastupdate = now
self.fps = self.fps * 0.9 + fps2 * 0.1
tx = 'Mean Frame Rate:  {fps:.3f} FPS'.format(fps=self.fps )
self.label.setText(tx)
QtCore.QTimer.singleShot(1, self._update)
self.counter += 1




if __name__ == '__main__':


app = QtGui.QApplication(sys.argv)
thisapp = App()
thisapp.show()
sys.exit(app.exec_())

这里有一种实时绘图的方法: 将图像作为图像数组,然后将图像绘制到多线程屏幕上。

示例使用一个 pyformectscreen (~ 30FPS) :

import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time


fig = plt.figure()


screen = pf.screen(title='Plot')


start = time.time()
for i in range(10000):
t = time.time() - start


x = np.linspace(t-3, t, 100)
y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
plt.xlim(t-3,t)
plt.ylim(-3,3)
plt.plot(x, y, c='black')


# If we haven't already shown or saved the plot, then we need to draw the figure first...
fig.canvas.draw()


image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))


screen.update(image)


#screen.close()

enter image description here

免责声明: 我是一个公式的维护者