How to export animation and save it in a video format like mp4,mpeg or...?

Hi @Bijan,
A plotly animation cannot be saved, directly, but there are two methods to create a video or a gif file from frames.

  1. The first method is the poor man method: It consists in updating your figure (the base figure) with the corresponding properties/attributes that usually are inserted in each go.Frame,
    Then save in a folder, each updated figure, as a png file, representing a frame.

The pngs files are uploaded to ezgif, https://ezgif.com/maker, to create a gif file
by setting correspondingly the Delay time.
Save the gif file on your system, and then upload it again to ezgif,
[Online GIF to MP4 Video converter] (Online GIF to MP4 Video converter) to convert the gif to a mp4 file.

Example:

import meshio
import numpy as np
from numpy import pi, sin, cos
import plotly.graph_objects as go
import plotly.io as pio

msh = meshio.read("face-mesh.obj") #https://raw.githubusercontent.com/empet/Datasets/master/Meshes/face-mesh.obj
verts = msh.points
middle = np.max(verts, axis=0) + np.min(verts, axis=0)/2
verts = verts - middle
I, J, K =  msh.cells_dict["triangle"].T
x, y, z = verts.T

colorscale = [[0, 'rgb(250,250,250)'], 
              [1, 'rgb(250,250,250)']]
figanim = go.Figure(go.Mesh3d(x=x, y=y, z=z, 
                          i=I, j=J, k=K, 
                          intensity=z, 
                          colorscale =colorscale,
                          showscale=False,
                          lighting=dict(ambient=0.1,
                                        diffuse=1,
                                        fresnel=3,  
                                        specular=0.5, 
                                        roughness=0.05),
                          lightposition=dict(x=100,
                                             y=200,
                                             z=1000)
                         ))

axis_prop =dict(visible=False, autorange=False)
scenes = dict(xaxis=dict(range=[-11.41, 11.41], **axis_prop),
              yaxis=dict(range=[-11.41, 11.41], **axis_prop),
              zaxis=dict(range=[-14.67, 4.37], **axis_prop),
              camera_eye=dict(x=-1.85, y=-1.85, z=0.65),
              aspectratio=dict(x=1.15, y=1.15, z=1.1))

figanim.update_layout(title_text="Hollow mask illusion", title_x=0.5, title_y=0.95,
                  font_size=16, font_color="white",
                  width=400, height=400, autosize=False, 
                  margin=dict(t=2, r=2, b=2, l=2),  #IMPORTANT to set only 2 pixels margin because 
                                                    #otherwise around each png there is a two big white space
                  paper_bgcolor='black',
                  scene= scenes)
figanim.show()
######### Below are the code lines corresponding to "animation", i.e. to creating a  png file
######### for each frame

def RotZ(t):
    return np.array([[cos(t), -sin(t), 0],
                     [sin(t), cos(t), 0],
                     [0, 0, 1]])

t = np.linspace(0, 2*pi,  73)
for k, s in enumerate(t):
    xr, yr, zr = RotZ(-s) @ verts.T
    figanim.update_traces(x=xr, y=yr, z=zr)#this line  is equivalent to go.Frame; It creates the png corresponding to a frame
    pio.write_image(figanim, f"images/{k+1:03d}.png", width=400, height=400, scale=1)#save the frame as png file in the folder images

This is the corresponding video: https://imgur.com/a/VRUw4AN

  1. The second methodis based on the Python package moviepy https://github.com/Zulko/moviepy

Install it with pip:

pip install moviepy

Below is an example I created two years ago. I hope it still works, because meantime I uninstalled Python and many packages, and reinstalled only those I’m working with.

import numpy as np
from scipy.spatial import Delaunay
import plotly.graph_objects as go
import  moviepy.editor as mpy
import io 
from PIL import Image

def plotly_fig2array(fig):
    #convert Plotly fig to  an array
    fig_bytes = fig.to_image(format="png")
    buf = io.BytesIO(fig_bytes)
    img = Image.open(buf)
    return np.asarray(img)

n = 20 # number of radii
h = 2/(n-1)
r = np.linspace(h, 2,  n)
theta = np.linspace(0, 2*np.pi, 60)
r, theta = np.meshgrid(r,theta)
r = r.flatten()
theta = theta.flatten()

x = r*np.cos(theta)
y = r*np.sin(theta)

# Triangulate the circular  planar region
tri = Delaunay(np.vstack([x,y]).T)
faces = np.asarray(tri.simplices)
I, J, K = faces.T

f = lambda h: np.sinc(x**2+y**2)+np.sin(x+h)   

fig = go.Figure(go.Mesh3d(x=x,
                     y=y,
                     z=f(0),
                     intensity=f(0),
                     i=I,
                     j=J,
                     k=K,
                     colorscale='matter_r', 
                     showscale=False))
                     
fig.update_layout(title_text='My hat is flying with MoviePy',
                  title_x=0.5,
                  width=500, height=500, 
                  scene_xaxis_visible=False, 
                  scene_yaxis_visible=False, 
                  scene_zaxis_visible=False)

# No Plotly frames are defined here!! Instead we define moviepy frames by
# converting each Plotly figure to  an array, from which MoviePy creates a clip
# The concatenated clips are saved as a gif file:
def make_frame(t):
    z = f(2*np.pi*t/2)
    fig.update_traces(z=z, intensity=z)  #These are the updates that usually are performed within Plotly go.Frame definition
    return plotly_fig2array(fig)

animation = mpy.VideoClip(make_frame, duration=2)
animation.write_gif("image/my_hat.gif", fps=20)

Search in moviepy docs to find out how to save the VideoClip as a mp4 file.

This is the resulted gif: https://imgur.com/a/s3p4iNU
and this one https://imgur.com/a/Ebbao7f the corresponding video

2 Likes