VCBStudio教程06VapourSynth基础与入门.docx

上传人:b****4 文档编号:5420737 上传时间:2023-05-08 格式:DOCX 页数:12 大小:21.19KB
下载 相关 举报
VCBStudio教程06VapourSynth基础与入门.docx_第1页
第1页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第2页
第2页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第3页
第3页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第4页
第4页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第5页
第5页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第6页
第6页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第7页
第7页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第8页
第8页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第9页
第9页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第10页
第10页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第11页
第11页 / 共12页
VCBStudio教程06VapourSynth基础与入门.docx_第12页
第12页 / 共12页
亲,该文档总共12页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

VCBStudio教程06VapourSynth基础与入门.docx

《VCBStudio教程06VapourSynth基础与入门.docx》由会员分享,可在线阅读,更多相关《VCBStudio教程06VapourSynth基础与入门.docx(12页珍藏版)》请在冰点文库上搜索。

VCBStudio教程06VapourSynth基础与入门.docx

VCBStudio教程06VapourSynth基础与入门

VCB-Studio教程06:

VapourSynth基础与入门

0.前言

BDRip压制的核心是批处理,会使用avs/vs对片源做预处理,是一个动漫ripper的入门。

历史上,AviSynth是最早成体系的,为动漫Rip设计的非编。

而VapourSynth在2014年初基本完善可用,现在逐步替代AviSynth。

想学习的人都会碰到这个问题:

如果我只学一个,我该学哪个?

如果我两个都学,我需要先学哪个?

解答这个问题就需要先对比一下avs和vs的优劣:

avs落后,vs先进;

avs教程多,vs教程少;

avs支持广,vs支持窄;

avs不规范,vs规范;

avs入门简单,深入难,vs入门难,深入简单

……

VCB-Studio系列教程选择先介绍VS,原因在于,VS是现在vcb-s的主要产能,而且vs的规范性使得介绍基础知识的时候,更容易让初学者理解和掌握。

VapourSynth主页:

官方使用文档:

在线词典:

 

1.简单的vs脚本

在之前的教程中,我们有给过例子:

importvapoursynthasvs

importsys

importhavsfuncashaf

importmvsfuncasmvf

core=vs.get_core(threads=8)

_cache_size=2000

source="00001.m2ts"

ripped="Symphogear"

src16=.LWLibavSource(source,format="yuv420p16")

rip16=.LWLibavSource(ripped,format="yuv420p16")

res=.Interleave([src16,rip16])

res=(res,full=False,depth=8)

_output()

 

作为Python的一个扩展,vs脚本本质上是Python的脚本。

在最开始我们需要载入(import)各种库,除了必须的VapourSynth核心,还有mvf(maven'sVapourSynthfunctions)和haf(holy'sVapourSynthfunctions)

core=vs.get_core(threads=8)

_cache_size=2000

这两句是载入vs运行环境,并且指定最大使用线程数和内存(MB)

接下来的部分,vs主要依赖赋值语句完成。

一个赋值语句的格式为:

变量=表达式

比如source,ripped,src16,res等就是变量。

Python的变量不需要声明,自动会判断是视频系列(clip),整数(int),还是字符串(string)等类型。

表达式,则有多种形式:

.直接赋值,比如source="00001.m2ts",debug=True,res=dbed这种直接用值来给定的,值可以是具体的数值,值可以是具体的数值,也可以是其他的变量(比如res=dbed,dbed就是另一个变量);

.简单运算,比如strength=80/100,output_depth=debug?

8:

10这样的运算;

这里详细讲一下表达式A?

B:

C的计算。

A叫做判断式,必须是一个布尔类型的表达式(只有True/False,或者1/0),B和C则是可能返回的值。

x=A?

B:

C等效于if(A)x=B;elsex=C;如果A成立,则x赋值为B,否则x赋值为C

比如说:

False?

0:

1返回的是1,因为判断式不成立,所以返回两个值中的后者

debug?

8:

10如果debug是True/1,则返回8,否则返回10

x<10?

10:

x<100?

100:

200是一个嵌套性的语句;拆开来看:

if(x<10)return10;

elseif(x<100)return100

elsereturn200

当x小于10的时候,返回10;当x在10-99的时候,返回100,否则,返回200

.函数赋值,res=.Interleave([src16,rip16]),这句就是调用.Interleave()这个函数,输入src16和rip16(严格来说,是它们用[]运算符,运算而出的结果,那就是它们的顺序组合),作为输入变量,来计算一个新的值。

最后,vs的输出,通过set_output()来完成。

_output()就是输出res这个值。

2.VS函数的调用

可以想象,vs脚本的本体是由大量的函数调用实现的。

函数的调用方式一般为:

n2…FunctionName(parameter1,parameter2,……)

domain是函数所在的库,比如.Interleave就是一层Core,二层std,下面才是函数名称Interleave。

或者(),只有一层mvf.

parameters是函数输入的变量,数值不定。

函数的输入,一般doc中有非常明确的规定。

比如说根据LWLibavSource的doc:

LWLibavSource(stringsource,intstream_index=-1,intthreads=0,intcache=1,intseek_mode=0,intseek_threshold=10,intdr=0,intfpsnum=0,intfpsden=1,intvariable=0,stringformat="",intrepeat=0,intdominance=1,stringdecoder="")

LWLibavSource一共可以接受source,stream_index,……decoder等14个输入;

这14个输入有着自己的类型要求,比如source要求是string,stream_index要求是int,等等;

这14个输入并非在调用的时候都需要有赋值。

除了source之外所有变量都有设定默认值/缺省值/defaultvalue,因此如果你不设定,这些输入自动设定为默认值。

在手动输入参数的时候,有两种方式:

1.赋值性传递/关键字传递(keywordargument),表现为A=B的形式,比如format="yuv420p16"。

这样的赋值,系统会先去找函数输入中有无一个叫做A的参数,如果有,把B的值给A;

2.直接传递/位置性传递(positionalargument),表现为直接放一个C。

比如:

filename="00001.m2ts"

src16=.LWLibavSource(filename,format="yuv420p16")

这里函数内第一个输入的是filename,它没有以赋值性的语句输入,那么系统判定为直接传递,传递的内容是source这个表达式,表达式求值得出,filename是一个变量,值为"00001.m2ts"。

所以系统会把"00001.m2ts"传递给函数第一顺位的输入,也就是source。

 

以上的脚本还等同于:

src16=.LWLibavSource("00001.m2ts",format="yuv420p16")。

这种是直接把值作为表达式,而不是再用变量传递;

filename="00001.m2ts"

src16=.LWLibavSource(source=filename,format="yuv420p16")

或者src16=.LWLibavSource(source="00001.m2ts",format="yuv420p16")

这种就是用赋值性传递,来干相同的事情。

source="00001.m2ts"

src16=.LWLibavSource(source=source,format="yuv420p16")

或者src16=.LWLibavSource(source,format="yuv420p16")

这种写法也是合法的。

注意这里source=source的意义:

前一个source,系统会在函数输入中寻找对应,后一个source,系统会在当前脚本中做表达式求值。

同理,直接写一个source,系统会先计算source作为一个表达式的值,再去以直接传递的方式去传递给函数。

 

3.函数中参数传递的机制

vs关于函数变量传递,遵循着这样的机制:

1.所有直接传递,必须在变量性传递之前。

比如LWLibavSource(source,format="yuv420p16")是可以的,而LWLibavSource(format="yuv420p16",source)在syntax上出错。

2.传递的过程中,先把所有变量性传递的参数给传递好,剩下没有被传递的参数,一个个按顺序,把直接传递的值给赋值过去。

比如说.MaskedMerge这个函数:

dMerge(clipclipa,clipclipb,clipmask[,int[]planes,bintfirst_plane=0])

从doc看,这个函数输入5个input:

clipa,clipb,mask,planes,first_plane.其中前三个没有默认值,因此必须在调用的时候输入;后两个用[]裹起来,意思是可以不输入。

first_planes有默认值0,planes因为是需要一个整数数组,长度未知因此没有默认值(读了doc就知道它在调用时候,知道了数组实际长度后,默认的赋值。

假设我们已经算好了edge,nonedge,mask这三个clip:

.MaskedMerge(nonedge,[0,1,2],False,mask=mask,clipb=edge)

系统会先把mask代表的值,传递给函数中mask这个input,然后把edge代表的值,传递给clipb这个input;

剩下clipa,planes,first_plane这三个没有输入的input,系统把nonedge传递给clipa,[0,1,2]传递给planes,False传递给first_clip。

所以它等效为:

.MaskedMerge(clipa=nonedge,clipb=edge,mask=mask,planes=[0,1,2],first_plane=False)

或者.MaskedMerge(nonedge,edge,mask,[0,1,2],False)

如果传递的过程中,某一个input被输入了两次(这种情况只可能是重复用赋值性传递来输入,想想为什么?

),那么vs会报错;

如果传递完毕后,必须输入的变量(doc中没有默认值,也没有用[]框起来)并未完全赋值,那么vs也会报错;

传递过程中,如果输入值的类型跟变量定义类型不匹配,比如你把一个字符串给了整数类型的变量,vs也会报错。

从代码可读性和减少出错的角度说,应该永远鼓励赋值性传递。

 

VS函数传递,可以允许嵌套以及串联。

比如说:

src16=.LWLibavSource(source="00001.m2ts")

res=.RemoveGrain(src16,20)

 

用嵌套的写法:

res=.RemoveGrain(.LWLibavSource(source="00001.m2ts"),20)

就是直接将函数作为一个表达式

用串联的写法(必须是vs规范的函数,比如都是core下面的):

res=.LWLibavSource(source="00001.m2ts").eGrain(20)

串联的时候,后续的core可以省略。

效果是将前面生成的clip,作为下一个函数,第一个直接传递的值。

4.一些简单的视频编辑

在本章中,我们讲述一些vs中常见的用法,方便大家学习和上手

 

4.1裁剪和缩放

裁剪靠的是el,缩放靠的是e36

doc分别为:

 

假设我们读入一个原生4:

3,通过加黑边做成1920x1080的视频,我们先把它切割成1440x1080(就是左右各240个像素),然后缩放成720p:

src=…

cropped=.CropRel(clip=src,left=240,right=240)

res=e.Spline36(clip=cropped,width=960,height=720)

_output()

看doc就知道,恰好所有的输入都是滤镜要求的前三顺位,所以上述代码可以简化为(用串联写法):

src=…

.CropRel(src,240,240).e36(960,720).set_output()

4.2分割与合并

分割靠的是()

合并靠的是()

以下是整个vcb-s教程体系中,我们对帧数标号的规定:

在绝大多数场合下(除了mkvtoolnix),视频的帧数是从0开始标号的。

简单说,如果一个视频有1000帧,那么所有帧的标号为:

0,1,2…999

mkvtoolnix是从1开始标号的:

1,2,3…1000。

然而,除非指定了是mkvtoolnix,任何讨论都假设帧数从0开始标号。

无论从0还是1开始标号,总帧数=末号-首号+1

如果我们说从a帧到b帧,我们默认是包括首尾的。

比如20-100帧,就是20,21,…99,100帧,一共是100-20+1=81帧。

回到Trim的用法:

(clipclip[,intfirst=0,intlast,intlength])

clip是必须输入的,first指定从哪一帧开始切割(默认是0),然后last和length两个指定一个。

(doc中告诉你如果两个都指定了会报错。

)如果不用赋值传递,比如(clip,20,100),那么输入的100会被判为last,因为last的序位在前

确定了first和last,Trim会切出clip从first到last的所有帧,注意是包括首尾的,总帧数为last-first+1;

如果是指定length,Trim会切出clip从first开始,一共length帧,这时候等效于指定last为length+first-1。

vs中有继承自Python的语法糖(SyntacticSugar)帮助你简单的写Trim:

video=clip[20:

101]

相当于video=.Trim(clip,20,101-1)

注意Trim里面写法是从x到y,语法糖写法是[x:

y+1],然而这两个效果都是切出从x到y这y-x+1帧。

因为在avs里面,切割也是用trim这个函数名称,写法和规则也是从x到y,所以实际操作时候,分割视频建议不要采用语法糖写法,而是坚持用传统的用法。

不然容易造成队友的混淆(因为量产中需要队友自己改Trim参数)

合并的Splice比较简单:

longvideo=.Splice([video1,video2])

就是把video1和video2按照顺序前后合并。

这么写要求video1和video2的尺寸和像素类型(比如同为YUV420P8)必须一致,帧率等其他性质可以不一致。

如果你要强行把两个尺寸或者像素类型不同的视频合并,vs也能办到:

longvideo=.Splice([video1,video2],mismatch=1)

不过实际操作中少有这样的例子就是了,毕竟不同尺寸和类型在一起加工限制很多,一般都需要你先转换统一格式,再合并。

如果要合并多个视频,只要增加数组就好了:

longvideo=.Splice([video1,video2,video3])

 

合并的写法更推荐用语法糖(avs里面就是这种写法):

longvideo=video1+video2+video3

简单明确易懂。

这时候要求video1,video2和video3的尺寸和像素类型必须一致,帧率等其他性质可以不一致,相当于默认mismatch=False。

 

4.3简单的降噪,去色带和加字幕

降噪用的是eGrain(),去色带用的是d(),加字幕用的是nder()

到这个点总该会自己去找doc了吧。

提示:

先从vsdoc主页右下方的search入手,找不到就Google关键字:

滤镜vapoursynth

src=…

nr=.RemoveGrain(src,[11,4])

dbed=core.d(nr,12,32,24,24,0,0)

res=nder(dbed,"")

_output()

尝试自己找到doc,对应着看看,每一个参数都是输入给哪个input,这个input的意义(至少在doc里字面意义)是什么。

5.VS里面对视频性质(clipproperty)和帧性质(frameproperty)的读取

vs里面可以直接读取一些关于视频和帧本身的性质,比如说视频的总长度,帧率,一帧的长宽,类型等。

这部分在中有详细解释,我们只列举最常用的几个:

_frames返回clip的总帧数。

所以要切掉视频的首帧(第0帧),可以这么写:

res=.Trim(clip,1,_frames-1)

t返回clip的宽和高。

比如我们想缩放到1/2大小:

res=e.Spline36(clip,//2,t//2)

#注意这里//2是做整数除法,出来的类型是int,否则/是浮点数除法,出来类型是float。

#而Resize类型滤镜要求输入int类型的数据。

 

顺道说一句关于Python的注释,#在Python中是注释掉后面一行字的作用,相当于C/C++中的//

如果想要大面积注释,类似/****/

可以用''''''

6.选择分支和Python中的缩进(indentation)

Python作为一个全能性编程语言,对很多现代编程中的概念都支持,最基础的选择分支和循环等自然不在话下。

这里我们举个列子:

src16作为源,res作为处理后准备输出的clip。

我们设置一个开关Debug,如果Debug=1/True则将src16和res交织输出,并转换为8bitRGB,否则将res转为YUV-10bit准备送给编码器:

Debug=0

ifDebug:

res=.Interleave([src16,res])#Interleave是将输入的视频一帧帧间隔显示

res=(res,full=False,depth=8)#ToRGB的作用是转为RGB,bitdepth已经指定为8

else:

res=.bitdepth(res,bits=10)#bitdepth是做精度转换

如果用类似C的伪代码写,大概风格为:

Debug=0;

if(Debug){

res=.Interleave([src16,res]);

res=(res,full=False,depth=8);

}else

res=.bitdepth(res,bits=10);

可见,Python里面没有类似{}来把一段代码组合起来,Python用的是缩进。

不同缩进层次来区分不同组合。

比如说:

res=.Interleave([src16,res])

res=(res,full=False,depth=8)

这两句都是通过一个tab来缩进,所以这两个相当于被大括号给框住,成为一段。

如果是:

Debug=0

ifDebug:

res=.Interleave([src16,res])

res=(res,full=False,depth=8)

执行逻辑就截然不同了;res=(res,full=False,depth=8)这句是跟if并列的,一定会在if语句做完之后被执行。

 

Python对缩进非常严格,任何不匹配都会报错。

注意tab和空格不可等同,哪怕在你看来4个空格等于一个tab。

 

其他一些VS的高级用法,比如runtime机制,比如自定义函数,我们会在以后的教程中详细说。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 党团工作 > 入党转正申请

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2