Animations In VCS¶
This Notebook demonstrates how to create animations in VCS
Animations are rendered via FFMPEG
Content¶
Preparing The Notebook¶
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
[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
[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
Prepare data¶
[3]:
import cdms2
f=cdms2.open("clt.nc")
clt = f("clt")
Create Frames¶
[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¶
[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
[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
Let’s prepare some data¶
[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¶
[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¶
[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¶
[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
[9]:
Slowing Down¶
[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
[10]:
Speeding up the animation¶
[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
[11]:
Example 3: Rotating Globe¶
In this example we will display a rotating earth Back to top
Preparing the data¶
[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¶
[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¶
[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¶
[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
[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
[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
[16]:
Animating Globe and Data¶
[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))
[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
[18]: