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

У меня нет опыта работы с 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 кадров в секунду....

4 ответа

Следует отметить, что человеческий мозг способен "видеть" до частоты кадров ~25 кадров в секунду. Более быстрые обновления на самом деле не решаются.

Matplotlib

С матплотлибом и его animation Модуль Пример из вопроса работает на моем компьютере с 84 кадров в секунду.

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 кадрами в секунду.

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

Если вы хотите анимировать сюжет, вы можете взглянуть на функциональность анимации в matplotlib под matplotlib.animation.Animation, Вот отличный учебник - https://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial.

Если вы просто хотите периодически обновлять растровое изображение adhoc, я не уверен, что matplotlib предназначен для того, чего вы пытаетесь достичь. Из документов matplotlib:

Matplotlib - это двухмерная библиотека черчения Python, которая генерирует показатели качества публикаций в различных печатных форматах и ​​интерактивных средах на разных платформах.

Если вы хотите периодически обновлять изображение adhoc на экране, вы можете заглянуть в библиотеки GUI для python. Вот краткое резюме доступных опций - 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.

Это использует 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 позвоните и предоставьте правильно нормированные кадры.

Учитывая, что вы говорили об использовании распространенных модулей, вот доказательство концепции использования 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()                                                                                                
Другие вопросы по тегам