阅读本文档的前置说明:
本文档用于讲解Python的moviepy库的自带函数的用法,主要目的是讲一下每个函数的每个参数的含义,无需一开始就全部掌握,粗略看一下就行,可以在后面自己开发过程,遇到不会用的函数再回过头来看看本文档,我将在后续的文章中,通过几下实际的案例来理解视频特效的开发流程。
MoviePy 是一个用于视频编辑的 Python 库,使用户能够处理、编辑和操作视频文件。这个库允许你剪辑视频、添加文本、合并视频剪辑,以及应用各种效果和转换。它建立在 NumPy、imageio 和 Decorator 等库的基础上,使得在处理视频时能够更加高效。
下面是一些 MoviePy 的主要功能和特点:
可以通过 pip 来安装 MoviePy,命令如下:
pip install moviepy
mask可以译为遮罩、遮片、蒙版,后面本文都称为遮罩。
mask遮罩是一种特殊的视频剪辑,每像素只有一个0-1之间的值(1表示完全可见的像素,0表示透明的像素)mask遮罩可以决定对应视频剪辑对应帧哪些像素可见、哪些不可见。
带mask遮罩的剪辑在导出为GIF或PNG图像时,用于定义图像的透明度。
标准剪辑输出的帧每像素包含RGB的3个0-255之间的值,而遮罩剪辑每像素只有一个0-1之间的值。
RGB:RGB代表红色(Red)、绿色(Green)、蓝色(Blue),这是最基本的颜色模式。
灰度:共256中颜色,可以理解为RGB数值相等的某个颜色。灰度也可通过百分比表示,范围从0%到100%,表示从纯白到纯黑过度。灰度往往作为判断通道饱和度或透明度的标准。
RGBA和ARGB是RGB加上了透明度(Alpha)通道,它决定了图像中的每个像素的透明度。ARGB的Alpha通道值放在前面,RGBA的Alpha通道值放在后面。
PARGB是预乘Alpha RGB的缩写,其中颜色值已经乘以透明度值。
当一个带alpha通道的透明图像和一个不带alpha通道的不透明图像进行合成时,可以实现一种半透明效果,它们的处理过程称为阿尔法混色。两张图像合成时,带alpha通道的图像一般称为源图像(前景),不带alpha通道的图像为背景图像。RGB值计算公式如下:
合成图像的像素显示颜色=源图像像素颜色 X alpha + 背景图像像素颜色 X (1- alpha)
一般的像素颜色表示使用RGB颜色空间,但美术人员更多的是使用HSV(HSL),因为可以方便的调整饱和度和亮度。
HSL即色相、饱和度、亮度(Hue, Saturation, Lightness),HSV即色相、饱和度、明度(Hue, Saturation, Value),又称HSB,其中B表示Brightness。
HSL 和 HSV 把颜色描述在圆柱体内的点:
HSL 和 HSV的区别:
HSL | HSV | |
饱和度 | 总是从完全饱和色变化到等价的灰色 | 在极大值V时,从全饱和色变化到白色 |
亮度 | 从黑色经过选择的色相到白色的完整范围 | V分量只走一半行程,从黑到选择的色相 |
详情可以查看:https://zh.wikipedia.org/wiki/HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4
关于HSL到RGB颜色的互相转换的Python实现代码可以参考:https://xxmdmst.blog.csdn.net/article/details/134470419
在moviepy中,所有剪辑的基类是Clip,常用的剪辑类包括:VideoClip、AudioClip、VideoFileClip、AudioFileClip、ImageSequenceClip、ImageClip、CompositeVideoClip、CompositeAudioClip、TextClip、ColorClip,它们之间的继承关系如下:
这些剪辑类除了基类Clip,都已经在模块 moviepy.editor中导入,因此使用这些剪辑类时,无需将各个模块单独import,只要从moviepy.editor导入即可。
说明:VideoClip的子类还有DataVideoClip、UpdatedVideoClip这2个。
start属性决定了当前剪辑在被CompositeVideoClip合并时,在整个视频中的起始播放时刻。
set_start语法:set_start(self, t, change_end=True)
t为需要设置的开始时间,可以是以下四种形式之一:
剪辑的时间参数如无特别说明都可以是这四种形式的时间。
change_end,表示是否修改剪辑的end的值
end属性决定了当前剪辑在被CompositeVideoClip合并时,在整个视频中的结束时刻。
set_end语法:set_end(self, t)
duration属性保存剪辑的时长,如果为None,表示剪辑无限长。可以调用set_duration改变剪辑的时长。
set_duration调用语法:set_duration(self, t, change_end=True)
if change_end:
self.end = None if (t is None) else (self.start + t)
else:
if self.duration is None:
raise Exception("Cannot change clip start when new"
"duration is None")
self.start = self.end - t
memoize属性用于控制剪辑是否应保留内存读取的最后一帧,如果为True保留,否则不保留,默认值为False,可以通过方法set_memoize进行修改。
set_memoize 调用语法:set_memoize(self, memoize)
memoized_t、memoized_frame属性
当memoize为True时,调用get_frame方法时memoized_t、memoized_frame用于保存最后一次读取的帧的位置和对应的帧。
注意:所有的set函数都被outplace装饰器处理,原剪辑不变,返回一个修改属性后的新剪辑。
copy方法会将调用者对应剪辑用浅拷贝方式复制一份,如果对应剪辑有音轨和遮罩,同样会浅拷贝复制到新剪辑中。
get_frame方法返回剪辑指定时刻位置的视频或音频帧,每个帧实际上一个表示RGB图像或音频的numpy类型数组。
默认使用self.make_frame(t)构造一个帧,返回前,缓存t到memoized_t,即将返回的帧到memoized_frame。
这样可以让方法被重复调用时直接返回前面已经生成的缓存。
fl方法是一个通用的剪辑处理方法,它返回一个新剪辑,新剪辑的所有帧是当前调用剪辑对象的帧经过函数fun变换处理后的帧。
调用语法:fl(self, fun, apply_to=None, keep_duration=True)
说明:
实际上fl方法就是将参数fun作为make_frame方法,而fun本身带2个参数,剪辑的get_frame方法和时间t,而fun可以对剪辑自身的get_frame(t)的返回值进行变换处理。
注意每一帧numpy数组的坐标如下:
下面我们先实现一个简单的需求,让视频向上滚动(在整个时间时长内滚动三次):
from moviepy.editor import VideoFileClip
src = VideoFileClip("t.mp4")
w, h = src.size
duration = src.duration
def fl(gf, t):
frame = gf(t)
dh = int(h*t*3/duration) % h
return np.vstack((frame[dh:], frame[:dh]))
newclip = src.fl(fl, apply_to='mask')
newclip.write_videofile("r.mp4")
下面我们实现视频在前一半时间内从底部恢复到顶部的效果(素材被设置为2秒的时长):
from moviepy.editor import VideoFileClip
src = VideoFileClip("t.mp4")
w, h = src.size
duration = 2
src = src.set_duration(duration)
def fl(gf, t):
frame = gf(t).copy()
frame[:max(0, h-round(h*t*2/duration))] = (0, 0, 0)
return frame
newclip = src.fl(fl, apply_to='mask')
newclip.write_videofile("r.mp4")
gf(t)返回了一个numpy数组对象,但是只读不能修改,所以复制一份进行修改,将不显示的部分设置为黑色。
时间为0时,黑色蒙版的高度为0->h;时间到一半时长时,黑色蒙版的高度降低到0->0。最终达到视频向上滚动的效果。
如果加大难度,实现视频从左下角恢复到右上角。其实这只需要在上面的基础上添加右侧的蒙版即可。
时间为0时,黑色蒙版的宽度为0->w;时间到一半时长时,黑色蒙版的宽度降低到w->w。最终达到视频从左下角到右上角滚动的效果。
def fl(gf, t):
frame = gf(t).copy()
frame[:max(0, h-round(h*t*2/duration))] = (0, 0, 0)
frame[:, round(w*t*2/duration):w] = (0, 0, 0)
return frame
如果我们考虑右侧蒙版不必重复修改右上角的黑色蒙版区域,那需要限制高度范围,高度变化为起始点h->h,结束点0->h:
def fl(gf, t):
frame = gf(t).copy()
x = round(w*t*2/duration)
y = round(h*t*2/duration)
frame[:max(0, h-y)] = (0, 0, 0)
frame[max(0, h-y):h, x:w] = (0, 0, 0)
return frame
fl_time方法返回一个新剪辑,新剪辑是调用剪辑的一个浅拷贝,但新剪辑的时间线被调整,实际上这个方法就是对剪辑进行一个基于时间特效的处理,如快播、慢播、倒序播放等。
fl_time(self, t_func, apply_to=None, keep_duration=False)
fl_time本质是本质是对fl的封装,相对fl的区别在于传入的t_func参数只需要关注时间t。
示例:
modifiedClip1 = my_clip.fl_time(lambda t: 3*t)
modifiedClip2 = my_clip.fl_time(lambda t: 1+sin(t))
fx方法是用于执行参数指定的函数,并返回函数的执行结果。
fx(self, func, *args, **kwargs)
该方法等同于执行:func(self, *args, **kwargs)
这个方法的用途是当需要使用一系列方法依次调用处理剪辑且每个方法返回的新剪辑作为下次调用者时可以简化语句。
如:clip.fx( mirrorx ).fx( volumex, 0.5).fx( resize, 0.3)
等同于:resize(volumex(mirrorx(clip), 0.5), 0.3)
用于设置fps的值,调用方法为:set_fps(self, fps),
用于设置是否有遮罩,调用方法为:set_ismask(self, ismask)
该方法修改浅拷贝对象的fps值,返回对象为新剪辑。
该方法用于判断对应时间是否在剪辑的start和end之间。
is_playing(self, t)
如果t是一个时间,且位于剪辑的start和end之间,则返回True,否则返回False。
t也可以是一个numpy数组,如果所有的时间都不在范围则返回True或False,否则返回每个时间是否在范围内的数组。
subclip方法用于由于抽取剪辑的指定时间段的剪辑段。
subclip(self, t_start=0, t_end=None)
默认t_end为None被设置为剪辑的duration,如果t_end为负数,则t_end被设置为剪辑的duration + t_end。
cutout方法将调用剪辑对象的剪辑去除掉指定位置段后返回。
cutout(self, ta, tb)
返回调用剪辑对象的剪辑去除ta到tb这一段之后的剪辑,如果原剪辑设置了duration属性,则返回剪辑的duration=原剪辑的duration-(tb - ta)。
总之subclip是截取指定范围,cutout是去掉指定范围。
iter_frames是为了迭代访问剪辑的所有帧,返回值为一个迭代器,每迭代一次返回下一帧。
iter_frames(self, fps=None, with_times = False, logger=None, dtype=None)
源码实现是根据get_frame(t)获取每一帧(返回numpy数组),而所有的时刻t通过duration和fps计算。
释放剪辑使用的所有资源,资源的释放在其子类实现。
视频剪辑类VideoClip主要有六个直接子类(VideofileClip、 ImageSequenceClip、CompositeVideoClip、ImageClip、DataVideoClip、UpdatedVideoClip)和两个间接子类(ColorClip, TextClip)。
构造方法语法:
__init__(self, make_frame=None, ismask=False, duration=None, has_constant_size=True)
参数释义:
size属性:剪辑的像素大小,(w,h)形式的二元组
w,h属性:即剪辑的宽和高,单位是像素,实际这两个属性就是从size属性获取的:self.size[0]和self.size[1]
ismask属性:表示是否是遮罩剪辑,遮罩剪辑的颜色值为0-1的浮点数,可以成为普通剪辑的mask属性。
make_frame属性:用于根据时间构建帧的函数,可以自定义该函数获得自定义的视频源。
mask属性表示剪辑对应的遮罩剪辑,可以通过add_mask、set_opacity或set_mask方法来添加或设置遮罩。
注意:遮罩只在多个剪辑使用CompositeVideoClip合成时有效,能够决定自身的透明度。
遮罩剪辑的颜色值为0-1的浮点数,普通剪辑的颜色值为RGB三元组,普通剪辑的mask属性可以指定遮罩剪辑。
aspect_ratio属性表示剪辑的宽高比,可以自己通过w/h来计算。
官网自定义make_frame示例:
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)
生成结果:
调用语法如下:save_frame(self, filename, t=0, withmask=True)
该方法用于将t指定时刻位置的帧保存到指定图像文件。
如果withmask为True,对应帧的遮罩会被写入图片的alpha通道层,可以生成PNG透明图像。
write_videofile方法用于将视频剪辑输出到文件,调用语法如下:
write_videofile(self, filename, fps=None, codec=None,
bitrate=None, audio=True, audio_fps=44100,
preset="medium",
audio_nbytes=4, audio_codec=None,
audio_bitrate=None, audio_bufsize=2000,
temp_audiofile=None,
rewrite_audio=True, remove_temp=True,
write_logfile=False, verbose=True,
threads=None, ffmpeg_params=None,
logger='bar')
参数说明如下:
针对codec一些常用的编解码器如下:
write_images_sequence方法用于将剪辑输出到一系列文件中,调用语法如下:
write_images_sequence(self, nameformat, fps=None, verbose=True,withmask=True, logger='bar')
参数说明如下:
write_gif将剪辑转换成gif动画输出到文件中,调用语法:
def write_gif(self, filename, fps=None, program='imageio',
opt='nq', fuzz=1, verbose=True,
loop=0, dispose=False, colors=None, tempfiles=False,
logger='bar'):
参数说明如下:
对剪辑指定时间段进行变换,剪辑的时长会自动调整。语法如下:
subfx(self, fx, ta=0, tb=None, **kwargs)
参数说明:
subfx实际上是调用基类Clip的fx方法来实现的。例如:
newclip = clip.subfx(vfx.speedx, 3, 6,factor = 0.5)
上面这段代码将视频剪辑的3-6秒的位置的剪辑播放速度变成原速度的一半,返回的newclip 总时长比clip 的时长多了3秒。
注:speedx本身是moviepy.video.fx.speedx下的一个函数,在moviepy.editor中被动态赋予了VideoClip类作为实例方法:
for method in [
"afx.audio_fadein",
"afx.audio_fadeout",
"afx.audio_normalize",
"afx.volumex",
"transfx.crossfadein",
"transfx.crossfadeout",
"vfx.crop",
"vfx.fadein",
"vfx.fadeout",
"vfx.invert_colors",
"vfx.loop",
"vfx.margin",
"vfx.mask_and",
"vfx.mask_or",
"vfx.resize",
"vfx.rotate",
"vfx.speedx"
]:
exec("VideoClip.%s = %s" % (method.split('.')[1], method))
fl_image方法是对fl方法的封装,传入的函数只负责处理当前帧的图像。
调用语法:fl_image(self, image_func, apply_to=None)
参数说明:
相对于fl方法,fl_image只变换图像,因此image_func不带时间参数。
示例:
def invert_green_blue(image):
return image[:,:,[0,2,1]]
modifiedClip = my_clip.fl_image( invert_green_blue )
fill_array将pre_array的高宽设置为参数shape。
调用语法:fill_array(self, pre_array, shape=(0, 0))
返回新数组post_array,shape大于pre_array本身的宽或高则扩展,用[1,1,1]黑色填充。
shape小于pre_array本身的宽或高,则丢弃多余的部分。
add_mask/to_mask/to_RGB方法
注意:遮罩对剪辑自身毫无作用,只在与其他剪辑合并时,能够决定自己的透明度。
合并剪辑请查看CompositeVideoClip函数。
add_mask方法用于给剪辑增加遮罩,遮罩的duration和大小与原剪辑相同,透明度为1。
to_mask方法返回一个由调用者剪辑实例构建的遮罩剪辑:to_mask(canal=0)。to_mask根据剪辑的颜色值生成遮罩,但已经有遮罩的情况直接返回原遮罩。canal指定了使用哪个颜色通道,0表示红色通道,1表示绿色通道,2表示蓝色通道,存储值为指定通道的颜色值/255。
to_RGB方法返回一个由遮罩剪辑生成的非遮罩剪辑。mask遮罩的颜色值是0到1的小数,将这个小数乘以255作为红绿蓝三通道的颜色值。若自身不是遮罩剪辑则直接返回。
on_color方法用于将当前剪辑放置到一个指定颜色背景的可能更大的剪辑上,用于在原始剪辑扩展大小时将空白处设置为指定颜色。返回值为处理后的新剪辑。
调用语法如下:
on_color(self, size=None, color=(0, 0, 0), pos=None, col_opacity=None)
例如我们可以给视频添加一个20px的蓝色透明边框:
clipVideo = VideoFileClip("t.mp4")
h,w = clipVideo.size
newclip = clipVideo.on_color(size=(h+20,w+20),color=(0,0,255),col_opacity=0.6)
set_audio方法将原剪辑的拷贝剪辑的音频设置为参数指定音频后返回新剪辑。
set_mask方法将原剪辑的拷贝剪辑的遮罩设置为参数指定剪辑后返回新剪辑。
set_opacity方法将原遮罩的颜色值与参数值相乘后返回。
set_position设置剪辑在合成剪辑的位置。调用语法:set_position(pos, relative=False)
pos:剪辑需要放置的坐标(x,y),x或y可以是如下值:
例如我们将一个视频扩展到三倍宽度后分别放置:
threads = 4
clipVideo = VideoFileClip("t.mp4")
w, h = clipVideo.size
bgclip = clipVideo.on_color(
size=(w*3+40, h+30), color=(0, 0, 255), col_opacity=0.6)
newclip = CompositeVideoClip([bgclip, clipVideo.set_position(('left', 'top')), clipVideo.set_position(
(w+20, 'center')), clipVideo.set_position((w*2+40, 'bottom'))], bg_color=(255, 0, 0), use_bgclip=True)
newclip.write_videofile("r.mp4", threads=threads)
newclip.close()
处理结果:
to_ImageClip方法将剪辑对应时刻t的帧转换成ImageClip图像剪辑,图像剪辑是所有帧都是固定图像数据的剪辑,所有帧都对应为图像数据。
调用语法:to_ImageClip(self, t=0, with_mask=True, duration=None)
说明:输出文件时要指定codec类型,否则可能播放失败。
newclip = clipVideo.to_ImageClip(duration=1).set_fps(8)
newclip.write_videofile("img.mp4",threads=threads)
without_audio返回一个去除了声音的新剪辑。
afx方法对原剪辑的声音进行变换返回新剪辑。调用语法:afx(fun, *a, **k)
本质上是执行了self.audio = self.audio.fx(fun, *a, **k)
DataVideoClip、UpdatedVideoClip、ImageClip、ColorClip、TextClip都是都是VideoClip的子类,它们都定义在模块文件VideoClip.py中。
DataVideoClip是VideoClip的直接子类,它的视频剪辑的连续帧都是从一系列数据集经过函数处理生成的,DataVideoClip类只有构造方法,没有独有属性和其他方法,因此DataVideoClip其实就是通过数据集经函数处理构造的视频剪辑。
构造方法:__init__(self, data, data_to_frame, fps, ismask=False, has_constant_size=True)
说明:
核心代码是make_frame = lambda t: self.data_to_frame(self.data[int(self.fps*t)])
基于该make_frame函数构建VideoClip。
UpdatedVideoClip是VideoClip的直接子类。
构造方法:__init__(self, world, ismask=False, duration=None)
参数world是一个有特殊要求的对象,该对象必须有一个属性clip_t、两个方法update和to_frame:
对应剪辑生成t时刻的帧时,如果world的clip_t 小于t,则会循环执行world.update方法,直到clip_t 大于等于t,此时再调用world.to_frame()输出帧。
ImageClip是VideoClip的直接子类,用于生成固定不变的视频剪辑。ImageClip是从一个图像文件或内存中图像数组数据生成的视频剪辑,对应视频任何时候都是显示该图像。
构造方法:__init__(self, img, ismask=False, transparent=True,fromalpha=False, duration=None)
参数说明:
如果图片不带alpha层,transparent和fromalpha会被忽略。fromalpha、ismask和transparent参数互斥,优先使用fromalpha=True的设置,其次使用ismask=True的设置,最后应用transparent=True的设置。具体实现为:
if img.shape[2] == 4:
if fromalpha:
img = 1.0 * img[:, :, 3] / 255
elif ismask:
img = 1.0 * img[:, :, 0] / 255
elif transparent:
self.mask = ImageClip(
1.0 * img[:, :, 3] / 255, ismask=True)
img = img[:, :, :3]
elif ismask:
img = 1.0 * img[:, :, 0] / 255
示例:
from moviepy.editor import ImageClip, vfx, VideoFileClip, CompositeVideoClip
src = VideoFileClip("t.mp4")
w, h = src.size
clip = ImageClip("mask.png", duration=src.duration)
clip = clip.fx(vfx.resize, (w-40, h//3))
r = CompositeVideoClip([src, clip])
r.write_videofile("r.mp4", fps=8)
原图:
生成的视频结果:
transparent默认为True,可以将png图片的透明度设置为剪辑的遮罩,而遮罩的透明度决定了其与其他剪辑合成的时候自身的透明度,png透明图片就可以完美的融合到目标剪辑中。
fl方法、fl_image方法和fl_time方法
ColorClip是仅显示同一种颜色的剪辑。
构造方法:__init__(self, size, color=None, ismask=False, duration=None, col=None)
参数说明:
示例:
colClip = ColorClip((360,360),color = (255,0,0),ismask=False,duration=5).set_fps(1)
colClip.write_videofile("red.mp4", codec='mpeg4')
该代码生成一个全红色的剪辑。
TextClip需要先调用ImageMagick将文本转换成一个png图片,使用前需要先安装ImageMagick,然后最好设置一下IMAGEMAGICK_BINARY环境变量为ImageMagick安装后的运行目录。moviepy通过命令行方式调用ImageMagick,该应用对应官方下载地址:http://www.imagemagick.org/script/download.php
TextClip构造方法语法如下:
__init__(self, txt=None, filename=None, size=None, color='black',
bg_color='transparent', fontsize=None, font='Courier',
stroke_color=None, stroke_width=1, method='label',
kerning=None, align='center', interline=None,
tempfilename=None, temptxt=None,
transparent=True, remove_temp=True,
print_cmd=False)
参数说明:
list方法:用于返回TextClip构造方法中font和color参数在执行机器上可以使用的相关取值列表。
调用语法为:list(arg)其中参数arg只有两个取值’font’和’color’
该方法是一个静态方法,直接带类名就可以调用。如:
TextClip.list('font')
TextClip.list('color')
以上代码执行时可能会因为编码原因报错,需要针对性修改源码。
search(string, arg)方法:就是对list方法的结果进行过滤,也是一个静态方法。例如:
>>> TextClip.search(b'red','color')
[b'DarkRed', b'IndianRed', b'IndianRed1', b'IndianRed2', b'IndianRed3', b'IndianRed4', b'MediumVioletRed', b'OrangeRed', b'OrangeRed1', b'OrangeRed2', b'OrangeRed3', b'OrangeRed4', b'PaleVioletRed', b'PaleVioletRed1', b'PaleVioletRed2', b'PaleVioletRed3', b'PaleVioletRed4', b'red', b'red1', b'red2', b'red3', b'red4', b'VioletRed', b'VioletRed1', b'VioletRed2', b'VioletRed3', b'VioletRed4']
>>> TextClip.search('GB','font')
['仿宋_GB2312']
注意搜索颜色时string前面带类型b,而搜索字体时不带b,这是因为’font’和’color’的类型不同导致的。
VideoFileClip类是VideoClip的直接子类,是从一个视频文件创建一个剪辑类。VideoFileClip加载视频文件时,可以调整剪辑对应分辨率大小,可以根据应用要求设定是否加载音频。VideoFileClip加载视频文件时,会调用FFMPEG_VideoReader来加载视频文件,加载时会对视频文件进行加锁处理。
filename属性用于存储读取视频文件的文件名,该文件名与读取视频文件给的名字完全一致,无需进行本地化路径转换。
构造方法:
__init__(self, filename, has_mask=False,
audio=True, audio_buffersize=200000,
target_resolution=None, resize_algorithm='bicubic',
audio_fps=44100, audio_nbytes=2, verbose=False,
fps_source='tbr')
参数说明:
close方法:构造方法会对视频文件进行加锁,并占用相关资源,如果要释放文件和资源,需要调用close方法或等加载处理的进程结束。
CompositeVideoClip是一种由其他视频剪辑组合构成一起播放的视频剪辑,这是大多数合成剪辑的基类。concatenate_videoclips在method参数设置为’compose’时,实际上是调用的CompositeVideoClip完成合成。
构造方法__init__(self, clips, size=None, bg_color=None, use_bgclip=False,ismask=False)
参数说明:
合成剪辑的一些属性处理逻辑:
clips属性保存合成剪辑所需要的所有剪辑的列表,但不包含背景剪辑,即如果use_bgclip为True,则clips保存的为构造方法clips参数对应列表第二个及之后的所有剪辑,如果use_bgclip为False,则就是构造方法clips参数对应的列表。
playing_clips方法:用于判断clips属性中对应剪辑在参数指定的t时刻是否处于播放状态,对处于播放状态的剪辑存放到一个列表中返回。调用语法:playing_clips(self, t=0)
close方法:用于关闭音频及由CompositeVideoClip创建的背景剪辑,其他资源的释放不处理。
write_images_sequence方法用于将剪辑输出到一系列图像文件中,而ImageSequenceClip则基本上与write_images_sequence过程可逆,用于将一系列图像生成剪辑。
ImageSequenceClip是VideoClip的直接子类,该类自身只有构造方法,其他方法和属性都是继承自父类。
构造方法:
__init__(self, sequence, fps=None, durations=None, with_mask=True,
ismask=False, load_images=False)
参数说明:
注意:ImageSequenceClip要求sequence对应的所有图像大小及表示像素的元组的大小都必须相同。
语法:
concatenate_videoclips(clips, method="chain", transition=None, bg_color=None, ismask=False, padding = 0)
method:拼接方法,有2种取值
transition:transition指定一个将在列表的每两个剪辑之间播放的剪辑 ,即结果剪辑不但会将参数对应剪辑拼接,而且会在两个剪辑拼接中间插入一个由transition指定的过场剪辑
bg_color:仅在method="compose"时使用,设置背景色,如果要一个透明剪辑,则设置为None,否则为一个代表RGB颜色的三元组,如(0,0,0)代表黑色,也即透明色
padding:仅在method=“compose"时使用,两个连续剪辑间的间隔时间。负数的padding参数会导致两个剪辑出现重叠,会制造出后一个剪辑逐渐变暗退出的效果。一个非0的padding值会自动将method置为"compose”
调用语法:
clips_array(array, rows_widths=None, cols_widths=None, bg_color = None)
参数说明:
官网示例:
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")
为了支持一些常规的变换处理,moviepy提供了一系列常用的变换函数,这些函数都在moviepy.video.fx包下,在moviepy.editor通过import moviepy.video.fx.all as vfx中将这些函数都加载到了vfx模块下,可以直接通过vfx.函数名方式调用。
blackwhite函数用于将剪辑变成灰度剪辑,也就是将剪辑中的彩色像素灰度化。
调用语法:blackwhite(clip, RGB = None, preserve_luminosity=True)
参数说明:
fadein使剪辑开始播放时在指定时间内从某种颜色中逐渐显示出来。
fadeout使剪辑快结束前在指定时间内逐渐淡隐于某种颜色。
调用语法:
fadein(clip, duration, initial_color=None)
fadeout(clip, duration, final_color=None)
duration为淡入淡出的时长,单位为秒。
两个函数内部的fl核心实现为:
# 作用于开始阶段
fading = (1.0*t/duration)
return fading*gf(t) + (1-fading)*initial_color
# 作用于结束阶段
fading = 1.0 * (clip.duration - t) / duration
return fading*gf(t) + (1-fading)*final_color
invert_colors将像素对应颜色进行反转,核心实现:
maxi = (1.0 if clip.ismask else 255)
return clip.fl_image(lambda f : maxi - f)
mask_color函数用于将一个剪辑自身进行变换后变成原剪辑的遮罩,当原剪辑与其他剪辑合成时,可以根据遮罩的透明度来确认其他剪辑的显示情况。
调用语法:mask_color(clip, color=None, thr=0, s=1)
mask_color的三个参数都用于计算遮罩剪辑的像素透明度,其运算过程如下:
对于每一帧图像im,首先计算x=np.sqrt(((im-color)**2).sum(axis=2))
表示原剪辑每个像素颜色与参数color之差的平方和。
当thr未设置时,遮罩像素透明度等于0或1,所有x不为0的像素透明度都为1。
当thr为非0有效时,遮罩像素透明度=x/(thr+x),即x站与thr之和的比例。当然当s不等于1时,遮罩像素透明度=x^s / (thr^s + x^s),x^s表示x的s次方。
总之,mask_color是一个基于剪辑自身颜色生成遮罩透明度的函数。
前面有提到Clip的fl_time方法是针对剪辑的时间线进行变换,下面我们看看相关的内置函数。
freeze函数将指定时刻位置的帧延时显示freeze_duration秒,相当于剪映中的定格效果。
调用语法:freeze(clip, t=0, freeze_duration=None, total_duration=None, padding_end=0)
参数说明:
功能说明:
freeze_region函数主要用于将剪辑中指定屏幕范围内容固定为参数指定的某个时刻的内容。
调用语法:freeze_region(clip, t=0, region=None, outside_region=None, mask=None)
注意:region,outside_region,mask只有第一个被设置的参数有效,例如region被设置时,outside_region和mask的设置会被忽略。
参数说明:
设置region,矩形区域内的内容固定不变:
#构建矩形区域固定显示为剪辑第5秒的内容
clipVideo1 = clipVideo.fx(vfx.freeze_region ,t=5,region=(200,300,500,700))
设置outside_region,矩形区域外的内容固定不变:
# 构建矩形区域外固定显示为剪辑第10秒的内容
clipVideo2 = clipVideo.fx(vfx.freeze_region, t=10, outside_region=(200,300,500,700))
设置一个透明度0.5的mask,此时15秒时刻的指定大小的画面融合到整个时间的所有帧(时间时长低于30秒):
# 取剪辑15秒时刻的屏幕作为一个新剪辑,新剪辑的遮罩设置为半透明
c2 = ColorClip((360, 400), ismask=True, color=0.5, duration=30)
clipVideo3 = clipVideo.fx(vfx.freeze_region, t=15, mask=c2)
loop函数将当前剪辑重复n次或重复至直到指定时间。
调用语法:loop(self, n=None, duration=None)
核心实现:self.fl_time(lambda t: t % self.duration)
然后设置目标时长。
speedx函数用于将视频倍速播放,factor会播放的速度。
调用语法:speedx(clip, factor=None, final_duration=None)
final_duration为新剪辑的最终播放时长,设置final_duration时,会根据final_duration的值计算出factor,原有的factor即使设置也会被忽略。
函数的核心实现为:
newclip = clip.fl_time(lambda t: factor * t, apply_to=['mask', 'audio'])
newclip = newclip.set_duration(clip.duration / factor)
time_mirror函数用于实现倒放效果,实现为:
self.fl_time(lambda t: self.duration - t, keep_duration=True)
time_symmetrize函数将当前剪辑和当前剪辑的倒放顺序合并。
和剪辑大小相关的变换函数,包括crop、even_size、margin和resize。
crop函数从剪辑中获取一个矩形区域的剪辑内容作为新的剪辑。
调用语法:crop(clip, x1=None, y1=None, x2=None, y2=None, width=None, height=None, x_center=None, y_center=None)
参数:
源码核心计算逻辑:
if width and x1 is not None:
x2 = x1 + width
elif width and x2 is not None:
x1 = x2 - width
if height and y1 is not None:
y2 = y1 + height
elif height and y2 is not None:
y1 = y2 - height
if x_center:
x1, x2 = x_center - width / 2, x_center + width / 2
if y_center:
y1, y2 = y_center - height / 2, y_center + height / 2
even_size函数将剪辑的宽和高都变成偶数,存在奇数则丢弃一行或一列。
在剪辑的四周增加一个外边框。
调用语法:margin(clip, mar=None, left=0, right=0, top=0, bottom=0, color=(0, 0, 0), opacity = 1.0)
说明:
resize函数用于调整剪辑的大小,包括缩小或放大。
调用语法:resize(clip, newsize=None, height=None, width=None, apply_to_mask=True)
resize会依次尝试使用OpenCV、PIL或Scipy进行缩放,都没有安装则无法使用。
newsize可以指定新剪辑的大小,未指定newsize时 width、height可以二选一指定一个,会等比例缩放自动计算另一个的值。
newsize还可以指定一个函数,例如:
def getsize(t):
if t<2:return (600,800)
else:return (0.8-1/t)
剪辑小于2秒的位置大小固定为(600,800),后面返回一个缩放比例,随着时间推移逐渐扩大。
注意:剪辑最终的大小为第一个帧返回的大小,后续扩大的帧会被裁剪,而缩小的帧则会填充黑色。
本文节介绍和剪辑内容相关的变换函数,包括mask_and、mask_or、mirror_x、mirror_y、painting、rotate、scroll、supersample。
mask_and函数用于将两个剪辑的所有像素的RGB值各取最小值作为新剪辑的像素RGB值。
调用语法:mask_and(clip, other_clip)
mask_or函数与mask_and相反,取最大值作为新剪辑的像素RGB值。
说明:两个剪辑的大小必须相同,other_clip可以是剪辑也可以是ndarray对象,新剪辑的duration被设置为clip的duration。
mirror_x、mirror_y函数分别将剪辑内容左右或上下颠倒。
调用语法:
mirror_x(clip, apply_to="mask")
mirror_y(clip, apply_to="mask")
如果apply_to=“mask”,则遮罩也进行同样处理。
rotate函数用于将剪辑逆时针旋转指定的角度或弧度。
调用语法:rotate(clip, angle, unit="deg", resample="bicubic", expand=True)
参数说明:
注意:
如果旋转角度是90度的倍数且expand=True,则直接使用numpy进行旋转,否则调用PIL库进行旋转。
若指定了expand=True且角度可能不为90度的倍数时,最好将所有帧的大小统一调整到一致,否则可能出现花屏的现象。
scroll函数是实现在屏幕上水平或垂直滚动播放剪辑的内容,如影片的片尾。
调用语法:scroll(clip, w=None, h=None, x_speed=0, y_speed=0, x_start=0, y_start=0, apply_to="mask")
参数说明:
经测试只有w或h小于剪辑原大小时,才能产生滚动的效果,在指定的大小内,滚动显示整个视频。是横向滚动还是纵向滚动由x_speed 和 y_speed 参数决定,也可以同时指定。
个人觉得没有需要这个函数的场景,真遇到可以自己编码实现任意自己想要的滚动效果。
supersample函数返回一个新剪辑,新剪辑每个帧的像素值被替换为该帧前后时段范围内的多个等间距帧的算术平均值。
调用语法:supersample(clip, d, nframes)
说明:
supersample返回的剪辑每个t时刻帧的像素的值计算方法如下:
该函数依赖OpenCV,要求必须安装OpenCV后才能使用。安装命令:
pip install opencv-python
该函数用于给剪辑指定的位置增加圆形的模糊效果。
调用语法:headblur(clip,fx,fy,r_zone,r_blur=None)
参数:
该函数的源码存在bug,完全可以自行实现。
修改点在于:
if r_blur is None: r_blur = 2*r_zone//3
# 和
im = gf(t).copy()
声音三要素:
AudioClip是一个音频剪辑的基类,其父类是Clip。AudioClip带有make_frame属性,该属性根据时间参数t返回一个列表,里面的元素是对应声道的numpy数组数据,数组的每个元素是-1到1之间的值。
构造方法:__init__(self, make_frame=None, duration=None, fps=None)
参数说明:
iter_chunks方法返回一个迭代器,通过这个迭代器可以返回一个包含音频剪辑内容块的数组。
调用语法:
iter_chunks(self, chunksize=None, chunk_duration=None, fps=None,quantize=False, nbytes=2, logger=None)
参数说明:
to_soundarray方法将音频片段转换为一个可以使用pygame播放或者使用wav格式保存的数组。
调用语法:to_soundarray(self, tt=None, fps=None, quantize=False, nbytes=2, buffersize=50000)
参数说明:
max_volume方法是取音频剪辑的最大音量,最大音量也就是音频数组元素绝对值的最大值。
调用语法:max_volume(self, stereo=False, chunksize=50000, logger=None)
write_audiofile方法用于将音频剪辑的内容输出到指定文件,该方法替换了低版本的to_audiofile方法。
调用语法:write_audiofile(self, filename, fps=None, nbytes=2, buffersize=2000, codec=None, bitrate=None, ffmpeg_params=None, write_logfile=False, verbose=True, logger='bar')
参数说明:
AudioFileClip是AudioClip的直接子类,用于从一个音频文件或音频数组中读入音频到内存构建音频剪辑。但AudioFileClip并不将整个音频文件装入内存,而是将部分内容读入和保存到内存,读入的部分包括当前最后一个读取的帧以及该帧前面和后面的部分帧。
构造方法:__init__(self, filename, buffersize=200000, nbytes=2, fps=44100)
参数说明:
AudioFileClip主要有如下属性,分别是:
coreader方法返回当前音频剪辑的一个副本拷贝。
coreader(self)
close方法关闭当前AudioFileClip的reader对象,其语法非常简单,就是close(self)。
volumex(clip, factor)可以分别调整多个声道的音量,例如将右声道静音,左声道音量调整为原来的一半:
audio.volumex([0.5,0])
audio_normalize(clip)函数将最大音量调整到1,其他声音等比例缩小。
页面更新:2024-02-19
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号