What?
Mask images have many applications. In photo editing, masks e.g. are used to succinctly subscribe where to clip images, or to define regions where an image should be transparant. In video editing, masks are used to describe so-called wiping (fancy transitions from one image to the next). In 3d programs, there's a kind of 3d equivalent of masks, sometimes called dynamic paint, to describe influence of bones on vertices, or to dynamically create vertex colors and displacements.
Can we use mask images in combination with vector animation to do some cool stuff? I'd like to think we can. The idea is not to animate the mask image itself, but to interpret the values in the mask image as different animation parameters. E.g. consider the following example of a heart image. The red pixels are considered "masked" pixels, whereas the surrounding transparent pixels are considered unmasked values. Based on whether a pixel is masked or unmasked, we can render different animations. Let's apply the idea on an example to make it more concrete...
I'll use this wikimedia picture as input
And automagically turn it into the following pschychedelic animation
How?
Here's the code. Sorry it's not particularly short or elegant, and it may need some tweaking with other mask images as input but it shows off the concept.
if __name__ == "__main__": import gizeh import moviepy.editor as mpy from scipy import misc from scipy.ndimage import zoom from numpy import pi from vectortween.NumberAnimation import NumberAnimation from vectortween.SequentialAnimation import SequentialAnimation import random # heart shape retrieved from https: // commons.wikimedia.org / wiki / File: Heart_coraz % C3 % B3n.svg
mask = misc.imread("heart.png") print("mask.shape = ", mask.shape, " mask.dtype = ", mask.dtype) H = mask.shape[0] W = mask.shape[1] subsample = 20
subsampled_mask = zoom(mask, (1 / subsample, 1 / subsample, 1)) print(subsampled_mask.shape) # debug code:
# misc.imsave("heart-subsampled.png", subsampled_mask) yrange = subsampled_mask.shape[0] xrange = subsampled_mask.shape[1] xwidth = subsample ywidth = subsample duration = 5
fps = 24
radii = {} def get_color(x, y, subsampled_mask): color = subsampled_mask[y][x] / 255
return color def masked(x, y, subsampled_mask): c = get_color(x, y, subsampled_mask) if sum(c) > 2: return 1
else: return 0 def make_frame(t): surface = gizeh.Surface(W, H) canim = SequentialAnimation([NumberAnimation(frm=0, to=2 * pi, tween=["easeOutQuad"])], repeats=int(duration)) canim2 = SequentialAnimation([NumberAnimation(frm=2 * pi, to=0, tween=["easeOutQuad"])], repeats=int(2 * duration)) radiusmod = SequentialAnimation([NumberAnimation(frm=0, to=10, tween=["easeInOutSine"]), NumberAnimation(frm=10, to=0, tween=["easeInOutSine"])], repeats=int(1.5 * duration)) for y in range(yrange): for x in range(xrange): if masked(x, y, subsampled_mask): pinkfactor = random.uniform(0.2, 0.8) if (x, y) not in radii: radii[(x, y)] = random.uniform(xwidth / 8, 3 * xwidth / 2) r = radii[(x, y)] modval = radiusmod.make_frame(t, 0, 0, duration - x / 20, duration) if modval is None: modval = 0
r += random.uniform(0, 3 * modval / 2) gizeh.circle(r, xy=((x + 0.5) * xwidth, (y + 0.5) * ywidth), fill=(1, pinkfactor, pinkfactor, 0.15), stroke=(1, 0.3, 0.3), stroke_width=1).draw(surface) else: alphafactor = random.uniform(0.2, 0.8) endpoint = canim.make_frame(t, 0, 0, duration - y / 20, duration) endpoint2 = canim2.make_frame(t, 0, 0, duration - x / 20, duration) if endpoint is not None: gizeh.arc(xwidth / 2, 0, endpoint, xy=((x + 0.5) * xwidth, (y + 0.5) * ywidth), fill=None, stroke=(1, 1, 1, alphafactor), stroke_width=2).draw(surface) if endpoint2 is not None: gizeh.arc(xwidth / 4, 0, endpoint2, xy=((x + 0.5) * xwidth, (y + 0.5) * ywidth), fill=(0, 1, 1, alphafactor), stroke=(1, 1, 1, alphafactor), stroke_width=2).draw(surface) return surface.get_npimage() clip = mpy.VideoClip(make_frame, duration=duration) clip.write_gif("example_heart1.gif", fps=fps, opt="nq")
Where can I find this magic vector tween library?
Glad you're asking! All the magic can be found here.
I'm curious to see what advanced animations you can come up with using a mask driven vector animation technique. Note: to add more layers of omgwow!!! nothing stops you from animating the mask image as well, or from generating the mask images programmatically. Or maybe you can go recursive.... how about you use the heart animation frames as animation masks for yet another, perhaps even more psychedelic animation?
You can also watch and listen to my recent music video "The Vows" that embeds vector animations generated using pyvectortween here:
I'm curious to see what advanced animations you can come up with using a mask driven vector animation technique. Note: to add more layers of omgwow!!! nothing stops you from animating the mask image as well, or from generating the mask images programmatically. Or maybe you can go recursive.... how about you use the heart animation frames as animation masks for yet another, perhaps even more psychedelic animation?
You can also watch and listen to my recent music video "The Vows" that embeds vector animations generated using pyvectortween here: