построение графика в реальном времени в цикле while с помощью matplotlib
Я пытаюсь построить некоторые данные с камеры в режиме реального времени с использованием OpenCV. Однако график в реальном времени (с использованием matplotlib), похоже, не работает.
Я выделил проблему в этом простом примере:
fig=plt.figure()
plt.axis([0,1000,0,1])
i=0
x=list()
y=list()
while i <1000:
temp_y=np.random.random()
x.append(i)
y.append(temp_y)
plt.scatter(i,temp_y)
i+=1
plt.show()
Я ожидал бы, что этот пример построит 1000 точек по отдельности. Что на самом деле происходит, так это то, что окно появляется с первой точкой, показывающей (хорошо с этим), а затем ждет завершения цикла, прежде чем он заполнит остальную часть диаграмма.
любые мысли, почему я не вижу точек, заполненных по одному?
10 ответов:
вот рабочая версия рассматриваемого кода (требуется хотя бы версия Matplotlib 1.1.0 от 2011-11-14):
import numpy as np import matplotlib.pyplot as plt plt.axis([0, 10, 0, 1]) for i in range(10): y = np.random.random() plt.scatter(i, y) plt.pause(0.05) plt.show()
обратите внимание на некоторые изменения:
- вызов
plt.pause(0.05)
как для рисования новых данных, так и для запуска цикла событий GUI (с учетом взаимодействия с мышью).
Если вы заинтересованы в построении графика в реальном времени, я бы рекомендовал изучить API анимации matplotlib. В частности, с помощью
blit
чтобы избежать перерисовки фона на каждом кадре может дать вам существенное увеличение скорости (~10x):#!/usr/bin/env python import numpy as np import time import matplotlib matplotlib.use('GTKAgg') from matplotlib import pyplot as plt def randomwalk(dims=(256, 256), n=20, sigma=5, alpha=0.95, seed=1): """ A simple random walk with memory """ r, c = dims gen = np.random.RandomState(seed) pos = gen.rand(2, n) * ((r,), (c,)) old_delta = gen.randn(2, n) * sigma while True: delta = (1. - alpha) * gen.randn(2, n) * sigma + alpha * old_delta pos += delta for ii in xrange(n): if not (0. <= pos[0, ii] < r): pos[0, ii] = abs(pos[0, ii] % r) if not (0. <= pos[1, ii] < c): pos[1, ii] = abs(pos[1, ii] % c) old_delta = delta yield pos def run(niter=1000, doblit=True): """ Display the simulation using matplotlib, optionally using blit for speed """ fig, ax = plt.subplots(1, 1) ax.set_aspect('equal') ax.set_xlim(0, 255) ax.set_ylim(0, 255) ax.hold(True) rw = randomwalk() x, y = rw.next() plt.show(False) plt.draw() if doblit: # cache the background background = fig.canvas.copy_from_bbox(ax.bbox) points = ax.plot(x, y, 'o')[0] tic = time.time() for ii in xrange(niter): # update the xy data x, y = rw.next() points.set_data(x, y) if doblit: # restore background fig.canvas.restore_region(background) # redraw just the points ax.draw_artist(points) # fill in the axes rectangle fig.canvas.blit(ax.bbox) else: # redraw everything fig.canvas.draw() plt.close(fig) print "Blit = %s, average FPS: %.2f" % ( str(doblit), niter / (time.time() - tic)) if __name__ == '__main__': run(doblit=False) run(doblit=True)
выход:
Blit = False, average FPS: 54.37 Blit = True, average FPS: 438.27
show
- вероятно, не лучший выбор для этого. То, что я хотел бы сделать, это использоватьpyplot.draw()
вместо. Вы также можете включить небольшую временную задержку (например,time.sleep(0.05)
) в цикле, так что вы можете видеть сюжеты происходит. Если я внесу эти изменения в ваш пример, это сработает для меня, и я вижу, что каждая точка появляется по одному.
ни один из методов не работал для меня. Но я нашел это в режиме реального времени matplotlib сюжет не работает, пока все еще в цикле
все, что вам нужно, это добавить
plt.pause(0.0001)
и чем вы могли видеть новый сюжет.
так что ваш код должен выглядеть так, и он будет работать
import matplotlib.pyplot as plt import numpy as np plt.ion() ## Note this correction fig=plt.figure() plt.axis([0,1000,0,1]) i=0 x=list() y=list() while i <1000: temp_y=np.random.random(); x.append(i); y.append(temp_y); plt.scatter(i,temp_y); i+=1; plt.show() plt.pause(0.0001) #Note this correction
Я знаю, что немного опоздал с ответом на этот вопрос. Тем не менее, я сделал некоторый код некоторое время назад, чтобы построить живые графики, которые я хотел бы поделиться:
################################################################### # # # PLOTTING A LIVE GRAPH # # ---------------------------- # # EMBED A MATPLOTLIB ANIMATION INSIDE YOUR # # OWN GUI! # # # ################################################################### import sys import os from PyQt4 import QtGui from PyQt4 import QtCore import functools import numpy as np import random as rd import matplotlib matplotlib.use("Qt4Agg") from matplotlib.figure import Figure from matplotlib.animation import TimedAnimation from matplotlib.lines import Line2D from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas import time import threading def setCustomSize(x, width, height): sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth()) x.setSizePolicy(sizePolicy) x.setMinimumSize(QtCore.QSize(width, height)) x.setMaximumSize(QtCore.QSize(width, height)) '''''' class CustomMainWindow(QtGui.QMainWindow): def __init__(self): super(CustomMainWindow, self).__init__() # Define the geometry of the main window self.setGeometry(300, 300, 800, 400) self.setWindowTitle("my first window") # Create FRAME_A self.FRAME_A = QtGui.QFrame(self) self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name()) self.LAYOUT_A = QtGui.QGridLayout() self.FRAME_A.setLayout(self.LAYOUT_A) self.setCentralWidget(self.FRAME_A) # Place the zoom button self.zoomBtn = QtGui.QPushButton(text = 'zoom') setCustomSize(self.zoomBtn, 100, 50) self.zoomBtn.clicked.connect(self.zoomBtnAction) self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0)) # Place the matplotlib figure self.myFig = CustomFigCanvas() self.LAYOUT_A.addWidget(self.myFig, *(0,1)) # Add the callbackfunc to .. myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,)) myDataLoop.start() self.show() '''''' def zoomBtnAction(self): print("zoom in") self.myFig.zoomIn(5) '''''' def addData_callbackFunc(self, value): # print("Add data: " + str(value)) self.myFig.addData(value) ''' End Class ''' class CustomFigCanvas(FigureCanvas, TimedAnimation): def __init__(self): self.addedData = [] print(matplotlib.__version__) # The data self.xlim = 200 self.n = np.linspace(0, self.xlim - 1, self.xlim) a = [] b = [] a.append(2.0) a.append(4.0) a.append(2.0) b.append(4.0) b.append(3.0) b.append(4.0) self.y = (self.n * 0.0) + 50 # The window self.fig = Figure(figsize=(5,5), dpi=100) self.ax1 = self.fig.add_subplot(111) # self.ax1 settings self.ax1.set_xlabel('time') self.ax1.set_ylabel('raw data') self.line1 = Line2D([], [], color='blue') self.line1_tail = Line2D([], [], color='red', linewidth=2) self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r') self.ax1.add_line(self.line1) self.ax1.add_line(self.line1_tail) self.ax1.add_line(self.line1_head) self.ax1.set_xlim(0, self.xlim - 1) self.ax1.set_ylim(0, 100) FigureCanvas.__init__(self, self.fig) TimedAnimation.__init__(self, self.fig, interval = 50, blit = True) def new_frame_seq(self): return iter(range(self.n.size)) def _init_draw(self): lines = [self.line1, self.line1_tail, self.line1_head] for l in lines: l.set_data([], []) def addData(self, value): self.addedData.append(value) def zoomIn(self, value): bottom = self.ax1.get_ylim()[0] top = self.ax1.get_ylim()[1] bottom += value top -= value self.ax1.set_ylim(bottom,top) self.draw() def _step(self, *args): # Extends the _step() method for the TimedAnimation class. try: TimedAnimation._step(self, *args) except Exception as e: self.abc += 1 print(str(self.abc)) TimedAnimation._stop(self) pass def _draw_frame(self, framedata): margin = 2 while(len(self.addedData) > 0): self.y = np.roll(self.y, -1) self.y[-1] = self.addedData[0] del(self.addedData[0]) self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ]) self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin])) self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin]) self._drawn_artists = [self.line1, self.line1_tail, self.line1_head] ''' End Class ''' # You need to setup a signal slot mechanism, to # send data to your GUI in a thread-safe way. # Believe me, if you don't do this right, things # go very very wrong.. class Communicate(QtCore.QObject): data_signal = QtCore.pyqtSignal(float) ''' End Class ''' def dataSendLoop(addData_callbackFunc): # Setup the signal-slot mechanism. mySrc = Communicate() mySrc.data_signal.connect(addData_callbackFunc) # Simulate some data n = np.linspace(0, 499, 500) y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5)) i = 0 while(True): if(i > 499): i = 0 time.sleep(0.1) mySrc.data_signal.emit(y[i]) # <- Here you emit a signal! i += 1 ### ### if __name__== '__main__': app = QtGui.QApplication(sys.argv) QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique')) myGUI = CustomMainWindow() sys.exit(app.exec_()) ''''''
просто попробуйте. Скопируйте-вставьте этот код в новый python-файл и запустите его. Вы должны получить красивый, плавно движущийся график:
Я знаю, что этот вопрос старый, но теперь есть пакет, доступный под названием drawnow на GitHub как "python-drawnow". Это обеспечивает интерфейс, похожий на drawnow MATLAB - вы можете легко обновить рисунок.
пример для вашего случая:
import matplotlib.pyplot as plt from drawnow import drawnow def make_fig(): plt.scatter(x, y) # I think you meant this plt.ion() # enable interactivity fig = plt.figure() # make a figure x = list() y = list() for i in range(1000): temp_y = np.random.random() x.append(i) y.append(temp_y) # or any arbitrary update to your figure's data i += 1 drawnow(make_fig)
python-drawnow-это тонкая обертка вокруг
plt.draw
но обеспечивает возможность подтверждения (или отладки) после отображения рисунка.
проблема, кажется, в том, что вы ожидаете
plt.show()
показать окно, а затем вернуться. Он этого не делает. Программа остановится в этот момент и возобновится только после закрытия окна. Вы должны быть в состоянии проверить это: если вы закроете окно, а затем появится другое окно.чтобы решить эту проблему, просто позвоните
plt.show()
один раз после вашего цикла. Тогда вы получите полный сюжет. (Но не в режиме реального времени планирую)вы можете попробовать установить ключевое слово-аргумент
block
такой:plt.show(block=False)
один раз в начале, а затем использовать.draw()
обновить.
вот версия, которую я получил, чтобы работать на моей системе.
import matplotlib.pyplot as plt from drawnow import drawnow import numpy as np def makeFig(): plt.scatter(xList,yList) # I think you meant this plt.ion() # enable interactivity fig=plt.figure() # make a figure xList=list() yList=list() for i in np.arange(50): y=np.random.random() xList.append(i) yList.append(y) drawnow(makeFig) #makeFig() The drawnow(makeFig) command can be replaced #plt.draw() with makeFig(); plt.draw() plt.pause(0.001)
линия drawnow (makeFig) может быть заменена на makeFig (); plt.нарисуйте () последовательность, и она все еще работает нормально.
верхние (и многие другие) ответы были построены на
plt.pause()
, но это был старый способ оживления сюжета в matplotlib. Это не только медленно, но и заставляет сосредоточиться на каждом обновлении (мне было трудно остановить процесс построения python).TL; DR: можно использовать
matplotlib.animation
(как указано в документации).после копания вокруг различных ответов и кусков кода, это на самом деле оказалось гладким способом рисование входящих данных бесконечно для меня.
вот мой код для быстрого старта. Он строит текущее время со случайным числом в [0, 100) каждые 200 мс бесконечно, а также обрабатывает автоматическое масштабирование вида:
from datetime import datetime from matplotlib import pyplot from matplotlib.animation import FuncAnimation from random import randrange x_data, y_data = [], [] figure = pyplot.figure() line, = pyplot.plot_date(x_data, y_data, '-') def update(frame): x_data.append(datetime.now()) y_data.append(randrange(0, 100)) line.set_data(x_data, y_data) figure.gca().relim() figure.gca().autoscale_view() return line, animation = FuncAnimation(figure, update, interval=200) pyplot.show()
вы также можете исследовать
blit
для еще более высокой производительности как в документации FuncAnimation.
Если вы хотите рисовать и не замораживать поток, как больше точки рисуются вы должны использовать plt.пауза() не время.сон()
im использует следующий код для построения серии координат xy.
import matplotlib.pyplot as plt import math pi = 3.14159 fig, ax = plt.subplots() x = [] y = [] def PointsInCircum(r,n=20): circle = [(math.cos(2*pi/n*x)*r,math.sin(2*pi/n*x)*r) for x in xrange(0,n+1)] return circle circle_list = PointsInCircum(3, 50) for t in range(len(circle_list)): if t == 0: points, = ax.plot(x, y, marker='o', linestyle='--') ax.set_xlim(-4, 4) ax.set_ylim(-4, 4) else: x_coord, y_coord = circle_list.pop() x.append(x_coord) y.append(y_coord) points.set_data(x, y) plt.pause(0.01)