I have a script that generates a sequence of matplotlib figures and writes them to disk using a TensorBoard SummaryWriter()
. TensorBoard offers the ability to move a little slider to advance forwards and backwards through the image sequence, but I'd like to convert the image sequence to a video or animation. Is there a way to do this?
Edit 1: Here's a simplified example of what my code current does. I'd like to take the images that are written to the log file by .add_figure
and convert them to a gif.
import matplotlib.pyplot as plt
import numpy as np
from torch.utils.tensorboard import SummaryWriter
n = 200
nframes = 25
x = np.linspace(-np.pi*4, np.pi*4, n)
tensorboard_writer = SummaryWriter()
for i, t in enumerate(np.linspace(0, np.pi, nframes)):
plt.plot(x, np.cos(x + t))
plt.plot(x, np.sin(2*x - t))
plt.plot(x, np.cos(x + t) + np.sin(2*x - t))
plt.ylim(-2.5,2.5)
fig = plt.gcf()
tensorboard_writer.add_figure(
tag='temp',
figure=fig,
global_step=i,
close=True)
You can use either imageio or matplotlib's FuncAnimation
to create an animation from a set of files representing the frames. As an example, I have created a set of files according to
import matplotlib.pyplot as plt
import numpy as np
n = 200
nframes = 25
x = np.linspace(-np.pi*4, np.pi*4, n)
for i, t in enumerate(np.linspace(0, np.pi, nframes)):
plt.plot(x, np.cos(x + t))
plt.plot(x, np.sin(2*x - t))
plt.plot(x, np.cos(x + t) + np.sin(2*x - t))
plt.ylim(-2.5,2.5)
plt.savefig('frame.'+str(i)+'.png', bbox_inches='tight', dpi=300)
plt.clf()
which creates a series of nframes
(25 for the example) files with the naming convention 'frame.n.png'
where n
is the frame number. Two propagating sinusoidal waves and the created wave are being plotted over one half cycle.
With the FuncAnimation
approach you then do
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure(figsize=(10, 6), dpi=300)
nframes = 25
def animate(i):
im = plt.imread('frame.'+str(i)+'.png')
plt.imshow(im)
plt.axis('off')
anim = FuncAnimation(fig, animate, frames=nframes, interval=(2000.0/nframes))
anim.save('output.gif', writer='imagemagick')
With the imageio approach you simply do
import imageio
nframes = 25
files = ['frame.'+str(i)+'.png' for i in range(nframes)]
frames = [imageio.imread(f) for f in files]
imageio.mimsave('output.gif', frames, fps=(nframes / 2.0))
Either approach produces this animation:
However, the imageio method is much faster compared to the FuncAnimation
method:
> $ time python3 imageio.py
real 0m9.483s
user 0m9.484s
sys 0m1.156s
> $ time python3 FuncAnimation.py
real 15m36.151s
user 3m28.375s
sys 12m3.578s
It is also worth noting, however, that the file generated by the FuncAnimation
approach is much smaller than the one created by the imageio approach.
2.5M Jan 23 18:36 FuncAnimation.gif
13M Jan 23 18:08 imageio.gif
You can of course also use ffmpeg
to do this if that is your preference over a programmatic approach.