我正在尝试使用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个点。实际发生的情况是,窗口弹出,显示第一个点(好吧),然后等待循环结束,然后填充图的其余部分。

有什么想法,为什么我没有看到点填充一个时间?


当前回答

这些方法对我都没用。 但我找到了这个 实时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

其他回答

如果你想要绘制而不是在绘制更多点时冻结线程,你应该使用plt.pause()而不是time.sleep()

我使用下面的代码来绘制一系列的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)

下面是问题代码的工作版本(至少需要2011年11月14日的Matplotlib 1.1.0版本):

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的事件循环(允许鼠标交互)。

表演可能不是最好的选择。我要做的是使用pyplot.draw()代替。您还可能希望在循环中包含一个小的时间延迟(例如,time.sleep(0.05)),以便您可以看到情节的发生。如果我对你的例子做这些改变,它对我有用,我看到每个点一次出现一个。

顶部(以及许多其他)答案是基于plot .pause()构建的,但这是matplotlib中动画情节的旧方法。它不仅很慢,而且还会导致在每次更新时捕获焦点(我很难停止绘图python进程)。

TL;DR:您可能需要使用matplotlib。动画(如文档中所述)。

在深入研究了各种答案和代码片段后,事实证明,这是一种平滑的方法,可以无限地绘制传入数据。

下面是快速启动的代码。它每200ms无限地用[0,100]中的随机数绘制当前时间,同时还处理视图的自动缩放:

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

你也可以在FuncAnimation文档中探索blit,以获得更好的性能。

一个来自blit文档的例子:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)
plt.show()

另一种选择是使用散景。在我看来,至少对于实时情节来说,这是一个不错的选择。下面是问题中代码的散景版本:

from bokeh.plotting import curdoc, figure
import random
import time

def update():
    global i
    temp_y = random.random()
    r.data_source.stream({'x': [i], 'y': [temp_y]})
    i += 1

i = 0
p = figure()
r = p.circle([], [])
curdoc().add_root(p)
curdoc().add_periodic_callback(update, 100)

对于运行它:

pip3 install bokeh
bokeh serve --show test.py

Bokeh通过websocket通信在web浏览器中显示结果。当数据由远程无头服务器进程生成时,它尤其有用。