Redian新闻
>
万字长文,学 Moviepy 看这篇!

万字长文,学 Moviepy 看这篇!

公众号新闻

作者:小小明 | 转自:法纳斯特

地址:https://xxmdmst.blog.csdn.net/article/details/134938026


阅读本文档的前置

本文档用于讲解Python的moviepy库的自带函数的用法,主要目的是讲一下每个函数的每个参数的含义,无需一开始就全部掌握,粗略看一下就行,可以在后面自己开发过程,遇到不会用的函数再回过头来看看本文档,我将在后续的文章中,通过几下实际的案例来理解视频特效的开发流程。

moviepy简介及基本概念

moviepy概述

MoviePy 是一个用于视频编辑的 Python 库,使用户能够处理、编辑和操作视频文件。这个库允许你剪辑视频、添加文本、合并视频剪辑,以及应用各种效果和转换。它建立在 NumPy、imageio 和 Decorator 等库的基础上,使得在处理视频时能够更加高效。

下面是一些 MoviePy 的主要功能和特点:

  1. 剪辑和合并视频: MoviePy 允许你从现有视频中选择特定的片段,然后将它们合并成一个新的视频文件。

  2. 添加文本和图形: 你可以在视频中添加文本、图形和其他元素,以创建字幕、水印或其他视觉效果。

  3. 视频效果: MoviePy 提供了一系列内置的视频效果,如模糊、旋转、缩放等,使用户能够轻松地对视频进行编辑和改进。

  4. 音频处理: 除了视频,MoviePy 还支持音频处理,你可以添加音轨、调整音量等。

  5. 格式转换: 通过 MoviePy,你可以将视频文件转换为不同的格式,以适应不同的设备和平台。

  6. 自定义效果: 如果内置效果不够,你还可以使用 MoviePy 提供的 API 来创建自定义的视频效果。

可以通过 pip 来安装 MoviePy,命令如下:

pip install moviepy

视频剪辑中的mask

mask可以译为遮罩、遮片、蒙版,后面本文都称为遮罩。

mask遮罩是一种特殊的视频剪辑,每像素只有一个0-1之间的值(1表示完全可见的像素,0表示透明的像素)mask遮罩可以决定对应视频剪辑对应帧哪些像素可见、哪些不可见。

带mask遮罩的剪辑在导出为GIF或PNG图像时,用于定义图像的透明度。

标准剪辑输出的帧每像素包含RGB的3个0-255之间的值,而遮罩剪辑每像素只有一个0-1之间的值。

RGB、灰度、RGBA、ARGB和PARGB

RGB:RGB代表红色(Red)、绿色(Green)、蓝色(Blue),这是最基本的颜色模式。

灰度:共256中颜色,可以理解为RGB数值相等的某个颜色。灰度也可通过百分比表示,范围从0%到100%,表示从纯白到纯黑过度。灰度往往作为判断通道饱和度或透明度的标准。

RGBAARGB是RGB加上了透明度(Alpha)通道,它决定了图像中的每个像素的透明度。ARGB的Alpha通道值放在前面,RGBA的Alpha通道值放在后面。

PARGB是预乘Alpha RGB的缩写,其中颜色值已经乘以透明度值。

当一个带alpha通道的透明图像和一个不带alpha通道的不透明图像进行合成时,可以实现一种半透明效果,它们的处理过程称为阿尔法混色。两张图像合成时,带alpha通道的图像一般称为源图像(前景),不带alpha通道的图像为背景图像。RGB值计算公式如下:
合成图像的像素显示颜色=源图像像素颜色 X alpha + 背景图像像素颜色 X (1- alpha)

HSL 和HSV

一般的像素颜色表示使用RGB颜色空间,但美术人员更多的是使用HSV(HSL),因为可以方便的调整饱和度和亮度。

HSL即色相、饱和度、亮度(Hue, Saturation, Lightness),HSV即色相、饱和度、明度(Hue, Saturation, Value),又称HSB,其中B表示Brightness。

image-20231201204709391

HSL 和 HSV 把颜色描述在圆柱体内的点:

  • 色相(Hue):描述颜色的种类(如红色、绿色、蓝色等),通常表示为一个角度,范围从0°到360°。
  • 饱和度(Saturation):描述颜色的强度,饱和度从0%(灰色)到100%(全色)。
  • 亮度(Lightness)明度(Value):描述颜色的明亮程度。0%表示全黑,100%表示全白。几乎等价于灰度值。

HSL 和 HSV的区别:

image-20231201210337806
image-20231201210245407

HSLHSV
饱和度总是从完全饱和色变化到等价的灰色在极大值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

剪辑基类Clip

在moviepy中,所有剪辑的基类是Clip,常用的剪辑类包括:VideoClip、AudioClip、VideoFileClip、AudioFileClip、ImageSequenceClip、ImageClip、CompositeVideoClip、CompositeAudioClip、TextClip、ColorClip,它们之间的继承关系如下:

image-20231209104209584

这些剪辑类除了基类Clip,都已经在模块 moviepy.editor中导入,因此使用这些剪辑类时,无需将各个模块单独import,只要从moviepy.editor导入即可。

**说明:**VideoClip的子类还有DataVideoClip、UpdatedVideoClip这2个。

Clip类的属性

start属性决定了当前剪辑在被CompositeVideoClip合并时,在整个视频中的起始播放时刻。

set_start语法:set_start(self, t, change_end=True)

t为需要设置的开始时间,可以是以下四种形式之一:

  • 秒,例如75.35
  • 分钟和秒组成的元组,例如(1,15.35)
  • 时、分、秒组成的元组,例如(0,1,15.35)
  • 用冒号分隔的时间字符串,例如’0:1:15.35’

剪辑的时间参数如无特别说明都可以是这四种形式的时间。

change_end,表示是否修改剪辑的end的值

  • 如果为False剪辑的end值保持不变,当end存在有效值时,剪辑的时长被设置为end-start
  • 默认为True,当duration存在有效值,end属性被更改为:start+duration

end属性决定了当前剪辑在被CompositeVideoClip合并时,在整个视频中的结束时刻。

set_end语法:set_end(self, t)

  • 设置end属性时,如果start属性已经被设置,则剪辑的duration属性会被设置为end-start
  • 如果start属性还没有设置,则start修改为end-duration,并限制最小值为0

duration属性保存剪辑的时长,如果为None,表示剪辑无限长。可以调用set_duration改变剪辑的时长。

set_duration调用语法:set_duration(self, t, change_end=True)

if change_end:
 self.end = None if (t is Noneelse (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装饰器处理,原剪辑不变,返回一个修改属性后的新剪辑。

Clip类的方法

copy方法

copy方法会将调用者对应剪辑用浅拷贝方式复制一份,如果对应剪辑有音轨和遮罩,同样会浅拷贝复制到新剪辑中。

get_frame(t)方法

get_frame方法返回剪辑指定时刻位置的视频或音频帧,每个帧实际上一个表示RGB图像或音频的numpy类型数组。

默认使用self.make_frame(t)构造一个帧,返回前,缓存t到memoized_t,即将返回的帧到memoized_frame。

这样可以让方法被重复调用时直接返回前面已经生成的缓存。

fl方法

fl方法是一个通用的剪辑处理方法,它返回一个新剪辑,新剪辑的所有帧是当前调用剪辑对象的帧经过函数fun变换处理后的帧。

调用语法:fl(self, fun, apply_to=None, keep_duration=True)

说明:

  • fun:参数fun是对剪辑帧进行变换的函数,要求带2个参数,第一个参数gf表示当前剪辑的get_frame方法,第二个参数为以秒为单位的剪辑位置t,t会作为gf的参数。fun函数的返回值为经过变换后的帧
  • apply_to:apply_to表示变换过滤器fl是否需要同时作用于剪辑的音频和遮罩,其值可以为’mask’、‘audio’、[‘mask’,‘audio’]
  • keep_duration:如果为True表示不改变剪辑的duration属性

实际上fl方法就是将参数fun作为make_frame方法,而fun本身带2个参数,剪辑的get_frame方法和时间t,而fun可以对剪辑自身的get_frame(t)的返回值进行变换处理。

注意每一帧numpy数组的坐标如下:

img

下面我们先实现一个简单的需求,让视频向上滚动(在整个时间时长内滚动三次):

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))] = (000)
    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))] = (000)
    frame[:, round(w*t*2/duration):w] = (000)
    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)] = (000)
    frame[max(0, h-y):h, x:w] = (000)
    return frame

fl_time方法

fl_time方法返回一个新剪辑,新剪辑是调用剪辑的一个浅拷贝,但新剪辑的时间线被调整,实际上这个方法就是对剪辑进行一个基于时间特效的处理,如快播、慢播、倒序播放等。

fl_time(self, t_func, apply_to=None, keep_duration=False)

  • t_func:参数t_func是对剪辑帧进行变换的函数,它将t时刻的帧替换为原剪辑t_func(t)位置的帧
  • apply_to:apply_to表示变换是否需要同时作用于剪辑的音频和遮罩,其值可以为’mask’、‘audio’、[‘mask’,‘audio’]
  • keep_duration:如果为True表示不改变剪辑的duration属性,缺省为False,如果变化修改了duration需要设置为False。剪辑的duration属性修改后必须调用set_duration设置duration属性,并在设置后返回新剪辑。

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方法是用于执行参数指定的函数,并返回函数的执行结果。

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)

set_fps/set_ismask方法

用于设置fps的值,调用方法为:set_fps(self, fps)

用于设置是否有遮罩,调用方法为:set_ismask(self, ismask)

该方法修改浅拷贝对象的fps值,返回对象为新剪辑。

is_playing方法

该方法用于判断对应时间是否在剪辑的start和end之间。

is_playing(self, t)

如果t是一个时间,且位于剪辑的start和end之间,则返回True,否则返回False。

t也可以是一个numpy数组,如果所有的时间都不在范围则返回True或False,否则返回每个时间是否在范围内的数组。

subclip方法

subclip方法用于由于抽取剪辑的指定时间段的剪辑段。

subclip(self, t_start=0, t_end=None)

默认t_end为None被设置为剪辑的duration,如果t_end为负数,则t_end被设置为剪辑的duration + t_end。

cutout方法

cutout方法将调用剪辑对象的剪辑去除掉指定位置段后返回。

cutout(self, ta, tb)

返回调用剪辑对象的剪辑去除ta到tb这一段之后的剪辑,如果原剪辑设置了duration属性,则返回剪辑的duration=原剪辑的duration-(tb - ta)。

总之subclip是截取指定范围,cutout是去掉指定范围。

iter_frames方法

iter_frames是为了迭代访问剪辑的所有帧,返回值为一个迭代器,每迭代一次返回下一帧。

iter_frames(self, fps=None, with_times = False, logger=None, dtype=None)

源码实现是根据get_frame(t)获取每一帧(返回numpy数组),而所有的时刻t通过duration和fps计算。

  • fps往往存在默认值,不需要手动指定
  • dtype无需设置,当读取到的数组类型不是uint8时,可以设置为uint8进行转换减少内存消耗,因为8个字节正好能够存储每个颜色通道0-255的颜色值。
  • 默认只返回对应帧,如果参数with_times为True,则同时返回对应帧的时刻位置
  • logger参数用于指定日志信息打印相关的类

close方法

释放剪辑使用的所有资源,资源的释放在其子类实现。

视频剪辑基类VideoClip

视频剪辑类VideoClip主要有六个直接子类(VideofileClip、 ImageSequenceClip、CompositeVideoClip、ImageClip、DataVideoClip、UpdatedVideoClip)和两个间接子类(ColorClip, TextClip)。

VideoClip的构造方法和属性

构造方法语法:

__init__(self, make_frame=None, ismask=False, duration=None, has_constant_size=True)

参数释义:

  • make_frame:帧的构建方法,被get_frame调用,默认从已有剪辑中获取,也可以自定义;
  • ismask:是否作为遮罩使用
  • duration:构建剪辑的时长
  • has_constant_size:表示是否所有帧长宽大小都相同,add_mask函数被调用时会使用该函数判断。如果该参数会True,add_mask函数会生成ColorClip(self.size, 1.0, ismask=True)的遮罩;如果为False,则获取每一帧的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方法来添加或设置遮罩。

  • add_mask:添加透明度固定为1(完全不透明)的遮罩
  • 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)

生成结果:

img

VideoClip的访问方法

save_frame方法

调用语法如下:save_frame(self, filename, t=0, withmask=True)

该方法用于将t指定时刻位置的帧保存到指定图像文件。

如果withmask为True,对应帧的遮罩会被写入图片的alpha通道层,可以生成PNG透明图像。

write_videofile方法

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')

参数说明如下:

  • filename:视频文件名,ffmpeg支持的视频格式都可以,例如 .ogv, .mp4, .mpeg, .avi, .mov等
  • fps:帧率,每秒编码的帧数
  • codec:用于图像编码的编解码器,可以是ffmpeg支持的任何编解码器。如果文件名的扩展名为“.mp4”、“.ogv”、“.webm”,则会自动设置相应编解码器,也可以指定其他编解码器。
  • bitrate:输出视频的比特率,也即码率BPS(Bits Per Second),指每秒传送的数据位数
  • audio:是否保存音频,如果为False则保存的视频无音频,如果为音频文件名字符串,则使用目标文件中的音频作为最终视频的音频。
  • audio_fps:声音的采样频率
  • preset:设置FFMPEG用于优化压缩的时间。字符串类型,可选值有:ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、 placebo。设置越快,则压缩率越低,文件越大;反正越慢,压缩率越高,文件越小。
  • audio_xxx相关参数含义可以查看AudioClip的说明。
  • temp_audiofile:如果输出由音频,则该参数用于指定要生成并合并到电影中的临时音频文件的名称,否则使用默认的临时文件名
  • rewrite_audio:这个参数在1.0.3版本中没有作用
  • remove_temp:是否删除生成的临时音频文件
  • write_logfile:如果为True,将为音频和视频输出记录日志文件。日志文件名为filename+'.log'
  • verbose:已废弃。
  • threads:指定ffmpeg处理时所使用的线程数
  • ffmpeg_params:需额外传递的其他ffmpeg参数,用列表传递,形如:[‘-option1’,‘value1’,‘-option2’,‘value2’]
  • logger:字符串类型,"bar"表示进度条、None 表示不设置、或任何程序日志记录器的名字

针对codec一些常用的编解码器如下:

  • ‘libx264’:视频压缩效果好的一款编解码器,MP4的缺省编解码器,视频质量通过bitrate参数调节
  • ‘mpeg4’:一种可选的MP4编解码器,可以替代’libx264’,可以获得更好的视频质量
  • ‘rawvideo’:完美的视频质量,但文件会巨大,对应视频文件为’.avi’
  • ‘png’:完美的视频质量,对应视频文件为’.avi’,但文件大小比’rawvideo’小
  • ‘libvorbis’:是一种完全开放、免费的编解码器,有不错的视频格式,但使用不广,对应视频文件为’.ogv’
  • ‘libvpx’:一种很适合在HTML5中使用的网络视频轻量级编开源解码器,对应视频文件为’.webm’

write_images_sequence方法

write_images_sequence方法用于将剪辑输出到一系列文件中,调用语法如下:

write_images_sequence(self, nameformat, fps=None, verbose=True,withmask=True, logger='bar')

参数说明如下:

  • nameformat:输出文件名的规则,使用nameformat % i生成图片文件名,i表示迭代出来的第几帧(从零开始)。
  • fps:每秒输出帧数,如果没指定则按剪辑的fps进行输出
  • withmask:是否将遮罩作为图像的alpha通道输出,用于生成png透明图片
  • verbose:是否输出处理信息
  • logger:字符串类型,"bar"表示进度条、None 表示不设置、或任何程序日志记录器的名字

write_gif方法

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')
:

参数说明如下:

  • program:用来进行转换的软件,支持imageioImageMagick或者是ffmpeg
  • opt:应用优化的选项,仅program使用ImageMagick时有效,opt可以是“optimizeplus”或“OptimizeTransparency”。
  • fuzz:仅program使用ImageMagick时有效,通过考虑小于fuzz%的颜色差异实际上是相同的来压缩GIF文件大小
  • loop:表示GIF文件播放时循环播放多少次,如果为0就一直不停地播放,否则播放设定次数后就停止,该参数由GIF文件头控制
  • dispose:仅program使用ImageMagick时有效,表示播放动画时渲染当前帧时,如何处理前一帧,该参数由GIF文件头控制,默认值为False实际值为1,否则设置True实际值为2。根据GIF中该控制参数的取值含义:
    • False表示当前帧只需在上一帧的基础上做局部刷新,上一帧中没有被当前帧覆盖的像素区域将继续展示。
    • True表示绘制当前帧之前,会先把前一帧的绘制区域恢复成背景色,这种方式常用于优化很多帧背景相同的情况,上一帧的背景色能通过当前帧的透明区域显示
  • colors:仅ImageMagick支持的参数,表示颜色的总个数。由于 GIF 格式颜色数的有效范围通常是 2 到 256 色,所以该参数设置2-256,设置更小的数值,文件会更小,但颜色失真也越多。
  • tempfiles:将每个帧写入一个临时文件,而不是写入到内存中,ImageMagick和ffmpeg支持。

subfx方法

对剪辑指定时间段进行变换,剪辑的时长会自动调整。语法如下:
subfx(self, fx, ta=0, tb=None, **kwargs)

参数说明:

  • fx:用于对剪辑进行变换处理的函数名
  • ta和tb:分别是剪辑段开始位置和结束位置。如果tb为None,则tb被设置为原剪辑的duration,如果tb为负数,则tb被设置为剪辑的duration + tb
  • kwargs调用fx函数时需要传入的关键字参数

subfx实际上是调用基类Clip的fx方法来实现的。例如:

newclip = clip.subfx(vfx.speedx, 36,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_image方法是对fl方法的封装,传入的函数只负责处理当前帧的图像。

调用语法:fl_image(self, image_func, apply_to=None)

参数说明:

  • image_func:对剪辑帧进行图像变换的函数,仅一个参数就是要处理的帧,这个帧直接通过get_frame去获取,image_func函数的返回值为经过变换后的帧
  • apply_to:apply_to表示变换是否需要同时作用于剪辑的音频和遮罩,其值可以为’mask’、‘audio’、[‘mask’,‘audio’]

相对于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方法

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方法用于将当前剪辑放置到一个指定颜色背景的可能更大的剪辑上,用于在原始剪辑扩展大小时将空白处设置为指定颜色。返回值为处理后的新剪辑。

调用语法如下:

on_color(self, size=None, color=(000), pos=None, col_opacity=None)
  • color:设定的背景色RGB颜色组

  • pos:原剪辑对应的剪辑在新剪辑的框架上的位置

  • col_opacity:以背景色构造的底部剪辑的不透明度,0表示完全透明,为1表示完全不透明。

例如我们可以给视频添加一个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/mask/opacity/position)

set_audio方法将原剪辑的拷贝剪辑的音频设置为参数指定音频后返回新剪辑。

set_mask方法将原剪辑的拷贝剪辑的遮罩设置为参数指定剪辑后返回新剪辑。

set_opacity方法将原遮罩的颜色值与参数值相乘后返回。

set_position设置剪辑在合成剪辑的位置。调用语法:set_position(pos, relative=False)

pos:剪辑需要放置的坐标(x,y),x或y可以是如下值:

  • 像素坐标
  • (“center”,“top”):设定水平居中,垂直位置到顶部,类似的设置还有’bottom’、‘right’、‘left’
  • (factorX,factorY):基于剪辑的大小设置相对位置为(0,1)之间的浮点数。

例如我们将一个视频扩展到三倍宽度后分别放置:

threads = 4
clipVideo = VideoFileClip("t.mp4")
w, h = clipVideo.size
bgclip = clipVideo.on_color(
    size=(w*3+40, h+30), color=(00255), 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=(25500), use_bgclip=True)
newclip.write_videofile("r.mp4", threads=threads)
newclip.close()

处理结果:

image-20231201160444480

to_ImageClip方法

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方法

without_audio返回一个去除了声音的新剪辑。

afx方法

afx方法对原剪辑的声音进行变换返回新剪辑。调用语法:afx(fun, *a, **k)

本质上是执行了self.audio = self.audio.fx(fun, *a, **k)

VideoClip子类使用案例

DataVideoClip、UpdatedVideoClip、ImageClip、ColorClip、TextClip都是都是VideoClip的子类,它们都定义在模块文件VideoClip.py中。

DataVideoClip

DataVideoClip是VideoClip的直接子类,它的视频剪辑的连续帧都是从一系列数据集经过函数处理生成的,DataVideoClip类只有构造方法,没有独有属性和其他方法,因此DataVideoClip其实就是通过数据集经函数处理构造的视频剪辑。

构造方法:__init__(self, data, data_to_frame, fps, ismask=False, has_constant_size=True)

说明:

  • data:用于生成视频剪辑的原始数据集列表
  • data_to_frame:将data数据集的某一秒的数据处理为帧
  • fps:生成剪辑的帧率

核心代码是make_frame = lambda t: self.data_to_frame(self.data[int(self.fps*t)])

基于该make_frame函数构建VideoClip。

UpdatedVideoClip

UpdatedVideoClip是VideoClip的直接子类。

构造方法:__init__(self, world, ismask=False, duration=None)

参数world是一个有特殊要求的对象,该对象必须有一个属性clip_t、两个方法update和to_frame:

  • world.clip_t :与world状态对应的剪辑时间
  • world.update()方法:更新world的状态(包括增加clip_t一个时间步)的方法
  • world.to_frame()方法:根据world的状态生成一个帧的方法

对应剪辑生成t时刻的帧时,如果world的clip_t 小于t,则会循环执行world.update方法,直到clip_t 大于等于t,此时再调用world.to_frame()输出帧。

ImageClip

ImageClip是VideoClip的直接子类,用于生成固定不变的视频剪辑。ImageClip是从一个图像文件或内存中图像数组数据生成的视频剪辑,对应视频任何时候都是显示该图像。

构造方法__init__(self, img, ismask=False, transparent=True,fromalpha=False, duration=None)

参数说明:

  • img:任何图像文件或代表一个RGB图像的数组(例如一个视频剪辑的帧数据)
  • ismask:设置为True时,图片的Red通道值构建剪辑
  • transparent:设置为True时,图片的alpha层作为剪辑的遮罩,图像的背景层作为视频剪辑,当前剪辑是一个带有遮罩的剪辑
  • fromalpha:设置为True时,图片的alpha层构建剪辑

如果图片不带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)

原图:

image-20231207171557513

生成的视频结果:

image-20231207171248529

transparent默认为True,可以将png图片的透明度设置为剪辑的遮罩,而遮罩的透明度决定了其与其他剪辑合成的时候自身的透明度,png透明图片就可以完美的融合到目标剪辑中。

fl方法、fl_image方法和fl_time方法

  • fl方法:与父类的VideoClip.fl等效,但返回VideoClip对象
  • fl_image方法:与父类相同,实现上仅处理第一帧,再全部返回这被处理的第一帧数据
  • fl_time方法:ImageClip的fl_time方法对ImageClip不进行任何处理,但该方法可能会影响剪辑的遮罩或音频,返回ImageClip对象

ColorClip

ColorClip是仅显示同一种颜色的剪辑。

构造方法:__init__(self, size, color=None, ismask=False, duration=None, col=None)

参数说明:

  • size:剪辑的大小,一个宽和高组成的元组
  • color:如果ismask为False,则为RGB颜色(默认为黑色全0),如果ismask为True,则color代表灰度值,为一个0到1之间的浮点数(col已被废弃使用color替代)
  • ismask:是否将剪辑作为遮罩
  • duration:剪辑时长

示例:

colClip = ColorClip((360,360),color = (255,0,0),ismask=False,duration=5).set_fps(1)
colClip.write_videofile("red.mp4", codec='mpeg4')

该代码生成一个全红色的剪辑。

TextClip

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)

参数说明:

  • txt:需要在剪辑中显示的文字,这个参数和参数filename可以相互替换,二者同时存在时txt优先
  • filename:存储需要在剪辑中显示文字的文件名
  • size:剪辑的大小,如果method参数为’label’可以设置为None,由moviepy自动根据文字设定,但如果method参数为’caption’,则必须设置,此时高度也可以设置为None,由moviepy根据文字数量、大小以及宽度自动设置
  • color:文字显示的前景色,请参考下面介绍list方法时的说明
  • bg_color:剪辑的背景色
  • fontsize:文字字体大小
  • font:字体设置,字体和运行机器相关,在机器上能使用的字体请参考下面介绍list方法时的说明
  • stroke_color:文字轮廓线的颜色,如果为None则没有轮廓线
  • stroke_width:轮廓线的宽度,可以为浮点数
  • method:可以设置为’label’或’caption’,设置为’label’时,图片将自动调整大小以适合剪辑的大小,这是该参数的缺省值。设置为’caption’时,文字将在size参数指定范围内显示,此时文字会自动换行,但自动换行功能由于使用的ImageMagick库的问题有时不能正确工作
  • kerning:更改字母之间的默认间距。例如kerning=-1与默认间距相比,将使字母之间的间距更接近1个像素
  • align:文字的对齐方式,仅在method设置为caption时生效,确实值为center,可选择的值包括:center 、 East 、 West 、 South 、 North
  • interline:整型,以像素为单位的显示文字行之间的行间距,对应ImageMagick的命令行参数interline-spacing
  • tempfilename:中间输出的临时文件名,这个中间临时文件是将文本转换成png图片先输出时的临时文件,该临时文件后续被moviepy调用ImageClip的构造方法进行处理生成剪辑,如果需要查看临时文件图片可以指定该文件名好方便查看
  • temptxt:临时文件名,用于在显示文本通过参数txt传值时,处理时将显示文本临时输出到临时文件,创建临时文件的目的老猿经阅读源代码认为是为了统一txt传参和filename传参的处理方式。如果该参数为None,moviepy将自动创建一个随机的临时文件,否则使用指定临时文件名创建临时文件
  • transparent:如果要考虑图像的透明度需要设置为True,这也是缺省值
  • remove_temp:是否删除临时文件
  • print_cmd:是否输出提交给ImageMagick处理的命令

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

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')

参数说明:

  • filename:视频文件名,可以带路径
  • has_mask:是否有遮罩 , 如果视频包含rgba颜色,则可以将透明度作为遮罩层,但视频一般不支持。
  • audio:如果不希望加载视频文件的音频,可以将audio参数设置为False
  • audio_buffersize:音频文件读取缓冲区大小,字节为单位,一般用缺省值足够,如果audio_buffersize比一个音频帧的大小还要小,会自动使用音频帧的大小代替
  • target_resolution:设置为加载后需要变换到的分辨率,是(width, height)形式的二元组,允许其中一个为None,则自动等比例缩放。使用该方法调整视频读取的分辨率,往往比后期修改视频分辨率快很多。
  • resize_algorithm:要改变加载后的视频分辨率,可以通过resize_algorithm指定调整分辨率的算法,缺省值为 “bicubic”,还可以是 “bilinear” 、"fast_bilinear"等。关于算法的更多信息请参考:https://ffmpeg.org/ffmpeg-scaler.html
  • audio_fps:声音的采样频率
  • audio_nbytes:声音采样的位数
  • verbose:是否在标准输出设备上显示处理信息
  • fps_source:从视频的元数据metadata哪个数据中获取fps值,默认设置为’tbr’,但可以设置为’fps’,这可能有助于导入慢动作视频,否则可能会出意外。

close方法:构造方法会对视频文件进行加锁,并占用相关资源,如果要释放文件和资源,需要调用close方法或等加载处理的进程结束。

CompositeVideoClip合成

CompositeVideoClip是一种由其他视频剪辑组合构成一起播放的视频剪辑,这是大多数合成剪辑的基类。concatenate_videoclips在method参数设置为’compose’时,实际上是调用的CompositeVideoClip完成合成。

构造方法__init__(self, clips, size=None, bg_color=None, use_bgclip=False,ismask=False)

参数说明:

  • clips:多个视频剪辑的列表,列表中的每个元素都是VideoClip类型的对象。列表中的每个剪辑都将显示在列表中其后面出现的剪辑的下面(参考下面合成剪辑的一些属性处理逻辑的最后一条)。每个剪辑的pos属性决定剪辑放置在最终合成剪辑屏幕的位置,每个剪辑的mask遮罩属性决定每个剪辑哪部分可见哪部分不可见
  • size:最终剪辑的大小(分辨率),如果size为None,则将clips中第一个剪辑的size作为最终剪辑的size
  • bg_color:设置合成剪辑的背景色,背景色用于剪辑未填充且无遮罩的区域,如果要一个透明剪辑,则设置为None,否则为一个代表RGB颜色的三元组,如(0,0,0)代表黑色,也即透明色。该参数只有use_bgclip为False的情况下使用,实际上是通过构建一个由bg_color指定颜色的ColorClip来实现的
  • use_bgclip:如果列表中的第一个剪辑应用作所有其他剪辑的“背景”,则设置为True。第一个剪辑的大小必须与最后合成剪辑的大小相同。如果没有透明度,则最终剪辑将没有遮罩
  • ismask:最终合成剪辑是否为遮罩剪辑。

合成剪辑的一些属性处理逻辑:

  • 如果每个剪辑都设置了duration属性,合成剪辑的duration会自动计算,计算时取clips中所有剪辑的end属性值作为最终剪辑的end属性值和duration属性值
  • 如果第一个剪辑为背景剪辑且该剪辑没有遮罩,则最终剪辑的透明度为False,否则需要看是否设置了背景色bg_color,如果未设置,则最终剪辑透明度为True,否则为False
  • 最终剪辑的fps为clips中所有剪辑的最大者
  • 最终剪辑的音频是所有clips剪辑音频的合成
  • 最终剪辑的遮罩是所有clips剪辑的遮罩合成,如果所有剪辑无遮罩则创建一个完全不透明的遮罩
  • 当所有剪辑都未指定pos属性时,默认所有剪辑在最终合成剪辑的位置为顶部居中,此时相关剪辑的显示内容会有很多重合,最上层的剪辑是clips中最后一个剪辑,最下层的剪辑是clips中第一个剪辑,除了最上层的剪辑,其他剪辑的内容只能超出其上层所有剪辑大小的部分才可见

clips属性保存合成剪辑所需要的所有剪辑的列表,但不包含背景剪辑,即如果use_bgclip为True,则clips保存的为构造方法clips参数对应列表第二个及之后的所有剪辑,如果use_bgclip为False,则就是构造方法clips参数对应的列表。

**playing_clips方法:**用于判断clips属性中对应剪辑在参数指定的t时刻是否处于播放状态,对处于播放状态的剪辑存放到一个列表中返回。调用语法:playing_clips(self, t=0)

**close方法:**用于关闭音频及由CompositeVideoClip创建的背景剪辑,其他资源的释放不处理。

ImageSequenceClip

write_images_sequence方法用于将剪辑输出到一系列图像文件中,而ImageSequenceClip则基本上与write_images_sequence过程可逆,用于将一系列图像生成剪辑。

ImageSequenceClip是VideoClip的直接子类,该类自身只有构造方法,其他方法和属性都是继承自父类。

构造方法:

__init__(self, sequence, fps=None, durations=None, with_mask=True,
                 ismask=False, load_images=False)

参数说明:

  • sequence:参数sequence可以是如下三种之一

    • 一个仅包含图片的文件夹路径目录名,目录下的文件将按字母顺序排列作为处理的系列图像文件
    • 一个图像文件名的列表,在这种情况下开发者可以通过load_images参数控制选择将文件图像一次性加载到内存中
    • 一个表示图像的numpy数组列表,注意这种情况不支持遮罩
  • fps:每秒读入的图像帧数,该参数可以通过设定durations来替代

  • durations:每个图像在剪辑中显示的时长列表,因此可以给每个图像不同的播放时间,如果设置了fps,该参数将不起作用

  • with_mask:是否将PNG图像的alpha层作为遮罩

  • ismask:是否将生成剪辑作为遮罩

  • load_images:是否在处理前集中将图像文件加载到内存,如果为True,则对于sequence对应图像文件的情况,一次性的将所有图像文件先加载到内存,否则每处理一个文件加载一个。该参数仅对sequence为文件名列表的情况生效。

注意:ImageSequenceClip要求sequence对应的所有图像大小及表示像素的元组的大小都必须相同。

concatenate_videoclips合成同屏播放的视频

语法:

concatenate_videoclips(clips, method="chain", transition=None, bg_color=None, ismask=False, padding = 0)

method:拼接方法,有2种取值

  • “chain”:生成一个简单输出多个剪辑连续帧的剪辑,不论这些剪辑是否相同大小(分辨率)都不进行修正。如果参数列表中的剪辑都没有遮罩,则最终拼接的剪辑也没有遮罩,否则最终剪辑的遮罩就是所有参数剪辑遮罩的拼接,如果对应参数指定剪辑没有遮罩则使用完全不透明的剪辑作为遮罩。如果参数对应的多个剪辑有不同大小又想将拼接剪辑直接输出到文件,需要使用“compose”方法
  • “compose”:如果参数对应的剪辑不具有相同分辨率,最终分辨率将是使所有剪辑都不必调整大小的分辨率。因此,最终剪辑具有参数列表中最高剪辑的高度和最宽剪辑的宽度。所有尺寸较小的剪辑将显示为居中。如果mask=True,则边界为透明,否则为由“bg-color”指定的颜色。所有输入剪辑的最高FPS为最终连接剪辑的FPS

transition: transition指定一个将在列表的每两个剪辑之间播放的剪辑 ,即结果剪辑不但会将参数对应剪辑拼接,而且会在两个剪辑拼接中间插入一个由transition指定的过场剪辑

**bg_color:**仅在method="compose"时使用,设置背景色,如果要一个透明剪辑,则设置为None,否则为一个代表RGB颜色的三元组,如(0,0,0)代表黑色,也即透明色

**padding:**仅在method=“compose"时使用,两个连续剪辑间的间隔时间。负数的padding参数会导致两个剪辑出现重叠,会制造出后一个剪辑逐渐变暗退出的效果。一个非0的padding值会自动将method置为"compose”

clips_array视频的堆叠(同屏显示)

调用语法:

clips_array(array, rows_widths=None, cols_widths=None, bg_color = None)

参数说明:

  • 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")
img

常见的vfx变换函数

为了支持一些常规的变换处理,moviepy提供了一系列常用的变换函数,这些函数都在moviepy.video.fx包下,在moviepy.editor通过import moviepy.video.fx.all as vfx中将这些函数都加载到了vfx模块下,可以直接通过vfx.函数名方式调用。

与颜色和透明度相关的变换

blackwhite函数

blackwhite函数用于将剪辑变成灰度剪辑,也就是将剪辑中的彩色像素灰度化。

调用语法:blackwhite(clip, RGB = None, preserve_luminosity=True)

参数说明:

  • clip:要处理的剪辑,通过fx或subfx调用时,会将调用者的实例对象self传入
  • RGB:设置RGB三种颜色的权重,默认为1:1:1,如果设置为’CRT_phosphor’,则RGB权重= [0.2125, 0.7154, 0.0721]
  • preserve_luminosity:用于控制是否保持亮度,如果为True则RGB权重换算到和为1,否则不变。
  • 三个值相加为1。在这里的亮度luminosity不是lightness,实际上是对明度的度量,也称为灰阶值,是不同权重的R、G、B的组合值。

fadein、fadeout函数

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函数

invert_colors将像素对应颜色进行反转,核心实现:

maxi = (1.0 if clip.ismask else 255)
return clip.fl_image(lambda f : maxi - f)

mask_color函数

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函数将指定时刻位置的帧延时显示freeze_duration秒,相当于剪映中的定格效果。

调用语法:freeze(clip, t=0, freeze_duration=None, total_duration=None, padding_end=0)

参数说明:

  • t 和 padding_end:t为剪辑冻结位置的时间位置,单位为秒,但如果t的值为字符串’end’,则t=剪辑时长-padding_end
  • freeze_duration:该帧持续显示的时间,浮点数,单位秒
  • total_duration:表示剪辑的最终生成的总时长,当freeze_duration为None时有效。

freeze_region函数

功能说明:

freeze_region函数主要用于将剪辑中指定屏幕范围内容固定为参数指定的某个时刻的内容。

调用语法:freeze_region(clip, t=0, region=None, outside_region=None, mask=None)

注意:region,outside_region,mask只有第一个被设置的参数有效,例如region被设置时,outside_region和mask的设置会被忽略。

参数说明:

  • t:要固定显示内容在剪辑中的时刻
  • region:取t时刻region对应矩形的屏幕内容作为固定显示内容,矩形是一个四元组 (x1, y1, x2, y2),分别指定矩形的左上角和右下角的坐标
  • outside_region:取t时刻outside_region对应矩形外的屏幕内容作为固定显示内容
  • mask:将t时刻对应内容构建一个固定画面的剪辑并设置遮罩为参数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((360400), ismask=True, color=0.5, duration=30)
clipVideo3 = clipVideo.fx(vfx.freeze_region, t=15, mask=c2)

loop函数

loop函数将当前剪辑重复n次或重复至直到指定时间。

调用语法:loop(self, n=None, duration=None)

核心实现:self.fl_time(lambda t: t % self.duration)

然后设置目标时长。

speedx函数

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函数

time_mirror函数用于实现倒放效果,实现为:

self.fl_time(lambda t: self.duration - t, keep_duration=True)

time_symmetrize函数

time_symmetrize函数将当前剪辑和当前剪辑的倒放顺序合并。

与大小相关的视频变换函数详解

和剪辑大小相关的变换函数,包括crop、even_size、margin和resize。

crop函数

crop函数从剪辑中获取一个矩形区域的剪辑内容作为新的剪辑。

调用语法:crop(clip, x1=None, y1=None, x2=None, y2=None, width=None, height=None, x_center=None, y_center=None)

参数:

  1. x1、y1:矩形区域左上角坐标
  2. x2、y2:矩形区域右下角坐标
  3. width、height:宽度和高度
  4. x_center、y_center:表示x1的坐标为x_center-width/2,x2的坐标为x_center+width/2,y_center类似处理

源码核心计算逻辑:

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函数

even_size函数将剪辑的宽和高都变成偶数,存在奇数则丢弃一行或一列。

margin函数

在剪辑的四周增加一个外边框。

调用语法:margin(clip, mar=None, left=0, right=0, top=0, bottom=0, color=(0, 0, 0), opacity = 1.0)

说明:

  • mar:外边框的宽度,以像素为单位,如果mar指定了有效值,则忽略 left、right、top、bottom的设定
  • left、right、top、bottom:边框左、右、顶和底的宽度
  • color:边框颜色
  • opacity :边框的不透明度,如果为0表示完全透明,1则完全不透明。与其他视频堆叠合并时有效。

resize函数

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和mask_or函数

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、mirror_y函数分别将剪辑内容左右或上下颠倒。

调用语法:

mirror_x(clip, apply_to="mask")
mirror_y(clip, apply_to="mask")
如果apply_to=“mask”,则遮罩也进行同样处理。

rotate函数

rotate函数用于将剪辑逆时针旋转指定的角度或弧度。

调用语法:rotate(clip, angle, unit="deg", resample="bicubic", expand=True)

参数说明:

  • angle:根据时间返回旋转角度的函数,也可以指定角度常数
  • unit:默认为deg角度,若指定为"rad",则angle可以填写弧度会被自动转换为角度
  • resample:图像的重采样算法默认值为’bicubic’
  • expand:默认为True会扩展每帧剪辑的大小以容纳旋转后的所有图像内容,确保所有图像信息不丢失。设置会False,则每帧大小保持和原剪辑一致,旋转超出部分会被裁切

注意:

如果旋转角度是90度的倍数且expand=True,则直接使用numpy进行旋转,否则调用PIL库进行旋转。

若指定了expand=True且角度可能不为90度的倍数时,最好将所有帧的大小统一调整到一致,否则可能出现花屏的现象。

scroll函数

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:滚动速度,单位为:像素/秒
  • x_start,y_start:从剪辑的哪个位置开始滚动
  • apply_to:是否对遮罩等进行同样处理

经测试只有w或h小于剪辑原大小时,才能产生滚动的效果,在指定的大小内,滚动显示整个视频。是横向滚动还是纵向滚动由x_speed 和 y_speed 参数决定,也可以同时指定。

个人觉得没有需要这个函数的场景,真遇到可以自己编码实现任意自己想要的滚动效果。

supersample函数

supersample函数返回一个新剪辑,新剪辑每个帧的像素值被替换为该帧前后时段范围内的多个等间距帧的算术平均值。

调用语法:supersample(clip, d, nframes)

说明:

supersample返回的剪辑每个t时刻帧的像素的值计算方法如下:

  1. 将[t-d,t+d]时间段平均分成nframes个等间距时刻tt
  2. 然后取这个nframes个对应时刻的帧,最终颜色叠加取平均值作为当前帧的像素值

headblur函数

该函数依赖OpenCV,要求必须安装OpenCV后才能使用。安装命令:

pip install opencv-python

该函数用于给剪辑指定的位置增加圆形的模糊效果。

调用语法:headblur(clip,fx,fy,r_zone,r_blur=None)

参数:

  • fx和fy用于获取t时刻,需要模糊处理的坐标(x,y)。
  • r_zone表示被模糊化处理的半径。
  • r_blur值越大模糊效果越明显,默认值为2*r_zone/3

该函数的源码存在bug,完全可以自行实现。

修改点在于:

if r_blur is None: r_blur = 2*r_zone//3
# 和
im = gf(t).copy()

音频剪辑基类AudioClip

背景知识介绍

声音三要素:

  • 音调:人耳对声音高低的感觉称为音调(也叫音频)。音调主要与声波的频率有关。声波的频率高,则音调也高。
  • 音量:人耳对声音强弱的主观感觉称为响度。一般说来,声波振动幅度越大则响度也越大。
  • 音色:音色与声波的振动波形有关,或者说与声音的频谱结构有关。也就是音品。音色是人们区别具有同样响度、同样音调的两个声音之所以不同的特性,或者说是人耳对各种频率、各种强度的声波的综合反应。

数字音频常用概念

  1. 采样率(Sample Rate):每秒从连续信号中提取并组成离散信号的采样个数,它用赫兹(Hz)来表示。一般音乐CD的采样率是44.1kHz,通常视频转换器也将这个采样率作为默认设置。常用的音频采样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,采样频率越高音质越好,但资源消耗越高。22.05kHz的采样频率是常用的, 44.1kHz已是CD音质,超过48kHz的采样对人耳已经没有意义
  2. 采样位数(Sample Bits):也称采样精度,指使用数字表示声音信号的数字的二进制位数,位数越多表示声音的精度越高,存储消耗越大。1 字节(8bit) 只能表示 256 种声音值,2 字节(16bit) 可以表示 65536 个声音值,越高播放的声音与源音的差距越小
  3. 通道数(channel):也称为声道数,在录制声音时在前后左右几个不同的方位同时获取声音,每个方位的声音就是一个声道。声道数是声音录制时的音源数量或回放时相应的扬声器数量,有单声道、双声道、多声道。moviepy1.0.3的版本支持单声道和双声道。
  4. 帧(frame):帧记录了一个声音单元,其长度为样本长度(采样位数)和通道数的乘积
  5. 码率(Bit Rate):也称为位速或比特率,指视频或音频文件在单位时间内使用的数据流量,针对编码格式,表示压缩编码后每秒的音频数据量大小。计算公式:比特率 = 采样率 x 采样位数 x 通道数,单位kbps,这里的k为1000

AudioClip简介

AudioClip是一个音频剪辑的基类,其父类是Clip。AudioClip带有make_frame属性,该属性根据时间参数t返回一个列表,里面的元素是对应声道的numpy数组数据,数组的每个元素是-1到1之间的值。

构造方法:__init__(self, make_frame=None, duration=None, fps=None)

参数说明:

  • make_frame:根据时间构建帧的方法,被get_frame调用
  • duration:音频剪辑的时长
  • fps:采样率

iter_chunks方法

iter_chunks方法返回一个迭代器,通过这个迭代器可以返回一个包含音频剪辑内容块的数组。

调用语法:

iter_chunks(self, chunksize=None, chunk_duration=None, fps=None,quantize=False, nbytes=2, logger=None)

参数说明:

  • chunksize:块的大小

  • chunk_duration:块包含的音频时长,其值不为None时,则设置chunksize=int(chunk_duration*fps)

  • fps:块输出的采样频率,如果为None则等于剪辑的采样频率

  • quantize:是否量化处理,如果为True,则将音频帧对应的音频数据每个元素的值限制在[-0.99,0.99]范围内,然后乘以2的(8*nbytes-1)次方

  • nbytes:音频采用位数,缺省值为2字节即16位

  • logger:是否开启日志,字符串类型,"bar"表示进度条、None 表示不设置、或任何程序日志记录器的名字

to_soundarray方法

to_soundarray方法将音频片段转换为一个可以使用pygame播放或者使用wav格式保存的数组。

调用语法:to_soundarray(self, tt=None, fps=None, quantize=False, nbytes=2, buffersize=50000)

参数说明:

  • tt:为时间浮点数或时间浮点数的列表,用于获取对应时间的音频数据
  • fps:采用频率,如果为None则等于剪辑的fps属性
  • quantize:是否量化处理
  • nbytes:音频采用位数,缺省值为2字节即16位
  • buffersize:缓冲区大小,从剪辑中转换时,该大小即为处理块的大小

max_volume方法

max_volume方法是取音频剪辑的最大音量,最大音量也就是音频数组元素绝对值的最大值。

调用语法:max_volume(self, stereo=False, chunksize=50000, logger=None)

  • stereo:是否立体声,该参数为True且剪辑的声道数为2才会在处理时作为立体声处理,否则作为单声道处理

write_audiofile方法

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')

参数说明:

  • filename:文件名,类型包括文件如mp3、wav、ogg、m4a等都可以
  • fps:帧率,与音频采样率含义相同,每秒编码的帧数,如果为None且音频剪辑设置了fps则以剪辑额的fps属性值作为输出,否则以缺省值44100输出
  • nbytes:音频的采用的位数
  • buffersize:输出缓冲区大小,以该大小作为输出时数据读取块的大小
  • bitrate:码率,音频比特率,字符串形式,如“50k”、“500k”、“3000k”,用于将确定输出文件中音频的大小/质量。请注意,这主要是一个指示性目标,输出文件的比特率不一定会按此设置
  • codec:用于音频编码的编解码器,如果没有指定则系统根据输出文件名类型来确认。默认值为“libmp3lame”,除非视频扩展名为“ogv”或“webm”,在这2种情况下,默认值为“libvorbis”。如果是16位wav音频设置为 ‘pcm_s16le’、32位wav音频则设置为 ‘pcm_s32le’,对应音频类型和使用的编解码器如下:
    • ogg:libvorbis
    • mp3:libmp3lame,
    • wav:pcm_s16le,、pcm_s24le,或pcm_s32le,根据采样位数确认
    • m4a:libfdk_aac
  • write_logfile:如果为True,将为音频输出记录日志文件。日志文件将以“.log”结尾,包含输出文件的名称
  • verbose:已经废弃使用,留下来是为了兼容性,以前用于打开/关闭消息。现在使用logger=None。
  • ffmpeg_params:需额外传递的其他ffmpeg参数,用列表传递,形如:[’-option1’,‘value1’,’-option2’,‘value2’]
  • logger:字符串类型,"bar"表示进度条、None 表示不设置、或任何程序日志记录器的名字

音频文件类AudioFileClip

AudioFileClip是AudioClip的直接子类,用于从一个音频文件或音频数组中读入音频到内存构建音频剪辑。但AudioFileClip并不将整个音频文件装入内存,而是将部分内容读入和保存到内存,读入的部分包括当前最后一个读取的帧以及该帧前面和后面的部分帧。

构造方法__init__(self, filename, buffersize=200000, nbytes=2, fps=44100)

参数说明:

  • filename:声音文件(可以是所有ffmpeg支持的音频文件类型,如mp3、wav、ogg、m4a等)或ffmpeg视频文件的的名字或者表示声音的数组。如果声音文件不是wav文件,moviepy首先使用fps、nbytes、bitrate将其转换成wav文件
  • buffersize:装入内存部分的大小,对应大小为音频帧的数量
  • nbytes:声音位数
  • fps:音频采样率,默认为44.1kHz

AudioFileClip主要有如下属性,分别是:

  • nchannels:原始音频帧的通道数
  • fps:加载后的音频采样率
  • buffersize:保存的构造方法的buffersize参数
  • filename:原始音频文件名
  • reader:当前AudioFileClip使用FFMPEG读取音频数据的对象,读取音频数据可以来源于是FFMPEG音频文件或视频文件,读取完的数据会被转换成原始数据。

coreader方法返回当前音频剪辑的一个副本拷贝。

coreader(self)

close方法关闭当前AudioFileClip的reader对象,其语法非常简单,就是close(self)。

音量调整函数

volumex(clip, factor)可以分别调整多个声道的音量,例如将右声道静音,左声道音量调整为原来的一半:

audio.volumex([0.5,0])

audio_normalize(clip)函数将最大音量调整到1,其他声音等比例缩小。

万水千山总是情,点个 👍 行不行
·················END·················

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
[旅游] 旅行第15季085城 - 冰岛Keflavík小镇 蓝湖温泉 地热发电站最近!没发烧,鼻子莫名发痒、堵塞的,立即打开这篇!【赠送GPT账号】如何使用ChatGPT完成科研、程序开发、论文写作等,看看这篇!「think step by step」还不够,让模型「think more steps」更有用《纽约客》万字长文还原OpenAI“宫斗”内幕世界上最令人羡慕的人去世了,万字长文回顾他的一生大模型学会写万字长文,AI企业「波形智能」完成千万元Pre-A轮融资|早起看早期万字长文:一文详解单元测试干了什么“龙年吉祥!”新西兰总理发长文,向华社送新年祝福![万字长文]美欧联手推动物联网安全重大合作,中国版物联网安全标签计划推出刻不容缓双林奇案录第三部之鹤鼎莲方壶: 第四节最近!没蚊子了,身上还莫名痒痒的,立即打开这篇!奥司他韦必须吃够5天?退烧后能不能停药?怎么吃娃不会吐?看这篇!《未来的底片》背后的意象感恩节采购看这篇!缺德舅节日好物指南[旅游] 旅行第15季088城 冰岛首都 Reykjavík 的冬天Python实战 | 使用 Python 和 TensorFlow 构建卷积神经网络(CNN)进行人脸识别中国汽车超日本成全球第一的逻辑(万字长文)万字长文:2024中国文娱产业的10个预判 | CEIS2024现场实录①里根,这个自称让美国更伟大的总统是如何让美国一代不如一代的网购大衣前一定要看这篇!五月天主唱阿信深夜发长文,回应“假唱风波”!人民日报发声!媒体:8场演唱会票房超6亿……前排目击历史!万字长文讲清中国汽车出口超日本成全球第一的逻辑一篇文章带你了解Python常用自动化测试框架——Pytest大模型学会写万字长文,AI企业「波形智能」完成千万元Pre-A轮融资|36氪首发刚刚!吴亦凡二审判决结果公布!吴亦凡妈妈发长文,透出不一般的信息…哈佛商学院疯传的万字长文PDF,曝光金融圈“矿工”真实一面…万字长文:为什么AI陪伴产品都想抄星野?查理芒格去世了,万字长文回顾他的一生三万字长文:JVM内存问题排查Cookbook太绝了!这2个AI摘要神器,帮我5秒读完万字长文!万字长文再论大语言模型的位置编码及其外推性蕙莲 第拾叁章女人的智商和勇气230页长文,涵盖5大科学领域,微软团队使用GPT-4探索LLM对科学发现的影响
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。