如何为Matplotlib绘图的时间序列添加动画效果

15
我想在matplotlib中绘制一系列.png图像。目标是快速绘制它们以模拟电影效果,但我有其他原因希望避免实际创建.avi文件或保存matplotlib图形,然后在Python之外按顺序查看它们。
我特别想在Python的for循环中按顺序查看图像文件。假设我已经正确导入了matplotlib,并且我有自己的函数'new_image()'和'new_rect()',下面是一些示例代码,由于show()函数调用GUI主循环的阻塞效应而无法正常工作:
 for index in index_list:
     img = new_image(index)
     rect = new_rect(index)

     plt.imshow(img)
     plt.gca().add_patch(rect)
     plt.show()

     #I also tried pausing briefly and then closing, but this doesn't
     #get executed due to the GUI mainloop from show()
     time.sleep(0.25)
     plt.close()

上述代码能够显示第一张图片,但程序会卡住并等待我手动关闭结果窗口。一旦我关闭了它,程序就会卡住,不会重新绘制新的图像数据。我应该怎么做?还要注意的是,我已经尝试用plt.draw()命令替换plt.show()命令,然后在for循环外添加plt.show()命令,但这样不会显示任何内容,而且程序会卡住。

我怀疑Matplotlib并没有真正考虑到这一点。但无论如何,如果您在Matplotlib特定的邮件列表上询问此事,您可能会获得更多信息。 - Thomas K
3个回答

8

基于http://matplotlib.sourceforge.net/examples/animation/simple_anim_tkagg.html

根据这个网站的示例,
import time
import numpy as np
import matplotlib
matplotlib.use('TkAgg') # do this before importing pylab

import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)

def animate():
    tstart = time.time()                   # for profiling
    data=np.random.randn(10,10)
    im=plt.imshow(data)

    for i in np.arange(1,200):
        data=np.random.randn(10,10)
        im.set_data(data)
        fig.canvas.draw()                         # redraw the canvas
    print 'FPS:' , 200/(time.time()-tstart)

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate)
plt.show()

plt.imshow可以接受浮点数组、uint8数组或PIL图像。因此,如果你有一个PNG文件目录,你可以将它们作为PIL图像打开,并像这样进行动画处理:

import matplotlib
matplotlib.use('TkAgg') # do this before importing pylab
import matplotlib.pyplot as plt
import Image
import glob

fig = plt.figure()
ax = fig.add_subplot(111)

def animate():
    filenames=sorted(glob.glob('*.png'))
    im=plt.imshow(Image.open(filenames[0]))
    for filename in filenames[1:]:
        image=Image.open(filename)
        im.set_data(image)
        fig.canvas.draw() 

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate)
plt.show()

当我用实际的彩色PNG图像替换随机绘图数据时,这种方法不起作用。这是我尝试的最初的事情之一。 - ely
它对我起作用了,只需要在开头添加一个函数调用: plt.ion() - MZHm
1
@MZHm:你能告诉我更多关于你是如何运行代码的吗?是从命令行还是从IDE?使用的操作系统和Matplotlib版本是什么?据我所知,上面的代码应该可以在不使用plt.ion的情况下从命令行中正常运行。虽然这里使用plt.ion也没问题,但避免使用plt.ion可以让你更好地控制显示的时间和方式。 - unutbu
我同意,在这种情况下它确实有效,并且避免使用ion()是更好的选择。然而,在我的一个稍微不同的用例中,我没有使用fig.canvas.manager.window.after()函数。在那种情况下,仅仅在之前调用ion()就可以了。但你是对的,是我的错误。 - MZHm

6
我发现最好的方法是在导入pylab后使用命令pylab.ion()。下面是一个脚本,它使用show()来显示不同的图形,每次调用pylab.draw()时都会显示,并且保持窗口一直显示。它使用简单的输入逻辑来决定何时关闭图形(因为使用show()意味着pylab不会处理窗口x按钮上的点击),但是将其作为另一个按钮或文本字段添加到您的GUI中应该很简单。
import numpy as np
import pylab
pylab.ion()

def get_fig(fig_num, some_data, some_labels):

    fig = pylab.figure(fig_num,figsize=(8,8),frameon=False)
    ax = fig.add_subplot(111)
    ax.set_ylim([0.1,0.8]); ax.set_xlim([0.1, 0.8]);
    ax.set_title("Quarterly Stapler Thefts")
    ax.pie(some_data, labels=some_labels, autopct='%1.1f%%', shadow=True);
    return fig

my_labels = ("You", "Me", "Some guy", "Bob")

# To ensure first plot is always made.
do_plot = 1; num_plots = 0;

while do_plot:
    num_plots = num_plots + 1;
    data = np.random.rand(1,4).tolist()[0]

    fig = get_fig(num_plots,data,my_labels)
    fig.canvas.draw()
    pylab.draw()

    print "Close any of the previous plots? If yes, enter its number, otherwise enter 0..."
    close_plot = raw_input()

    if int(close_plot) > 0:
        pylab.close(int(close_plot))

    print "Create another random plot? 1 for yes; 0 for no."
    do_plot = raw_input();

    # Don't allow plots to go over 10.
    if num_plots > 10:
        do_plot = 0

pylab.show()

通过修改这里的基本逻辑,我可以让它关闭窗口并按顺序绘制图像以模拟播放电影,或者我可以保持键盘控制来控制播放速度。
注意:这在各种平台上都能正常工作,并且似乎比上面的窗口画布管理方法更有效,并且不需要'TkAgg'选项。

0

我已经实现了一个方便的脚本,正好适合您的需求。在这里尝试一下吧。

以下是一个示例,展示了图像及其边界框:

import os
import glob
from scipy.misc import imread
from matplotlib.pyplot import Rectangle

video_dir = 'YOUR-VIDEO-DIRECTORY'

img_files = glob.glob(os.path.join(video_dir, '*.jpg'))
box_files = glob.glob(os.path.join(video_dir, '*.txt'))

def redraw_fn(f, axes):
    img = imread(img_files[f])
    box = bbread(box_files[f])  # Define your own bounding box reading utility
    x, y, w, h = box
    if not redraw_fn.initialized:
        im = axes.imshow(img, animated=True)
        bb = Rectangle((x, y), w, h,
                       fill=False,  # remove background
                       edgecolor="red")
        axes.add_patch(bb)
        redraw_fn.im = im
        redraw_fn.bb = bb
        redraw_fn.initialized = True
    else:
        redraw_fn.im.set_array(img)
        redraw_fn.bb.set_xy((x, y))
        redraw_fn.bb.set_width(w)
        redraw_fn.bb.set_height(h)
redraw_fn.initialized = False

videofig(len(img_files), redraw_fn, play_fps=30)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接