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

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


当前回答

顶部(以及许多其他)答案是基于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()

其他回答

这是使用while循环绘制动态实时matplot动画的正确方法

有一篇关于这方面的文章:

PIP安装赛璐珞#这将捕获图像/动画

import matplotlib.pyplot as plt
import numpy as np
from celluloid import Camera # getting the camera
import matplotlib.animation as animation
from IPython import display
import time
from IPython.display import HTML

import warnings
%matplotlib notebook
warnings.filterwarnings('ignore')
warnings.simplefilter('ignore')

fig = plt.figure() #Empty fig object
ax = fig.add_subplot() #Empty axis object
camera = Camera(fig) # Camera object to capture the snap

def f(x):
    ''' function to create a sine wave'''
    return np.sin(x) + np.random.normal(scale=0.1, size=len(x))

l = []

while True:
    value = np.random.randint(9) #random number generator
    l.append(value) # appneds each time number is generated
    X = np.linspace(10, len(l)) # creates a line space for x axis, Equal to the length of l

    for i in range(10): #plots 10 such lines
        plt.plot(X, f(X))

    fig.show() #shows the figure object
    fig.canvas.draw() 
    camera.snap() # camera object to capture teh animation
    time.sleep(1)

以及用于储蓄等:

animation = camera.animate(interval = 200, repeat = True, repeat_delay = 500)
HTML(animation.to_html5_video())
animation.save('abc.mp4') # to save 

输出是:

我知道这个问题很老了,但是现在在GitHub上有一个叫做“python-drawnow”的包。这提供了一个类似于MATLAB的drawnow的界面——您可以轻松地更新图形。

用例示例:

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的一个薄包装。绘图,但提供了图形显示后确认(或调试)的能力。

带有保留线条风格的圆形缓冲区的活动情节:

import os
import time
import psutil
import collections

import matplotlib.pyplot as plt

pts_n = 100
x = collections.deque(maxlen=pts_n)
y = collections.deque(maxlen=pts_n)
(line, ) = plt.plot(x, y, linestyle="--")

my_process = psutil.Process(os.getpid())
t_start = time.time()
while True:
    x.append(time.time() - t_start)
    y.append(my_process.cpu_percent())

    line.set_xdata(x)
    line.set_ydata(y)
    plt.gca().relim()
    plt.gca().autoscale_view()
    plt.pause(0.1)

问题似乎是您希望plot .show()显示窗口,然后返回。它不会那样做。程序将在此时停止,只有在您关闭该窗口后才恢复。您应该能够测试这一点:如果您关闭窗口,然后另一个窗口将弹出。

要解决这个问题,只需在循环之后调用一次plt.show()。然后你就得到了完整的情节。(但不是“实时绘图”)

你可以尝试像这样设置关键字参数块:plt.show(block=False)在开始时,然后使用.draw()来更新。

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

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浏览器中显示结果。当数据由远程无头服务器进程生成时,它尤其有用。