Loading resources as clips#
The first step for making a video with MoviePy is to load the resources you wish to include in the final video.
In this section we present the different sorts of clips and how to load them. For information on modifying a clip, see Modifying clips and apply effects. For how to put clips together see Compositing multiple clips. And for how to see/save theme, see Previewing and saving video clips (we will usually save them in example, but we wont explain here).
There’s a lot of different resources you can use with MoviePy, and you will load those differents resources with different subtypes of Clip
, and more preciselly of AudioClip
for any audio element, or VideoClip
for any visual element.
The following code summarizes the base clips that you can create with moviepy:
from moviepy import *
import numpy as np
# Define some constants for later use
black = (255, 255, 255) # RGB for black
# Random noise image of 200x100
make_frame = lambda t: np.random.randint(low=0, high=255, size=(100, 200, 3))
# A note by producing a sinewave of 440 Hz
make_frame_audio = lambda t: np.sin(440 * 2 * np.pi * t)
# Now lets see how to load different type of resources !
# VIDEO CLIPS`
clip = VideoClip(
make_frame, duration=5
) # for custom animations, where make_frame is a function returning an image as numpy array for a given time
clip = VideoFileClip("example.mp4") # for videos
clip = ImageSequenceClip(
"example_img_dir", fps=24
) # for a list or directory of images to be used as a video sequence
clip = ImageClip("example.png") # For a picture
clip = TextClip(
font="./example.ttf", text="Hello!", font_size=70, color="black"
) # To create the image of a text
clip = ColorClip(
size=(460, 380), color=black
) # a clip of a single unified color, where color is a RGB tuple/array/list
# AUDIO CLIPS
clip = AudioFileClip(
"example.wav"
) # for audio files, but also videos where you only want the keep the audio track
clip = AudioClip(
make_frame_audio, duration=3
) # for custom audio, where make_frame is a function returning a float (or tuple for stereo) for a given time
The best to understand all these clips more thoroughly is to read the full documentation for each in the Api Reference.
Realasing resources by closing a clip#
When you create some types of clip instances - e.g. VideoFileClip
or AudioFileClip
- MoviePy creates a subprocess and locks the file. In order to release those resources when you are finished you should call the close()
method.
This is more important for more complex applications and is particularly important when running on Windows. While Python’s garbage collector should eventually clean up the resources for you, closing them makes them available earlier.
However, if you close a clip too early, methods on the clip (and any clips derived from it) become unsafe.
So, the rules of thumb are:
Call
close()
on any clip that you construct once you have finished using it and have also finished using any clip that was derived from it.Even if you close a
CompositeVideoClip
instance, you still need to close the clips it was created from.Otherwise, if you have a clip that was created by deriving it from from another clip (e.g. by calling
with_mask()
), then generally you shouldn’t close it. Closing the original clip will also close the copy.
Clips act as context managers. This means you
can use them with a with
statement, and they will automatically be closed at the end of the block, even if there is
an exception.
from moviepy import *
try:
with AudioFileClip("example.wav") as clip:
raise Exception("Let's simulate an exception")
except Exception as e:
print("{}".format(e))
# clip.close() is implicitly called, so the lock on my_audiofile.mp3 file is immediately released.
Categories of video clips#
Video clips are the building blocks of longer videos. Technically, they are clips with a clip.get_frame(t)
method which outputs a HxWx3
numpy array representing the frame of the clip at time t
.
There are two main type of video clips:
animated clips (made with
VideoFileClip
,VideoClip
andImageSequenceClip
), which will always have duration.unanimated clips (made with
ImageClip
,VideoClip`TextClip
andColorClip
), which show the same picture for an a-priori infinite duration.
There are also special video clips called masks, which belong to the categories above but output greyscale frames indicating which parts of another clip are visible or not.
A video clip can carry around an audio clip (AudioClip
) in audio
which is its soundtrack, and a mask clip in mask
.
Animated clips#
Thoses are clips whose image will change in time, and who have a duration and a number of Frames Per Second.
VideoClip#
VideoClip
is the base class for all the other video clips in MoviePy. If all you want is to edit video files, you will never need it. This class is practical when you want to make animations from frames that are generated by another library.
All you need is to define a function make_frame(t)
which returns a HxWx3 numpy array (of 8-bits integers) representing the frame at time t
.
Here is an example where we will create a pulsating red circle with graphical library pillow.
from PIL import Image, ImageDraw
import numpy as np
from moviepy import *
import math
WIDTH, HEIGHT = (128, 128)
RED = (255, 0, 0)
def make_frame(t):
frequency = 1 # One pulse per second
coef = 0.5 * (1 + math.sin(2 * math.pi * frequency * t)) # radius varies over time
radius = WIDTH * coef
x1 = WIDTH / 2 - radius / 2
y1 = HEIGHT / 2 - radius / 2
x2 = WIDTH / 2 + radius / 2
y2 = HEIGHT / 2 + radius / 2
img = Image.new("RGB", (WIDTH, HEIGHT))
draw = ImageDraw.Draw(img)
draw.ellipse((x1, y1, x2, y2), fill=RED)
return np.array(img) # returns a 8-bit RGB array
clip = VideoClip(
make_frame, duration=2
) # we define a 2s duration for the clip to be able to render it later
clip.write_gif(
"circle.gif", fps=15
) # we must set a framerate because VideoClip have no framerate by default
Resulting in this.
Note
Clips that are made with a make_frame
do not have an explicit frame rate nor duration by default, so you must provide duration at clip creation and a frame rate (fps
, frames per second) for write_gif()
and write_videofile()
, and more generally for any methods that requires iterating through the frames.
For more, see VideoClip
.
VideoFileClip#
A VideoFileClip
is a clip read from a video file (most formats are supported) or a GIF file. This is probably one of the most used object ! You load the video as follows:
from moviepy import *
myclip = VideoFileClip("example.mp4")
# video file clips already have fps and duration
print("Clip duration: {}".format(myclip.duration))
print("Clip fps: {}".format(myclip.fps))
myclip = myclip.with_subclip(0.5, 2) # Cutting the clip between 0.5 and 2 secs.
print("Clip duration: {}".format(myclip.duration)) # Cuting will update duration
print("Clip fps: {}".format(myclip.fps)) # and keep fps
myclip.write_videofile(
"result.mp4"
) # the output video will be 1.5 sec long and use original fps
Note
These clips will have an fps
(frame per second) and duration
attributes, which will be transmitted if you do small modifications of the clip, and will be used by default in write_gif()
, write_videofile()
, etc.
For more, see VideoFileClip
.
ImageSequenceClip#
This ImageSequenceClip
is a clip made from a series of images :
from moviepy import *
# A clip with a list of images showed for 1 second each
myclip = ImageSequenceClip(
[
"example_img_dir/image_0001.jpg",
"example_img_dir/image_0002.jpg",
"example_img_dir/image_0003.jpg",
],
durations=[1, 1, 1],
)
print(
"Clip duration: {}".format(myclip.duration)
) # 3 images, 1 seconds each, duration = 3
print("Clip fps: {}".format(myclip.fps)) # 3 seconds, 3 images, fps is 3/3 = 1
# This time we will load all images in the dir, and instead of showing theme for X seconds, we will define FPS
myclip2 = ImageSequenceClip("./example_img_dir", fps=30)
print(
"Clip duration: {}".format(myclip2.duration)
) # fps = 30, so duration = nb images in dir / 30
print("Clip fps: {}".format(myclip2.fps)) # fps = 30
myclip.write_gif("result.gif") # the gif will be 3 sec and 1 fps
myclip2.write_gif(
"result2.gif"
) # the gif will be 30 fps, duration will vary based on number of images in dir
When creating an image sequence, sequence
can be either a list of image names (that will be played in the provided order), a folder name (played in alphanumerical order), or a list of frames (Numpy arrays), obtained for instance from other clips.
Warning
All the images in list/folder/frames must be of the same size, or an exception will be raised
For more, see ImageSequenceClip
.
DataVideoClip#
DataVideoClip
is a video clip who take a list of datasets, a callback function,
and make each frame by iterating over dataset and invoking the callback function with the current data as first argument.
You will probably never use this. But if you do, think of it like a VideoClip
, where you make frames not based on time,
but based on each entry of a data list.
from moviepy import *
import numpy as np
# Dataset will just be a list of colors as RGB
dataset = [
(255, 0, 0),
(0, 255, 0),
(0, 0, 255),
(0, 255, 255),
(255, 0, 255),
(255, 255, 0),
]
# The function make frame take data and create an image of 200x100 px fill with the color
def make_frame(data):
frame = np.full((100, 200, 3), data, dtype=np.uint8)
return frame
# We create the DataVideoClip, and we set FPS at 2, making a 3s clip (because len(dataset) = 6, so 6/2=3)
myclip = DataVideoClip(data=dataset, data_to_frame=make_frame, fps=2)
# Modifying fps here will change video FPS, not clip FPS
myclip.write_videofile("result.mp4", fps=30)
For more, see For more, see DataVideoClip
.
UpdatedVideoClip#
Warning
This is really advanced usage, you will probably never need it, if you do, please go read the code.
UpdatedVideoClip
is a video whose make_frame requires some objects to be updated before we can compute it.
This is particularly practical in science where some algorithm needs to make some steps before a new frame can be generated, or maybe when trying to make a video based on a live exterior context.
When you use this, you pass a world object to it. A world object is an object who respect thoses 3 rules :
It has a
clip_t
property, indicating the current world time.It has an
update()
method, that will update the world state and is responsible for increasingclip_t
when a new frame can be drown.It has a
to_frame()
method, that will render a frame based on world current state.
On get_frame()
call, your UpdatedVideoClip
will try to update the world until world.clip_t
is superior or equal to frame time, then it will call world.to_frame()
.
from moviepy import *
import numpy as np
import random
# Imagine we want to make a video that become more and more red as we repeat same face on coinflip in a row
# because coinflip are done in real time, we need to wait until a winning row is done to be able
# to make the next frame.
# This is a world simulating that. Sorry, it's hard to come up with examples...
class CoinFlipWorld:
def __init__(self, fps):
"""
FPS is usefull because we must increment clip_t by 1/FPS to have UpdatedVideoClip run with a certain FPS
"""
self.clip_t = 0
self.win_strike = 0
self.reset = False
self.fps = fps
def update(self):
if self.reset:
self.win_strike = 0
self.reset = False
print("strike : {}, clip_t : {}".format(self.win_strike, self.clip_t))
print(self.win_strike)
# 0 tails, 1 heads, this is our simulation of coinflip
choice = random.randint(0, 1)
face = random.randint(0, 1)
# We win, we increment our serie and retry
if choice == face:
self.win_strike += 1
return
# Different face, we increment clip_t and set reset so we will reset on next update.
# We dont reset immediately because we will need current state to make frame
self.reset = True
self.clip_t += 1 / self.fps
def to_frame(self):
red_intensity = 255 * (
self.win_strike / 10
) # 100% red for 10 victories and more
red_intensity = min(red_intensity, 255)
# A 200x100 image with red more or less intense based on number of victories in a row
return np.full((100, 200, 3), (red_intensity, 0, 0), dtype=np.uint8)
world = CoinFlipWorld(fps=5)
myclip = UpdatedVideoClip(world=world, duration=10)
# We will set FPS to same as world, if we was to use a different FPS, the lowest from world.fps and our write_videofile fps param
# will be the real visible fps
myclip.write_videofile("result.mp4", fps=5)
Unanimated clips#
Thoses are clips whose image will, at least before modifications, stay the same. By default they have no duration nor FPS. Meaning you will need to define thoses if you try to do operation needing such information (for example rendering).
ImageClip#
ImageClip
is the base class for all unanimated clips, it’s a video clip that always displays the same image. Along with VideoFileClip
it’s one of the most used kind of clip.
You can create one as follows:
from moviepy import *
import numpy as np
# Random RGB noise image of 200x100
noise_image = np.random.randint(low=0, high=255, size=(100, 200, 3))
myclip1 = ImageClip("example.png") # You can create it from a path
myclip2 = ImageClip(noise_image) # from a (height x width x 3) RGB numpy array
myclip3 = VideoFileClip("./example.mp4").to_ImageClip(
t="00:00:01"
) # Or load videoclip and extract frame at a given time
For more, see ImageClip
.
TextClip#
A TextClip
is a clip that will turn a text string into an image clip.
TextClip
accept many parameters, letting you configure the apparence of the text, such as font and font size,
color, interlining, text alignement, etc.
The font you want to use must be an OpenType font, and you will set it by passing the path to the font file.
Here are a few example of using TextClip
:
from moviepy import *
font = "./example.ttf"
# First we use as string and let system autocalculate clip dimensions to fit the text
# we set clip duration to 2 secs, if we do not, it got an infinite duration
txt_clip1 = TextClip(
font=font,
text="Hello World !",
font_size=30,
color="#FF0000",
bg_color="#FFFFFF",
duration=2,
) # Red
# This time we load text from a file, we set a fixed size for clip and let the system find best font size,
# allowing for line breaking
txt_clip2 = TextClip(
font=font,
filename="./example.txt",
size=(500, 200),
bg_color="#FFFFFF",
method="caption",
color=(0, 0, 255, 127),
) # Blue with 50% transparency
# we set duration, because by default image clip are infinite, and we cannot render infinite
txt_clip2 = txt_clip2.with_duration(2)
txt_clip1.write_videofile(
"result1.mp4", fps=24
) # ImageClip have no FPS either, so we must defined it
txt_clip2.write_videofile("result2.mp4", fps=24)
Note
The parameter method
let you define if text should be written and overflow if too long (label
) or be automatically breaked (caption
).
For a more detailed explaination of all the parameters, see TextClip
.
ColorClip#
A ColorClip
is a clip that will return an image of only one color. It is sometimes usefull when doing compositing (see Compositing multiple clips).
from moviepy import *
myclip = ColorClip(
size=(200, 100), color=(255, 0, 0), duration=1
) # Color is passed as a RGB tuple
myclip.write_videofile(
"result.mp4", fps=1
) # We really dont need more than 1 fps do we ?
For more, see ColorClip
.
Mask clips#
Masks are a special kind of VideoClip
with the property is_mask
set to True
. They can be attached to any other kind of VideoClip
through method with_mask()
.
When a clip as a mask attached to it, this mask will indicate which pixels will be visible when the clip is composed with other clips (see Compositing multiple clips). Masks are also used to define transparency when you export the clip as GIF file or as a PNG.
The fundamental difference between masks and standard clips is that standard clips output frames with 3 components (R-G-B) per pixel, comprised between 0 and 255, while a mask has just one composant per pixel, between 0 and 1 (1 indicating a fully visible pixel and 0 a transparent pixel). Seen otherwise, a mask is always in greyscale.
When you create or load a clip that you will use as a mask you need to declare it. You can then attach it to a clip with the same dimensions :
from moviepy import *
import numpy as np
# Random RGB noise image of 200x100
makeframe = lambda t: np.random.rand(100, 200)
# To define the VideoClip as a mask, just pass parameter is_mask as True
maskclip1 = VideoClip(makeframe, duration=4, is_mask=True) # A random noise mask
maskclip2 = ImageClip("example_mask.jpg", is_mask=True) # A fixed mask as jpeg
maskclip3 = VideoFileClip("example_mask.mp4", is_mask=True) # A video as a mask
# Load our basic clip, resize to 200x100 and apply each mask
clip = VideoFileClip("example.mp4")
clip_masked1 = clip.with_mask(maskclip1)
clip_masked2 = clip.with_mask(maskclip2)
clip_masked3 = clip.with_mask(maskclip3)
Note
In the case of video and image files, if these are not already black and white they will be converted automatically.
Also, when you load an image with an alpha layer, like a PNG, MoviePy will use this layer as a mask, except if you pass transparent=False
.
Any video clip can be turned into a mask with to_mask()
, and a mask can be turned to a standard RGB video clip with to_RGB()
.
Masks are treated differently by many methods (because their frames are different) but at the core, they are VideoClip
, so you can do with theme everything you can do with a video clip: modify, cut, apply effects, save, etc.
Using audio elements with audio clips#
In addition to VideoClip
for visual, you can use audio elements, like an audio file, using the AudioClip
class.
Both are quite similar, except AudioClip
method get_frame()
return a numpy array of size Nx1
for mono, and size Nx2
for stereo.
AudioClip#
AudioClip
is the base class for all audio clips. If all you want is to edit audio files, you will never need it.
All you need is to define a function make_frame(t)
which returns a Nx1
or Nx2
numpy array representing the sound at time t
.
from moviepy import *
import numpy as np
# Producing a sinewave of 440 Hz -> note A
make_frame_audio = lambda t: np.sin(440 * 2 * np.pi * t)
# AUDIO CLIPS
clip = AudioClip(make_frame_audio, duration=3)
For more, see AudioClip
.
AudioFileClip#
AudioFileClip
is used to load an audio file, this is probably the only kind of audio clip you will use.
You simply pass him the file you want to load :
from moviepy import *
import numpy as np
# Works for audio files, but also videos file where you only want the keep the audio track
clip = AudioFileClip("example.wav")
clip.write_audiofile("./result.wav")
For more, see AudioFileClip
.
AudioArrayClip#
AudioArrayClip
is used to turn an array representing a sound into an audio clip. You will probably never use it, unless you need to use the result of some third library without using a temporary file.
You need to provide a numpy array representing the sound (of size Nx1
for mono, Nx2
for stereo), and the number of fps, indicating the speed at which the sound is supposed to be played.
import numpy as np
from moviepy import *
# We want to play those notes
notes = {"A": 440, "B": 494, "C": 523, "D": 587, "E": 659, "F": 698}
note_duration = 0.5
total_duration = len(notes) * note_duration
sample_rate = 44100 # Number of samples per second
note_size = int(note_duration * sample_rate)
total_size = note_size * len(notes)
def make_frame(t, note_frequency):
return np.sin(note_frequency * 2 * np.pi * t)
# We generate all frames timepoints
times = np.linspace(0, total_duration, total_size)
# We make an array of size N*1, where N is the number of frames * total duration
audio_array = np.zeros((total_size, 2))
i = 0
for note, frequency in notes.items():
for _ in range(note_size):
audio_array[i][0] = make_frame(times[i], frequency)
i += 1
# Create an AudioArrayClip from the audio samples
audio_clip = AudioArrayClip(audio_array, fps=sample_rate)
# Write the audio clip to a WAV file
audio_clip.write_audiofile("result.wav", fps=44100)
For more, see AudioArrayClip
.