The scenario we're looking at today is the following. We have some stored data on disk, that we would like to plot using a Matplotlib animation, and save this animation as a video file. This is a reasonably common occurrence for researchers, but I had trouble finding tutorials online for this problem. Many of the tutorials generated their data via simulations on the fly, which didn't directly map to the case of recorded data.
There were two stumbling blocks I ran into, that had non-obvious solutions. First, Matplotlib's animation takes a function pointer to a user-defined function, for example def animate(i), but the animate function is then controlled by the iterator i. The second major stumbling block I hit, is that your animation function def animate(i): should return the data structures that need to be updated.
In this example, I have saved some data to a file in JSON format; each entry has a position and a velocity parameter. I read this file in line-by-line, and create a scrolling plot of the time series. I save this animated scrolling plot as a video file to disk, using ffmpeg and x264 for video encoding.
import sys
import os
import json
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from time import time
import itertools
from matplotlib.path import Path
import matplotlib.patches as patches
import threading
def init():
plotline.set_data([], [])
dplotline.set_data([],[])
return plotline,dplotline,
def animate(i):
#ax.clear()
global frame_num, buffersize
if (frame_num%100==0):
print frame_num
line=f.readline()
jsondata=json.loads(line)
frame_num+=1
if "Position" in jsondata:
pos= jsondata["Position"]
if "Velocity" in jsondata:
vel=jsondata["Velocity"]
pos_buf.append(pos)
vel_buf.append(vel)
while len(pos_buf)>buffersize:
pos_buf.pop(0)
while len(vel_buf)>buffersize:
vel_buf.pop(0)
x=np.linspace(-len(pos_buf)/framerate,0,len(pos_buf))
dx=np.linspace(-len(vel_buf)/framerate,0,len(vel_buf))
plotline.set_data(x,headpose_buf)
dplotline.set_data(dx,headvel_buf)
return plotline, dplotline,
def folder_from_path(file):
split_path=file.split("/")
folder=split_path[0]
for i in range(1,len(split_path)-1):
folder+="/"+split_path[i]
return folder
#Setting up globals
file=""
frame_num=0
framerate=20.0
buffersize=5*20
vel_buf=[]
pos_buf=[]
fig, ax = plt.subplots()
ax.set_xlim(-5.0,2.0)
ax.set_ylim(-180,180)
my_dpi=96
fig.set_size_inches(640/my_dpi, 360/my_dpi)
plotline, =ax.plot([],[],lw=2,color='b')
dplotline, =ax.plot([],[], lw=2, color='r')
if __name__=='__main__':
if len(sys.argv)<2:
print "Usage:\nplot_data.py file"
exit()
#Parsing command-line arguments, checking the input file exists.
file= sys.argv[1]
print file
if (os.path.isfile(file)==False):
print "Problem with file"
exit()
folder=folder_from_path(file)
#Writing video to same folder
outfile=folder+"/data.avi"
print folder
#Getting number of data points from file. A little hackey, but I couldn't control animate() with the file open()
line_count=0
with open(file, 'r') as f:
for read_data in f:
line_count=line_count+1
print line_count
f=open(file,'r')
Writer = animation.writers['ffmpeg']
writer = Writer(fps=25, metadata=dict(artist='Sayanan'), extra_args=['-vcodec','libx264'])
anim = animation.FuncAnimation(fig, animate,init_func=init, frames=line_count-1,blit=True, interval=1, repeat=False)
anim.save(outfile,writer=writer)
f.close()
#plt.show()
So there you go. A basic program that plots animation, and saves the resulting video to disk, using recorded data, stored in JSON on disk.
Edit: Just a quick addendum. I also had some trouble plotting animations of a grid of custom polygons, which could be useful for mapping, occupancy grids, or similar data. Here's a quick demo for how to generate animations of this sort:
"Demo of a grid of PathPatch objects."
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from time import time
import itertools
from matplotlib.path import Path
import matplotlib.patches as patches
def animate(i):
prob=np.random.rand(np.size(yr)*np.size(xl))
ax.clear()
cells=[]
for i in range(0,len(P1)):
verts=[P1[i], P2[i], P3[i], P4[i], P1[i] ]
path = Path(verts, codes)
patch = patches.PathPatch(path, color=[1-prob[i],prob[i],0], lw=2)
cells.append(ax.add_patch(patch))
return cells
fig, ax = plt.subplots()
fig.set_size_inches(5,20)
width=3.5
height=5.0
xl=np.arange(-8.75,8.75,width)
yr=np.arange(-50.0,50.0,height)
ax.set_xlim(xl[0],xl[np.size(xl)-1]+width)
ax.set_ylim(yr[0],yr[np.size(yr)-1]+height)
cells=[]
print yr[10]
print np.size(yr)
print np.size(xl)
P1=list(itertools.product(xl,yr+height))
P2=list(itertools.product(xl+width, yr+height))
P3=list(itertools.product(xl+width, yr))
P4=list(itertools.product(xl,yr))
prob=np.random.rand(np.size(yr)*np.size(xl))
codes = [Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
]
for i in range(0,len(P1)):
verts=[P1[i], P2[i], P3[i], P4[i], P1[i] ]
path = Path(verts, codes)
patch = patches.PathPatch(path, color=[1-prob[i],prob[i],0], lw=2)
cells.append(ax.add_patch(patch))
anim = animation.FuncAnimation(fig, animate, blit=True, save_count=0,interval=10)
plt.show()
hello,
ReplyDeletei was wondering if you'd be able to post the json data file that goes with this example. i'm not familiar with using json and it'd be helpful to have this to learn. thanks so much for posting this...i've been searching for something like this for a while.
jinu
Sure thing. You can generate a sample json file with the following Python script.
Deleteimport json
import numpy as np
f=open('data.json','w')
data={}
for t in range(0,100):
p=np.sin(np.pi/24*t)
v=np.cos(np.pi/24*t)
data["Position"]=p
data["Velocity"]=v
write_string=json.dumps(data)+'\n'
f.write(write_string)
f.close()