如何动画展示时间序列的matplotlib图形

15 投票
3 回答
25449 浏览
提问于 2025-04-16 21:28

我想在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.show()命令换成plt.draw(),然后把plt.show()放在for循环外面,但这样什么也不显示,程序还是卡住。

3 个回答

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)
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 可以接受浮点数组、无符号8位整数数组,或者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()
6

我找到的最好的方法是在导入pylab后使用命令 pylab.ion()

这里有一个脚本,它使用了 show(),但每次调用 pylab.draw() 时都会显示不同的图形,并且图形窗口会一直保持打开状态。它使用简单的输入逻辑来决定何时关闭图形窗口(因为使用 show() 的话,pylab就无法处理窗口上关闭按钮的点击),不过这应该很容易添加到你的图形界面中,可以作为另一个按钮或者文本框。

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' 选项。

撰写回答