построение графика в режиме реального времени в цикле 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.пауза() не время.sleep()
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)