如何动画展示时间序列的matplotlib图形
我想在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 个回答
我写了一个很实用的脚本,正好符合你的需求。你可以在这里试试。
下面是一个示例,展示了图片和它的边框框:
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)
根据这个链接: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()
我找到的最好的方法是在导入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' 选项。