Python音视频开发:moviepy中剪辑基类Clip详解

Karen110
• 阅读 1655

一、概述

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

Python音视频开发:moviepy中剪辑基类Clip详解

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

说明:

VideoClip的子类还有DataVideoClip、UpdatedVideoClip这2个。

二、Clip类的属性

2.1、start属性

start属性用于多个子剪辑拼接成一个剪辑的场景,每个子剪辑通过设置自己的start来指定自己在合成后的剪辑中的播放开始时间,该属性通过方法set_start来设置。

set_start语法:

set_start(self, t, change_end=True)

说明:

该方法会返回一段调用者的浅拷贝剪辑,该拷贝剪辑的start会被设置为t。
其中:

  • t为需要设置的开始时间,表示方法可以是如下四种之一:

  • 秒,为一个浮点数数字,如75.35;

  • 分钟和秒组成的元组,如(1,15.35);

  • 时、分、秒组成的元组,如(0,1,15.35)

  • 用冒号分隔的时间字符串,如‘0:1:15.35’

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

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

  • 如果为False,则copy剪辑的end值保持不变,当end存在有效值时,copy剪辑的duration属性被设置为end-start

  • 如果为True,且剪辑duration存在有效值,则拷贝剪辑的end属性被更改为:start+duration

2.2、end属性

end属性用于多个子剪辑拼接成一个剪辑的场景,每个子剪辑通过设置自己的end来指定自己在合成后的剪辑中的播放的结束时间,该属性通过方法set_end来设置。

set_end语法

set_end(self, t)

说明:

  • 类似set_start方法,该方法返回一个调用者的拷贝剪辑,该拷贝剪辑的end被设置为t

  • 如果该拷贝剪辑设置了start属性,则剪辑的duration属性会被设置为end-start,start属性保持不变

  • 如果该拷贝剪辑未置start属性,则duration属性保持不变,start属性被设置为max(0,end-duration)

2.3、duration属性

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

set_duration调用语法:

set_duration(self, t, change_end=True)

说明:

  • 返回一段调用剪辑的拷贝,拷贝剪辑的时长被设置为t

  • 如果change_end 为True,且t为None,拷贝剪辑的end被设置为None

  • 如果change_end 为True,且t为有效时间,拷贝剪辑的end被设置为start+t

  • 如果change_end 为False,且t为None,则报异常

  • 如果change_end 为False,且t为有效时间,拷贝剪辑的start被设置为end-t

2.4、memoize属性

memoize属性用于控制剪辑是否应保留内存读取的最后一帧,如果为True保留,否则不保留,默认值为False,可以通过方法set_memoize进行修改。

set_memoize 调用语法:

set_memoize(self, memoize)

2.5、memoized_t、memoized_frame属性

当memoize为True时,则memoized_t、memoized_frame用于保存最后一次读取的帧的位置和对应的帧。当memoize为True时,这两个属性在get_frame方法中修改。

三、Clip类的方法

3.1、copy方法

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

调用语法:copy(self)

注:

  1. 浅拷贝就是复制一个对象时,仅复制该对象直接属性的内容到新对象,但对象下的属性如果指向了一个其他对象时,只复制了这个指向其他对象的引用,不复制其他对象的内容,实际上新拷贝对象指向对象与原对象属性指向的属性对象完全相同。

  2. 深度拷贝是指针对浅拷贝来说,深度拷贝复制对象及其下所有层级的数据每个层级都是复制一份,每个层级的指向对象相对复制对象都会发生变更。这样复制后复制对象及其下的元素和原对象的变更都不会相互影响。

3.2、get_frame方法

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

调用语法:get_frame(self, t)

该方法执行时:

  1. 如果剪辑的memoize属性为True,且t=memoized_t,则直接返回memoized_frame;

  2. 否则使用self.make_frame(t)方法构造一个帧返回,并根据memoize的值确认是否记录memoized_t、memoized_frame。

注意:

get_frame方法看起来很简单,但实际上很多剪辑的变换处理就是在该方法中完成的,这个变换是由make_frame来完成的。make_frame方法在Clip类中没有静态定义,这个是由要对帧进行访问的地方(如具体类型的剪辑子类中读取帧或其他对帧进行变换处理的地方)来定义赋值的,也就是make_frame是真正对帧进行读取或变换处理的方法。make_frame没有静态定义,只能动态赋值,动态赋值是通过调用方法set_make_frame实现的(注意调用set_make_frame也会返回一个新剪辑,改变的是返回剪辑的make_frame,调用者还是不受set_make_frame的影响)。实际上make_frame可能包含两部分逻辑,一部分是必须的真正的帧数据读取过程,一部分是可选的对帧数据进行变换处理的过程。在Clip类中提供的通用变换处理fl方法就是通过这种方式来实现帧变换的。请参考fl方法的说明。

3.3、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)的返回值进行变换处理

示例代码:

下面的代码表示一个h像素高剪辑的视频内容帧的下半部部分被剪辑掉,newclip剪辑的高度变为h/2。

src = VideoFileClip(r"F:\video\抖音-爱拼才会赢.mp4")      
h = src.size[1]  
fl = lambda gf,t : gf(t)[int(t):int(t)+h/2, :]  
newclip = src.fl(fl, apply_to='mask')  
newclip.write_videofile(r"F:\video\抖音-爱拼才会赢_re.mp4"  
12345

备注:

上面这段代码在fl方法的例子内是作为滚屏来介绍的,但最后结果变成了裁剪掉50%的内容,是因为t值的变化很小,导致相邻帧看起来没什么变化导致,如果要实现滚屏需要调整这个变换方法,具体方法老猿还在反复测试,也希望通过这个来更深入了解fl变换。另外moviepy还提供了滚屏实现方法,那就是vfx.scroll,以后再研究。

3.4、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方法实际上是将新剪辑t时刻的帧设置为原剪辑t_func(t)时刻的帧,如果t_func(t)结果对应时间范围超出了原剪辑,对于视频帧则直接取原剪辑的最后一帧(t_func(t)大于end)或最开始帧(t_func(t)<0),否则可能报错。更多内容请参考《moviepy音视频剪辑:使用fl_time进行诸如快播、慢播、倒序播放等时间特效处理的原理、代码实现以及需要注意的坑》

3.5、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)

3.6、set_fps方法

set_fps方法非常简单,就是用于设置fps的值,调用方法为:set_fps(self, fps),但该方法修改的不是调用剪辑对象的值,而是调用对象剪辑的浅拷贝对象的fps值,返回对象为新剪辑。

3.7、set_ismask方法

set_ismask方法非常简单,就是用于设置是否有遮罩,调用方法为:set_ismask(self, ismask)`,但该方法修改的不是调用剪辑对象的值,而是调用对象剪辑的浅拷贝对象的ismask值,返回对象为新剪辑。

3.8、is_playing方法

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

调用语法:is_playing(self, t)

说明:

如果t是一个时间,且位于剪辑的start和end之间,则返回True,否则返回False。
如果t是一个numpy数组,如果数组的所有元素代表时间都不在剪辑的start和end之间则返回False,否则返回一个向量[b_1, b_2, b_3…], 如果数组指定位置i的时间在剪辑的start和end之间,则b_i为True,否则为False。

3.9、subclip方法

subclip方法用于从调用剪辑中取指定的剪辑段构造一个新剪辑对象返回,原剪辑保持不变。

调用语法:subclip(self, t_start=0, t_end=None)

说明:

返回调用剪辑的t_start到t_end之间的剪辑段,如果t_end为None,则t_end被设置为剪辑的duration,如果t_end为负数,则t_end被设置为剪辑的duration + t_end。

3.10、cutout方法

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

调用语法:cutout(self, ta, tb)

说明:

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

例子:

from  moviepy.editor import *  
from  moviepy.Clip import *  
clip = VideoFileClip(r"F:\video\WinBasedWorkHard.mp4")  
newclip = clip.cutout(1,5)  
newclip.write_videofile(r"F:\video\WinBasedWorkHard_new.mp4")  
12345

3.11、iter_frames方法

iter_frames方法不是一个剪辑编辑的方法,其功能是为了迭代访问剪辑的所有帧,返回值为一个迭代器,每迭代一次返回下一帧。

调用语法:

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

说明:

  • iter_frames方法将剪辑的每帧作为一个HxWxN的numpy数组返回,N为1表示遮罩剪辑帧,N=3为RGB剪辑帧

  • 如果剪辑本身有fps参数,则fps为可选参数

  • 使用图片流编写视频时,dtype设置为“uint8”

  • 如果参数with_times 为True,迭代返回对应帧的位置时间和帧本身,否则只返回对应帧

  • logger参数没有作用,会被强制使用进度条模式显示日志信息

3.12、close方法

释放剪辑使用的所有资源,实际上该方法目前没有意义,其代码为“pass”,资源的释放应该在其子类实现。

关于原文链接

本文在CSDN的“老猿Python”首发,头条号、微信公众号和百家号“老猿Python”转载,由于这些平台对外部链接的限制,文中不能包含外部链接。如果是头条请点击文章底部最下方的“了解更多”跳转CSDN阅读原文,如果是微信公众号请点击文章底部最下方的“阅读原文”跳转CSDN阅读原文,否则请在百度搜索或CSDN搜索中输入"CSDN老猿Python”加文章标题关键字搜寻本文。

本文转转自微信公众号老猿Python原创https://mp.weixin.qq.com/s/xrUC8BpIt2RsUcXXb6fghA,如有侵权,请联系删除。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Wesley13 Wesley13
3年前
4cast
4castpackageloadcsv.KumarAwanish发布:2020122117:43:04.501348作者:KumarAwanish作者邮箱:awanish00@gmail.com首页:
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这