Clips transformations and effects¶
There are several categories of clip modifications in MoviePy:
- The very common methods to change the attributes of a clip:
clip.set_duration
,clip.set_audio
,clip.set_mask
,clip.set_start
etc. - The already-implemented effects. Core effects like
clip.subclip(t1, t2)
(keep only the cut between t1 and t2), which are very important, are implemented as class methods. More advanced and less common effects likeloop
(makes the clip play in a loop) ortime_mirror
(makes the clip play backwards) are placed in the special modulesmoviepy.video.fx
andmoviepy.audio.fx
and are applied with theclip.fx
method, for instanceclip.fx(time_mirror)
(makes the clip play backwards),clip.fx(black_white)
(turns the clip black and white), etc. - The effects that you can create yourself. using
All these effects have in common that they are not inplace: they do NOT modify the original clip, instead they create a new clip that is a version of the former with the changes applied. For instance:
my_clip = VideoFileClip("some_file.mp4")
my_clip.set_start(t=5) # does nothing, changes are lost
my_new_clip = my_clip.set_start(t=5) # good !
Also, when you write clip.resize(width=640)
, it does not immediately applies the effect to all the frames of the clip, but only to the first frame: all the other frames will be resized only when required (that is, when you will write the whole clip to a file of when you will preview it). Said otherwise, creating a new clip is neither time nor memory hungry, all the computations happen during the final rendering.
Time representations in MoviePy¶
Many methods that we will see accept times as arguments. For instance clip.subclip(t_start,t_end)
which cuts the clip between two times. For these methods, times can be represented either in seconds (t_start=230.54
), as a couple (minutes, seconds) (t_start=(3,50.54)
), as a triplet (hour, min, sec) (t_start=(0,3,50.54)
) or as a string (t_start='00:03:50.54')
).
Most of the time when the times are not provided they are guessed, for instance in clip.subclip(t_start=50)
it is implied that t_end
corresponds to the end of the clip, in clip.subclip(t_end=20)
it is implied that t_start=0. If the time is negative it is considered as the time before the end of the clip: clip.subclip(-20, -10)
cuts the clip between 20s before the end and 10s before the end.
Methods to change the clip attributes¶
clip.fx¶
Suppose that you have some functions implementing effects on clips, i.e. functions which, given a clip and some arguments, return a new clip:
effect_1(clip, args1) -> new clip
effect_2(clip, args2) -> new clip
effect_3(clip, args3) -> new clip
where args
represent arguments and/or keyword arguments. To apply these functions, in that order, to one clip, you would write something like
newclip = effect_3( effect_2( effect_1(clip, args3), args2), args1)
but this is not easy to read. To have a clearer syntax you can use clip.fx
:
newclip = (clip.fx( effect_1, args1)
.fx( effect_2, args2)
.fx( effect_3, args3))
Much better ! There are already many effects implemented in the modules moviepy.video.fx
and moviepy.audio.fx
. The fx methods in these modules are automatically applied to the sound and the mask of the clip if it is relevant, so that you don’t have to worry about modifying these. For practicality, when you use from moviepy.editor import *
, these two modules are loaded as vfx
and afx
, so you may write something like
from moviepy.editor import *
clip = (VideoFileClip("myvideo.avi")
.fx( vfx.resize, width=460) # resize (keep aspect ratio)
.fx( vfx.speedx, 2) # double the speed
.fx( vfx.colorx, 0.5)) # darken the picture
For convenience, when you use moviepy.editor
, frequently used methods such as resize
can be called in a simpler way: clip.resize(...)
instead of clip.fx( vfx.resize, ...)
Methods to create custom effects¶
clip.fl¶
You can modify a clip as you want using custom filters with clip.fl_time
, clip.fl_image
, and more generally with clip.fl
.
You can change the timeline of the clip with clip.fl_time
like this:
modifiedClip1 = my_clip.fl_time(lambda t: 3*t)
modifiedClip2 = my_clip.fl_time(lambda t: 1+sin(t))
Now the clip modifiedClip1
plays the same as my_clip
, only three times faster, while modifiedClip2
will play my_clip
by oscillating between the times t=0s and t=2s. Note that in the last case you have created a clip of infinite duration (which is not a problem for the moment).
You can also modify the display of a clip with clip.fl_image
. The following takes a clip and inverts the green and blue channels of the frames:
def invert_green_blue(image):
return image[:,:,[0,2,1]]
modifiedClip = my_clip.fl_image( invert_green_blue )
Finally, you may want to process the clip by taking into account both the time and the frame picture. This is possible with the method clip.fl(filter)
. The filter must be a function which takes two arguments and returns a picture. the fist argument is a get_frame
method (i.e. a function g(t)
which given a time returns the clip’s frame at that time), and the second argument is the time.
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
modifiedClip = my_clip.fl( scroll )
This will scroll down the clip, with a constant height of 360 pixels.
When programming a new effect, whenever it is possible, prefer using fl_time
and fl_image
instead of fl
if possible when implementing new effects. The reason is that, when these effects are applied to
ImageClips, MoviePy will recognize that these methods do not need to be applied to each frame, which will
result in faster renderings.