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

我想要一个redraw_figure函数来更新图形“实时”(随着代码运行),并且如果我在断点处停止,将显示最新的绘图。

这是一些演示代码:

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.7FPS)

我可以将其实现为:
def redraw_figure():
    plt.gcf().canvas.flush_events()
    plt.show(block=False)

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

奇怪的是,实际上有效的方法是两次调用该节目:
def redraw_figure():
    plt.gcf().canvas.flush_events()
    plt.show(block=False)
    plt.show(block=False)

如果您在任何行上中断,它都能提供约11FPS的速度,并且可以使绘图保持最新状态。

现在,我听说它说“block”关键字已被弃用。而且两次调用相同的函数似乎还是很奇怪的,也许是不可移植的黑客。

那么,该功能以合理的帧速率绘制,不是很大的麻烦,并且最好能在后端和系统上运行,该如何处理呢?

一些注意事项:
  • 我在OSX上,并使用TkAgg后端,但是欢迎在任何后端/系统上使用解决方案
  • 交互式模式“开”将不起作用,因为它不会实时更新。当解释器等待用户输入时,它只会在Python控制台中更新。
  • blog建议实现:

    def redraw_figure():
    无花果= plt.gcf()
    fig.canvas.draw()
    fig.canvas.flush_events()

  • 但是至少在我的系统上,这根本不会重绘图。

    因此,如果有人有答案,您将直接让我和成千上万的其他人感到非常高兴。他们的幸福可能会渗入他们的 friend 和亲戚,以及他们的 friend 和亲戚,等等,这样您就有可能改善数十亿人的生活。

    结论

    ImportanceOfBeingErnest展示了如何使用blit进行更快的绘图,但这并不像在redraw_figure函数中放入其他内容那样简单(您需要跟踪要重绘的内容)。

    最佳答案

    首先,问题中发布的代码在我的计算机上以7 fps的速度运行,并且QT4Agg作为后端。

    现在,正如herehere等许多帖子中所建议的那样,可以选择使用blit。尽管this article提到blit会导致严重的内存泄漏,但我无法观察到。

    我对您的代码进行了一些修改,并比较了使用blit和不使用blit的帧速率。下面的代码给出

  • 在没有blit的情况下运行时为28 fps
  • 含litt 的
  • 175 fps

    码:
    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 documentation所说:“对于绘图,pyqtgraph不像matplotlib那样完整/成熟,但是运行速度更快。”

    我将上面的示例移植到pyqtgraph。尽管它看起来很丑陋,但在我的机器上却以250 fps的速度运行。

    总结一下,
  • matplotlib(不带 Blob ):28 fps
  • matplotlib(带有blitting):175 fps
  • pyqtgraph: 250 fps

  • 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_())
    

    10-07 19:11
    查看更多