Matplotlib相当于pygame flip

时间:2018-05-10 14:57:00

标签: python numpy matplotlib pygame

我有一个快速动画的程序,它在pygame下完美运行,出于技术原因,我只需要使用matplotlib或其他普遍的模块来做同样的事情。

程序结构大致如下:

pygame.init()        
SURF = pygame.display.set_mode((500, 500))
arr = pygame.surfarray.pixels2d(SURF) # a view for numpy, as a 2D array
while ok:
    # modify some pixels of arr
    pygame.display.flip()
pygame.quit()

我没有低级matplotlib经验,但我认为可以用matplotlib做同等的事情。换句话说:

如何共享数字的位图,修改一些像素并刷新屏幕?

这是一个最小的工作示例,它在我的计算机上每秒翻转250帧(超过屏幕......):

import pygame,numpy,time
pygame.init()
size=(400,400)        
SURF = pygame.display.set_mode(size)
arr = pygame.surfarray.pixels2d(SURF) # buffer pour numpy   
t0=time.clock()

for counter in range(1000):
        arr[:]=numpy.random.randint(0,0xfffff,size)
        pygame.display.flip()      
pygame.quit()

print(counter/(time.clock()-t0))

修改

我在答案中尝试了适应症:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

fig = plt.figure()


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 = plt.imshow(f(x, y), animated=True)

count=0
t0=time.clock()+1
def updatefig(*args):
    global x, y,count,t0
    x += np.pi / 15.
    y += np.pi / 20.
    im.set_array(f(x, y))
    if time.clock()<t0:
        count+=1
    else:
        print (count)
        count=0
        t0=time.clock()+1     
    return im,

ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True)
plt.show()

但这只能提供20 fps ......

4 个答案:

答案 0 :(得分:8)

应该注意的是,人脑能够“看到”高达~25 fps的帧率。实际上没有解决更快的更新。

Matplotlib

使用matplotlib及其animation模块,问题中的示例在我的计算机上以 84 fps 运行。

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

在pyqtgraph中获得更高的帧速率,它将在我的计算机上以 295 fps 运行。

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

答案 1 :(得分:7)

如果要为绘图设置动画,则可以在matplotlib.animation.Animation下查看matplotlib中的动画功能。这是一个很棒的教程 - https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial

如果您只想定期更新adhoc位图,我不确定matplotlib是否适用于您要实现的目标。来自matplotlib docs:

  

Matplotlib是一个Python 2D绘图库,可以生成各种硬拷贝格式和跨平台交互式环境的出版物质量数据。

如果您想定期更新屏幕上的adhoc图像,您可能需要查看python的GUI库。以下是可用选项的简短摘要 - https://docs.python.org/3/faq/gui.html。 Tkinter是一个非常标准的,并附带python。您可以使用ImageTk中的pillow模块创建/修改图片,以便通过Tkinter显示 - http://pillow.readthedocs.io/en/4.2.x/reference/ImageTk.html

答案 2 :(得分:6)

如果您只需要为matplotlib画布设置动画,那么动画框架就是答案。有一个简单的例子here基本上可以满足您的要求。

如果这将成为更复杂的应用程序的一部分,您可能希望更好地控制特定的后端。

这是Qt基于this matplotlib example松散使用的快速尝试。

它使用QTimer进行更新,可能还有Qt中可能附加的空闲回调。

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

你应该注意的一件事是imshow计算第一帧的图像标准化。在随后的帧中,它调用set_data,因此规范化保持不变。如果你想更新它,你可以改为调用imshow(可能更慢)。或者您可以在第一个vmin来电中使用vmaximshow手动修复它,并提供正确规范化的帧。

答案 3 :(得分:6)

鉴于您谈到使用广泛的模块,这里使用OpenCV进行概念验证。它在这里运行速度非常快,每秒最多可生成250-300帧。没有什么太花哨的,只是为了表明,如果你不使用任何绘图功能matplotlib,那么它不应该是你的第一选择。

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