Search code examples
imagematplotlibanimationvideotensorboard

Convert TensorBoard Image Sequence to Video/GIF


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)


Solution

  • 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:

    enter image description here

    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.