When you hear about Python, you might have in mind the powerful interpreted programming language used for data science, artificial intelligence and for web apps with Flask or Django. However, Python is not only capable of achieving all of this, but here in Stack Builders we’ve also used it to create and render a fully customized video with animations, text and draw vectors using Python’s Moviepy and Gizeh libraries.
Creating a customized video might sound easy, especially if tools for video editing are an option. Creating one or maybe a couple of them might even sound fun, but if you need to generate hundreds or even thousands of customized videos daily, this does not sound like a good idea. Imagine Facebook manually creating millions of videos for friendship anniversaries, birthdays and marketing campaigns for all their users. That would require an extreme amount of time to do, create delays in the delivery of the videos, and take away from an ideal user experience.
Producing a video programmatically might not be a task developers come across daily. Having a firm understanding of what the final video should look like is vital for accomplishing this task.
Here is where software development comes in handy and helps automate tasks to make our lives easier once again. Having a toolbelt filled with tools like Python gives us the upper hand in this challenge. This open source tutorial/contribution will explain how to work with multiple video segments using instances from Gizeh and Moviepy together.
You might have heard of FFMPEG or ImageMagick for image and video edition in a programmatically way. MoviePy is a Python module for video editing (Python wrapper for FFMPEG). It provides functions for cutting, concatenations, title insertions, video compositing, video processing, and the creation of custom effects. It can read and write common video and audio formats and be run on any platform with Python 2.7 or 3+.
For this tutorial, Python 3 is required and will be using pipenv. All the modules can be installed from PyPI (Python Package Index) using
pip. For further instructions check this file here.
This sample code will help to understand some basic concepts about Python, Moviepy and Gizeh in a fun way.
import Moviepy.editor as mpy import Gizeh as gz from math import pi
In this piece of code we are telling Python to add the libraries that are going to be used in this tutorial to keep it simple. We are adding all of the MoviePy tools inside its editor module and renaming it to mpy to keep it short.
In Gizeh's case, we are adding it completely and renaming it to
And finally we import
math python module to make some calculations easier in the code.
BLUE = (59/255, 89/255, 152/255) def render_text(t): surface = gz.Surface(640, 60, bg_color=(1, 1, 1))
render_text function will set up all that Gizeh needs to draw what we want. This function is important to make the process easy to understand, keeping Gizeh code apart from Moviepy code. This function receives as a parameter the time
t that will be explained later on.
You might have noticed that
BLUE is a RGB color. Gizeh works with RGB but on a scale from
1, so white is
(1, 1, 1) equivalent to
(255, 255, 255). In the case of
BLUE we explicitly divide the Stack Builders blue
(59, 89, 152) to match Gizeh's format.
Gizeh likes to works like an artist; it needs a canvas to draw all of the things we specify in our code. So we need to create a
background color. In this case our surface will be
background color = (1, 1, 1).
text = gz.text("Let's build together", fontfamily="Charter", fontsize=30, fontweight='bold', fill=BLUE, xy=(320, 40))
With the surface ready, we can start drawing things in Gizeh. To draw the text in our surface, we will be using
gz.text. This function receives as a first parameter the text to render. The other parameters are used to style the text (key parameters that are self explanatory).
xy receives coordinates for positioning the text inside the surface. In this case
xy = (320, 40).
text.draw(surface) return surface.get_npimage()
Now with the
text ready we can
draw it in the
surface created earlier on. To accomplish this we just need to tell the
text on which surface it needs to be drawn.
To get the surface with all of the content drawn on it, we use
surface.get_npimage(). This returns a numpy array with all of the drawings on it. This array can be used with Moviepy.
text = mpy.VideoClip(render_text, duration=10)
It's time to use Moviepy! Now, using
VideoClip we are able to create an instance of a video in memory. With the
render_text function returning the text in a numpy array, we can convert the array into a Moviepy clip. The
mpy.VideoClip function receives as required parameters: what we want to render or the frame that will be converted into a video clip and the duration it should have. Do you remember the
t parameter in the
render_text function? Internally
mpy.VideoClip saves the frame of the clip corresponding to the time
t in the frame. The resulting video clip will automatically size to match our previous canvas (
640x60), so there is no need to specify it.
Now that we have the text ready, we can add some images to the video to make it more attractive to the viewers. For this we will be using the following code:
SB_logo_path = './assets/StackBuildersLogo.jpg' sb_logo = mpy.ImageClip(SB_logo_path).\ set_position(('center', 0)).\ resize(width=200)
mpy.ImageClip function receives as a parameter the image to render. In this case the image path is set in the variable
ImageClip attributes, it's possible to position the image using x, y coordinates. In this case, it's possible to mix keywords like
top with specific coordinates.
resize attribute reduces the size of the image keeping its ratio aspect using the key arguments
height like in the snippet. It is possible to set a custom size explicitly using a tuple with the new dimensions
With the text and image ready there is just one thing left to implement: Gizeh drawings.
Gizeh is capable of drawing multiple elements like lines, circles, rectangles, etc. However, in this case we are going to use
gz.star that is going to draw a custom star for us. (Just because this video deserves 5 Gizeh stars)
def draw_stars(t): surface = gz.Surface(640, 120, bg_color=(1, 1, 1))
As for the text, we first start by setting the surface we are going to use to draw the stars.
for i in range(5): star = gz.star(nbranches=5, radius=120*0.2, xy=[100*(i+1), 50], fill=(0, 1, 0), angle=t*pi) star.draw(surface)
This is where the fun begins! As we said before, this video deserves 5 stars, and since drawing them one-by-one could be very repetitive, we are going to use a loop. The
gz.star element has as a first parameter
nbranches that represents the number of star tips we want to draw. Then the
radius determines the size of the stars (imagine a circle surrounding the star). The
xy parameter works exactly as in
gz.text. In this case we are just calculating the
x coordinate depending on the for loop step to position each star dynamically and using the
fill parameter our star will be green. To animate the star we are using the
angle parameter that gives us the change of rotating the star on its own axis. This attribute uses radians, so to rotate the stars we are multiplying
pi (180 degrees) by the time in the video.
At the end of each loop step, a star is drawn on the surface we previously set. This works in the same way as in
Finally, a numpy array is returned.
stars = mpy.VideoClip(draw_stars, duration=10)
mpy.VideoClip, we have all of the stars inside a clip. (Like we did with the
WHITE = (255, 255, 255) VIDEO_SIZE = (640, 480)
Before generating and writing the complete video, we first need to create some variable to keep the code easy to understand. The first one is
WHITE. It is going to be the background color. Unlike Gizeh, Moviepy uses the traditional RGB scale (0 - 255).
The second one is
VIDEO_SIZE, we will be using a standard of
video = mpy.CompositeVideoClip( [ sb_logo, text.set_position( ('center', sb_logo.size)), stars.set_position( ('center', sb_logo.size + text.size) ) ], size=VIDEO_SIZE).\ on_color( color=WHITE, col_opacity=1).set_duration(10)
mpy.CompositeVideoClip we can generate a
VideoClip made up of other clips displayed together. The first parameter is an array of the clips to be used. In the case of
star clips, the positions are set here directly to use the
size that provides a tuple of the clip dimensions
(width, height). This helps to calculate in an easier way the
y-position of each clip.
The second parameter in
CompositeVideoClip is the size of the final clip. In this case
on_color attribute helps to set the background color of the
on_color attribute has some extra parameters to change
color opacity, but for now we don't need them.
And last but not least, the video duration is set to 10 seconds using
write_videofile attribute writes in the disk the
VideoClip result of the
CompositeVideoClip stored in memory. Its first parameter is the
video file name, then we define the
fps rate. The
write_videofile has multiple parameters but we just need to set these two for now.
Now if we run the code using
python python_video.py, we will have a 10 seconds video like this:
Figure 1 - Video generated by our code
This might seem simple, but its our first video generated by code! If you want to dig deeper into the code, here is the link of the final version of the project
Creating a video is a fun task and doing it using code is even better!
This tutorial proves that you don't need to be a Python expert to generate a video using code from scratch. This is one of the advantages of Python as a language. It's useful beyond tasks like web development, data science or machine learning. Its versatility allows you to accomplish tasks like rendering customized images and videos with an easy to understand syntax so you can start using it if you have any programming experience with other languages.
Creating multimedia content with code might be tricky at the beginning, but once you get started it's hard to stop adding multiple animations or new segments.
Following this tutorial step by step gives the basic concepts needed to start using Moviepy and Gizeh with Python to experiment with videos or image rendering. Animating video objects with these libraries is also doable adding just a bit of complexity in the calculations.