MoviePy中文手册

MoviePy是一个用于视频编辑的Python模块,它可被用于一些基本操作(如剪切、拼接、插入标题)、视频合成(即非线性编辑)、视频处理和创建高级特效。它可对大多数常见视频格式进行读写,包括GIF。

示例如下(IPython Notebook环境) image0

用户手册

下载与安装

安装

pip安装方式:如果你安装了pip,只需要在终端输入如下命令(若没有安装ez_setup,则会自动安装)

(sudo) pip install moviepy

如果你既没有安装setuptools也没有安装ez_setup,那么上述命令就会运行失败。在这种情况下,你需要在安装前运行:

(sudo) pip install ez_setep

手动安装方式:从 PyPI 或者 Github (开发版本)上下载源码。将所有文件解压到同一文件夹,打开终端并输入

(sudo) python setup.py install

MoviePy依赖 NumpyimageioDecoratortqdm ,他们将在安装MoviePy的同时自动安装。运行平台为Windows/Mac/Linux,并使用Python2.7以上的版本和Python3。如果在安装MoviePy及其依赖包的过程中存在问题,请提供反馈。

MoviePy依赖FFMPEG软件对视频进行读写。不用对此担心,在你第一次使用MoviePy的时候,FFMPEG将会自动由ImageIO下载和安装(不过需要花一些时间)。如果你想使用FFMPEG的特定版本,你可以设置FFMPEG_BINARY环境变量。详见moviepy/config_defaults.py

可选但有用的依赖包

ImageMagick 只有在你想要添加文字时需要用到。它可作为一个后端用在GIF上,但如果不安装ImageMagick,你也可以用MoviePy处理GIF。

一旦你安装了ImageMagick,它会被MoviePy自动检测到,除了Windows环境! Windows用户在手动安装MoviePy之前,应在moviepy/config_defaults.py文件中指定ImageMagick binary的路径,并叫做convert。看起来应该像是这样:

IMAGEMAGICK_BINARY = "C:\\Program Files\\ImageMagick_VERSION\\convert.exe"

你也可以设置IMAGEMAGICK_BINARY环境变量,详见moviepy/config_defaults.py

PyGame 在视频和声音预览中会使用到。不过如果你想要在服务器上使用MoviePy,但的确需要手动进行高级的视频编辑,Pygame就没什么用处啦。

对于高级的图片处理,你至少需要一个以下所列的包。例如使用clip.resize方法,就至少需要安装Scipy、PIL、Pillow或OpenCV其中一个。

  • Python图片库(PIL)或它的分支 Pillow 更好。
  • Scipy (用于追踪、分割等)可在没有安装PIL和OpenCV的情况下用于调整视频剪辑的大小。
  • Scikit Image 可能被用于高级的图片处理。
  • OpenCV 2.4.6 或更高版本(在cv2包中提供)可能被用于高级的图片处理。

如果你使用linux,这些软件都一定在你的仓库中。

MoviePy入门

此页面为你讲解如何使用MoviePy进行编辑。想了解更多的话,详阅作品展示脚本示例

快速准备

这个章节说明了使用MoviePy的场合以及工作原理。

我需要MoviePy吗?

这些是你想用Python编辑视频的理由:

  • 你需要用很复杂的方法来处理或组成大量的视频;
  • 你想在web服务器(Django、Flask等)自动生成视频或GIF动图;
  • 你想自动完成无聊的任务,如插入标题、追踪对象、剪切场景、制作结尾的演职人员表、字幕等等;
  • 你想用代码实现视频编辑软件所不能达到的视频效果;
  • 你想将从别的Python库里(如Matplotlib、Mayavi、Gizeh、scikit-images等)生成的图片制作成动画。

但在以下情况中,MoviePy并非最好的选择:

  • 你只需要对视频进行逐帧分析(如人脸识别或其他有趣的东西),使用MoviePy和别的库可以联合完成。但是事实上,仅使用 imageioOpenCV 或者SimpleCV这些专用库即可达到要求。
  • 你只需要视频文件转换,或者将一系列图片文件转换成视频。在这种情况下,直接调用ffmpeg(或avconvmencoder等)将比使用MoviePy更快速、更有效率地使用内存。
优势与局限性

MoviePy在以下方面表现出色:

  • 易于上手:一行即可完成一项基本操作。对于新手来说,代码易学易懂。
  • 灵活:视频和音频的每一帧都尽在掌握,创作属于自己的特效就像Python一样简单。
  • 便携:代码使用十分常见的软件(Numpy和FFMPEG),而且可以在几乎所有版本的Python和几乎所有的机器上运行。

局限性:MoviePy目前还无法对流媒体进行处理(从摄像头或者远程设备获取视频),并且MoviePy并不是被设计成用来对电影的连续帧进行处理(例如视频去抖,你需要寻找另外的软件)。如果你同时处理大量视频,音频以及图片序列(数量大于100)可能会出现内存问题,但是这个问题会在将来的版本当中进行修复。

代码示例

在典型的MoviePy脚本是这样的:加载视频或音频、修改、将他们连接在一起并最后生成一个新视频文件。作为一个示例,我们加载一个关于我上个假期的视频,降低音量,在前十秒添加标题在视频的中央,然后写入一个文件:

# Import everything needed to edit video clips
from moviepy.editor import *

# Load myHolidays.mp4 and select the subclip 00:00:50 - 00:00:60
clip = VideoFileClip("myHolidays.mp4").subclip(50,60)

# Reduce the audio volume (volume x 0.8)
clip = clip.volumex(0.8)

# Generate a text clip. You can customize the font, color, etc.
txt_clip = TextClip("My Holidays 2013",fontsize=70,color='white')

# Say that you want it to appear 10s at the center of the screen
txt_clip = txt_clip.set_pos('center').set_duration(10)

# Overlay the text clip on the first video clip
video = CompositeVideoClip([clip, txt_clip])

# Write the result to a file (many options available !)
video.write_videofile("myHolidays_edited.webm")
MoviePy的工作原理

MoviePy使用ffmpeg软件来读取和导出视频和音频文件。也使用(可选)ImageMagick来生成文字和制作GIF文件。不同媒体的处理依靠Python的快速的数学库Numpy。高级效果和增强功能使用一些Python的图片处理库(PIL,Scikit-image,scipy等)。

http://zulko.github.io/moviepy/_images/explanations.jpeg

基本概念

在MoviePy中,核心对象是剪辑,可以使用AudioClipsVideoClips来处理。剪辑可被修改(剪切、降低速度、变暗等)或与其他剪辑混合组成新剪辑。剪辑可被预览(使用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.flclip.fx等),可以用简单的几行代码实现你自己的效果。详见视频转换与效果

你还可以在moviepy.video.tools找到一些高级的效果来对视频中的对象进行追踪、画简单的形状和颜色渐变(对于遮罩来说很有用)、生成字幕和结束时的演职人员表等。参见高级工具中的详细描述。

最后,尽管MoviePy没有生动的用户界面,它也有许多方法来预览剪辑,使你能够调试脚本,从而确保你的视频在高质量的同时一切正常。详见如果更有效率地使用MoviePy

混合剪辑

视频合成,也称为非线性编辑,事实上就是把许多视频剪辑放在一起,变成一个新剪辑。这个视频就是一个很好的例子,告诉你可以用MoviePy做些什么样的合成:

原视频链接已失效

在开始之前要注意,一般来说视频剪辑都会带有音轨和遮罩,他们也同样是剪辑。当你将这些剪辑组合在一起时,音轨和剪辑的遮罩将会自动生成音轨和最终剪辑的遮罩。所以在大多数情况下,你并不必为混合音频和遮罩担心。

堆叠和连接剪辑

有两种简单的方法能将剪辑放在一起:一是连接(使它们变成一个更长的剪辑,一个接一个播放),二是堆起来(使他们并排组成画面更大的剪辑)。

使用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")

final_clip是一个剪辑,使clip 1、2和3一个接一个播放。注意,剪辑不一定必须要相同尺寸。如果各剪辑尺寸不同,那么它们将被居中播放,而画面大小足够包含最大的剪辑,而且你可以选择一种颜色来填充边界部分。你还有许多其它可选项(详见函数的文档),例如你可以通过transition=my_clip选项来在剪辑之间加一个过场。

使用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")

从而你可以获得一个看起来是这样的剪辑:

http://zulko.github.io/moviepy/_images/stacked.jpeg

合成视频剪辑

CompositeVideoClip类提供了一个非常灵活的方法来合成剪辑,但它比concatenate_videoclipsclips_array更复杂一些。

video = CompositeVideoClip([clip1,clip2,clip3])

当前video播放clip1clip2clip1的上层,而clip3clip1clip2的上层。举例来说,如果clip2clip3clip1有同样的尺寸,那么只有在顶层的clip3能在视频中可见,除非clip3clip2被遮罩隐藏了一部分。注意,在默认情况下,合成的视频和第一个剪辑的尺寸相同(因为它通常是一个背景)。但是有时,你想让你的剪辑在更大的合成视频里浮动,所以你需要像这样特意修改最终合成视频的尺寸:

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)])
剪辑的定位

如果clip2clip3都比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位置在图片的最上方:

http://zulko.github.io/moviepy/_images/videoWH.jpeg

合成音频剪辑

当你将视频剪辑混合在一起时,MoviePy将会把它们各自的音轨自动合成为最终剪辑的音轨,所以不用担心需要自行合成音轨啦。 如果你想使用许多音频源来自定义音轨,可以使用CompositeAudioClipconcatenate_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

这一节总结了一些技巧和诀窍,可以帮助你充分利用全球范围内已知的经验来使用MoviePy。

使用IPython Notebook是使用MoviePy的最好方法。它使得预览剪辑变得很容易(在此节中会介绍到),有自动完成功能,而且可以显示库里不同方法的文档。

我应该使用moviepy.editor吗?

这篇文档里的大部分示例使用了moviepy.editor子模块,但是这个子模块并不适用于所有需求。简单来说,如果你使用MoviePy手动编辑视频,那就建议使用此子模块;如果你在更大的库、更大的项目或web服务器上使用MoviePy,最好避免使用它,转而加载你需要的函数。

moviepy.editor模块能以以下三种方式加载:

from moviepy.editor import * # imports everything, quick and dirty
import moviepy.editor as mpy # Clean. Then use mpy.VideoClip, etc.
from moviepy.editor import VideoFileClip # just import what you need

通过这些代码,moviepy.editor模块会做许多幕后工作:它将获取MoviePy里最常见的类、函数和子包,初始化一个PyGame会话(在PyGame已安装的情况下)使得视频剪辑可被预览,并执行一些快捷方式,例如对剪辑添加resize变换。以这种方式,你可以使用clip.resize(width=240)来替代更长的clip.fx(resize,width=240)写法。总之,moviepy.editor提供了你所需要的播放和剪辑功能,但是需要一些时间来加载(大约1秒)。所以如果你所需要的一或两个效果在别的库里,直接将你所需要的导入是更好的选择,就像这样:

from moviepy.video.io.VideoFileClip import VideoFileClip
from moviepy.video.fx.resize import resize
预览剪辑的多种方法

当你使用MoviePy通过不断尝试来编辑视频或试图达到某种效果的时候,生成每个示例的视频的时间可能会很长。这一节提供了一些提高速度的小窍门。

clip.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
clip.show and clip.preview

使用clip.showclip.preview方法,使剪辑在Pygame的窗口中可视。这两个方法是预览的最快方法,当剪辑在生成和播放的同事,它们还可以获得坐标和像素的颜色。这些方法需要PyGame的支持,以及moviepy.editor模块的使用。

clip.show模块可以在没有写入文件的情况下预览剪辑里的一帧。如下是如何在PyGame窗口中显示一帧:

my_clip.show() # shows the first frame of the clip
my_clip.show(10.5) # shows the frame of the clip at t=10.5s
my_clip.show(10.5, interactive = True)

最后一行(有interactive=True的一行)以一种交互的形式显示一帧:如果你单击这一帧的任意一处,它将输出位置和像素的颜色。当你结束查看时,按下esc键。

如下代码可预览剪辑:

my_clip.preview() # preview with default fps=15
my_clip.preview(fps=25)
my_clip.preview(fps=15, audio=False) # don't generate/play the audio.
my_audio_clip.preview(fps=22000)

如果你单击正在预览的视频剪辑中某帧的任意一处,将输出单击的位置和像素的颜色。按esc结束预览。

注意,如果剪辑很复杂而且你的电脑速度也不够快,剪辑的预览速度将会比真实速度慢。在这种情况下,你可以尝试降低帧率(例如将为10),或者用clip.resize降低剪辑的尺寸。

ipython_display

使用IPython Notebook来显示剪辑很实用,尤其是你不想用clip.show()clip.preview()时。看起来就像这样:

http://zulko.github.io/moviepy/_images/demo_preview.jpeg

通过使用ipython_display,你可以从文件或直接从片段里潜入视频、图片或声音:

ipython_display(my_video_clip) # embeds a video
ipython_display(my_imageclip) # embeds an image
ipython_display(my_audio_clip) # embeds a sound

ipython_display("my_picture.jpeg") # embeds an image
ipython_display("my_video.mp4") # embeds a video
ipython_display("my_sound.mp3") # embeds a sound

只有当ipython_display在一个笔记本单元的最后一行才有效。你也可以调用ipython_display作为一个剪辑方法:

my_video_clip.ipython_display()

如果对所呈现的视频的帧率有所要求,你可以在ipython_display里指定fps=25

如果你只需要播放一个视频剪辑在时间t的快照,可以这样写:

my_video_clip.ipython_display(t=15) # will display a snapshot at t=15s

你还可以提供有效的HTML5选项作为关键词的参数。例如,如果剪辑太大,你可以这么写:

ipython_display(my_clip, width=400) # HTML5 will resize to 400 pixels

例如,当你编辑一个GIF动画,而且想检查它是否正确循环,你可以使视频自动启动和循环(即无限重复):

ipython_display(my_clip, autoplay=1, loop=1)

重要的是,ipython_display实际上是将剪辑嵌入了你的笔记本里。优势在于你可以移动笔记本或将其放在网络上发布,视频将会运行。缺陷在于笔记本的文件尺寸会变得非常巨大。根据你的浏览器,多次重新计算和播放视频会在cache和RAM中占用一些空间(仅当密集使用时出现此问题)。重启浏览器即可解决问题。

使用matplotlib

自定义动画

MoviePy提供了一种生成自定义动画的方式:通过定义一个函数,以numpy数组的形式返回一个在给定的时间内一帧的动画。

例如,在如下的工作流中:

from moviepy.editor import VideoClip

def make_frame(t):
    """Returns an image of the frame for time t."""
    # ... create the frame with any library here ...
    return frame_for_time_t # (Height x Width x 3) Numpy array

animation = VideoClip(make_frame, duration=3) # 3-second clip

这个动画可以像通常一样导出:

# export as a video file
animation.write_videofile("my_animation.mp4", fps=24)
# export as a GIF
animation.write_gif("my_animation.gif", fps=24) # usually slower
matplotlib的简易实例

一个使用matplotlib的动画实例如下:

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('matplotlib.gif', fps=20)
在Jupyter Notebook中运行

如果你在Jupyter Notebook中运行,你可以体会到用ipython_display方法将视频剪辑嵌入在输出单元中的优势。示例如下:

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.ipython_display(fps=20, loop=True, autoplay=True)

MoviePy的音频操作

这个章节会告诉你如何使用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时的一帧。它们分为两类,一种是动态剪辑(由VideoFileClipVideoClip组成),另一种是非动态剪辑,在任意无限长的持续时间中展示同一图片(ImageClipTextClipColorClip)。还有一种特殊的视频剪辑叫做遮罩,同属于上面的类别,但是输出灰度值帧以表示另一个剪辑里的可见部分。一个视频剪辑可以携带一个音频剪辑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_gifwrite_videofile,以及需要遍历每一帧的方法提供帧速率(fps,每秒的帧数)。

VideoFileClip

视频文件剪辑是由视频文件(支持大多数格式)或GIF文件中读取的剪辑。你可以像这样加载一个视频:

myclip = VideoFileClip("some_video.avi")
myclip = VideoFileClip("some_animation.gif")

注意,这些剪辑都有fps(帧速率)属性,当你对剪辑做小改动时,帧速率将会被传送,而且会以write_videofilewrite_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

详见VideoFileClip

ImageSequenceClip

这是一种由一系列图片组成的剪辑,你可以这样调用:

clip = ImageSequenceClip(images_list, fps=25)

作品展示

脚本示例

移动的字母

原文链接:http://zulko.github.io/moviepy/examples/moving_letters.html

我认为这个例子很好地展示了基于脚本编辑的有趣之处(想象是手动实现它的)。

视频地址:https://youtu.be/jj5qrHl5ZS0

代码如下:

import numpy as np
from moviepy.editor import *
from moviepy.video.tools.segmenting import findObjects

# WE CREATE THE TEXT THAT IS GOING TO MOVE, WE CENTER IT.

screensize = (720,460)
txtClip = TextClip('Cool effect',color='white', font="Amiri-Bold",
                   kerning = 5, fontsize=100)
cvc = CompositeVideoClip( [txtClip.set_pos('center')],
                        size=screensize)

# THE NEXT FOUR FUNCTIONS DEFINE FOUR WAYS OF MOVING THE LETTERS


# helper function
rotMatrix = lambda a: np.array( [[np.cos(a),np.sin(a)], 
                                 [-np.sin(a),np.cos(a)]] )

def vortex(screenpos,i,nletters):
    d = lambda t : 1.0/(0.3+t**8) #damping
    a = i*np.pi/ nletters # angle of the movement
    v = rotMatrix(a).dot([-1,0])
    if i%2 : v[1] = -v[1]
    return lambda t: screenpos+400*d(t)*rotMatrix(0.5*d(t)*a).dot(v)
    
def cascade(screenpos,i,nletters):
    v = np.array([0,-1])
    d = lambda t : 1 if t<0 else abs(np.sinc(t)/(1+t**4))
    return lambda t: screenpos+v*400*d(t-0.15*i)

def arrive(screenpos,i,nletters):
    v = np.array([-1,0])
    d = lambda t : max(0, 3-3*t)
    return lambda t: screenpos-400*v*d(t-0.2*i)
    
def vortexout(screenpos,i,nletters):
    d = lambda t : max(0,t) #damping
    a = i*np.pi/ nletters # angle of the movement
    v = rotMatrix(a).dot([-1,0])
    if i%2 : v[1] = -v[1]
    return lambda t: screenpos+400*d(t-0.1*i)*rotMatrix(-0.2*d(t)*a).dot(v)



# WE USE THE PLUGIN findObjects TO LOCATE AND SEPARATE EACH LETTER

letters = findObjects(cvc) # a list of ImageClips


# WE ANIMATE THE LETTERS

def moveLetters(letters, funcpos):
    return [ letter.set_pos(funcpos(letter.screenpos,i,len(letters)))
              for i,letter in enumerate(letters)]

clips = [ CompositeVideoClip( moveLetters(letters,funcpos),
                              size = screensize).subclip(0,5)
          for funcpos in [vortex, cascade, arrive, vortexout] ]

# WE CONCATENATE EVERYTHING AND WRITE TO A FILE

final_clip = concatenate_videoclips(clips)
final_clip.write_videofile('../../coolTextEffects.avi',fps=25,codec='mpeg4')

15世纪舞蹈的重建

原文链接:http://zulko.github.io/moviepy/examples/dancing_knights.html

视频地址:https://youtu.be/Qu7HJrsEYFg

# -*- coding: utf-8 -*-

"""
Result: https://www.youtube.com/watch?v=Qu7HJrsEYFg

This is how we can imagine knights dancing at the 15th century, based on a very
serious historical study here: https://www.youtube.com/watch?v=zvCvOC2VwDc

Here is what we do:

0- Get the video of a dancing knight, and a (Creative Commons) audio music file.
1- load the audio file and automatically find the tempo
2- load the video and automatically find a segment that loops well
3- extract this segment, slow it down so that it matches the audio tempo,
   and make it loop forever.
4- Symmetrize this segment so that we will get two knights instead of one
5- Add a title screen and some credits, write to a file.


This example has been originally edited in an IPython Notebook, which makes it
easy to preview and fine-tune each part of the editing.
"""

from moviepy.editor import *
from moviepy.video.tools.cuts import find_video_period
from moviepy.audio.tools.cuts import find_audio_period


# Next lines are for downloading the required videos from Youtube.
# To do this you must have youtube-dl installed, otherwise you will need to
# download the videos by hand and rename them, as follows:
#     https://www.youtube.com/watch?v=zvCvOC2VwDc => knights.mp4
#     https://www.youtube.com/watch?v=lkY3Ek9VPtg => frontier.mp4

import os
if not os.path.exists("knights.mp4"):
    os.system("youtube-dl zvCvOC2VwDc -o knights.mp4")
    os.system("youtube-dl lkY3Ek9VPtg -o frontier.mp4")
#==========


# LOAD, EDIT, ANALYZE THE AUDIO


audio = (AudioFileClip("frontier.mp4")
         .subclip((4,7), (4,18))
         .audio_fadein(1)
         .audio_fadeout(1))

audio_period = find_audio_period(audio)
print ('Analyzed the audio, found a period of %.02f seconds'%audio_period)


# LOAD, EDIT, ANALYZE THE VIDEO


clip = (VideoFileClip("./knights.mp4", audio=False)
        .subclip((1,24.15),(1,26))
        .crop(x1=332, x2=910, y2=686))

video_period = find_video_period(clip, tmin=.3)
print ('Analyzed the video, found a period of %.02f seconds'%video_period)

edited_right = (clip.subclip(0,video_period)
                .speedx(final_duration=2*audio_period)
                .fx(vfx.loop, duration=audio.duration)
                .subclip(.25))

edited_left = edited_right.fx(vfx.mirror_x)

dancing_knights = (clips_array([[edited_left, edited_right]])
                   .fadein(1).fadeout(1).set_audio(audio).subclip(.3))

# MAKE THE TITLE SCREEN


txt_title = (TextClip("15th century dancing\n(hypothetical)", fontsize=70,
               font="Century-Schoolbook-Roman", color="white")
             .margin(top=15, opacity=0)
             .set_position(("center","top")))

title = (CompositeVideoClip([dancing_knights.to_ImageClip(), txt_title])
         .fadein(.5)
         .set_duration(3.5))


# MAKE THE CREDITS SCREEN


txt_credits = """
CREDITS

Video excerpt: Le combat en armure au XVe siècle
By J. Donzé, D. Jaquet, T. Schmuziger,
Université de Genève, Musée National de Moyen Age

Music: "Frontier", by DOCTOR VOX
Under licence Creative Commons
https://www.youtube.com/user/DOCTORVOXofficial

Video editing © Zulko 2014
 Licence Creative Commons (CC BY 4.0)
Edited with MoviePy: http://zulko.github.io/moviepy/
"""

credits = (TextClip(txt_credits, color='white',
            font="Century-Schoolbook-Roman", fontsize=35, kerning=-2,
            interline=-1, bg_color='black', size=title.size)
          .set_duration(2.5)
          .fadein(.5)
          .fadeout(.5))


# ASSEMBLE EVERYTHING, WRITE TO FILE

final = concatenate_videoclips([title, dancing_knights, credits])

final.write_videofile("dancing_knights.mp4", fps=clip.fps,
                      audio_bitrate="1000k", bitrate="4000k")

一个简单的音乐视频

原文链接:http://zulko.github.io/moviepy/examples/ukulele_concerto.html

视频地址:https://youtu.be/AqGZ4JFkQTU

这只是一个没有声音的示例(对于一个音乐视频来说是不完整的),我将用一个带有真实音乐的视频示例来取代它(99%的代码将一致)。MoviePy的哲学就是,我每做一个新视频,我就会复制粘贴这些代码,然后修改其中的几行。

from moviepy.editor import *

# UKULELE CLIP, OBTAINED BY CUTTING AND CROPPING
# RAW FOOTAGE

ukulele = VideoFileClip("../../videos/moi_ukulele.MOV", audio=False).\
               subclip(60+33, 60+50).\
               crop(486, 180, 1196, 570)

w,h = moviesize = ukulele.size

# THE PIANO FOOTAGE IS DOWNSIZED, HAS A WHITE MARGIN, IS
# IN THE BOTTOM RIGHT CORNER 

piano = (VideoFileClip("../../videos/douceamb.mp4",audio=False).
         subclip(30,50).
         resize((w/3,h/3)).    # one third of the total screen
         margin( 6,color=(255,255,255)).  #white margin
         margin( bottom=20, right=20, opacity=0). # transparent
         set_pos(('right','bottom')) )



# A CLIP WITH A TEXT AND A BLACK SEMI-OPAQUE BACKGROUND

txt = TextClip("V. Zulkoninov - Ukulele Sonata", font='Amiri-regular',
	               color='white',fontsize=24)

txt_col = txt.on_color(size=(ukulele.w + txt.w,txt.h-10),
                  color=(0,0,0), pos=(6,'center'), col_opacity=0.6)


# THE TEXT CLIP IS ANIMATED.
# I am *NOT* explaining the formula, understands who can/want.
txt_mov = txt_col.set_pos( lambda t: (max(w/30,int(w-0.5*w*t)),
                                  max(5*h/6,int(100*t))) )



# FINAL ASSEMBLY
final = CompositeVideoClip([ukulele,txt_mov,piano])

一个音频示例

原文链接:http://zulko.github.io/moviepy/examples/example_with_sound.html

这里是一个使用MoviePy将带有声音的视频剪辑组合在一起的例子。这里是将两个场景放在一起:

视频地址:https://youtu.be/gtyFuIoH7W0

代码如下:

"""
Description of the video:
The screen is split in two parts showing Carry and Audrey at the phone,
talking at the same time, because it is actually two scenes of a same
movie put together.
"""

from moviepy.editor import *
from moviepy.video.tools.drawing import color_split


duration = 6 # duration of the final clip

# LOAD THE MAIN SCENE
# this small video contains the two scenes that we will put together.

main_clip = VideoFileClip("../../videos/charadePhone.mp4")
W,H = main_clip.size



# MAKE THE LEFT CLIP : cut, crop, add a mask 
                            
mask = color_split((2*W/3,H),
                   p1=(W/3,H), p2=(2*W/3,0),
                   col1=1, col2=0,
                   grad_width=2)
                   
mask_clip = ImageClip(mask, ismask=True)
                   
clip_left = (main_clip.coreader()
                      .subclip(0,duration)
                      .crop( x1=60, x2=60 + 2*W/3)
                      .set_mask(mask_clip))


# MAKE THE RIGHT CLIP : cut, crop, add a mask 
                   
mask = color_split((2*W/3,H),
                   p1=(2,H), p2=(W/3+2,0),
                   col1=0, col2=1,
                   grad_width=2)

mask_clip = ImageClip(mask, ismask=True)

clip_right = (main_clip.coreader()
                       .subclip(21,21+duration)
                       .crop(x1=70, x2=70+2*W/3)
                       .set_mask(mask_clip))




# ASSEMBLE AND WRITE THE MOVIE TO A FILE

cc = CompositeVideoClip([clip_right.set_pos('right').volumex(0.4),
                         clip_left.set_pos('left').volumex(0.4)],
                         size = (W,H))
#cc.preview()
cc.write_videofile("../../biphone3.avi",fps=24, codec='mpeg4')

星战式的开场标题

原文链接:http://zulko.github.io/moviepy/examples/star_worms.html

这是一个近似的效果(透视会需要一些更复杂的转换),但是可以用来练手。

警告:剪辑带有声音。

让我们来看看这个教程,它展示了不同的步骤:

视频地址:https://youtu.be/5euLdo8L0o0

代码如下:

"""
Description of the video:
Mimic of Star Wars' opening title. A text with a (false)
perspective effect goes towards the end of space, on a
background made of stars. Slight fading effect on the text.

"""

import numpy as np
from skimage import transform as tf

from moviepy.editor import *
from moviepy.video.tools.drawing import color_gradient


# RESOLUTION

w = 720
h = w*9/16 # 16/9 screen
moviesize = w,h



# THE RAW TEXT
txt = "\n".join([
"A long time ago, in a faraway galaxy,",
"there lived a prince and a princess",
"who had never seen the stars, for they",
"lived deep underground.",
"",
"Many years before, the prince's",
"grandfather had ventured out to the",
"surface and had been burnt to ashes by",
"solar winds.",
"",
"One day, as the princess was coding",
"and the prince was shopping online, a",
"meteor landed just a few megameters",
"from the couple's flat."
])


# Add blanks
txt = 10*"\n" +txt + 10*"\n"


# CREATE THE TEXT IMAGE


clip_txt = TextClip(txt,color='white', align='West',fontsize=25,
                    font='Xolonium-Bold', method='label')


# SCROLL THE TEXT IMAGE BY CROPPING A MOVING AREA

txt_speed = 27
fl = lambda gf,t : gf(t)[int(txt_speed*t):int(txt_speed*t)+h,:]
moving_txt= clip_txt.fl(fl, apply_to=['mask'])


# ADD A VANISHING EFFECT ON THE TEXT WITH A GRADIENT MASK

grad = color_gradient(moving_txt.size,p1=(0,2*h/3),
                p2=(0,h/4),col1=0.0,col2=1.0)
gradmask = ImageClip(grad,ismask=True)
fl = lambda pic : np.minimum(pic,gradmask.img)
moving_txt.mask = moving_txt.mask.fl_image(fl)


# WARP THE TEXT INTO A TRAPEZOID (PERSPECTIVE EFFECT)

def trapzWarp(pic,cx,cy,ismask=False):
    """ Complicated function (will be latex packaged as a fx) """
    Y,X = pic.shape[:2]
    src = np.array([[0,0],[X,0],[X,Y],[0,Y]])
    dst = np.array([[cx*X,cy*Y],[(1-cx)*X,cy*Y],[X,Y],[0,Y]])
    tform = tf.ProjectiveTransform()
    tform.estimate(src,dst)
    im = tf.warp(pic, tform.inverse, output_shape=(Y,X))
    return im if ismask else (im*255).astype('uint8')

fl_im = lambda pic : trapzWarp(pic,0.2,0.3)
fl_mask = lambda pic : trapzWarp(pic,0.2,0.3, ismask=True)
warped_txt= moving_txt.fl_image(fl_im)
warped_txt.mask = warped_txt.mask.fl_image(fl_mask)


# BACKGROUND IMAGE, DARKENED AT 60%

stars = ImageClip('../../videos/stars.jpg')
stars_darkened = stars.fl_image(lambda pic: (0.6*pic).astype('int16'))


# COMPOSE THE MOVIE

final = CompositeVideoClip([
         stars_darkened,
         warped_txt.set_pos(('center','bottom'))],
         size = moviesize)


# WRITE TO A FILE

final.set_duration(8).write_videofile("starworms.avi", fps=5)

# This script is heavy (30s of computations to render 8s of video)



"""=====================================================================

    CODE FOR THE VIDEO TUTORIAL

  We will now code the video tutorial for this video.
  When you think about it, it is a code for a video explaining how to
  make another video using some code (this is so meta !).
  This code uses the variables of the previous code (it should be placed
  after that previous code to work).

====================================================================="""



def annotate(clip,txt,txt_color='white',bg_color=(0,0,255)):
    """ Writes a text at the bottom of the clip. """
    
    txtclip = TextClip(txt, fontsize=20, font='Ubuntu-bold',
                       color=txt_color)
                       
    txtclip = txtclip.on_color((clip.w,txtclip.h+6), color=(0,0,255),
                        pos=(6,'center'))
                        
    cvc =  CompositeVideoClip([clip , txtclip.set_pos((0,'bottom'))])
    
    return cvc.set_duration(clip.duration)


def resizeCenter(clip):
    return clip.resize( height=h).set_pos('center')

    
def composeCenter(clip):
    return CompositeVideoClip([clip.set_pos('center')],size=moviesize)



annotated_clips = [ annotate(clip,text) for clip,text in [
               
(composeCenter(resizeCenter(stars)).subclip(0,3),
    "This is a public domain picture of stars"),

(CompositeVideoClip([stars],moviesize).subclip(0,3),
    "We only keep one part."),

(CompositeVideoClip([stars_darkened],moviesize).subclip(0,3),
    "We darken it a little."),

(composeCenter(resizeCenter(clip_txt)).subclip(0,3),
    "We generate a text image."),

(composeCenter(moving_txt.set_mask(None)).subclip(6,9),
    "We scroll the text by cropping a moving region of it."),

(composeCenter(gradmask.to_RGB()).subclip(0,2),
    "We add this mask to the clip."),

(composeCenter(moving_txt).subclip(6,9),
    "Here is the result"),

(composeCenter(warped_txt).subclip(6,9),
    "We now warp this clip in a trapezoid."),

(final.subclip(6,9),
    "We finally superimpose with the stars.")
]]

# Concatenate and write to a file

concatenate_videoclips(annotated_clips).write_videofile('tutorial.avi', fps=5)

部分隐藏的演职人员表

原文链接:http://zulko.github.io/moviepy/examples/masked_credits.html

视频地址:https://youtu.be/NsTgBah6Ebk

在看这个视频的代码之前,这里有一个讲解了不同步骤的教程视频(也是由MoviePy制作的):

视频地址:https://youtu.be/7tKABfc0Yzw

代码如下:

from moviepy.editor import *
from moviepy.video.tools.credits import credits1

# Load the mountains clip, cut it, slow it down, make it look darker
clip = (VideoFileClip('../../videos/badl-0001.mov', audio=False)
           .subclip(37,46)
           .speedx( 0.4)
           .fx( vfx.colorx, 0.7))

# Save the first frame to later make a mask with GIMP (only once)
#~ clip.save_frame('../../credits/mountainMask2.png')


# Load the mountain mask made with GIMP
mountainmask = ImageClip('../../credits/mountainMask2.png',ismask=True)

# Generate the credits from a text file
credits = credits1('../../credits/credits.txt',3*clip.w/4)
scrolling_credits = credits.set_pos(lambda t:('center',-10*t))


# Make the credits scroll. Here, 10 pixels per second
final = CompositeVideoClip([clip,
                            scrolling_credits,
                            clip.set_mask(mountainmask)])
                            
final.subclip(8,10).write_videofile("../../credits_mountains.avi")

以绘画效果冻结帧

原文链接:http://zulko.github.io/moviepy/examples/painting_effect.html

这是一种我们经常在西部片之类看到的效果:

视频地址:https://youtu.be/aC5CifkacSI

这个方法可以使照片看起来像绘画:

  • 使用Sobel算法对图片进行边缘检测,从而获得一张看起来像黑白手绘的照片。
  • 叠加图像的数组使颜色更加华丽,并添加在上一步骤中获得的轮廓。

最终剪辑将会把三个部分联结起来:特效之前的部分,带有特效的部分和特效之后的部分。带有特效的部分以以下方式获得:

  • 选择一帧进行冻结,使之转为绘画效果,并使其成为一个图像剪辑。
  • 添加文字剪辑“Audrey”至图像剪辑。
  • 将修改过的图像剪辑添加至原帧的顶层,但给其添加淡入淡出效果。

代码如下:

""" requires scikit-image installed (for vfx.painting) """
 
from moviepy.editor import *

# WE TAKE THE SUBCLIPS WHICH ARE 2 SECONDS BEFORE & AFTER THE FREEZE

charade = VideoFileClip("../../videos/charade.mp4")
tfreeze = cvsecs(19.21) # Time of the freeze, 19'21

# when using several subclips of a same clip, it can be faster
# to create 'coreaders' of the clip (=other entrance points).
clip_before = charade.coreader().subclip(tfreeze -2,tfreeze)
clip_after = charade.coreader().subclip(tfreeze ,tfreeze +2)


# THE FRAME TO FREEZE

im_freeze = charade.to_ImageClip(tfreeze)
painting = (charade.fx( vfx.painting, saturation = 1.6,black = 0.006)
                   .to_ImageClip(tfreeze))
                 
txt = TextClip('Audrey',font='Amiri-regular',fontsize=35)

painting_txt = (CompositeVideoClip([painting,txt.set_pos((10,180))])
                   .add_mask()
                   .set_duration(3)
                   .crossfadein( 0.5)
                   .crossfadeout( 0.5))

# FADEIN/FADEOUT EFFECT ON THE PAINTED IMAGE

painting_fading = CompositeVideoClip([im_freeze,painting_txt])

# FINAL CLIP AND RENDERING

final_clip =  concatenate_videoclips([ clip_before,
                            painting_fading.set_duration(3),
                            clip_after])

final_clip.write_videofile('../../audrey.avi',fps=charade.fps,
                        codec = "mpeg4", audio_bitrate="3000k")

根据图片放置剪辑

原文链接:http://zulko.github.io/moviepy/examples/compo_from_image.html

你想做一些像这样复杂的合成吗?

视频地址:https://youtu.be/1hdgNxX-tas

在这个脚本里,我们将使用这个由Inkscape生成的图片:

http://zulko.github.io/moviepy/_images/compo_from_image.jpeg

我们将会发现这张图片的区域和不同的剪辑适配这些区域:

from moviepy.editor import *
from moviepy.video.tools.segmenting import findObjects

# Load the image specifying the regions.
im = ImageClip("../../ultracompositing/motif.png")

# Loacate the regions, return a list of ImageClips
regions = findObjects(im)


# Load 7 clips from the US National Parks. Public Domain :D
clips = [VideoFileClip(n, audio=False).subclip(18,22) for n in
     [ "../../videos/romo_0004.mov",
      "../../videos/apis-0001.mov",
      "../../videos/romo_0001.mov",
      "../../videos/elma_s0003.mov",
      "../../videos/elma_s0002.mov",
      "../../videos/calo-0007.mov",
      "../../videos/grsm_0005.mov"]]

# fit each clip into its region
comp_clips =  [c.resize(r.size)
                .set_mask(r.mask)
                .set_pos(r.screenpos)
               for c,r in zip(clips,regions)]

cc = CompositeVideoClip(comp_clips,im.size)
cc.resize(0.6).write_videofile("../../composition.mp4")

# Note that this particular composition takes quite a long time of
# rendering (about 20s on my computer for just 4s of video).

(注意,有些照片的尺寸是扭曲的,无法照顾到他们的宽高比。这可以由更多的行数改变)

人脸追踪与模糊

原文链接: http://zulko.github.io/moviepy/examples/headblur.html



视频地址:https://youtu.be/FWCKYTRCrBI



首先,为了实现人脸追踪与模糊的目的,我们需要对面部进行追踪。 例如,我们可以获得两个方法fx以及fy,这样的话(fx(t),fy(t))就表示了在时间t时刻,头部中心的位置。这个步骤可以很容易使用 manual_tracking 实现。 接下来我们则需要对头部中心区域附近的视频进行模糊。



代码如下:

import pickle

from moviepy.editor import *
from moviepy.video.tools.tracking import manual_tracking, to_fxfy


# LOAD THE CLIP (subclip 6'51 - 7'01 of a chaplin movie)
clip = VideoFileClip("../../videos/chaplin.mp4").subclip((6,51.7),(7,01.3))

# MANUAL TRACKING OF THE HEAD

# the three next lines are for the manual tracking and its saving
# to a file, it must be commented once the tracking has been done
# (after the first run of the script for instance).
# Note that we save the list (ti,xi,yi), not the functions fx and fy
# (that we will need) because they have dependencies.

#txy, (fx,fy) = manual_tracking(clip, fps=6)
#with open("../../chaplin_txy.dat",'w+') as f:
#    pickle.dump(txy)



# IF THE MANUAL TRACKING HAS BEEN PREVIOUSLY DONE,
# LOAD THE TRACKING DATA AND CONVERT IT TO FUNCTIONS x(t),fy(t)

with open("../../chaplin_txy.dat",'r') as f:
    fx,fy = to_fxfy( pickle.load(f) )


# BLUR CHAPLIN'S HEAD IN THE CLIP

clip_blurred = clip.fx( vfx.headblur, fx, fy, 25)


# Generate the text, put in on a grey background

txt = TextClip("Hey you ! \n You're blurry!", color='grey70',
               size = clip.size, bg_color='grey20',
               font = "Century-Schoolbook-Italic", fontsize=40)
               
               
# Concatenate the Chaplin clip with the text clip, add audio

final = concatenate_videoclips([clip_blurred,txt.set_duration(3)]).\
          set_audio(clip.audio)

# We write the result to a file. Here we raise the bitrate so that
# the final video is not too ugly.

final.write_videofile('../../blurredChaplin.avi', bitrate="3000k")

快捷方式

特效与滤波
模糊视频中的所有帧
from skimage.filter import gaussian_filter
from moviepy.editor import VideoFileClip

def blur(image):
    """ Returns a blurred (radius=2 pixels) version of the image """
    return gaussian_filter(image.astype(float), sigma=2)

clip = VideoFileClip("my_video.mp4")
clip_blurred = clip.fl_image( blur )
clip_blurred.write_videofile("blurred_video.mp4")
剪切视频
场景侦测
视频合成
给视频添加标题
gif动图制作艺术
无用却有趣的操作
获取视频的平均帧
from moviepy.editor import VideoFileClip, ImageClip
clip = VideoFileClip("video.mp4")
fps= 1.0 # take one frame per second
nframes = clip.duration*fps # total number of frames used
total_image = sum(clip.iter_frames(fps,dtype=float,progress_bar=True))
average_image = ImageClip(total_image/ nframes)
average_image.save_frame("average_test.png")

部分隐藏的演职人员表

原文链接: http://zulko.github.io/moviepy/examples/several_characters.html



视频地址:https://youtu.be/sZMyzzGlsc0

在阅读这个视频的代码之前,这有个解释了不同步骤的教程视频(也是由MoviePy制作的):

视频地址:https://youtu.be/7tKABfc0Yzw

代码如下:

from moviepy.editor import *
from moviepy.video.tools.credits import credits1

# Load the mountains clip, cut it, slow it down, make it look darker
clip = (VideoFileClip('../../videos/badl-0001.mov', audio=False)
           .subclip(37,46)
           .speedx( 0.4)
           .fx( vfx.colorx, 0.7))

# Save the first frame to later make a mask with GIMP (only once)
#~ clip.save_frame('../../credits/mountainMask2.png')


# Load the mountain mask made with GIMP
mountainmask = ImageClip('../../credits/mountainMask2.png',ismask=True)

# Generate the credits from a text file
credits = credits1('../../credits/credits.txt',3*clip.w/4)
scrolling_credits = credits.set_pos(lambda t:('center',-10*t))


# Make the credits scroll. Here, 10 pixels per second
final = CompositeVideoClip([clip,
                            scrolling_credits,
                            clip.set_mask(mountainmask)])
                            
final.subclip(8,10).write_videofile("../../credits_mountains.avi")

"THE-END"特效

原文链接: http://zulko.github.io/moviepy/examples/the_end.html



视频地址:https://youtu.be/sZMyzzGlsc0


让我们来看下这个:

这里这个片段有一个“The End”写在画面的中间,而在这个片段之上,才是真正的视频。真正的视频拥有一个遮罩,其对应一个白色(不透明)圆圈在黑色(透明)背景上。

在视频的最开始,白色圆圈十分的大,以致我们看到的全部是真正的视频,而没有看到“The End”的片段。接着,这个被色圆圈逐渐缩小,因此我们看的真正的视频画面越来越小,“The End”的画面则逐渐出现。



代码如下:

from moviepy.editor import *
from moviepy.video.tools.drawing import circle

clip = VideoFileClip("../../videos/badl-0006.mov", audio=False).\
           subclip(26,31).\
           add_mask()
           
w,h = clip.size

# The mask is a circle with vanishing radius r(t) = 800-200*t               
clip.mask.get_frame = lambda t: circle(screensize=(clip.w,clip.h),
                                       center=(clip.w/2,clip.h/4),
                                       radius=max(0,int(800-200*t)),
                                       col1=1, col2=0, blur=4)


the_end = TextClip("The End", font="Amiri-bold", color="white",
                   fontsize=70).set_duration(clip.duration)

final = CompositeVideoClip([the_end.set_pos('center'),clip],
                           size =clip.size)
                           
final.write_videofile("../../theEnd.avi")

MoviePy Docker

原文链接: http://zulko.github.io/moviepy/docker.html

前提条件

  1. 首先确保好正确安装了Docker Docker安装地址:Docker for Mac, Docker for windows, linux, etc
  2. 编译Dockerfile
docker build -t moviepy -f Dockerfile

从Docker运行git源测试的步骤

  1. 首先在moviepy容器内自定义bash提示符
cd tests
docker run -it -v `pwd`:/tests moviepy bash
  1. 运行测试
cd tests
python test_issues.py

从Docker运行你自己的moviepy脚本

将路径改变到你的脚本所在的地方

如果moviepy的Docker已经在运行中,你可以通过如下代码连接:

docker exec -it moviepy python myscript.py

如果Docker还没有在运行

docker run -it moviepy bash
python myscript.py

你同样可以通过一行代码新建一个容器并运行你的代码

docker run -it -v `pwd`:/code moviepy python myscript.py

听说你想安装OpenCV 2.4.6?

原文链接: http://zulko.github.io/moviepy/opencv_instructions.html

OpenCV对于moviepy来说并不是特别推荐安装,他的安装过程并不总是简单,而且我发现其十分不稳定。若要使用,使用时请额外小心!

安装过程对于Windows来说似乎比较简单,而在Linux上,这是我在网上找到的教程:

  • 如果你之前通过包管理器安装过OpenCV,请在继续之前先移除任何版本的OpenCV;
  • 将OpenCV的源码解压到任意目录下,并在该目录下启动终端;
  • 建立一个新的目录并进入该目录:
mkdir release
cd release
  • 运行cmake。这是我所使用的命令:
cmake -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=OFF -D INSTALL_C_EXAMPLES=ON -D INSTALL_PYTHON_EXAMPLES=ON -D BUILD_EXAMPLES=ON ..
  • 运行make。这个过程可能需要几分钟(在我的电脑上大约用了15分钟)。
make
  • 最后,进行安装。
sudo make install

大功告成!

你可以通过打开python控制台并输入如下代码来检查OpenCV是否正常运行:

import cv2
print cv2.__version__

建议

请不要删除你的release文件夹。如果以后你碰到了跟.so文件相关的OpenCV奇怪的bug, 可以直接重做sudo make install这一步。

常见问题及其解决

原文链接: http://zulko.github.io/moviepy/FAQ.html

这个部分会随着MoviePy的进一步更新逐渐进行补充,目前的规划包括:MoviePy Studio, MoviePy WebApp, MoviePy OS, MoviePy Trust Inc., and the MoviePy 基金会。

并不是bug的常见错误

下面是一些并未被考虑为bug的常见错误(但你依然可以请求改变)。如果这些答案对你并不实用,请在GitHub上提交一个bug报告,或者提交到Reddit上专门的论坛,亦或者是librelist

MoviePy生成的视频并不能被我最喜欢的视频播放器所播放

已知问题:视频分辨率的某一维度不是偶数,例如:720*405;同时你使用了MPEG4编码器例,如libx264(MoviePy中默认)。在这种情况下,生成出来的视频的格式将会只能被某些播放器读取,例如VLC。

我似乎不能用MoviePy读取任何视频

已知问题:你应该使用了不被推荐的FFMPEG版本,请从网站安装最近的版本,而不是从你系统本身的仓库中安装!(详见 下载与安装

预览视频使得他们变得更慢了

这说明你的系统配置不足以实时渲染视频片段。这种情况下,请不要犹豫调整preview这个选项:例如,降低声音与食品片段的fps(11000 Hz依然足够好了)。同时,使用resize降低视频的大小也有帮助。

高级工具

参考手册

这个文档现在看来可能有些凌乱,之后它会被重新整理。 如果你想要查看源码或定位到一个特定的函数,请查阅MoviePy 团队项目

  • Clip 剪辑
    • clip 剪辑
  • Classes of Video Clips 视频剪辑的类型
    • VideoClip 视频剪辑
      • VideoFileClip 视频文件剪辑
      • ImageClip 图片剪辑
      • ColorClip 颜色剪辑
      • TextClip 文字剪辑
      • CompositeVideoClip 合成视频剪辑
  • AudioClip 音频剪辑
    • AudioClip 音频剪辑
    • AudioFileClip 音频文件剪辑
    • CompositeAudioClip 合成视频剪辑
  • moviepy.video.fx (vfx)
    • moviepy.video.fx.all.accel_decel
    • moviepy.video.fx.all.blackwhite
    • moviepy.video.fx.all.blink
    • moviepy.video.fx.all.colorx
    • moviepy.video.fx.all.crop
    • moviepy.video.fx.all.even_size
    • moviepy.video.fx.all.fadein
    • moviepy.video.fx.all.fadeout
    • moviepy.video.fx.all.freeze
    • moviepy.video.fx.all.freeze_region
    • moviepy.video.fx.all.gamma_corr
    • moviepy.video.fx.all.headblur
    • moviepy.video.fx.all.invert_colors
    • moviepy.video.fx.all.loop
    • moviepy.video.fx.all.lum_contrast
    • moviepy.video.fx.all.make_loopable
    • moviepy.video.fx.all.margin
    • moviepy.video.fx.all.mask_and
    • moviepy.video.fx.all.mask_color
    • moviepy.video.fx.all.mask_or
    • moviepy.video.fx.all.mirror_x
    • moviepy.video.fx.all.mirror_y
    • moviepy.video.fx.all.painting
    • moviepy.video.fx.all.resize
    • moviepy.video.fx.all.rotate
    • moviepy.video.fx.all.scroll
    • moviepy.video.fx.all.speedx
    • moviepy.video.fx.all.supersample
    • moviepy.video.fx.all.time_mirror
    • moviepy.video.fx.all.time_symmetrize
  • audio.fx
    • moviepy.audio.fx.all.audio_fadein
    • moviepy.audio.fx.all.audio_fadeout
    • moviepy.audio.fx.all.audio_loop
    • moviepy.audio.fx.all.volumex
  • video.tools
    • Credits 演职人员表
    • Drawing 绘图
    • Segmenting 分割
    • Subtitles 字幕
    • Tracking 追踪
  • audio.tools
  • FFMPEG tools
  • Decorators 装饰器
  • Organization of MoviePy’s code

贡献

MoviePy是一个开源软件,原作者为Zulko,并根据MIT licence发行。它可以在Windows、Mac和Linux环境中以Python2或Python3运行。代码托管于Github,你可以在此提交改进、报告bug及寻求帮助。在Reddit上有MoviePy的论坛,在librelist上也有邮件列表。

汉化说明

此汉化版本由 APhun`WYR`_Fan Xiao 共同维护。