MoviePy 简介
MoviePy是一个用于视频编辑的Python模块,它可被用于一些基本操作(如剪切、拼接、插入标题)、视频合成(即非线性编辑)、视频处理和创建高级特效。它可对大多数常见视频格式进行读写,包括GIF。
MoviePy Windows 安装
安装执行如下命令:
pip install moviepy
依赖软件包安装Ffmpeg
MoviePy依赖Ffmpeg软件对视频进行读写。
第一种:如果你想使用FFMPEG的特定版本,你可以设置FFMPEG_BINARY计算机环境变量。
第二种:修改moviepy/config_defaults.py文件,指定Ffmpeg可执行目录地址。
依赖软件包安装ImageMagick
ImageMagick 在你想要添加文字时需要用到。
第一种:如果你想使用ImageMagick的特定版本,你可以设置IMAGEMAGICK_BINARY计算机环境变量。
第二种:修改moviepy/config_defaults.py文件,指定
ImageMagick可执行目录地址。
示例:
IMAGEMAGICK_BINARY = "D:\\Program Files\\ImageMagick-7.1.1-Q16\\magick.exe"
MoviePy 工作原理
MoviePy 使用软件 ffmpeg 读写视频和音频文件,借助 ImageMagick 合成文本和写入 GIF 文件。视频的处理过程使用到 Numpy、Scipy 等 Python 库。
MoviePy 核心概念
在MoviePy中,核心对象是剪辑,可以使用AudioClips
或VideoClips
来处理。剪辑可被修改(剪切、降低速度、变暗等)或与其他剪辑混合组成新剪辑。剪辑可被预览(使用PyGame或IPython Notebook),也可生成文件(如MP4文件、GIF文件、MP3文件等)。以VideoClips
为例,它可以由一个视频文件、一张图片、一段文字或者一段卡通动画而来。它可以包含音频轨道(即AudioClip
)和一个遮罩(一种特殊的VideoClip
),用于表明当两个剪辑混合时,哪一部分的画面被隐藏)。详见生成与导出视频剪辑和混合剪辑。
你可使用MoviePy的很多效果对一个剪辑进行修改(如clip.resize(width="360")
、clip.subclip(t1,t2)
、clip.fx(vfx.black_white)
或使用用户自行实现的效果。MoviePy提供许多函数(如clip.fl
、clip.fx
等),可以用简单的几行代码实现你自己的效果。详见视频转换与效果。
你还可以在moviepy.video.tools
找到一些高级的效果来对视频中的对象进行追踪、画简单的形状和颜色渐变(对于遮罩来说很有用)、生成字幕和结束时的演职人员表等。参见高级工具中的详细描述。
MoviePy 使用
快速入门
MoviePy 典型代码示例:视频加载和预览
from moviepy.editor import *
# 视频加载
clip = VideoFileClip("video/street.mp4")
# 视频预览
clip.preview()
MoviePy 典型代码示例:加载视频或音频、修改、将他们连接在一起并最后生成一个新视频文件。
from moviepy.editor import *
# 加载视频并选择 00:00:10 - 00:00:20 的片段
clip = VideoFileClip("video/street.mp4").subclip(10, 20)
# 降低音量 (volume x 0.8)
clip = clip.volumex(0.8)
# 生成字幕片段,可自定义字体和颜色等
txt_clip = TextClip("街道风景2024", fontsize=70, color='white')
# 设置在屏幕中央显示 10 秒
txt_clip = txt_clip.set_pos('center').set_duration(10)
# 在载入的原视频上层覆盖文本视频
video = CompositeVideoClip([clip, txt_clip])
# 将输出结果写入文件中
video.write_videofile("video/streetCopy.mp4")
混合剪辑
视频合成,也称为非线性编辑,事实上就是把许多视频剪辑放在一起,变成一个新剪辑。
连接视频
将视频片段放在一起,最简单的方法是连接它们,在新剪辑中一个接一个地播放。视频连接通过函数 concatenate_videoclips
实现:
from moviepy.editor import VideoFileClip, concatenate_videoclips
clip1 = VideoFileClip("myvideo.mp4")
clip2 = VideoFileClip("myvideo2.mp4").subclip(50,60)
clip3 = VideoFileClip("myvideo3.mp4")
final_clip = concatenate_videoclips([clip1,clip2,clip3])
final_clip.write_videofile("my_concatenation.mp4")
堆叠视频
除了一个接一个播放的合并方式,MoviePy 还能将多个片段在同一画面中同时播放,形成片段堆叠效果,使用 clip_array
实现:
from moviepy.editor import VideoFileClip, clips_array, vfx
clip1 = VideoFileClip("myvideo.mp4").margin(10) # add 10px contour
clip2 = clip1.fx( vfx.mirror_x)
clip3 = clip1.fx( vfx.mirror_y)
clip4 = clip1.resize(0.60) # downsize 60%
final_clip = clips_array([[clip1, clip2],
[clip3, clip4]])
final_clip.resize(width=480).write_videofile("my_stack.mp4")
合成剪辑/合成视频剪辑
CompositeVideoClip类提供了一个非常灵活的方法来合成剪辑,但它比concatenate_videoclips
和clips_array
更复杂一些。
语法格式:
video = CompositeVideoClip([clip1,clip2,clip3])
当前video
播放clip1
,clip2
在clip1
的上层,而clip3
在clip1
和clip2
的上层。举例来说,如果clip2
与clip3
和clip1
有同样的尺寸,那么只有在顶层的clip3
能在视频中可见,除非clip3
和clip2
被遮罩隐藏了一部分。注意,在默认情况下,合成的视频和第一个剪辑的尺寸相同(因为它通常是一个背景)。但是有时,你想让你的剪辑在更大的合成视频里浮动,所以你需要像这样特意修改最终合成视频的尺寸:
video = CompositeVideoClip([clip1,clip2,clip3], size=(720,460))
起始和终止时间
在一个合成剪辑中,所有的剪辑开始的时间都被clip.start
属性所指定。你可以像这样设定开始时间:
clip1 = clip1.set_start(5) # start after 5 seconds
例如你的剪辑看起来会是这样:
video = CompositeVideoClip([clip1, # starts at t=0 clip2.set_start(5), # start at t=5s clip3.set_start(9)]) # start at t=9s
在上面的例子中,clip2
可能先于clip1
的结束时间开始。在这种情况下,你可以给clip2
添加一个1秒钟的淡入效果:
video = CompositeVideoClip([clip1, # starts at t=0 clip2.set_start(5).crossfadein(1), clip3.set_start(9).crossfadein(1.5)])
剪辑的定位
如果clip2
和clip3
都比clip1
小,你可以决定他们出现在视频中的位置。在这里,我们指定了剪辑左上角像素的坐标:
video = CompositeVideoClip([clip1, clip2.set_pos((45,150)), clip3.set_pos((90,100))])
有很多方法可以指定位置:
clip2.set_pos((45,150)) # x=45, y=150 , in pixels clip2.set_pos("center") # automatically centered # clip2 is horizontally centered, and at the top of the picture clip2.set_pos(("center","top")) # clip2 is vertically centered, at the left of the picture clip2.set_pos(("left","center")) # clip2 is at 40% of the width, 70% of the height of the screen: clip2.set_pos((0.4,0.7), relative=True) # clip2's position is horizontally centered, and moving down ! clip2.set_pos(lambda t: ('center', 50+t) )
当指定坐标的时候请记住,y
坐标的0位置在图片的最上方:
合成音频剪辑
当你将视频剪辑混合在一起时,MoviePy将会把它们各自的音轨自动合成为最终剪辑的音轨,所以不用担心需要自行合成音轨啦。 如果你想使用许多音频源来自定义音轨,可以使用CompositeAudioClip
和concatenate_audioclips
来进行混音。
from moviepy.editor import * # ... make some audio clips aclip1, aclip2, aclip3 concat = concatenate_audioclips([aclip1, aclip2, aclip3]) compo = CompositeAudioClip([aclip1.volumex(1.2), aclip2.set_start(5), # start at t=5s aclip3.set_start(9)])
预览剪辑
当你使用MoviePy通过不断尝试来编辑视频或试图达到某种效果的时候,生成每个示例的视频的时间可能会很长。这一节提供了一些提高速度的小窍门。
save_frame方法
很多时候,只需要视频中的一帧,即可说明你是否正确操作。你可以像这样保存剪辑中的一帧:
my_clip.save_frame("frame.jpeg") # saves the first frame my_clip.save_frame("frame.png", t=2) # saves the frame a t=2s
from moviepy.editor import *
# 视频加载
clip = VideoFileClip("video/streetCopy.mp4")
# 视频帧保存
clip.save_frame("video/frame.png")
使用matplotlib
自定义动画
MoviePy提供了一种生成自定义动画的方式:通过定义一个函数,以numpy数组的形式返回一个在给定的时间内一帧的动画。
import matplotlib.pyplot as plt
import numpy as np
from moviepy.editor import VideoClip
from moviepy.video.io.bindings import mplfig_to_npimage
x = np.linspace(-2, 2, 200)
duration = 2
fig, ax = plt.subplots()
def make_frame(t):
ax.clear()
ax.plot(x, np.sinc(x**2) + np.sin(x + 2*np.pi/duration * t), lw=3)
ax.set_ylim(-1.5, 2.5)
return mplfig_to_npimage(fig)
animation = VideoClip(make_frame, duration=duration)
animation.write_gif('video/matplotlib.gif', fps=20)
音频操作
这个章节会告诉你如何使用MoviePy去创建和编辑音频剪辑。
注意,当你使用MoviePy来剪切、混合或连接视频剪辑时,音频是自动处理的。如果你有兴趣来编辑音频文件,或者你想自定义视频的音频剪辑,请阅读此章。
音频剪辑的构成
在MoviePy中,音频剪辑和视频剪辑很相似:它们拥有长度,能以同样的方式被剪辑和组成,等等。一个显著的差异是audioclip.get_frame(t)
创建一个新的音频剪辑
音频剪辑可由一个音频文件创建,或从视频剪辑的音轨里提取出来。
from moviepy.editor import * audioclip = AudioFileClip("some_audiofile.mp3") audioclip = AudioFileClip("some_video.avi")
详见AudioFileClip
。
或者你也可以得到一个已创建的视频剪辑的音轨。
videoclip = VideoFileClip("some_video.avi") audioclip = videoclip.audio
合成音频剪辑
导出和预览音频剪辑
你也可以导出分配一个音频剪辑当作视频剪辑的音轨。
videoclip2 = videoclip.set_audio(my_audioclip)
生成和导出视频剪辑
视频和音频剪辑是MoviePy里的核心对象。在此节里我们展示了如何生成不同种类的剪辑,以及如何将它们写入文件。关于修改剪辑信息(剪切、特效等),见视频转换与效果。关于如何将多个视频进行合成,见混合剪辑。关于如何在导出前预览视频,见如何更有效率地使用MoviePy。
下面的代码总结了你可以用MoviePy生成的剪辑基本类型。
# VIDEO CLIPS
clip = VideoClip(make_frame, duration=4) # for custom animations (see below)
clip = VideoFileClip("my_video_file.mp4") # or .avi, .webm, .gif ...
clip = ImageSequenceClip(['image_file1.jpeg', ...], fps=24)
clip = ImageClip("my_picture.png") # or .jpeg, .tiff, ...
clip = TextClip("Hello !", font="Amiri-Bold", fontsize=70, color="black")
clip = ColorClip(size=(460,380), color=[R,G,B])
# AUDIO CLIPS
clip = AudioFileClip("my_audiofile.mp3") # or .ogg, .wav... or a video !
clip = AudioArrayClip(numpy_array, fps=44100) # from a numerical array
clip = AudioClip(make_frame, duration=3) # uses a function make_frame(t)
视频剪辑的类型
长视频是由视频剪辑组成的。这些剪辑可以使用专门的clip.get_frame()
方法输出一个HxWx3的numpy数组来表示剪辑在时间t时的一帧。它们分为两类,一种是动态剪辑(由VideoFileClip
和VideoClip
组成),另一种是非动态剪辑,在任意无限长的持续时间中展示同一图片(ImageClip
、TextClip
、ColorClip
)。还有一种特殊的视频剪辑叫做遮罩,同属于上面的类别,但是输出灰度值帧以表示另一个剪辑里的可见部分。一个视频剪辑可以携带一个音频剪辑clip.audio
作为她的音轨,和一个遮罩剪辑。
VideoClip
VideoClip
是MoviePy中所有其它视频剪辑里最基本的种类。如果你只想编辑视频文件,你就永远不会用到它。当你想通过从别的库里生成的帧来生成动画时,这个类就会很有用。你需要做的只是定义一个make_frame(t)
函数,使它返回一个HxWx3的numpy数组(8位整数)来代表时间t时的帧。这里是一个使用制图库Gizeh的例子:
import gizeh
import moviepy.editor as mpy
def make_frame(t):
surface = gizeh.Surface(128,128) # width, height
radius = W*(1+ (t*(2-t))**2 )/6 # the radius varies over time
circle = gizeh.circle(radius, xy = (64,64), fill=(1,0,0))
circle.draw(surface)
return surface.get_npimage() # returns a 8-bit RGB array
clip = mpy.VideoClip(make_frame, duration=2) # 2 seconds
clip.write_gif("circle.gif",fps=15)
http://zulko.github.io/moviepy/_images/circle.gif
注意,剪辑使用make_frame并没有一个精确的帧速率,所以你必须要为write_gif
和write_videofile
,以及需要遍历每一帧的方法提供帧速率(fps
,每秒的帧数)。
VideoFileClip
视频文件剪辑是由视频文件(支持大多数格式)或GIF文件中读取的剪辑。你可以像这样加载一个视频:
myclip = VideoFileClip("some_video.avi")
myclip = VideoFileClip("some_animation.gif")
注意,这些剪辑都有fps
(帧速率)属性,当你对剪辑做小改动时,帧速率将会被传送,而且会以write_videofile
、write_gif
等的默认设置会使用。例如:
myclip = VideoFileClip("some_video.avi")
print (myclip.fps) # prints for instance '30'
# Now cut the clip between t=10 and 25 secs. This conserves the fps.
myclip2 = myclip.subclip(10, 25)
myclip2.write_gif("test.gif") # the gif will have 30 fps
ImageSequenceClip
这是一种由一系列图片组成的剪辑,你可以这样调用:
clip = ImageSequenceClip(images_list, fps=25)