B. *_* M. 8 python pygame numpy matplotlib
我有一个快速动画的程序,它在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()
Run Code Online (Sandbox Code Playgroud)
我没有低级别的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))
Run Code Online (Sandbox Code Playgroud)
编辑
我在答案中尝试了指示:
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()
Run Code Online (Sandbox Code Playgroud)
但这仅提供20 fps ....
应该注意的是,人脑能够"看到"高达~25 fps的帧率.实际上没有解决更快的更新.
使用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()
Run Code Online (Sandbox Code Playgroud)
在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_())
Run Code Online (Sandbox Code Playgroud)
如果要为绘图设置动画,则可以在matplotlib下查看动画功能matplotlib.animation.Animation.这是一个很棒的教程 - https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial.
如果您只是想定期更新adhoc位图,我不确定matplotlib是否适用于您要实现的目标.来自matplotlib文档:
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.
如果您只需要为matplotlib画布设置动画,那么动画框架就是答案.有一个简单的例子,在这里,做基本上你问什么.
如果这将成为更复杂的应用程序的一部分,您可能希望更好地控制特定的后端.
这是Qt基于这个matplotlib示例的松散尝试.
它正在使用a 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_())
Run Code Online (Sandbox Code Playgroud)
你应该注意的一件事是imshow计算第一帧的图像标准化.在随后的帧中,它正在调用,set_data因此规范化保持不变.如果你想更新它,你可以打电话imshow(可能更慢).或者你可以只用手工修复vmin,并vmax在第一个imshow呼叫,并提供适当的标准化框架.
鉴于您谈到使用广泛的模块,这里是使用概念的证明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()
Run Code Online (Sandbox Code Playgroud)