Modifying clips and apply effects#
Of course, once you will have loaded a Clip
the next step of action will be to modify it to be able to integrate it in your final video.
- To modify a clip, there is three main courses of actions :
The built-in methods of
VideoClip
orAudioClip
modifying the properties of the object.The already-implemented effects of MoviePy you can apply on clips, usually affecting the clip by applying filters on each frame of the clip at rendering time.
The transformation filters that you can apply using
transform()
andtime_transform()
.
How modifications are applied to a clip ?#
Clip copy during modification#
The first thing you must know is that when modifying a clip, MoviePy will never modify that clip directly. Instead it will return a modified copy of the original and let the original untouched. This is known as out-place instead of in-place behavior.
To illustrate :
# Import everything needed to edit video clips
from moviepy import *
# Load example.mp4
clip = VideoFileClip("example.mp4")
# This does nothing, as multiply_volume will return a copy of clip which you will loose immediatly as you dont store it
# If you was to render clip now, the audio would still be at full volume
clip.with_multiply_volume(0.1)
# This create a copy of clip in clip_whisper with a volume of only 10% the original, but does not modify the original clip
# If you was to render clip right now, the audio would still be at full volume
# If you was to render clip_whisper, the audio would be a 10% of the original volume
clip_whisper = clip.with_multiply_volume(0.1)
# This replace the original clip with a copy of it where volume is only 10% of the original
# If you was to render clip now, the audio would be at 10%
# The original clip is now lost
clip = clip.with_multiply_volume(0.1)
This is an important point to understand, because it is one of the most recurrent source of bug for newcomers.
Memory consumption of effect and modifications#
When applying an effect or modification, it does not immediately apply the effect to all the frames of the clip, but only to the first frame: all the other frames will only be modified when required (that is, when you will write the whole clip to a file of when you will preview it).
It means that creating a new clip is neither time nor memory hungry, all the computation happen during the final rendering.
Time representations in MoviePy#
Many methods that we will see accept duration or timepoint as arguments. For instance clip.with_subclip(t_start, t_end)
which cuts the clip between two timepoints.
MoviePy usually accept duration and timepoint as either :
a number of seconds as a
float
.a
tuple
with(minutes, seconds)
or(hours, minutes, seconds)
.a
string
such as'00:03:50.54'
.
Also, you can usually provide negative times, indicating a time from the end of the clip. For example, clip.with_subclip(-20, -10)
cuts the clip between 20s before the end and 10s before the end.
Modify a clip using the with_*
methods#
The first way to modify a clip is by modifying internal properties of your object, thus modifying his behavior.
Thoses methods usually starts with the prefix with_
or without_
, indicating that they will return a copy of the clip with the properties modified.
So, you may write something like :
from moviepy import VideoFileClip
from moviepy import vfx, afx
myclip = VideoFileClip("example.mp4")
myclip = myclip.with_end(5) # stop the clip after 5 sec
myclip = myclip.without_audio() # remove the audio of the clip
In addition to the with_*
methods, a handful of very common methods are also accessible under shorter name, thoses are:
crop()
rotate()
Modify a clip using effects#
The second way to modify a clip is by using effects that will modify the frames of the clip (which internally are no more than numpy arrays) by applying some sort of functions on them.
MoviePy come with many effects implemented in moviepy.video.fx
for visual effects and moviepy.audio.fx
for audio effects.
For practicality, these two modules are loaded in MoviePy as vfx
and afx
, letting you import them as from moviepy import vfx, afx
.
To use thoses effects, you simply need to instanciate them as object and apply them on your Clip
using method with_effects()
, with a list of Effect
objects you want to apply.
For convenience the effects are also dynamically added as method of VideoClip
and AudioClip
classes at runtime, letting you call them as simple method of your clip.
So, you may write something like :
from moviepy import VideoFileClip
from moviepy import vfx, afx
myclip = VideoFileClip("example.mp4")
myclip = myclip.with_effects(
[vfx.Resize(width=460)]
) # resize clip to be 460px in width, keeping aspect ratio
# fx method return a copy of the clip, so we can easily chain them
myclip = myclip.with_effects(
[vfx.MultiplySpeed(2), afx.MultiplyVolume(0.5)]
) # double the speed and half the audio volume
# because effects are added to Clip at runtime, you can also call them directly from your clip as methods
myclip = myclip.with_effects([vfx.MultiplyColor(0.5)]) # darken the clip
Note
MoviePy effects are automatically applied to both the sound and the mask of the clip if it is relevant, so that you don’t have to worry about modifying these.
For a list of those effects, see moviepy.video.fx
and moviepy.audio.fx
.
In addition to the effects already provided by MoviePy, you can obviously Creating your own effects and use them the same way.
Modify a clip apparence and timing using filters#
In addition to modify a clip properties and using effects, you can also modify the apparence or timing of a clip by using your own custom filters with time_transform()
, image_transform()
, and more generally with transform()
.
All thoses methods works by taking as first parameter a callback function that will receive either a clip frame, a timepoint, or both, and return a modified version of thoses.
Modify only the timing of a Clip#
You can change the timeline of the clip with time_transform(your_filter)
.
Where your_filter
is a callback function taking clip time as a parameter and returning a new time :
from moviepy import VideoFileClip
import math
my_clip = VideoFileClip("example.mp4")
# You can define a function the classical way
def accel_x3(time: float) -> float:
return time * 3
modified_clip1 = my_clip.time_transform(accel_x3)
# Of you can also use lambda function
modified_clip2 = my_clip.time_transform(lambda t: 1 + math.sin(t))
Now the clip modified_clip1
plays three times faster than my_clip
, while modified_clip2
will be oscillating between 00:00:00 to 00:00:02 of my_clip
. Note that in the last case you have created a clip of infinite duration (which is not a problem for the moment).
Note
By default time_transform()
will only modify the clip main frame, without modifying clip audio or mask for VideoClip
.
If you wish to also modify audio and/or mask you can provide the parameter apply_to
with either 'audio'
, 'mask'
, or ['audio', 'mask']
.
Modifying only the apparence of a Clip#
For VideoClip
, you can change the apparence of the clip with image_transform(your_filter)
.
Where your_filter
is a callback function, taking clip frame (a numpy array) as a parameter and returning the transformed frame :
from moviepy import VideoFileClip
import numpy
my_clip = VideoFileClip("example.mp4")
def invert_green_blue(image: numpy.ndarray) -> numpy.ndarray:
return image[:, :, [0, 2, 1]]
modified_clip1 = my_clip.image_transform(invert_green_blue)
Now the clip modified_clip1
will have his green and blue canals inverted.
Note
You can define if transformation should be applied to audio and mask same as for time_transform()
.
Note
Sometimes need to treat clip frames and mask frames in a different way. To distinguish between the two, you can always look at their shape, clips are H*W*3
, and masks H*W
.
Modifying both the apparence and the timing of a Clip#
Finally, you may want to process the clip by taking into account both the time and the frame picture, for example to apply visual effects variating with time.
This is possible with the method transform(your_filter)
.
Where your_filter
is a callback function taking two parameters, and returning a new frame picture. Where first argument is a get_frame
method (i.e. a function get_frame(time)
which given a time returns the clip’s frame at that time), and the second argument is the time.
from moviepy import VideoFileClip
import math
my_clip = VideoFileClip("example.mp4")
def scroll(get_frame, t):
"""
This function returns a 'region' of the current frame.
The position of this region depends on the time.
"""
frame = get_frame(t)
frame_region = frame[int(t) : int(t) + 360, :]
return frame_region
modified_clip1 = my_clip.transform(scroll)
This will scroll down the clip, with a constant height of 360 pixels.
Note
You can define if transformation should be applied to audio and mask same as for time_transform()
.
Note
When programming a new effect, whenever it is possible, prefer using time_transform
and image_transform
instead of transform
when implementing new effects.
The reason is that, though they both internally relly on transform
when these effects are applied to ImageClip
objects, MoviePy will recognize they only need to be applied once instead of on each frame, resulting in faster renderings.
To keep things simple, we have only addressed the case of VideoClip
, but know that the same principle applies to AudioClip
, except that instead of a picture frame, you will have an audio frame, which is also a numpy array.