我有一个快速动画的程序,它在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 ......
答案 0 :(得分:8)
应该注意的是,人脑能够“看到”高达~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()
在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
来电中使用vmax
和imshow
手动修复它,并提供正确规范化的帧。
答案 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()