Animations In VCS

This Notebook demonstrates how to create animations in VCS

Animations are rendered via FFMPEG

Preparing The Notebook

Back to top

VCS 2.12 does not produce animation directly visible in the notebook The class bellow helps with this Nightly versions of vcs do produce animations objects that are adequate for notebooks

In [1]:
class FFMPEG(object):
    def __init__(self,source,width=320,height=240,controls=True):
        self.source = source
        self.width = width
        self.height = height
        self.controls=controls
    def _repr_html_(self):
        html = "<video width='%i' height='%i'" % (self.width,self.height)
        if self.controls:
            html += "controls"
        html += "><source src='%s' type='video/mp4'>" % (self.source)
        return  html

We also need some sample data

In [2]:
import requests
r = requests.get("https://uvcdat.llnl.gov/cdat/sample_data/clt.nc",stream=True)
with open("clt.nc","wb") as f:
    for chunk in r.iter_content(chunk_size=1024):
        if chunk:  # filter local_filename keep-alive new chunks
            f.write(chunk)

Example 1: Simple 2D data animation

In this example we will show a simple 2D animation

Back to top

Prepare data

In [3]:
import cdms2
f=cdms2.open("clt.nc")
clt = f("clt")

Create Frames

Back to top

In [4]:
# make directory for pngs if not present
import os
if not os.path.exists("pngs"):
    os.makedirs("pngs")

import vcs
x=vcs.init(bg=True)

for i in range(clt.shape[0]):
    x.clear()
    x.plot(clt[i])
    x.png("pngs/2d_%s.png" % str(i).zfill(6))

Create Animation

Back to top

In [5]:
import glob
name = "2d_animation.mp4"

# Create animation into file
out = x.ffmpeg(name,sorted(glob.glob("pngs/2d*png")))
if isinstance(out,bool): # older vcs
    print "Preparing ffmpeg for Jupyter embedding"
    out = FFMPEG(name,width=800,height=600,controls=True)
else: # nightlies
    print "Already Jupyter embedded"
out
Already Jupyter embedded
Out[5]:

Example 2: 1D data moving behind a 1D target data

In this example we will show a fix 1D target dataset and a moving red dataset in the back

Back to top

Let’s prepare some data

In [6]:
import cdutil
import numpy

# Open data file
f=cdms2.open("clt.nc")

# reads in data
s=f("clt")

# Computes time serie
ts=cdutil.averager(s,axis="xy")

# remove the mean
mean = ts.mean()
ts -= mean

# create some random data with some extra points to fake the move
Nextra = 120
ts2 = numpy.random.rand(len(ts)+Nextra)*4.-2.

# When plotting we will get data for 2 extra years than our target grid
# Prepare the "faxe time" axis
fake_time_axis = cdms2.createAxis(numpy.arange(-12,132))
fake_time_axis.designateTime()
fake_time_axis.id = "time"
fake_time_axis.units="months since 1979"

Prepare graphic methods and templates

Back to top

In [7]:
# Create a "blank" template
blank = vcs.createtemplate()
blank.blank()
blank.data.priority=1 # turn only data area

# Create the "black" 1D line
black = vcs.create1d()
black.linecolor ="black"
black.markercolor = [0,0,0,0]
black.linewidth = 4
black.datawc_y1= -3
black.datawc_y2 = 3
black.datawc_x1 = -12
black.datawc_x2 = 132

# And from it let's create the "red" moving one
red = vcs.create1d(source=black.name)
red.linecolor="red"
red.linewidth=2
red.linetype = "dot"

Create each frame/png

Back to top

In [8]:
x=vcs.init(bg=True)
for i in range(Nextra-24):
    # Get some subset of data
    tsub = cdms2.MV2.array(ts2[i:i+len(ts)+24]) # Two years worth of extra data
    # Aplly fake axis to make it look like it moves
    tsub.setAxis(0,fake_time_axis)
    # clear and plot
    x.clear()
    x.plot(ts,black)
    x.plot(tsub,red,blank)
    # save data to a png file
    x.png("pngs/1d_%s.png" % str(i).zfill(6))
/Users/doutriaux1/anaconda2/envs/nightly2/lib/python2.7/site-packages/vcs/VTKPlots.py:1005: MaskedArrayFutureWarning: setting an item on a masked array which has a shared mask will not copy the mask and also change the original mask array in the future.
Check the NumPy 1.11 release notes for more information.
  data[:] = numpy.ma.masked_invalid(data, numpy.nan)

Create animation

Back to top

In [9]:
name = "1d_animation.mp4"

# Create animation into file
out = x.ffmpeg(name,sorted(glob.glob("pngs/1d*png")))
if isinstance(out,bool): # older vcs
    print "Preparing ffmpeg for Jupyter embedding"
    out = FFMPEG(name,width=800,height=600,controls=True)
else: # nightlies
    print "Already Jupyter embedded"
out
Already Jupyter embedded
Out[9]:

Slowing Down

Back to top

In [10]:
import glob
name = "1d_animation_slow.mp4"

# Create animation into file
out = x.ffmpeg(name,sorted(glob.glob("pngs/1d*png")),rate=2)
if isinstance(out,bool): # older vcs
    print "Preparing ffmpeg for Jupyter embedding"
    out = FFMPEG(name,width=800,height=600,controls=True)
else: # nightlies
    print "Already Jupyter embedded"
out
Already Jupyter embedded
Out[10]:

Speeding up the animation

Back to top

In [11]:
import glob
name = "1d_animation_fast.mp4"

# Create animation into file
out = x.ffmpeg(name,sorted(glob.glob("pngs/1d*png")),rate=200)
if isinstance(out,bool): # older vcs
    print "Preparing ffmpeg for Jupyter embedding"
    out = FFMPEG(name,width=800,height=600,controls=True)
else: # nightlies
    print "Already Jupyter embedded"
out
Already Jupyter embedded
Out[11]:

Example 3: Rotating Globe

In this example we will display a rotating earth Back to top

Preparing the data

Back to top

In [12]:
f=cdms2.open("clt.nc")
clt=f("clt",time=slice(0,1),longitude=(0,361),squeeze=1) # Read the time slice and squeeze it out

Preparing the vcs objects

Back to top

In [13]:
# create new canvas
x = vcs.init(bg=True)

# Create projection
polar = vcs.createprojection("rotate")
polar.type = "orthographic"
polar.centerlongitude=0.
polar.centerlatitude=45.

# Create isofill method
iso = vcs.createisofill()
iso.levels = range(0,110,10)
iso.fillareacolors = vcs.getcolors(range(0,110,10))

iso.projection = polar

Preparing the images

Back to top

In [14]:
for centerLongitude in range(0,366,5):
    polar.centerlongitude = centerLongitude
    x.clear()
    x.plot(clt,iso)
    x.png("pngs/rotate_%s.png" % str(centerLongitude).zfill(6))

Creating the animation

Back to top

In [15]:
name = "rot_animation.mp4"

# Create animation into file
print "files used",glob.glob("pngs/rotate_*png")
out = x.ffmpeg(name,sorted(glob.glob("pngs/rotate_*png")), rate=15)
if isinstance(out,bool): # older vcs
    print "Preparing ffmpeg for Jupyter embedding"
    out = FFMPEG(name,width=800,height=600,controls=True)
else: # nightlies
    print "Already Jupyter embedded"
out
files used ['pngs/rotate_000000.png', 'pngs/rotate_000005.png', 'pngs/rotate_000010.png', 'pngs/rotate_000015.png', 'pngs/rotate_000020.png', 'pngs/rotate_000025.png', 'pngs/rotate_000030.png', 'pngs/rotate_000035.png', 'pngs/rotate_000040.png', 'pngs/rotate_000045.png', 'pngs/rotate_000050.png', 'pngs/rotate_000055.png', 'pngs/rotate_000060.png', 'pngs/rotate_000065.png', 'pngs/rotate_000070.png', 'pngs/rotate_000075.png', 'pngs/rotate_000080.png', 'pngs/rotate_000085.png', 'pngs/rotate_000090.png', 'pngs/rotate_000095.png', 'pngs/rotate_000100.png', 'pngs/rotate_000105.png', 'pngs/rotate_000110.png', 'pngs/rotate_000115.png', 'pngs/rotate_000120.png', 'pngs/rotate_000125.png', 'pngs/rotate_000130.png', 'pngs/rotate_000135.png', 'pngs/rotate_000140.png', 'pngs/rotate_000145.png', 'pngs/rotate_000150.png', 'pngs/rotate_000155.png', 'pngs/rotate_000160.png', 'pngs/rotate_000165.png', 'pngs/rotate_000170.png', 'pngs/rotate_000175.png', 'pngs/rotate_000180.png', 'pngs/rotate_000185.png', 'pngs/rotate_000190.png', 'pngs/rotate_000195.png', 'pngs/rotate_000200.png', 'pngs/rotate_000205.png', 'pngs/rotate_000210.png', 'pngs/rotate_000215.png', 'pngs/rotate_000220.png', 'pngs/rotate_000225.png', 'pngs/rotate_000230.png', 'pngs/rotate_000235.png', 'pngs/rotate_000240.png', 'pngs/rotate_000245.png', 'pngs/rotate_000250.png', 'pngs/rotate_000255.png', 'pngs/rotate_000260.png', 'pngs/rotate_000265.png', 'pngs/rotate_000270.png', 'pngs/rotate_000275.png', 'pngs/rotate_000280.png', 'pngs/rotate_000285.png', 'pngs/rotate_000290.png', 'pngs/rotate_000295.png', 'pngs/rotate_000300.png', 'pngs/rotate_000305.png', 'pngs/rotate_000310.png', 'pngs/rotate_000315.png', 'pngs/rotate_000320.png', 'pngs/rotate_000325.png', 'pngs/rotate_000330.png', 'pngs/rotate_000335.png', 'pngs/rotate_000340.png', 'pngs/rotate_000345.png', 'pngs/rotate_000350.png', 'pngs/rotate_000355.png', 'pngs/rotate_000360.png', 'pngs/rotate_000365.png']
Already Jupyter embedded
Out[15]:

Controlling

But it is rotating THE WRONG WAY!!!*

Why?

FFMPEG uses the images passed by the list in the order passed.

Look above the order is with growing centerLongitude

Let’s fix this by passing the image sequence in reversed order

In [16]:
name = "rot_animation_correct_order.mp4"

# Create animation into file
out = x.ffmpeg(name,sorted(glob.glob("pngs/rotate_*png"))[::-1], rate=15)
if isinstance(out,bool): # older vcs
    print "Preparing ffmpeg for Jupyter embedding"
    out = FFMPEG(name,width=800,height=600,controls=True)
else: # nightlies
    print "Already Jupyter embedded"
out
Already Jupyter embedded
Out[16]:

Animating Globe and Data

In [17]:
# Create pngs
clt = f("clt",longitude=(0,361))
for i,centerLongitude in enumerate(range(0,361,4)):
    polar.centerlongitude = centerLongitude
    x.clear()
    x.plot(clt[i],iso)
    x.png("pngs/all_%s.png" % str(centerLongitude).zfill(6))
In [18]:
name = "rot_animation_all.mp4"

# Create animation into file
out = x.ffmpeg(name,sorted(glob.glob("pngs/all_*png"))[::-1], rate=10)
if isinstance(out,bool): # older vcs
    print "Preparing ffmpeg for Jupyter embedding"
    out = FFMPEG(name,width=800,height=600,controls=True)
else: # nightlies
    print "Already Jupyter embedded"
out
Already Jupyter embedded
Out[18]: