5

I am currently trying to save a mayavi animation generated by my simulation, so I don't have to rerun the code each time to see it.

plt = points3d(x_coord, y_coord, z_coord)

msplt = plt.mlab_source
@mlab.animate(delay=100)
def anim():
    f = mlab.gcf()
    while True:
        #animation updates here
        msplt.set(x = x_coord, y = y_coord, z = z_coord)

        yield


anim()
mlab.savefig(filename = 'ani.mp4')
mlab.show()

I have tried saving it through the pipleline editor and just get a still of the frame it is on, and mlab.savefig doesn't generate a file. Any help appreciated.

  • 1
    Very creative, using `mlab.savefig()` to try to export to .mp4. As you might surmise, VTK does not work this way. You may be able to make a movie out of screen capture instead. For instance on X11 systems this can be done with `ffmpeg -x11grab` – aestrivex Jul 29 '14 at 21:16
  • Do you have a specific code example? –  Jul 30 '14 at 22:14
  • I wrote something similar (though not vetted by continuous use) in my neuroimaging visualization program that depends on ffmpeg. Look at the `make_movie` function in https://github.com/aestrivex/cvu/blob/master/cvu/dataview.py. This is not as straightforward as "give me the code to make it work." Which is ok, because it's tricky and your question is not really about mayavi as mayavi doesn't support this. – aestrivex Jul 31 '14 at 14:55

2 Answers2

4

The following will will work for both viewing the animation, saving each frame as a 'png', and then converting them to a movie, BUT it is perhaps fastest in this case to forgo playing the animation, and just cycle through the data saving figures, and then using this method to make a video.

from mayavi import mlab
import numpy as np
import os

# Output path for you animation images
out_path = './'
out_path = os.path.abspath(out_path)
fps = 20
prefix = 'ani'
ext = '.png'

# Produce some nice data.
n_mer, n_long = 6, 11
pi = np.pi
dphi = pi/1000.0
phi = np.arange(0.0, 2*pi + 0.5*dphi, dphi, 'd')
mu = phi*n_mer
x = np.cos(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
y = np.sin(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
z = np.sin(n_long*mu/n_mer)*0.5

# Init plot
plt = mlab.points3d(x[0], y[0], z[0])

padding = len(str(len(x)))

# Define data source and update routine
msplt = plt.mlab_source
@mlab.animate(delay=10)
def anim():
    f = mlab.gcf()
    for i in range(len(x)):
        #animation updates here
        msplt.set(x=x[i], y=y[i], z=z[i])

        # create zeros for padding index positions for organization
        zeros = '0'*(padding - len(str(i)))

        # concate filename with zero padded index number as suffix
        filename = os.path.join(out_path, '{}_{}{}{}'.format(prefix, zeros, i, ext))

        mlab.savefig(filename=filename)

        yield

anim()
mlab.view(distance=15)
mlab.show()

import subprocess
ffmpeg_fname = os.path.join(out_path, '{}_%0{}d{}'.format(prefix, padding, ext))
cmd = 'ffmpeg -f image2 -r {} -i {} -vcodec mpeg4 -y {}.mp4'.format(fps,
                                                                    ffmpeg_fname,
                                                                    prefix)
print cmd
subprocess.check_output(['bash','-c', cmd])

# Remove temp image files with extension
[os.remove(f) for f in os.listdir(out_path) if f.endswith(ext)]
ryanjdillon
  • 17,658
  • 9
  • 85
  • 110
0

Instead of saving the images to disk and then stitching them together it's also possible to pipe them directly to ffmpeg using the python-ffmpeg package.

import ffmpeg

# Set up the figure
width = 200
height = 200
mlab.options.offscreen = True  # Stops the view window popping up and makes sure you get the correct size screenshots.
fig = mlab.figure(size=(width, height))

# ... set up the scene ...

# Define update function
def update_scene(idx):
    # -- update the scene 
    return


# Initialise ffmpeg process
output_args = {
    'pix_fmt': 'yuv444p',
    'vcodec': 'libx264',
    'r': 25,
}

process = (
    ffmpeg
        .input('pipe:', format='rawvideo', pix_fmt='rgb24', s=f'{width}x{height}')
        .output('animation.mp4', **output_args)
        .overwrite_output()
        .run_async(pipe_stdin=True)
)

fig.scene._lift()  # Throws an error without this.
for i in range(100):
    update_scene(i)
    screenshot = mlab.screenshot(mode='rgb', antialiased=True)
    frame = Image.fromarray(screenshot, 'RGB')
    process.stdin.write(frame.tobytes())

# Flush video
process.stdin.close()
process.wait()
lopsided
  • 2,370
  • 6
  • 28
  • 40