Matplotlib эквивалент пигментного флип - PullRequest
0 голосов
/ 10 мая 2018

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

EDIT

Что я пробую с указанием в ответах:

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 ]

0 голосов
/ 17 мая 2018

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

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 кадр / с на моем компьютере.

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 голосов
/ 13 мая 2018

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

0 голосов
/ 15 мая 2018

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

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

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

Matplotlib - это библиотека для построения 2D-графиков 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...