Matplotlib equivalent of pygame flip

前端 未结 4 529
天涯浪人
天涯浪人 2021-01-05 05:21

I have a program with rapid animations which works perfectly under pygame, and for technical reasons, I need to do the same using only matplotlib or an other widespread modu

相关标签:
4条回答
  • 2021-01-05 05:53

    Given you talked about using widespread modules, here's a proof of concept using OpenCV. It runs pretty fast here, up to 250-300 generated frames per second. It's nothing too fancy, just to show that maybe if you're not using any plotting feature matplotlib shouldn't really be your first choice.

    import sys                                                                                 
    import time                                                                                
    import numpy as np                                                                         
    import cv2                                                                                 
    
    if sys.version_info >= (3, 3):                                                             
        timer = time.perf_counter                                                              
    else:                                                                                      
        timer = time.time                                                                      
    
    def f(x, y):                                                                               
        return np.sin(x) + np.cos(y)                                                           
    
    # ESC, q or Q to quit                                                                      
    quitkeys = 27, 81, 113                                                                     
    # delay between frames                                                                     
    delay = 1                                                                                  
    # framerate debug init                                                                     
    counter = 0                                                                                
    overflow = 1                                                                               
    start = timer()                                                                            
    
    x = np.linspace(0, 2 * np.pi, 400)                                                         
    y = np.linspace(0, 2 * np.pi, 400).reshape(-1, 1)                                          
    
    while True:                                                                                
        x += np.pi / 15.                                                                       
        y += np.pi / 20.                                                                       
    
        cv2.imshow("animation", f(x, y))                                                       
    
        if cv2.waitKey(delay) & 0xFF in quitkeys:                                              
            cv2.destroyAllWindows()                                                            
            break                                                                              
    
        counter += 1                                                                           
        elapsed = timer() - start                                                              
        if elapsed > overflow:                                                                 
            print("FPS: {:.01f}".format(counter / elapsed))                                    
            counter = 0                                                                        
            start = timer()                                                                                                
    
    0 讨论(0)
  • 2021-01-05 06:10

    If you just need to animate a matplotlib canvas the animation framework is the answer. There's a simple example here that does basically what you ask.

    If this is going to be part of a more complex application you probably want finer control over a specific backend.

    Here's a quick attempt using Qt loosely based on this matplotlib example.

    It's using a QTimer for the updates, probably there's also some idle callback in Qt you could attach to.

    import sys
    
    import numpy as np
    import matplotlib as mpl
    mpl.use('qt5agg')
    from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
    from matplotlib.figure import Figure
    from PyQt5 import QtWidgets, QtCore
    
    size = (400, 400)
    
    class GameCanvas(FigureCanvas):
        def __init__(self, parent=None, width=5, height=4, dpi=100):
            fig = Figure(figsize=(width, height), dpi=dpi)
    
            self.axes = fig.gca()
            self.init_figure()
    
            FigureCanvas.__init__(self, fig)
            self.setParent(parent)
    
            timer = QtCore.QTimer(self)
            timer.timeout.connect(self.update_figure)
            timer.start(10)
    
        def gen_frame(self):
            return np.random.randint(0,0xfffff,size)
    
        def init_figure(self):
            self.img = self.axes.imshow(self.gen_frame())
    
        def update_figure(self):
            self.img.set_data(self.gen_frame())
            self.draw()
    
    class ApplicationWindow(QtWidgets.QMainWindow):
        def __init__(self):
            QtWidgets.QMainWindow.__init__(self)
            self.main_widget = QtWidgets.QWidget(self)
    
            dc = GameCanvas(self.main_widget, width=5, height=4, dpi=100)
            self.setCentralWidget(dc)
    
        def fileQuit(self):
            self.close()
    
        def closeEvent(self, ce):
            self.fileQuit()
    
    app = QtWidgets.QApplication(sys.argv)
    appw = ApplicationWindow()
    appw.show()
    sys.exit(app.exec_())
    

    One thing you should be careful with is that imshow computes the image normalization on the first frame. In the subsequent frames it's calling set_data so the normalization stays the same. If you want to update it you can call imshow instead (probably slower). Or you could just fix it manually with vmin and vmax in the first imshow call and provide properly normalized frames.

    0 讨论(0)
  • 2021-01-05 06:11

    It should be noted that the human brain is capable of "seeing" up to a framerate of ~25 fps. Faster updates are not actually resolved.

    Matplotlib

    With matplotlib and its animation module the example from the question runs with 84 fps on my computer.

    import time
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    
    fig, ax = plt.subplots()
    
    
    def f(x, y):
        return np.sin(x) + np.cos(y)
    
    x = np.linspace(0, 2 * np.pi, 400)
    y = np.linspace(0, 2 * np.pi, 400).reshape(-1, 1)
    
    im = ax.imshow(f(x, y), animated=True)
    text = ax.text(200,200, "")
    
    class FPS():
        def __init__(self, avg=10):
            self.fps = np.empty(avg)
            self.t0 = time.clock()
        def tick(self):
            t = time.clock()
            self.fps[1:] = self.fps[:-1]
            self.fps[0] = 1./(t-self.t0)
            self.t0 = t
            return self.fps.mean()
    
    fps = FPS(100)
    
    def updatefig(i):
        global x, y
        x += np.pi / 15.
        y += np.pi / 20.
        im.set_array(f(x, y))
        tx = 'Mean Frame Rate:\n {fps:.3f}FPS'.format(fps= fps.tick() ) 
        text.set_text(tx)     
        return im, text,
    
    ani = animation.FuncAnimation(fig, updatefig, interval=1, blit=True)
    plt.show()
    

    PyQtGraph

    In pyqtgraph a higher framerate is obtained, it would run with 295 fps on my computer.

    import sys
    import time
    from pyqtgraph.Qt import QtCore, QtGui
    import numpy as np
    import pyqtgraph as pg
    
    class FPS():
        def __init__(self, avg=10):
            self.fps = np.empty(avg)
            self.t0 = time.clock()
        def tick(self):
            t = time.clock()
            self.fps[1:] = self.fps[:-1]
            self.fps[0] = 1./(t-self.t0)
            self.t0 = t
            return self.fps.mean()
    
    fps = FPS(100)
    
    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)
    
            #### Set Data  #####################
            self.x = np.linspace(0, 2 * np.pi, 400)
            self.y = np.linspace(0, 2 * np.pi, 400).reshape(-1, 1)
    
            #### Start  #####################
            self._update()
            
        def f(self, x, y):
                return np.sin(x) + np.cos(y)
            
        def _update(self):
    
            self.x += np.pi / 15.
            self.y += np.pi / 20.
            self.img.setImage(self.f(self.x, self.y))
    
            tx = 'Mean Frame Rate:\n {fps:.3f}FPS'.format(fps= fps.tick() ) 
            self.label.setText(tx)
            QtCore.QTimer.singleShot(1, self._update)
    
    
    if __name__ == '__main__':
    
        app = QtGui.QApplication(sys.argv)
        thisapp = App()
        thisapp.show()
        sys.exit(app.exec_())
    
    0 讨论(0)
  • 2021-01-05 06:11

    If you want to animate a plot, then you can take a look at the animation functionality in matplotlib under matplotlib.animation.Animation. Here's a great tutorial - https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial.

    If you just want to periodically update an adhoc bitmap, I am not sure matplotlib is meant for what you are trying to achieve. From matplotlib docs:

    Matplotlib is a Python 2D plotting library which produces publication quality figures in a variety of hardcopy formats and interactive environments across platforms.

    If you would like to periodically update an adhoc image on the screen, you may want to look into GUI libraries for python. Here is a short summary of available options - https://docs.python.org/3/faq/gui.html. Tkinter is a pretty standard one and is shipped with python. You can use the ImageTk module in pillow to create/modify images for displaying via Tkinter - http://pillow.readthedocs.io/en/4.2.x/reference/ImageTk.html.

    0 讨论(0)
提交回复
热议问题