Skip to content

Latest commit

 

History

History
1382 lines (977 loc) · 95.2 KB

高清视频转码进阶指南.md

File metadata and controls

1382 lines (977 loc) · 95.2 KB

高清视频转码进阶指南

说明:并非所有内容都是100%正确的,部分内容仍待完善。 源项目地址:https://git.concertos.live/Encode_Guide/Encode_Guide/

1. 工具

1.1 eac3to

尽管我们并不推荐使用闭源软件,但毫无疑问,在没有真正优秀的方案的情况下, eac3to^1^ 是一个很有用的分流处理工具。而如果你不想下载闭源软件(更不要说支持它),你也可以简单地使用带有一个单独的分流器的 MKVToolNix^2^ 。

对于 Windows 用户,只需在 Doom9 (译者注:一个国外压制论坛)即可简单地下载到。Unix 用户必须通过 Wine 来使用 eac3to 。When specifying the path, either use Winepath or swap out the slashes accordingly yourself.

1.2 VapourSynth

VapourSynth 的安装十分简单。无论是 Windows , Mac 上的 Homebrew ,还是各个发行版的软件源,都有 VapourSynth 的安装程序。

VapourSynth 的文档列出了存放你的插件的路径。对于 Windows ,推荐的路径是 <AppData>\VapourSynth\plugins32 或 <AppData>\VapourSynth\plugins64 。Unix用户们可以创建一个配置文件来指定路径。

用于 VapourSynth 的 Python 脚本应当被放置在你的 Python site-packages 文件夹。在 Arch Linux ,这位于 /usr/lib64/Python3.*/sitepackages/ 。Windows 用户们可以在他们的账户 AppData 文件夹中找到这一路径。

1.3 编辑器

目前有4种主流的 VapourSynth 编辑器:

  • VapourSynth Editor^3^
  • Yuuno^4^
  • VapourSynth Preview^5^
  • AvsPmod^6^

每个 Linux 软件源都有 VSEdit 。例如,在 Arch 中,其位于 aur/vapoursyntheditor ;Windows 下有一个对应的安装包; Mac 用户能够通过 Homebrew 来安装它。

Yuuno 是一个 Jupyter notebooks 的扩展,能够允许你编辑和输出 VapourSynth 脚本。你可以通过以下代码来安装:

$ pip install yuuno
$ yuuno jupyter install

注意,你必须已经事先安装过了 Jupyter 或 Jupyter Lab 。

VapourSynth Preview 使用一个单独的文本编辑器或 IDE 来编辑和输出 VapourSynth 脚本,这使它对于那些不喜欢 VSEdit 内置的编辑器的人来说十分有用。

AvsPmod 是一个用于 AviSynth 的编辑器,但是也支持 VapourSynth 。

这些编辑器都拥有各自的一些优点和缺点,但对于新手,我更推荐使用 VSEdit 来进行本地编辑;而 yuuno 则更适用于那些还需要在服务器上编写脚本的用户。这是因为只需要很少量的配置,就可以远程使用 Jupyter 。对于 yuuno 用户,建议尝试使用 Jupyter Lab 来代替 Jupyter 。

对于这两者,更多的区别如下:

VSEdit

  • 不需要保持浏览器开启。
  • 内置了基准测试和转码工具。
  • 通过 CTRL + SHIFT + 方向键,可以轻松地浏览视频。
  • 更稳定。
  • 几乎不需再进行开发。
  • 更多的PT用户在使用它,因此更容易得到帮助和支持。
  • 针对 VapourSynth 语法规则特别定制的高亮显示和输入建议。
  • 允许存储模板。

yuuno

  • 很便于远程使用。
  • 通过 iPython 的魔法命令可以很容易地进行输出。
  • Way better comparison tools via %%vspreview clipa --diff clipb ⇒ hovering over preview changes clip.
  • 不太成熟,因此可能会导致更多的崩溃。
  • 允许你在同一个 Jupyter 文本中编辑和输出多个脚本。

1 https://forum.doom9.org/showthread.php?t=125966

2 https://mkvtoolnix.download/

3 https://bitbucket.org/mystery_keeper/vapoursynth-editor

4 https://yuuno.encode.moe

5 https://github.com/Endilll/vapoursynth-preview/

6 https://avspmod.github.io/


1.4 x264 和 x265

安装 x264 和 x265甚至比安装 VapourSynth 更为简单,同时你也可以使用一些修改版。最主流的 x264 修改版是 tmod 。相比于原版,值得注意的改动包括:淡化补偿(fade-compensate),这对**宏块树(mbtree)**设为开启时的淡化转码十分有用;更多的自适应量化模式(aq mode)等。除非你不使用以上这些选项(目前几乎已经没人这样做了),那么你可以仅使用原版的 x264 。而如果你希望在转码时更加精确地控制,毫无疑问你应该尝试 tmod 并捆绑使用它提供的 aq mode ,这样你就能够榨取出更高的质量,从而在保护噪点的同时不至于破坏线条。

最主流的 x265 修改版则是 yukki ,却并不那么让人眼前一亮。All this includes is some cosmetic changes like a nicer ETA.

1.5 Opus,qAAC,以及 fdkaac

为了转码 Opus ,你需要安装 opus-tools 软件包。

qAAC 则需要 iTunes 运行库。在 Mac 上,这没什么要说的。Windows 用户可以安装 iTunes 或者 QuickTime 。另外一个可行的方案是绿色版程序,在无需安装iTunes的同时囊括了必要的运行库。Arch 可以在 AUR 中找到 qaac-wine 软件包。

1.6 MKVToolNix

请确保安装最新版本的 MKVToolNix 。它将会安装多个工具,其中包括最重要的 mkvtoolnix-gui 和 mkvextract 。

2. 分流(demux)

2.1 FFmpeg 和 bdinfo

对分流工作,有一个十分杰出的基于 FFmpeg 的外壳程序: bdinfo^7^ 。但不要被它的名字骗了,它并非是用于生成很多站点所需要的碟片扫描信息。在 GitHub 的仓库上的 README 文档足够帮助你掌握它的用法。你将会在分流工作中主要用到 -x 和 -L 选项。

2.32 eac3to 的问题:50 fps, HEVE无损分流,以及其他

如果你需要对包含 HEVC 流的蓝光原盘进行无损分流(换句话说,你的播放列表是由上百个一个接一个播放的 m2ts 文件组成的),你需要使用 makemkv 来代替 eac3to 。

其他由 FFmpeg 和 bdinfo 所产生的问题(其实实际上是 libbluray导致的)同样可以通过换用 makemkv 来解决,或者直接将源导入 mkvtoolnix 。

3 视频编码

3.1 你的第一个脚本

一个没有额外的过滤的最基本的标准VapourSynth,看起来像这样:

# 如果你使用yuuno作为你的VSEdit编辑器,那么你需要取消下面两行注释
#%load_ext yuuno # 这个告诉Jupyter载入yuuno
#%%vspreview # 这个允许将与允许你预览输出内容
import vapoursynth as vs
core = vs.core 
src = core.ffms2.Source(r"/path/to/source.mkv") # Windows环境下,要使用r''
out = core.std.Crop(src, top=138, bottom=138) # 切除黑边
out.set_output() # 为vspipe标记输出的变量

3.1.1 调整大小(Resizing)

Firstly, note that there’ll be a separate section later on for descaling。在这里,我将解释调整大小方法以及,不用方法的好处。

如果您想要调整大小,重要的是不要对纵横比进行不必要的修改。如果要缩小比例,首先要弄清楚宽度和高度应该是多少。例如,你想缩小到720p,首先需要裁剪,然后弄清楚你是缩放到720高,还是1280宽。如果是前者,则宽度应为:

width = round(720 * src.height / (2 * src.width)) * 2

如果是后者,代码很相似:

height = round(1280 * src.width / (2 * src.height)) * 2

您还可以使用awsmfunc中的cropresize封装函数来执行这些计算和调整大小。

调整的大小方法有很多,主要的几种如下:

  • PointPoint也被称为最近邻大小调整,是最简单的大小调整方式,因为除了放大每个像素或缩小时获取周围每个像素的平均值,它实际上没有做任何其他事情。它的产生的结果是很糟糕的,但因为在放大的时候,他不会进行任何的模糊处理,所以它适合进行放大后查看每个像素的数值。It is also self-inverse, so you can scale up and then back down with it and get the same result you started with.
  • Bilinear调整大小的速度非常快,但是会导致非常模糊的结果以及明显的锯齿。
  • Bicubic调整大小的速度同样很快。但由于混叠(noticeable aliasing)而导致的结果非常模糊。可以通过修改参数以获得更清晰的结果,但这将导致更多的混叠(noticeable aliasing)。
  • Lanczos调整大小的速度比较慢,可以获得非常清晰的结果。但是它会产生非常明显的振铃声。(However, it creates very notice- able ringing artifacts.
  • Blackmanminlobe调整大小(需要使用fmtconv9进行调整)是经过修改的lanczos调整大小方法,具有较少的(ringing artifacts)。对于YUV444编码的色度提升,绝对值得考虑使用这种大小调整器(稍后会详细介绍)。
  • Spline调整大小的速度很慢,但效果却很好。可以使用多种样条调整器,其中Spline16比Spline36快,但结果稍差;Spline36与Spline64结果十分相似,因此没有理由使用后者。 所以Spline36是用于缩小内容的推荐大小调整方法。
  • Nnedi3
  • FSRCNNX

在附录中提供了这些缩放方法的比较,附录中的图16和图17分别是缩小尺寸和放大尺寸,此外,由于bicubic方法在更改其参数将在输出上产生非常显着的结果,因此在附录图18还包括了对不同参数配置的bicubic放大比例的比较示意。为了满足额外的好奇,我在图19中加入了缩小到原始分辨率的后的对比,以及在附录中图20中展示了同一缩放方法下先缩小然后放大的结果。

虽然这些截图可以帮助你更好地了解不同的大小,但它们只是单个花苗的一小部分。如果你想更好地了解这些大小调整方式的效果,我建议你自己做这些放大,(watching them in motion),然后交错它们(std.Interleave)。

对于进行缩小尺寸时候,不用的调整方法没有放大时差异那么明显。但是,我们不建议以此为借口,在缩小时选择调整大小的方式时偷懒。

TL;DR: Use core.resize.Spline36 for downscaling.

3.2 Filtering

有几件事值得在这里先提一下。首先,大多数蓝光的颜色是YUV420P8,范围有限(most Blu-rays come in YUV420P8 with limited range.)。这里的第一组信息是YUV。这代表了我们视频的平面,Y是亮度U和V是我们的色度平面。

其次关于颜色,本例中是4:2:0,指定了我们不同平面的大小。常见的有一共有三种:4:2:0,表示色度平面是亮度平面的一半大小(例如一个1920×1080的视频,它的色度平面为960×540)。4:2:2,则色度平面在水平轴上是一半大小,在垂直轴上是全部大小,4:4:4,即所有平面大小相同。在回放过程中,视频播放器将色度平面放大到与亮度相同的大小。较小的色度平面不明显,但可以肯定知道,当放大时,就可以看出明显的差异。为了说明这一点,下面是一个来自AnoHana的例子,分别是从720p放大到1080p的4:2:0和4:4:4 的两个版本。

图片

在这种情况下,最差的调整大小(bilinear)被用于色度平面。如果您使用类似mpv的KrigBilateral,这应该看起来更好。

另一个例子,

图片

下面要说明的是P8。这指的是位深度,这个例子中它是8-Bit。大多数视频现在是以8-Bit方式储存,因为8-Bit的AVC(H.264)有最好的硬件兼容性。然而,由于8-Bit导致没有足够的值(0-255)可用,它很容易引入诸如条带(Banding)之类的错误。较高的Bit在这方面则不会有太大的问题,而且由于其更好的准确性,高位深(比如10-Bit)实际上更擅长在较低的文件大小情况下存储视频。然而,10-Bit的AVC几乎没有硬件兼容性,而且需要更长的编码时间,因此使用private trackers 的人通常不喜欢它。大多数内容实际上是10-Bit或12-Bit生成的。由于精度的提高,过滤中最常用的位深度是16-Bit。值得注意的是,超高清蓝光(UHD)的格式是YUV420P10,意思是10-Bit。

最后一部分,则要讲述的内容是限制范围。在全范围8-Bit中,我们的范围是0到255之间的所有值。然而,电视通常无法显示所有这些值,亮度被限制在16 - 235,色度被限制在16 - 240。许多消费者的内容,例如蓝光也在有限的范围内。如果你给电视一个全范围的视频,它会简单地把所有的值都设置为16和或235/240和高于相同的值(例如黑色或白色)。(it will simply make everything 16 and under or 235/240 and over the same value (e.g. black or white). )

3.2.1检查你的源

这可能会占用最多的时间:检查源中的问题。这需要你遍历观看整个源文件并亲自检查是否存在:条带(Band),混叠(aliasing ),脏线(dirty lines ),以及其他可能出现的问题。好消息是,VSEdit允许您简单地通过CTRL + SHIFT +左/右箭头键自定义步宽款浏览源视频。这个步宽可以在预览窗口的右下角定义。我建议在1秒到3秒之间。显然,速度越慢越好,因为你将覆盖更多的帧。

将一个过滤器应用到一个给定的范围,使用如下的脚本:

filtered = my_filter(src)
out = awf.rfs(src, filtered, mappings="[FIRST_FRAME LAST_FRAME]")

3.2.2 抖动(Dithering)

3.2.3 去色带(Debanding)

这是最常见的问题。Deband发生时,码率不足(bitstarving )和低质量的设置导致平滑的渐变突然变成颜色变化,这显然使最后看起来很糟糕。好消息是更高的位深度可以帮助解决这个问题,因为可以使用更多的值来创建渐变。因此,很多Deband是在16位完成的,然后抖动到10位或8位再过滤过程完成。

关于Deband,需要注意的一件重要的事情是,你应该一直使用mask,最好是边缘mask或类似的mask。详见3.2.14 !

对于VapourSynth来说,有两种很棒的工具可以用来固定绑带:f3kdb16和fvsfunc的gradfun3。后者不太常用,但有一个内置的mask。

让我们先看看f3kdb: VapourSynth相关代码默认设置如下:

deband = core.f3kdb.Deband(src = clip, range = 15, y = 64, cb = 64, cr =
64, grainy = 64, grainc = 64, dynamic_grain = False, output_depth = 8)

这些设置对一些人来说可能不言自明,但下面是它们的解释:

  • src 这显然是你的来源clip
  • range 它指定了用来计算像素是否为Band的范围。范围越高,意味着用于计算的像素越多,意味着需要更多的处理能力。默认值通常为15是可以的。
  • y 最重要的设置,因为大多数(明显的)band发生在亮度平面上。它指定了在亮度平面上被认为是band的像素的差异数值。你应该从地数值开始缓慢尝试增加,直到对应的band被解散。如果设置的太高,很多细节会被视为band,因此会产生模糊。
  • cb/cr 和y一样,只是不同的这是色度平面。然而,色度平面上的条带是相当罕见的,所以你可以(leave this off. )
  • grainy/grainc 为了防止band的再次发生和抵消顺滑In order to keep banding from re-occurring and to counteract smoothing,通常在deband过程后加入噪点。或者,您可以使用自定义的噪点发生器,它将为您提供更好的输出(参见3.2.10)。
  • dynamic_grain 默认情况下,f3kdb添加的噪点是静态的。这种压缩效果更好,因为变化明显更少,但它通常在真人内容中看起来不太好,所以通常建议将其设置为True,除非您使用的是动画内容。
  • output_depth 设置这个为你想要在deband后继续进行clip的bit depth 。如果你一直工作在8-bit,你可以忽略这个选项。

这是一个非常简单的debanding的例子:

If you want to use f3kdb together with the gradfun3 mask, you can use fag3kdb17. 我推荐用这个方法deband效果非常好,但是retinex_edgemask或debandmask在大多数情况下都更好(尽管前者速度慢得多)。

最常用的可选方法是gradfun3,由于它的参数不太直接,所以可能不太受欢迎。It works by smoothing the source, limiting this via mvf.LimitFilter to the values specified by thr and elast, then merging with the source via its internal mask (although using an external mask is also possible). 虽然可以使用它来descaling,但大多数人更喜欢在另一个步骤中得方法。

许多人认为gradfun3产生的结果比f3kdb更平滑。由于它比f3kdb有更多的选择,而且在使用它的时候,大部分时间都不用为遮罩而烦恼,它十分值得了解如何去使用:

import fvsfunc as fvf
deband = fvf.GradFun3(src, thr=0.35, radius=12, elast=3.0, mask=2, mode=3,ampo=1, ampn=0, pat=32, dyn=False, staticnoise=False, smode=2,  thr_det=2 + round(max(thr - 0.35, 0) / 0.3), debug=False, thrc=thr,radiusc=radius,elastc=elast,planes=list(range(src.format.num_planes)), ref=src,bits=src.format.bits_per_sample)

许多这些值是用于fmtconv位深转换的,因此,这个文档文档证明是有用的。在GradFun3中的Descaling与其他方式没有太大的不同,所以我不会讨论这个。其他一些可能感兴趣的参数是:

  • thr 等价于y/cb/cr,你可能想要提高或减小它。
  • radius 与f3kdb的range 具有相同的效果。
  • smode 设置平滑模式。它通常最好保留默认值,一个双边过滤器,或者设置为5。如果你想使用CUDA-enabled GPU 而不是你的CPU。
  • mask 如果设置为0,则禁用mask。否则,它将设置要进行的std.Maximum和std.Minimum调用的次数。
  • planes 设置需要处理的平面。
  • mode 是fmtconv使用的抖动模式。
  • ampn/staticnoise 设置fmtconv应该添加多少噪点,它是否是静态的。对于真人动作内容来说,值得设置。
  • debug 允许您查看mask。
  • elast is ”the elasticity of the soft threshold.” Higher values will do more blending between the debanded and the source clip.

如果你进行deband的剪辑相比其他没有band的部分只有很少噪点,那么你应该考虑使用一个单独的函数来添加匹配的噪点,这样场景融合起来更容易。如果有很多噪点,您可需要考虑使用的有adptvgrnMod、adaptive_grain或GrainFactory3;对于不太明显的噪点或者对于通常只有很少噪点的明亮场景,你也可以使用grain.Add 。本内容将在后面的3.2.10 小节中进一步阐述。

下面是来自Mirai的一个例子(完整的脚本后面提供):

如果您希望自动进行band检测,可以使用基于bandmask的banddtct的检测函数。请确保适当地调整值并检查输出的完整。在3.2.17中有一个论坛帖子的链接。您还可以使用较高的luma_scaling值运行adptvgrnMod或adaptive_grain,希望噪点能完全覆盖它。在3.2.10中有更多关于这方面的内容。

3.2.4 修复脏线和不合适的边界

(原文??)另一个非常常见的问题,至少在真人电影中,是脏线。通常出现在视频的边框上,其中一行或一列像素显示的亮度值与周围的行相比有差距。(----)

另一种形式的脏线是当色度平面出现在黑条上。通常,这些应该剪掉。相反的情况也会发生,当拥有合法的亮度信息平面,缺乏色度信息平面。

有六种常用的滤镜用来修复脏线:

  • cf 的 ContinuityFixer

    ContinuityFixer 通过将指定的行/列与其周围指定的行/列数量进行比较,并通过最小二乘回归找到新值。其设置如下:

    fix = core.cf.ContinuityFixer(src=clip, left=[0, 0, 0], right=[0, 0,
    0], top=[0, 0, 0], bottom=[0, 0, 0], radius=1920)

    这是假设你使用1080p的素材,因为radius的值被设置为根据源的分辨率所定义的可能的最长数值。我建议使用更低的值,但是不要比3低(,although not going much lower than 3),在这一点上,您可以复制像素(参见请在下面的FillBorders)。可能会让大多数新手感到困惑的是数组,我已经为修复的行/列输入了值。这些数组表示应用于三个平面的值。通常情况下,脏线只会出现在亮度平面上,所以通常可以让其他两个为0。请注意,数组不是必需的,因此您也可以只输入希望应用修复的行/列的数量,然后所有平面都将被处理。

    ContinuityFixer非常擅长的一件事是摆脱不规则,比如点。它也比bbmod和FixBrightnessProtect2快,但是应该将其作为这两者的后备方案。

  • awsmfunc的bbmod

    这是原始BalanceBorders函数的一个模组。它和ContinuityFixer非常相似,但是使用更高的blurthresh的数值,可以得到更好的结果。如果效果不理想,可以修改降低数值,但是模糊值设置越低,效果越差。它也比havsfuncsgvsfunc中的版本快得多,因为只处理必要的像素。

    import awsmfunc as awf
    bb = awf.bbmod(src=clip, left=0, right=0, top=0, bottom=0,
    thresh=[128, 128, 128], blur=[20, 20, 20], scale_thresh=False,
    cpass2=False)

    threshblur的数组同样对应的YUV的值。建议先尝试blur=999,然后降低这个值,反复尝试,直到你得到合适的效果。

    thresh指定结果与输入之间的差异有多大。这意味着这个值越低越好。blur 是滤镜的强度,较低的值比较强,较大的值比较弱。如果你设置blur =1,你基本上复制行。(If you set blur=1, you’re basically copying rows. )

  • fb’s的FillBorders

这个函数基本上只是复制下一行/列。虽然这听起来很傻,但当向下缩放导致底部的行比顶部的行更多,并且由于YUV420的mod2高度,不得不填充其中一行时,它可能非常有用。

fill = core.fb.FillBorders(src=clip, left=0, right=0, bottom=0, top=0,mode="fillmargins")

这个函数的一个非常有趣的用法类似ContinuityFixer ,它可以用于灰色边界或边界不匹配他们的环境,无论什么亮度修复应用。这可以通过下面的脚本完成:

fill = core.fb.FillBorders(src=clip, left=0, right=0, bottom=0,top=0,mode="fillmargins")
merge = core.std.Merge(clipa=clip, clipb=fill, weight=[0,1])

你也可以分离平面并处理个别的色度平面,尽管这只是稍微快一点。允许您为fb指定每个平面值的封装函数是awsmfunc中的FillBorders

  • edgefixer的ReferenceFixer

这需要edgefixer的原始版本(cf只是它的一个旧接口,但是使用起来更好,处理过程也没有改变)。我从来没有发现它的好用途,但在理论上,它是相当整洁的。它与参考剪辑进行比较,以调整其边缘固定。

fix = core.edgefixer.Reference(src, ref, left=[0, 0, 0], right=[0, 0, 0], top=[0, 0, 0], bottom=[0, 0, 0], radius = 1920)
  • rekt的rektlvls

this is basically FixBrightnessProtect2 and FixBrightness in one with the additional fact that not the entire frame is processed. 它的参数非常简单。将调高为增亮,调低变暗。设置prot_val参数为0的则和 FixBrightness的功能相似,这意味着需要更改调整值。

from rekt import rektlvls
fix = rektlvls(src, rownum=None, rowval=None, colnum=None,colval=None, prot_val=20)

如果你想一次处理多行,你可以输入一个列表(例如:rownum=[0,12))

不应该忽视的一点是,将这些修复程序(除了rektlvls)应用到太多的行/列可能会导致最终结果看起来比较模糊。因此,建议尽可能使用rektlvls,或者只对必要的行/列应用应用亮度修复。如果方法失败,最好在使用ContinuityFixer之前尝试bbmod

需要注意的是,你应该在调整大小之前修复脏线,否则会引入更多的脏线。并且,重要的是要注意,如果你使用FillBorders填充了边缘的一条黑色的线,你应该用你的大小调整方法后删除它。例如,将顶部填充线的Clip从1920×1080调整为1280×536:

top_crop = 138
bot_crop = 138
top_fill = 1
bot_fill = 0
src_height = src.height - (top_crop + bot_crop) - (top_fill + bot_fill)
crop = core.std.Crop(src, top=top_crop, bottom=bot_crop)
fix = core.fb.FillBorders(crop, top=top_fill, bottom=bot_fill,mode="fillmargins")
resize = core.resize.Spline36(1280, 536, src_top=top_fill,src_height=src_height)

如果您处理的是对角线边框,这里的正确方法是通过Mask进行遮盖源,与调用FillBorders的结果合并。这方面的一个示例(来自D-Z0N3编码的 Your Name (2016) )

图片

Your Name 的不正确的边线的例子。D-Z0N3使用了mask,Geek没有使用mask。Geek lacks any resemblance of grain, while D-Z0N3 keeps it in tact whenever possible. It may have been smarter to use the mirror mode in FillBorders, but hindsight is 20/20.

D-Z0N3使用的代码(16bith):

mask = core.std.ShufflePlanes(src, 0, vs.GRAY).std.Binarize(43500)
cf = core.fb.FillBorders(src, top=6).std.MaskedMerge(src, mask)  

具体为什么要进行mask遮盖的示例在附录中,如图22所示

为了说明脏线可能是什么样子,下面是一个示例关于ContinuityFixer和只修复颜色的FillBorders

源vs过滤的脏线修复来自从D-Z0N3编码A Silent Voice 。在前三行使用ContinuityFixer,在最左边的两列使用FillBorders,放大到15倍。

脏线很难发现。如果你在随机检查不同帧的边界时候不能发现有问题,那么一般证明没有问题。如果您知道有每边都有小黑线的边界,您可以使用类似以下脚本的方法。

def black_detect(clip, thresh=None):
	if thresh == None:
		thresh = (25 * (1 << clip.format.bits_per_sample) - 1) / 255mask = core.std.ShufflePlanes(clip, 0, vs.GRAY).std.Binarize("{0}".format(thresh)).std.Invert().std.Maximum().std.Inflate().std.Maximum().std.Inflate()
		l = core.std.Crop(mask, right=clip.width / 2)
		r = core.std.Crop(mask, left=clip.width / 2)
		mask_test = core.std.StackHorizontal([r, l])
		t = core.std.Crop(mask_test, top=clip.height / 2)
		b = core.std.Crop(mask_test, bottom=clip.height / 2)
		mask_test = core.std.StackVertical([t, b])
	return mask_test

This script will make values under the threshold value (i.e. the black borders) show up as vertical or horizontal white lines in the middle on a mostly black background. 你可以通过这个活动浏览你的视频。您还可以尝试使用blckdtct,它会为您扫描视频。

其他可变脏线是一个棘手的修复类型,需要手动检查场景。

一个与“脏线”非常相似的问题是“糟糕的边界”。在不同的场景(如IMAX或4:3),黑色边框有时可能不是完全黑色,或完全混乱。为了解决这个问题,可以简单地剪裁它们并将它们添加回去。您可能还想修复沿途可能出现的脏线。

crop = core.std.Crop(src, left=100, right=100)
clean = core.cf.ContinuityFixer(crop, left=2, right=2, top=0, bottom=0,radius=25)
out = core.std.AddBorders(clean, left=100, right=100)  

3.2.5 Anti-Aliasing 反锯齿

这可能是最常见的问题。如果你想要解决这个问题,首先要确定这个问题是由实际的叠影引起的,而不是由糟糕的缩放引起的。如果你已经确认过,我推荐的工具是TAAmbk套件

import vsTAAmbk as taa
aa = taa.TAAmbk(clip, aatype=1, aatypeu=None, aatypev=None, preaa=0, strength=0.0, cycle=0, mtype=None, mclip=None, mthr=None, mthr2=None, mlthresh=None, mpand=(1, 0), txtmask=0, txtfade=0, thin=0, dark=0.0, sharp=0, aarepair=0, postaa=None, src=None, stabilize=0, down8=True, showmask=0, opencl=False, opencl_device=0, **args)

GitHub的README十分详细,但这这里还是有些需要进行解释的内容:

  • aatype: (Default: 1)

这里的值可以是表示亮度平面AA类型的数字,也可以是表示其AA类型的字符串。

0: lambda clip, *args, **kwargs: type('', (), {'out': lambda:
clip}),
1: AAEedi2,
2: AAEedi3,
3: AANnedi3,
4: AANnedi3UpscaleSangNom,
5: AASpline64NRSangNom,
6: AASpline64SangNom,
-1: AAEedi2SangNom,
-2: AAEedi3SangNom,
-3: AANnedi3SangNom,
'Eedi2': AAEedi2,
'Eedi3': AAEedi3,
'Nnedi3': AANnedi3,
'Nnedi3UpscaleSangNom': AANnedi3UpscaleSangNom,
'Spline64NrSangNom': AASpline64NRSangNom,
'Spline64SangNom': AASpline64SangNom,
'Eedi2SangNom': AAEedi2SangNom,
'Eedi3SangNom': AAEedi3SangNom,
'Nnedi3SangNom': AANnedi3SangNom,
'PointSangNom': AAPointSangNom,

我推荐的是Eedi3, Nnedi3, Spline64SangNomNnedi3SangNom。这SangNom的两种模式都具有令人难以置信的破坏性,只有在绝对必要的情况下才应该使用。Nnedi3通常是你的最佳选择;它不是很强大或具有破坏性,但通常足够好,而且相当快。Eedi3慢得令人难以置信,但比Nnedi3更强,并且不像SangNom模式那么具有破坏性。

  • aatypeu: (Default: same as aatype)

Clip的格式为YUV时,为U平面选择主AA内核。

  • aatypev: (Default: same as aatype)

Clip的格式为YUV时,为V平面选择主AA内核。

  • strength: (Default: 0)

predown的强度。有效范围为[0,0.5],在应用主AA内核之前,剪辑将首先缩小到(1-strength )*clip_resolution,然后由主AA内核放大到原始分辨率。对于由很糟糕的放大造成的严重的叠影的Clip,这通常是很有好处。当使用不适合升级的AA内核时自动禁用。如果可能的话,不要增加,也不要降低。

  • preaa: (Default: 0)

选择preaa模式。

​ – 0: 不使用 ​ – 1: 垂直 ​ – 2: 水平 ​ – -1:两者

在应用主AA内核之前,先完成一个preaaPreaa基本上是daa的简化版本。Pretty useful for dealing with residual comb caused by poor deinterlacing. 否则,不要使用它。

  • cycle:(Default: 0)

设置主AA内核的循环次数。用于非常非常糟糕的叠影和3D叠影。

  • mtype: (Default: 1)

选择要使用的边缘mask类型。目前有三种mask类型:

​ – 0: No mask ​ – 1: Canny mask ​ – 2: Sobel mask ​ – 3: Prewitt mask

Mask总是在8位深下构建的。所有这些选项都很好,但是您可以测试一下,看看哪种结果最好。

  • mclip: (Default: None)

使用你自己Clip的Mask,而不是内部建立一个。如果设置了mclip,脚本将不会构建其他mask了。你应该自己处理mask的分辨率、位深、格式等问题。

  • mlthresh: (Default None)

Set luma thresh for n-pass mask.使用列表或元组来指定亮度的部分。

  • mpand: (Default: (1, 0))

Use a list or tuple to specify the loop of mask expanding and mask inpanding.

  • txtmask: (Default: 0)

创建一个mask来保护屏幕上的白色标题。值为亮度的阈值,有效范围为0-255。当亮度大于阈值,色度为128±2时,视为标题。

  • txtfade: (Default: 0)

设置褪色的长度。用于的淡入文本。

  • thin: (Default: 0)

在应用主AA内核之前,Warp the line by aWarpSharp2 。

  • dark: (Default: 0.0)

在应用主AA内核之前,通过Toon使线变暗。

  • sharp: (Default: 0)

锐化剪辑后,应用主要AA内核。* 0: No sharpen. * 1 inf: LSFmod(defaults=’old’) * 0 1: Simlar to Avisynth’s sharpen() * -1 0: LSFmod(defaults=’fast’) * -1: ContraSharpen

  • aarepair: (Default: 0)

使用repair删除由主AA内核引入的工件。根据不同的修复方式,源的Clip中的像素将会处理过后的Clip的3x3的邻进的中值或平均值所替代。强烈建议在主AA内核模式包含SangNom时使用repair。如需更多信息,请查看rgvs.html#rgvs.Repair 。很难让它正常工作

  • postaa: (Default: False)

Whether use soothe to counter the aliasing introduced by sharpening.

  • src: (Default: clip)

Use your own src clip for sharp, repair, mask merge, etc.

  • stabilize: (Default: 0)

使用MVTools稳定时间变化。值为时间半径。有效范围为[0,3]。

  • down8: (Default: True)

如果您将此设置为True,剪辑将在应用主AA内核之前下降到8位深,并在应用主AA内核之后回升到原始的位深度。在位深转换中,采用LimitFilter来减少损失。

  • showmask: (Default: 0)

如果设置为非0数值,则输出mask而不是已处理的clip。 – 0: 正常输出 – 1: 只输出mask – 2: tack mask and clip – 3: 交错输出mask和clip ​ – -1: 只输出mask的文本

  • opencl: (Default: False)

是否使用opencl版本的一些插件。目前有三个插件可以使用opencl:

​ – TCannyCL ​ – EEDI3CL ​ – NNEDI3CL

这可以加快进程,这显然是伟大的,因为抗锯齿通常是非常缓慢的。

  •  **opencl_device**: (Default: 0)  
    

选择一个opencl设备。为了找出哪一个是正确的,进行下面:

core.nnedi3cl.NNEDI3CL(clip, 1, list_device=True).set_output()
  •  **other parameters**:  
    

将被收集到一个特定类型的词典

注意,有很多非常好的反锯齿方法,以及许多不同类型的mask,你可以选择使用(e.g. other edgemasks, clamping one method’s changes to those of another method etc.)。然而,大多数方法都基于与那些TAA非常相似的实现方法。

如果你的整个视频遭受叠影问题,由于糟糕的放大行为,这不是不可能的。在这种情况下,在决定是否需要执行任何反锯齿之前,先进行除垢或调整大小。

下面是一个来自Non Non Biyori - Vacation的反锯齿修复的例子:

在这个例子中,我们做了以下事情:

mask = kgf.retinex_edgemask(src).std.Binarize(65500).std.Maximum().std.Inflate()
aa = taa.TAAmbk(src, aatype=2, mtype=0, opencl=True)
out = core.std.MaskedMerge(src, aa, mask)

3.2.7 Deringing 消除振铃效应

术语 “振铃”(ringing)效应 指图像中边缘过渡的伪影,最常见的是 **蚊式噪声(mosquito noise)**和 边缘增强伪影(edge enhancement artifacts)。ringing 现象在低质量视频源上非常普遍。但是,由于拍摄设备的不足和糟糕的压缩方法,即使是高码率的视频会也容易出现这种情况。要解决此问题,建议使用 HQDeringmod 或 EdgeCleaner(来自 scoll),我建议使用前者。这些方法主要目的是模糊和锐化边缘,然后通过 **边缘遮罩(edgemasks)**合并。它们非常易用,因此你可以自己阅读和了解它们,并熟悉它们的用途。由于 rgvs.Repair 可能非常暴力,因此,如果你使用这些函数,并且默认值不能产生令人满意的结果,那么请尝试使用修复值。

![图片alt](图片地址 ''图 10: 左图为原图,右图HQDeringmod(mrad=5, msmooth=10, drrep=0)。这种消除振铃效应的方式非常暴力,我不太推荐。图片来源自One Ok Rock音乐会蓝光,码率37 mbps'')

3.2.8 Dehaloing 去晕

**"光晕"(Haloing)**正如其名,是边缘周围的粗而亮的线条。这一问题常出现在没有正确地调整大小之后。你也可能会发现糟糕的 **降尺度(descaling)**或者对低质的视频源进行降尺度后会出现明显的光晕。要解决此问题,应该使用 havsfunc 的 DeHalo_alpha 或其已蒙版(mask)处理的同类产品 FineDehalo。如果使用前者,则必须编写自己的蒙版(mask),因为未进行蒙版处理的去晕通常会导致不好的结果。关于如何编写简单的去晕蒙版,请查看encode.moe的指南30

由于 FineDehalo 是 DeHalo_alpha 的包装器,因此它们共享一些参数:

FineDehalo(src, rx=2.0, ry=None, thmi=80, thma=128, thlimi=50, thlima=100, darkstr=1.0, brightstr=1.0, showmask=0, contra=0.0, excl=True, edgeproc=0.0) # ry defaults to rx
DeHalo_alpha(clp, rx=2.0, ry=2.0, darkstr=1.0, brightstr=1.0, lowsens=50, highsens=50, ss=1.5)

AviSynth 上的 wiki 已经给出了很好的说明: http://avisynth.nl/index.php/ DeHalo_alpha#Syntax_and_Parameters and http://avisynth.nl/index.php/FineDehalo# Syntax_and_Parameters

3.2.9 Denoising 降噪

降噪是一个相当棘手的问题。真人编码永远不会降噪,而动画编码通常需要大量降噪。你要为动漫做这件事的主要原因是它本身并没有噪点,但是压缩会引入噪点,而位深转换则会引起抖动(dither)。前者是不需要的,而后者则是我们想要的。你可能还会在闪回(flashbacks)之类的过程中遇到明显的颗粒感(grains)。去除不必要的噪点将有助于压缩并消除一些轻微的抖动/颗粒感;降噪对于10bit编码很有用,平滑的信号源可以更好地进行编码,效果出众;对于8bit则效果更为突出,更多的颗粒可以防止出现条纹等问题。有时,在压缩以外也需要进行降噪工作。比如,假设你要对一部动画电影进行编码,在该动画电影中,具有闪回原有情节的场景。动画电影通常是以1080p制作的,但大多数动画剧集并不是。因此,可能会遇到很多放大的影片,充满了1080p的噪点颗粒。在这种情况下,需要对噪点颗粒进行去粒化(degrain)重新缩放(rescale),然后再将噪点**合并(merge)**回去。31(通常,在去粒化的剪辑片段上建立边缘遮罩,缩小源剪辑片段更加合理,但这样处理也并不糟。)

degrained = core.knlm.KNLMeansCL(src, a=1, h=1.5, d=3, s=0, channels="Y", device_type="gpu",   device_id=0)
descaled = fvf.Debilinear(degrained, 1280, 720)
upscaled = nnedi3_rpow2(descaled, rfactor=2).resize.Spline36(1920, 1080).std.Merge(src, [0,1])
diff = core.std.MakeDiff(src,  degrained,  planes=[0]) merged = core.std.MergeDiff(upscaled, diff, planes=[0])

3.2.10 Graining 粒化

由于颗粒和抖动是最难压缩的东西,很多视频源都只包含很少的颗粒,或者已去除明显的颗粒。为解决这个问题,或者压缩没有颗粒的区域,手动添加颗粒通常会是一项有利的举措。在颗粒被去除的情况下,通常需要先去除颗粒,然后再重新添加回去。这对于动画压制非常有用,缺少颗粒通常会使重编码难以维持渐变。 当我们人为添加颗粒时,我们可以选择使用静态颗粒。在动画中这不是很明显,并且压缩效果要好得多,因此通常是对动画内容编码的最佳选择。但是,它在实景内容中通常非常突兀,因此,静态颗粒不常用于**私有种子服务器(private tracker)**的编码中。

标准的粒化函数为 grain.Add(也有其他函数):

grained = core.grain.Addclipvar = 1constant = False)`

此处 var 选项表示强度,一般不会调得太高。如果调得太高,你会发现颗粒会变得非常明显,以至于你可能要尝试匹配颗粒从而使其不过于明显。

最著名的粒化函数是 GrainFactory3 。使用此函数可以指定如何将 Grain.Add 应用到三个不同的亮度等级(亮,中,暗);同时该函数也用 resize.Bicubic 来缩放亮度,通过修改 Sharpen 选项的 b 和 c 参数对其进行锐化。你必须修改大小,锐度和阈值参数,进行匹配可能很难,但是会产生很棒的效果,特别是针对需要更多看起来更自然的颗粒的实景内容。 自动化程度更高的选项是 adaptive_grain 。它的工作原理和 GrainFactory3 类似,但是会根据整个帧的亮度值和特定区域的亮度值,在帧的各个部分应用可变数量的颗粒。较少的自定义选项使其成为一个更好用的选项,且对动画效果很好。由于其考虑整个帧画面的平均亮度,因此能产生非常好的效果。

除了单独这两个函数之外,还可以将两者组合为一个名为 "adptvgrnMod"32 的函数使用。这会将 GrainFactory3 的锐度和尺寸规格选项添加到 adaptive_grain。由于颗粒仅以一种尺寸添加到一个(通常小于帧画面的)图像上,因此该函数运行很快。如果颗粒大小在不同的亮度水平下没有变化(通常这是数字产生的颗粒),那么与上述两个函数相比,这可以带来更好的结果。 如果你对此感到好奇,请参考图4中 Mirai 的 **去色带(debanding)**的示例,该示例中使用了 adptvgrnMod 进行粒化处理。

3.2.11 Deblocking 解块/去区块

解块通常等效于平滑,通常是在画面顶层使用另一个蒙版。最常用的函数是 havsfunc 中的 Deblock_QED。主要参数是

  • quant1:块边缘解块的强度。 默认值为24。你通常会大幅调高该值。
  • quant2:块内部解块的强度。 默认值为26。调高该值可能也有帮助。 其他流行的选项是 deblock.Deblock,它非常强大,并且应用广泛;dfttest.DFTTest,它虽然相对较弱,但仍然非常暴力;还有 fvf.AutoDeblock ,它对于对 MPEG-2 源进行解块非常有用,可以用于整个视频。另一种流行的方法是简单去色带,因为解块和去色带非常相似。对于 AVC 蓝光源来说,这也是一个不错的选项。

3.2.12 Detinting 色彩校正(?)

If you’ve got a better source with a tint and a worse source without a tint, and you’d like to remove it, 你可以通过时间块和DrDre的色彩匹配工具(Color Matching Tool)。首先,将两个参考快照添加到工具中,导出LUT(Look Up Table?),保存并通过以下方式添加:

clip = core.resize.Point(src, matrix_in_s="709", format=vs.RGBS) 
detint = core.timecube.Cube(clip, "LUT.cube")
out = core.resize.Point(detint, matrix=1, format=vs.YUV420P16)

![图片alt](图片地址 ''图 11: Source with tint on left, tint removed on right. 这个示例来自D-Z0N3压制的你的名字(Your Name,2016)。该帧也经过了抗锯齿处理。'')

同样,如果你有所谓的伽马错误,或更准确地说,是双范围压缩(将有限范围的压缩完全应用于已经过范围受限处理的剪辑片段),则只需执行以下操作(适用于16bit):

out = core.std.Levels(src, gamma=0.88, min_in=4096, max_in=60160, min_out=4096, max_out=60160, planes=0)`

![图片alt](图片地址 ''图12:左侧为双倍范围压缩,右侧为gamma错误修复。'')

通常会要求使用0.88的值,其他伽玛值也不是不可以。但当黑色的亮度值为218而不是235,就必须要要用0.88。请勿在低位深视频中执行此操作,原因如图23所示。如果色度平面也受到影响,则必须分别处理它们:

out = core.std.Levels(src, gamma=0.88, min_in=4096, max_in=61440, min_out=4096, max_out=61440, planes=[1, 2])

你也可以使用awsmfunc中的fixlvls包装器执行这些操作。

如果你的源具有不正确的颜色矩阵,则可以使用以下方法解决:

out = core.resize.Pointsrcmatrix_in_s ='470bg'matrix_s ='709'

“470bg”通常称为601。调整大小的原因是矩阵转换发生在YUV到RGB转换之间,这意味着我们需要扩大色度。用点调整大小是强制性的。你需要一些参考源知道是否应该这样做,最好别是网络源。从技术上讲,你可以识别出不好的颜色,然后知道有必要改变一下矩阵。

![图片alt](图片地址 ''图13:燃烧(Burning,2018)中进行矩阵转换的示例。此示例使用TayTO的重编码。最值得注意的区域是她的粉红色胸罩和背景中的红色。'')

3.2.13 Dehardsubbing and Delogoing 去除硬字幕以及去除台标

虽然这个问题在动画中特别常见,但在一些真人电影中也会出现,在电视台播放的许多音乐视频或音乐会中也会有标识,因此我们十分需要了解如何去除硬字幕或台标。对于台标,非常可以考虑Delogo插件。你需要台标的.lgd文件来使用它。你可以通过自己喜欢的搜索引擎简单地查找它,应该能找到一些东西,之后就很简单了。 去除硬字幕的最常见方法是比较两个视频源,一个带有硬字幕,一个参考源不带有硬字幕。我推荐的函数是kagefunc的hardsubmask和hardsubmask_fades。前者仅对带有黑白字幕的来源有用,而后者可用于台标以及去除硬字幕。两者的重要参数都是expand选项,这意味着要调用std.Maximum。根据你的视频源质量和检测到的数量,可能需要降低这些参数值。

我们也可以用Delogo进行类似的操作来创建一个mask:

此处尚无示例脚本。

准备好蒙版后,你将需要合并没有硬字幕的参考源与主要源。此过程中可能需要使用一些着色处理,因为二者可能有色差。值得注意的是,这样做要远好于将好的视频源(有硬字幕)替换成差的视频源。如果你很懒,通常可以毫无问题地将这些蒙版应用到整个剪辑片段中,无需遍历整个视频来寻找硬字幕区域。

3.2.14 masking 遮罩

这是最复杂的部分,除了动画encoders,大多数encoders都倾向于忽略。mask有助于保护重要的细节,防止其被滤镜破坏。PT组的encoders最通常使用的mask是binarize masks :

y = core.std.ShufflePlanes(src, 0, vs.GRAY) 
mask = core.std.Binarize(y, 5000)
merge = core.std.MaskedMerge(filtered, src, mask)

这个例子中,我假设我们工作在16bit下。std.Binarize在此执行的内容是,使我们的位深所允许的每个小于5000的值变为最小值,而使每个大于5000的值成为最大值。这意味着将复制源上每个在5000亮度以上像素点,通常这被称为luma mask,即亮度遮罩。luma mask通常用于需要处理暗色区域的色带问题。

我们还可以使用其中一个色度平面来执行此操作:

u = core.std.ShufflePlanes(src, 1, vs.GRAY) 
mask = core.std.Binarize(u, 5000)
mask = core.resize.Bilinear(mask, 1920, 1080) 
mask = core.std.Inflate(mask)
merge = core.std.MaskedMerge(filtered, src, mask)

你可能已经注意到,我执行了同样的二值化,而且同时调整了mask的尺寸并进行了Inflate。调整大小的原因显然是因为色度平面在YUV420中的分辨率较低,resizer的选择对一些人来说似乎有些奇怪。使用bilinear resizer会导致模糊,这意味着mask周围的像素也将受到影响,这通常对抗锯齿是有用的。出于相同的原因,我又添加了一个std.Inflate,尽管通常它对于亮度mask比色度mask更有用。

更有趣和有用的mask是edgemasksdebanding specific masks。对于edgemasks,VapourSynth用户具有很大的优势,因为kgf.retinex_edgemask令人难以置信的准确,有着不可思议的结果。该edgemasks获取源的图像,使用retinex算法提高暗区的对比度和亮度,然后将输出的TCanny mask层叠加在Kirsch mask上。两个常见用途是debandanti-aliasing

retinex = kgf.retinex_edgemask(src)
antialiasingmask = retinex.std.Binarize(65000).std.Inflate() 
antialiasingmerge = core.std.MaskedMerge(src, antialiasing, antialiasingmask)
debandmask = retinex.std.Binarize(7000).std.Maximum().std.Inflate() 
merge = core.std.MaskedMerge(deband, src, debandmask)

对于debanding,你通常会想要尽可能从源获取更多的信息,以免破坏细节。因此我们在低值进行二值化,并使用std.Maximum和std.Inflate来扩大mask范围。我们要用此mask将视频源中的内容添加到DebandedClip中。有很多不同的方法可以对这个mask进行操作,可以用某个值乘以高于阈值的所有内容(std.Expr(retinex, "x 7000 > x 10 * x ?"),也可以只最大化和膨胀,让它变得简单,或者你可以做任何想做的操作。

在一个非常不同但类似的方式中,抗锯齿通常只应用于明显的边缘,因此我们在高值上进行二值化。std.Inflate的调用就十分重要,它可以让我们获得应用了抗锯齿的全部结果。此处,我们要通过mask将向视频源中添加抗锯齿的结果。

其他有用的edgemasks有:

  • std.Prewitt
  • std.Sobel 通常比Prewitt更准确。但是在不能选择Kirsch或Retinex类型mask时,还是推荐对它们进行测试。
  • tcanny.TCanny 这基本上是在模糊后的Clip上叠加了一个Sobel mask。
  • kgf.kirsch 在明亮的场景中会产生与retinex_edgemask几乎相同的结果,因为它是其组件之一。因为它是其组件之一。因为它使用更多的方法(directions),导致比其他方法慢,但会得到很好的结果。

所有这些的比较可以在图24和25下的附录中找到。

尽管边缘mask非常适合应用于deband,但它们通常也会检测到条带本身的边缘,而且通常很慢。很好的的替代品包括GradFun3的mask和debandmask,后者是非常快的,并能得到更好的结果,但它是膨胀的。对于GradFun3,可以使用Frechdachs的Fag3kdb包装器。我建议尽可能使用debandmask,但边缘mask通常会证明在黑暗的场景中产生更好的结果,所以需要做一些一些测试。要从GradFun3中获取mask输出,可以执行以下操作:

mask = fvf.GradFun3(src,debug = 1)

![图片alt](图片地址 ''图14:GradFun3(左上),debandmask(右上),retinex_edgemask(左下)和retinex_edgemask.std.Maximum().std.Inflate()(右下)的比较。'')

对使用和不使用mask进行强的Deband操作区别感到好奇的人,请参考附录中的图26,看看一个简单的边缘mask对Deband影响的对比。当使用亮度/颜色 mask时,请不要忘记测试是否应该在之前的mask上使用适当的边缘mask或debanding专用mask,因为简单的亮度mask并不能保证边缘不被破坏! 附录中的图27和图28都有这方面的例子。 现在我们已经介绍了常见的mask,让我们继续学习那些可以帮助你操作mask或创建你自己的mask的函数。我将简要介绍一下,因为它们的文档写的很详细。

  • std.Maximum / Minimum:使用此值可以增大或缩小蒙版,你可能还希望将 coordinate = [0、1、2、3、4、5、6、7] 应用于任何适合你的数字,以便指定周围像素的权重。
  • std.Inflate / Deflate:类似于前面的功能,但它不是应用最大的像素,而是将它们合并,从而获得轻微的边缘模糊。在大多数mask的结尾处很有用,这样你就可以在mask区域之间得到一个轻微的过渡。
  • std.Expr:已知为非常复杂的函数。通过反向波兰语符号应用逻辑(Applies logic via reverse Polish notation)。如果你不了解,请在Wikipedia上阅读相关条目。你可以执行的一些很酷的操作是使某些像素变亮,同时保持其他像素不变(而不是像使用std.Binarize那样使它们变暗): std.Expr(" x 2000 > x 10 * x ?") 这将使每个大于2000的值乘以10,而其他值则不变。一个很好的案例是介于两个值之间: std.Expr(" x 10000 > x 15000 < and x {} = x 0 = ?".format(2 ** src.format.bits_per_sample-1)) 这使介于10000和15000之间的每个值成为位深度允许的最大值,并使其余的归零,就像std.Binarize那样。几乎每个功能都可以通过该函数表达出来。
  • std.MakeDiff和std.MergeDiff:不言自明,可以在去粒化处理的剪辑片段上应用某些内容,然后将其颗粒合并回去,如“降噪”部分中所述。
  • std.Convolution:将矩阵应用于像素。其文档对此进行了很好的说明,如果没有理解,请仔细阅读。许多蒙版是通过卷积内核定义的。你可以用它来做很多事情,例如std.Expr。举个例子,如果要平均像素周围的所有值,请执行std.Convolution([1,1,1,1,0,1,1,1,1])
  • std.Transpose:转置(即翻转)Clip。
  • std.Turn180:旋转180度。
  • std.BlankClip:一帧纯色。你可以用它替换不良的背景,或者在整个影片中添加颗粒感但又不希望片尾字幕充满颗粒感的情况下使用。要获得电视的范围,可以对8bit黑色使用std.BlankClip(src,color = [16,128,128])也可用于制作基于区域的蒙版。
  • std.Invert:不言自明。你也可以只交换通过蒙版合并的剪辑片段,而不这样做。
  • std.Limiter:你可以使用此功能将像素限制为某些值。用于维持电视范围(std.Limiter(min = 16,max = 235))
  • std.Median:这将每个像素替换为其附近的中值。没多大用。
  • std.StackHorizo​​ntal / std.StackVertical:将剪辑片段堆叠在另一个之上/之下。
  • std.Merge:这可以让您以给定的权重合并两个Clip。权重为 0 将返回第一个Clip,而 1 将返回第二个Clip。你给它的第一项是clip列表,第二项是每个平面的权重列表。这是将第二个clip中的色度合并到第一个clip中的亮度平面的方法:std.Merge([fmaskirst,second],[0,1])。如果没有给出第三个值,则将第二个复制到第三个平面。
  • std.MaskedMerge:根据给定的mask,将第二个Clip合并到第一个Clip。
  • std.ShufflePlanes:从Clip中提取或合并平面。例如,你可以使用std.ShufflePlanes(src,0,vs.GRAY)获得亮度平面。

如果你想只在某个区域应用某些东西,你可以使用封装好的函数rekt34或rekt_fast。后者只将你的函数应用于给定的区域,这就加快了速度,对于抗锯齿和类似的慢速滤镜相当有用。关于它的一些封装好的函数已经有了,比如用于抗锯齿的rektaa。 rekt_fast和lambda函数相关,因此你需要输入rekt_fast(src, lambda x: core.f3kdb.Deband(x)),而非core.f3kdb.Deband(src)。

还有一个非常特殊的函数是std.FrameEval。它允许你做的是评估一个片段的每一帧,并再每一帧上应用一个特定的函数。这很令人困惑,但是VapourSynth的文档中有一些很好的示例:http://www.vapoursynth.com/doc/functions/frameeval.html。除非你有兴趣写一个需要这个功能的函数,否则你可能永远不会使用它。但是,许多函数都会用它,包括kgf.adaptive_grain,awf.FrameInfo,fvf.AutoDeblock,TAAmbk等。我能想到的一个例子是,根据帧类型应用不同的Deband来展示这一点。

import functools
def FrameTypeDeband(n, clip):
    if clip.get_frame(n).props._PictType.decode()  ==  "B":
        return core.f3kdb.Deband(clip, y=64, cr=0, cb=0, grainy=64, grainc=0, keep_tv_range=True, dynamic_grain=False)
    elif clip.get_frame(n).props._PictType.decode() == "P":
        return core.f3kdb.Deband(clip, y=48, cr=0, cb=0, grainy=64, grainc=0, keep_tv_range=True, dynamic_grain=False)
    else:
        return core.f3kdb.Deband(clip, y=32, cr=0, cb=0, grainy=64, grainc=0, keep_tv_range=True, dynamic_grain=False)

out = core.std.FrameEval(src, functools.partial(FrameTypeDeband, clip=src))

如果想了解更多信息,建议阅读Irrational Encoding Wizardry GitHub组的指南:https://guide.encode.moe/encoding/masking-limiting-etc.html,并阅读大多数你喜欢用的Python和VapourSynth的函数说明。几乎所有的优秀压制都应该使用一些蒙版,或者针对特定的情况开发自己的蒙版。

3.2.15 Filter order

滤镜使用的顺序错误可能导致破坏性或糟糕的结果。建议使用以下顺序:

st=>start: 加载源
o1=>operation: 裁剪
o2=>operation: 提高位深
o3=>operation: IVTC and MPEG-2 deblock
o4=>operation: Fix dirty lines
o5=>operation: Detint
o6=>operation: Resize or descale
o7=>operation: Denoise
o8=>operation: Dehalo and dering
o9=>operation: Anti-aliasing
o10=>operation: Deband and deblock
o11=>operation: Graining
e=>end: Dither to output depth
st->o1->o2->o3->o4->o5->o6->o7->o8->o9->o10->o11->end

图15:应用每个滤镜程序的推荐顺序。在某些情况下,可能必须先进行降噪处理。

3.2.16 Example Scripts 示例脚本

Mirai (2018):

import vapoursynth as vs
import fvsfunc as fvf
import mvsfunc as mvf
import kagefunc as kgf
import vsTAAmbk as taa
import havsfunc as haf
from debandmask import *
from rekt import *
from adptvgrnMod import * 

core = vs.get_core()
src = core.ffms2.Source("")

# 提升到16位,因为我喜欢工作在高位深下工作。
b16 = mvf.Depth(src, 16).std.Crop(top=20, bottom=22)

# 填充第一行,填充前两行的色度。
fb1 = core.fb.FillBorders(b16, top=1)
fb2 = core.fb.FillBorders(b16, top=2)
b16 = core.std.Merge(fb1, fb2, [0,1])

#超轻的去噪。这么弱的去噪,在BM3D中没有意义。
b16 = core.knlm.KNLMeansCL(b16, a=3, h=0.1, d=2, device_type='gpu', device_id=0, channels='Y')
b16 = core.knlm.KNLMeansCL(b16, a=2, h=0.2, d=1, device_type='gpu', device_id=0, channels='UV')

#轻微dehalo
b16 = haf.FineDehalo(b16, rx=2.0, thmi=80, thma=128, thlimi=25, thlima=100, darkstr=0.5, brightstr=0.5)

# 片头时的脏线。 清洁边缘,然后是晕。Cleaning edges,then halos. 
cf = core.std.Crop(b16, left=94, top=292, right=1018, bottom=290).fb.FillBorders(top=1, left=1, bottom=1).edgefixer.ContinuityFixer(left=[2, 2, 2], top=[2, 2, 2],right=2, bottom= [0, 2, 2], radius=15)
fb = rekt_fast(b16, lambda x: core.fb.FillBorders(x, left=2, top=2, right=1, bottom=2).std.Merge(cf, [1,0]), left=94, top=292, right=1018, bottom=290)
dh = rekt_fast(fb, lambda x: haf.FineDehalo(x, rx=2.0, thmi=80, thma=128, thlimi=25, thlima=100, darkstr=0.5, brightstr=2.3), left=94, top=292, right=1018, bottom=290)
sf = fvf.rfs(b16, dh, "[1434 2296]")

cf = core.std.Crop(b16, left=94, top=302, right=1018, bottom=300).fb.FillBorders(left=1).edgefixer.ContinuityFixer(left=[2, 2, 2], top=1, right=1, bottom= [1, 2, 2], radius=5)
fb = rekt_fast(b16, lambda x:  core.fb.FillBorders(x,  left=2,  top=1, right=1, bottom=2).std.Merge(cf, [1,0]), left=94, top=302, right=1018, bottom=300) 
dh = rekt_fast(fb, lambda x: haf.FineDehalo(x, rx=2.0, thmi=80, thma=128, thlimi=25, thlima=100, arkstr=0.5, brightstr=1.5), left=94, top=302, right=1018, bottom=300)
sf = fvf.rfs(sf, dh, "[133711 135117] [135360 136057] [136143 137216] [137282 138288] [138377 138757] [138820 140782]")

cf = core.std.Crop(b16, left=94, top=302, right=1018, bottom=300).fb.FillBorders(left=1).edgefixer.ContinuityFixer(left=[2, 2, 2], top=1, right=1, bottom= [1, 2, 2], radius=5)
fb = rekt_fast(b16, lambda x:  core.fb.FillBorders(x,  left=2,  top=1, right=1, bottom=2).std.Merge(cf, [1,0]), left=94, top=302, right=1018, bottom=300)
dh = rekt_fast(fb, lambda x: haf.FineDehalo(x, rx=2.0, thmi=80, thma=128, thlimi=25, thlima=100, darkstr=0.5, brightstr=1.5).f3kdb.Deband(y=48, cb=0, cr=0, range=5, grainy=64, grainc=32, output_depth=16, keep_tv_range=True), left=94, top=302, right=1018, bottom=300)
sf = fvf.rfs(sf, dh, "[135118 135296] [138305 138376]")

mask = core.std.ShufflePlanes(b16, 0, vs.GRAY).std.Trim(2400, 2401) * src.num_frames
mask = rekt(mask, core.std.BlankClip(b16, 1920, 1038, format=vs.GRAY16), left=666, top=292, right=1114, bottom=744)
dh_lim = core.std.MaskedMerge(dh, b16, mask) sf = fvf.rfs(sf, dh_lim, "[2297 2329]")

# 4:3剪裁的场景。用我自己的黑色边框替换原本边框,以防止它们有不同的黑色阴影。
crop = core.std.Crop(b16, left=254, right=254)
fb = core.fb.FillBorders(crop, left=1, right=1).std.Merge(crop, [1,0]).edgefixer.ContinuityFixer(left=1, right=1, top=0, bottom=0, radius=50).std.AddBorders(left=254, right=254, color=[4096, 32768, 32768])
sf = fvf.rfs(sf, fb, "[33448 34196]")

# Placebo edgemask binarized so we only get the obvious edges, then inflated.
mask = kgf.retinex_edgemask(b16).std.Binarize(65500).std.Maximum().std.Inflate()

# 明显的叠影。
aa = taa.TAAmbk(b16, aatype=2, mtype=0, opencl=False)
aa = core.std.MaskedMerge(b16, aa, mask)
sf = fvf.ReplaceFramesSimple(sf, aa, mappings="[4225 4727] [18340 18387] [129780 131148]")

# 轻微的叠影.
aa = taa.TAAmbk(b16, aatype=3, mtype=0, opencl=False) 
aa = core.std.MaskedMerge(b16, aa, mask)
sf = fvf.ReplaceFramesSimple(sf, aa, mappings="[55394 55451] [55649 55782] [120840 120901]")

#非常明显的叠影。
aa = taa.TAAmbk(b16, aatype=6, mtype=0, repair=16) 
aa = core.std.MaskedMerge(b16, aa, mask)
sf = fvf.ReplaceFramesSimple(sf, aa, mappings="[107405 107462]")

#强烈的混叠,我试图用一个糟糕的mask来修复 
mask = kgf.retinex_edgemask(b16).std.Binarize(65500).std.Maximum().std.Minimum(coordinates=[1,0,1,0,0,1,0,1]).std.Deflate().std.Deflate()
aa = taa.TAAmbk(b16, aatype=6, mtype=0, opencl=False) 
aa = core.std.MaskedMerge(b16, aa, mask)
sf = fvf.ReplaceFramesSimple(sf, aa, mappings="[55510 55580]")

#我只是简单的标注了一下,这需要大量的工作,所以我就决定不做了。
#sf = fvf.rfs(sf, ?, "[65880 66478]")
#sf = fvf.rfs(sf, ?, "[120902 121051] [121790 121905] [122388 122528] [123038 123153] [126686 126812] [128740 128953]") 
#Banding? [121063 121095] [121906 121968] [122530 122576]

#缺少噪点的地方增加噪点。
gr = adptvgrnMod(b16, strength=2.5, size=1.25, sharp=35, static=False, luma_scaling=3, grain_chroma=False)
sf = fvf.rfs(sf, gr, "[120840 120901]")

#用标准deband mask进行deband。所有这些去带区域几乎没有噪点,所以我在上面加了一些。
dbmask = debandmask(b16, lo=6144, hi=12288, lothr=320, hithr=384, mrad=2) deband = core.f3kdb.Deband(b16, y=34, cb=0, cr=0, range=10, grainy=16, grainc=8, output_depth=16, keep_tv_range=True)
merge = core.std.MaskedMerge(deband, b16, dbmask)
merge = adptvgrnMod(merge, strength=2, size=1.5, sharp=25, static=False, luma_scaling=5, grain_chroma=True)
sf = fvf.rfs(sf, merge, "[3174 3254] [3540 3655] [7463 7749] [41056 41597] [63482 64106] [91033 91164]")

# 使用retinex进行deband. 
mask = kgf.retinex_edgemask(b16).std.Maximum().std.Inflate().std.Maximum().std.Inflate()  
deband = core.f3kdb.Deband(b16, y=48, cb=48, cr=48, range=15, grainy=16,
grainc=16, output_depth=16, keep_tv_range=True)
merge = core.std.MaskedMerge(deband, b16, mask)
merge = adptvgrnMod(merge, strength=2.2, size=1.25, sharp=15, static=False, luma_scaling=5, grain_chroma=True)
sf = fvf.rfs(sf, merge, "[77952 78034] [93358 93443]")

#用gradfun3的mask进行deband.
deband = Fag3kdb(b16, thry=54, thrc=54, radiusy=10, radiusc=6, grainy=32, grainc=16)
sf = fvf.rfs(sf, deband, "[25 263]")

# 抖动回到8-bit.
final = mvf.Depth(sf, 8, dither=7)

# 用简单的黑色屏幕代替黑色区域,以避免发生轻微的变化。但通常没有必要。
blank = core.std.BlankClip(src.std.Crop(top=20, bottom=22), 1920, 1038, color=[16, 128, 128])
final = fvf.rfs(final, blank, "[0 24] [1352 1433] [58945 59016] [75563 75633] [78351 78421] [81130 81141] [81261 81272] [93967 94062] [99889 99959] [118093 118147] [140928 140951]")

final.set_output()

Sword Art Online: The Movie - Ordinal Scale (2017):

Sword.Art.Online.The.Movie.Ordinal.Scale.2017.ITA.1080p.BluRay.AC3.x264.D-Z0N3


import vapoursynth as vs
core = vs.get_core()
import fvsfunc as fvf
import kagefunc as kgf
import havsfunc as hvf
import vsTAAmbk as taa
import fag3kdb
import nnedi3_rpow2 as nnrp
src = core.ffms2.Source("")
resize = src #我调用了这个,但太懒了,不想改变它。

# Rescaling a flashback with grain.
dn = core.knlm.KNLMeansCL(src,   d=3,   a=1,   s=0,   h=1.5,   device_type="gpu", device_id=1, channels="Y")
diff = core.std.MakeDiff(src, dn,  planes=[0])  ds = fvf.Debicubic(dn, 1280, 720)
us = nnrp.nnedi3_rpow2(ds, 2, 1920, 1080, kernel="Spline36") merged = core.std.MergeDiff(us, diff, planes=[0])
src = fvf.ReplaceFramesSimple(resize, merged, mappings="[3418 3507] [3508 5145] [75916 76205] [76253 76323] [77720 77790]")

# Rescaling a flashback without grain.
ds = fvf.DescaleAA(dn,  1280,  720).std.MergeDiff(diff,  planes=[0])  
src = fvf.ReplaceFramesSimple(src, ds, mappings="[3298 3417]")

# 调整为16-bit. 我担心性能问题,上面使用了8-bit
src = fvf.Depth(src, 16)

# 我喜欢为16-bit建立一个单独的变量,而把src留给8-bit,但是这里没有这样做。这样我就可以复制粘贴命令了。
b16 = src

# 抗锯齿。你也许能看出,现在可以用rekt_fast或者干脆用rektaa来代替crop和stacking。??
aa = core.std.Crop(b16, left=400, right=1006)
aa = taa.TAAmbk(aa, aatype=-3, preaa=-1, strength=0, mtype=2, opencl=True) 
left = core.std.Crop(b16, right=1920 - 400)
right = core.std.Crop(b16, left=1920 - 1006)
aa = core.std.StackHorizontal([left, aa, right]).std.Crop(top=208, bottom=456)
top = core.std.Crop(b16, bottom=1080 - 208) 
bottom = core.std.Crop(b16, top=1080 - 456)
aa = core.std.StackVertical([top, aa, bottom])
sfaa = fvf.ReplaceFramesSimple(b16, aa, mappings="[42583 42813] [58812 59050] [65211 65281] [92132 92274]")

#用标准的ass mask进行deband.
db = b16.f3kdb.Deband( range=15, y=60, cb=60, cr=60, grainy=22, grainc=22, output_depth=16)
mask = kgf.retinex_edgemask(b16).std.Inflate() 
merged = core.std.MaskedMerge(db, b16, mask)
sfdb = fvf.ReplaceFramesSimple(sfaa, merged, mappings="[3508 3603] [17600 17706] [41865 42113] [76922 77488] [78444 78598] [81054 81280] [150853 150933] [152057 152288] [152324 152424] [152443 152508] [152521 152686] [171669 172433] [172561 172643] [170283 170557]")

# 在10000-25000亮度范围外进行deband。
db = b16.f3kdb.Deband(range=10, y=160, cb=0, cr=0, grainy=28, grainc=0, output_depth=16)
mask = core.std.ShufflePlanes(b16, 0, vs.GRAY).std.Expr("x 10000 < x 25000 > or x 10 * x 10 / ?")
merged = core.std.MaskedMerge(db, b16, mask)
sfdb = fvf.ReplaceFramesSimple(sfdb, merged, mappings=" [96133 96273]")

# 修复在演职员名单中的脏线。同样的,rekt_fast在当时也是很有用的。
bot = core.std.Crop(sfdb, top=1080 - 330)
middle = core.std.Crop(sfdb, top=318, bottom=330).edgefixer.ContinuityFixer(top=1, bottom=1, left=0, right=0, radius=5)
fb = core.fb.FillBorders(middle, top=2, bottom=2)
middle = core.std.Merge(fb, middle, [1, 0])
top = core.std.Crop(sfdb, bottom=1080 - 318)
merge = core.std.StackVertical([top, middle, bot])
right = core.std.Crop(merge, left=1920 -  134)
middle = core.std.Crop(merge, left=1018,right=134).edgefixer.ContinuityFixer(left=2, right=2, top=0, bottom=0, radius=5)
fb = core.fb.FillBorders(middle, left=2, right=2)
middle = core.std.Merge(fb, middle, [1, 0])
left = core.std.Crop(merge, right=1920 - 1018)
merge = core.std.StackHorizontal([left, middle, right])
sfc = fvf.ReplaceFramesSimple(sfdb, merge, mappings="[165067 167168] [167403 169466] [169842 170557] [170558 171041]")

# 抖动到8bit输出。 
final = fvf.Depth(sfc, 8)

final.set_output()

BTS - Blood, Sweat and Tears BTS - Blood, Sweat & Tears 2016 1080p ProRes FLAC 2.0 AVC x264 10-bit - A.R.M.Y bandmask35的快速说明: 边缘蒙版通过发现梯度很大的区域起作用,所以最简单的卷积是: $$ \left[ \begin{matrix} 0 & 0 & 0 \ 1 & 0 & -1 ,\ 0 & 0 & 0 \end{matrix} \right] $$ 这表示我们在检查左侧和右侧像素之间的差异。通过检查与原始像素的差异,我们可以通过移动相减识别没有颗粒的区域: $$ \left[ \begin{matrix} 0 & 0 & 0 \ -1 & -1 & 0 ,\ 0 & 0 & 0 \end{matrix} \right] $$ 不过,这并不完全是这里发生的事情;我们会在多次卷积迭代之后执行减法运算,因此可以在更多像素上获得渐变。然后,我们将其二值化,最小化以去除噪声,然后再次最大化。

import vapoursynth as vs
core =  vs.get_core()
import fvsfunc as fvf
import kagefunc as kgf
import havsfunc as hvf
from adptvgrnMod
import * from rekt import *
from bandmask import *

#加载,提高位深并裁剪
src = core.ffms2.Source("Blood, Sweat & Tears_BTS -187617728.mov")
hbd = fvf.Depth(src, 16)
crp = hbd.std.Crop(top=134, bottom=134)

#有一个场景的底部少了四条线,其他地方的也有脏线。 
# 每一个平面都被单独修复了。
acr = crp.std.Crop(bottom=4)
ycr = acr.std.ShufflePlanes(0, vs.GRAY)
ucr = acr.std.ShufflePlanes(1, vs.GRAY)
vcr = acr.std.ShufflePlanes(2, vs.GRAY)

ufx = rektlvls(ucr, [ucr.height - 2], [-6.5], prot_val=0).fb.FillBorders(top=1, mode="fillmargins").cf.ContinuityFixer(bottom=1, radius=3)
vfx = vcr.cf.ContinuityFixer(bottom=3, radius=5).fb.FillBorders(bottom=2, top=1, mode="fillmargins")
lvl = rektlvls(ycr, [ycr.height - 2, ycr.height - 3], [-30, -6], prot_val=10)
fmg = lvl.cf.ContinuityFixer(bottom=2, radius=3).fb.FillBorders(bottom=1, top=1, mode="fillmargins")

# 合并修复,并将取样改为4:2:0,然后添加边框。
acr = core.std.ShufflePlanes([fmg, ufx, vfx], [0, 0, 0], vs.YUV)
rsz = acr.resize.Spline36(format=vs.YUV420P16)
adb = rsz.std.AddBorders(bottom=4)

#视频的其余部分只有上下少了一行像素,修复并修改取样.
fmg  =  crp.fb.FillBorders(top=1,  bottom=1,  mode="fillmargins")  
rsz = fmg.resize.Spline36(format=vs.YUV420P16)

#拼接修复的东西
trm = rsz.std.Trim(0, 5232) + adb.std.Trim(5233, 5260) + rsz.std.Trim(5261)

#使用亮度mask在几个场景种进行deband,并添加噪点。
dbn = trm.f3kdb.Deband(y=64, cr=0, cb=0, range=6, grainy=0, grainc=0, output_depth=16)
msk =kgf.retinex_edgemask(trm).std.Binarize(11000).std.Maximum().std.Inflate()
mrg = dbn.std.MaskedMerge(trm, msk)
grn = adptvgrnMod(mrg, size=1.4, sharp=90, luma_scaling=4, grainer=lambda x: core.grain.Add(x, var=1.6, uvar=1.0, constant=False))
snf = fvf.rfs(trm, grn, "[1612 1616] [7779 7794]")

#由于没有边缘,所以用bandmask修复了一些block。
bmk = bandmask(trm, 200).std.Crop(top=500).std.AddBorders(top=500)
grn = adptvgrnMod(dbn, size=1.3, sharp=90, luma_scaling=4, grainer=lambda x: core.grain.Add(x, var=.4, uvar=.3, constant=False))
mrg = trm.std.MaskedMerge(grn, bmk)
snf = fvf.rfs(snf, mrg, "[7964 7980] [8009 8018]")

# 有一个场景缺少噪点,所以用bandmask和adptvgrnMod来修复这个问题。
# 我很沮丧,开始复制粘贴随机的Maximize/Inflate调用。 
bmk = bandmask(trm, thr=300)
lmk = trm.std.ShufflePlanes(0, vs.GRAY).std.Binarize(55000).std.Maximum().std.Maximum().std.Maximum().std.Maximum
gmk = core.std.Expr([bmk, lmk], "x y -").std.Crop(bottom=225, top=100).std.AddBorders(bottom=225, top=100).std.Maximum().std.Inflate().std.Maximum().std.Inflate().std.Maximum().std
gmk = kgf.iterate(gmk, core.std.Maximum, 3)
grn = adptvgrnMod(trm, size=1.2, sharp=80, luma_scaling=1, grainer=lambda x: core.grain.Add(x, var=1.4, uvar=1.0, constant=False))
mrg = core.std.MaskedMerge(trm, grn, gmk) snf = fvf.rfs(snf, mrg, "[7579 7778]")

#在一场景种进行Dehalo。 
fdh = hvf.FineDehalo(trm)
grn = adptvgrnMod(fdh, size=1.2, sharp=80, luma_scaling=8, grainer=lambda x: core.grain.Add(x, var=.8, uvar=.5, constant=False))
snf = fvf.rfs(snf, grn, "[6523 6591]")

#抖动到输出位深。 
out = fvf.Depth(snf, 10)
out.set_output()

Other fun scripts to read through:

3.2.17 Forum and Blog Posts 论坛和博客

Public:

3.3 x264 and x265

我们推荐的两个主要视频编码器是x264和x265。x265因为较新所以从技术上来说应该更为优秀,两者也都各有优劣之处。 作为更成熟的编解码器,x264会更有一致性(符合预期),这意味着你不太可能会遇到随机出现的莫名其妙看起来很糟糕的场景。并且它有着更好的硬件支持和快得多的编码速度。编码速度上的差异通常至少是5倍。 另一方面,x265的优势在于它可以存储HDR信息而且更为有效,特别是对于简单的内容(如低码率动画)。

3.3.1 8-bit and 10-bit

我们建议为x264使用的两种位深分别是8位和10位。如果你正在用x265,请始终使用10位,因为它在各个方面都更好。在x264中,8位有更多的硬件兼容性(10位几乎没有),而且速度快得多。然而,10位x264不但在速度上比x265快,通常还能够节省相当多的空间。此外,10位编码在保存渐变方面要好得多(也就是说,你不必担心重复出现色带),这使得它在编码无噪点的内容(比如动画)方面非常受欢迎。

3.3.2 x264参数(设置)

来自AHD指南(硬件兼容性的为灰色,部分条目有改动): 一般参数(设置)

  • --level 4.1 for DXVA。
  • --b-adapt 2 使用(x264有的)最好的算法来决定如何安排B帧。
  • --min-keyint 通常应该是视频的帧率,例如,如果你编码的是23.976 fps的内容,就使用24帧。这是设置i帧之间的最小距离。
  • --vbv-bufsize 78125 -vbv-maxrate 62500 for DXVA(旧的指南使用较低的值,是考虑到将编码内容写入到蓝光盘来播放的可能性,现在不再考虑这一点,因为其他设置会破坏这种兼容。新的值是--level 4.1可以做到的最大值,如果你的设备因为这个而中断,并不是编码出错,是你的设备不符合DXVA规范)。
  • --rc-lookahead 250,如果使用mbtree,则为60或更高。它设置了x264可以提前查看的帧数,这对mbtree很关键。这个设置会消耗大量的内存(我个人现在把这个值设为250,因为它对内存的需求是2GB左右)。如果你没有开启mbtree,并且脚本中有很多ReplaceFramesSimple调用,那么这个值一定要调低。
  • --me umh是最低要求。如果你的CPU足够快,也许能忍受tesa的减速。esa花费的时间和tesa一样多,但没有任何好处,所以如果你想要减慢你的编码来尝试捕捉移动向量,就使用tesa,尽管调高并不总是值得的。这并不是一个真正需要测试的参数,但是在源的质量较差时,如果使用tesa,可能会从x264中压榨出更多的性能。
  • --direct auto会自动选择预测模式(spatial/temporal)
  • --subme 10或11(我个人刚刚设置为11,编码速度的差异在3-4%以内)
  • --trellis 2
  • --no-dct-decimate dct-decimate会牺牲质量换取速度。把它关掉好了,因为你的电脑很可能足以应付。- --no-fast-pskip 与上面的类似。
  • --preset veryslow or placebo,尽管我们改变的参数会让veryslow变成placebo,也无所谓了。
特定于源的参数(设置):
  • --bitrate / --crf 码率是Kbps(千比特每秒),crf采用浮点数,越低质量越好。这是最重要的参数;如果码率不足,质量肯定会很次。如果码率过高,就会臃肿不堪(如果人们想下载大文件,他们会得到一个remux)。当然,需要的码率可以有很大变化,主要取决于来源。

  • --deblock -3:-3到1:1。对于真人表演,大多数人只需要固定用-3:-3。对于动画来说,通常取-3:-2和0:0之间的值,这两个值之间的偏移量取1是惯例(常见值是-1:0、0:-1和-2:-1)。

  • --qcomp 0.6(默认)到0.8比较有效。不要将其设置得过高或过低,否则编码的整体质量将受到影响。这个参数对mbtree有很大的影响。qcomp值越高,mbtree就越弱,所以对于动画来说,0.80左右是最优的。--qcomp 0将产生固定码率,而--qcomp 1将产生恒定的QP(量化参数)。

  • --aqi-mode 1到3。1在每帧基础上分配码率,2倾向于分配更多码率到前景,并可以在一个小范围的帧内分配码率,3是2的变形版本,它试图分配更多码率到帧的黑暗部分。想要知道什么值是最好的,唯一的方法就是去测试。然而,几乎每一个源最终在使用aq_mode 3时看起来都是最好的。

  • --aq-strength 0.5至1.3值得一试。更高的值可能有助于(减少)色块?。较低的值将倾向于分配更多码率到前景,类似于aql-mode 2。当使用CRF进行编码时,此参数会极大地影响码率,因此如果您打算使用CRF进行最终编码,那么使用2 pass测试此参数可能是值得的。

  • --merange 24(永远不要低于这个值)到64,设置太高(超过128)可能会有害,32或48将适合大多数编码。一般来说,对于有很多动作的电影(例如动作片),1080p用32-48,720p用32(当使用umh时),会有帮助。人物对话可以用像24这样比较低的值。对编码速度的影响是显而易见的,但并没到可怕的程度。使用umh时,我喜欢使用48喂1080p, 32喂720p;使用tesa时,我喜欢用32喂1080p, 32喂720p。

  • --no-mbtree 我强烈建议对启用和禁用mbtree都进行测试,因为通常这会导致两种非常不同的编码结果。mbtree基本上尝试降低块的质量,而不是帧,因此只有帧中不重要的部分得到更少的码率。为此,它需要知道一个块稍后被引用的频率,这就是为什么--rc-lookahead应该设置为250。它对于静态背景的东西很有用,比如动画,或者你使用了高qcomp(.75或以上)而且mbtree将有较低影响的时候。在测试这是否是一个合适的选项时,你可能必须重新测试每个参数,特别是qcomp、psyo-rd和ipratio。

  • --ipratio 1.15到1.40,1.30通常是首选。这是I帧和P帧之间的码率分配比率。

  • --pbratio 1.05到1.30,通常是1.20。这是P帧和B帧之间的码率分配比率。这个值应该总是比-ipratio低0.10左右,所以在测试ipratio时降低它。如果启用了mbtree,那么这个参数将不会产生任何影响,因为它是由mbtree本身决定的。

  • --psy-rd 0.40:0到1.15:0。 0.95:0到1.15:0用于真人电影。第一个数字是psy rd强度,第二个是psy-trellis强度。这试图让x264保持内容的复杂度(细节),而避免变得模糊。对于动漫,0.40到1.000.00是常用范围。psy-trellis通常会引入很多晕轮,但可以帮助维护(减少)抖动。对真人电影你可以尝试0.00到0.15之间的值,对动画你可以尝试将值一直增加到最高0.50,然而如果你用提高aq-strength来替代的话,通常会得到更好的结果。

  • --bframes 6到16,这是设置可以被替换为B帧的连续P帧的最大数量。第一次运行测试用16,根据x264日志设置:x264 [info]:consecutive B-frames:1.0% 0.0% 0.0% 14.9% 23.8% 13.9% 15.8% 8.9% 9.9% 0.0% 11.9% 0.0% 0.0% 0.0% 0.0% 0.0%从第一个百分比开始计数,从0开始数0,选择大于1%的最大值,本例为11。(或者把这个值设为16,因为允许更多的bframes不会对编码有害,而且有助于压缩,对速度的影响不是很大。)

  • --ref 设置每个P帧可以作为参考的前一帧的数量。如果不关心硬件兼容性(和/或正在进行10位编码),那么将其设置为16。虽然对性能的影响很大,但大多数时候还是值得的。计算你可以使用的数字,或者如果你正在使用所提供的CLI输入,在结束的时候它将通过x264为您计算。尽可能使用最大值。

    最大的ref值的计算方法如下: 对于 --level 4.1,根据H.264标准,最大的DPB(解码图像缓冲区)大小是12288 kb。由于每一帧都以YV12格式存储,即每像素存储1.5字节,所以1920x1088的帧就是1920×1088×1.5 = 3133440字节= 3060kb。12288÷3060kb = 4.01568627,所以可以使用最多4个参考帧。记住,计算时把两个维度都取整为mod16值,即使你没有编码mod16! 让我们计算一下1920x800的情况。1920×800×1.5 = 2304000字节= 2250kb。12288÷2250kb = 5.45777778,所以最多可以使用5个参考帧。注意,这些转换是用的2进制,所以1kb == 1024字节。如果计算错误,也没关系——如果你用得过多, x264将显示一个警告,这样你就知道是否需要更改它。

  • --zone 对于去色带和色块是非常有用的,因为这些区域需要更大的码率来保持视觉无损。语法如下:--zones 0,100,crf = 10/101,200 crf = 15 或者--zones 0,100,b=5/101,200,b=10,本例中b是码率系数。您也可以将此用于没有分配足够码率的区域。特别常见的区域是深色的场景或有很多红色的场景。渐隐也会导致欠码并且需要设置zone。你也可以在()期间来降低码率以节省()。

  • --output-depth 8或10 取决于你编码的内容。

  • --output-csp i444 if你的编码是4:4:4,否则不用管它。

3.3.3 x265 Settings

这里的文档已经非常好了,所以我只需要介绍一下推荐值:

与源无关的参数(设置):
  • --preset veryslow or slower
  • --no-rect 对于较慢的电脑。有些许可能被证明是有用的,但很可能并不值得。
  • --no-amp 与rect相似,尽管它看起来稍微更有效。
  • --no-open-gop
  • --no-cutree 因为这似乎是一个很糟糕的mbtree实现(应用)。
  • --no-rskip rskip会牺牲一些质量来换取加速,所以对于差点的cpu来说,它值得考虑。
  • --no-sao 因为sao是x265中最愚蠢的实现(设置)之一。
  • --ctu 64
  • --min-cu-size 8
  • --rdoq-level 2
  • --max-merge 5
  • --rc-lookahead 60 虽然并不相关,但只要大于min-keyint。
  • --ref 好cpu用6,差点的用4。
  • --bframes 16 或者你最终的bframes日志输出的值。
  • --rd 3或4 (目前是一样的)
  • --subme 5. 你也可以把它改成7,但众所周知这会导致锐化。
  • --merange 57 只要不要低于32,就不会有问题。
  • --high-tier
  • --range limited
  • --aud
  • --repeat-headers
受源影响(依赖于源)的参数(设置):
  • --colorprim 9 for HDR, 1 for SDR.
  • --colormatrix 9 for HDR, 1 for SDR.
  • --transfer 16 for HDR, 1 for SDR.
  • --hdr10 for HDR.
  • --hdr10-opt for 4:2:0 HDR, --no-hdr10-opt for 4:4:4 HDR and SDR.
  • --master-display "G(8500,39850)B(6550,2300)R(35400,14600)WP(15635,16450)L(10000000,20)" with the values for L coming from your source’s MediaInfo output。 L的值取自于源的媒体信息。
  • --max-cll "711,617" again fromyour source’s MediaInfo.
  • --cbqpoffs and --crqpoffs 通常应该在-3和0之间。 This sets an offset between the bitrate applied to the luma and the chroma planes。它设置了应用于亮度和色度平面的比特率之间的偏移量。
  • --qcomp between 0.60 and 0.80.
  • --aq-mode 4, 3, 2, 1, or --hevc-aq 4和3通常是两个最好的选择。
  • --aq-strength between 0.80 and 1.50.
  • --deblock -4:-4 to 0:0, 与x264相同。真正用的时候你可以用默认-3:-3
  • --ipratio and --pbratio 与x264相同。
  • --psy-rd 0.50 to 2.00, 与x264相似。
  • --psy-rdoq 通常从0.00到2.00不等。
  • --no-strong-intra-smoothing on sharp/grainy content, you can leave this on for blurry content, as it’s an additional blur that’ll help prevent banding. 用于锐化/噪点的内容。对于模糊不清的内容,可以把这个选项打开,因为这是一个额外的模糊,有助于防止色带。
  • --output-depth 10 for 10-bit output.
实验性质的参数(设置)
  • --scenecut-aware-qp
  • --scenecut-window 550
  • --max-qp-delta 2
  • --hist-scenecut
  • --hist-threshold 0.02

首先,需要选择视频文件中较小的区域作为参考,因为对整个文件进行测试将花费很长时间。推荐的方法是使用awsmfunc的SelectRangeEvery函数:

import awsmfunc as awf
out = awf.SelectRangeEvery(clip, every=15000, length=250, offset=[1000,5000])

这里,第一个数字是节之间的偏移量,第二个数字是每个节的长度,偏移量数组是开始和结束的偏移量。

你会想要一个足够长的剪辑(通常有几千帧),包括暗、亮、静态和动作场景,但是,这些应该大致平均分布,就像在整个视频里一样。

当测试参数时,你应该总是使用2-pass编码,因为许多参数会在一定程度上改变CRF得到的码率。对于最终的编码,两者都很好,尽管CRF更快一些。

为了找出哪种设置是最好的,将它们相互比较,并与源进行比较。你可以将它们个别的交错,或者用awsmfunc通过一个文件夹来让它们交错。通常需要给它们打上标签,这样你就知道你在看哪个片段了。

# Load the files before this
src = awf.FrameInfo(src, "Source")
test1 = awf.FrameInfo(test1, "Test 1")
test2 = awf.FrameInfo(test2, "Test 2")
out = core.std.Interleave([src, test1, test2])
# You can also place them all in the same folder and do
src = awf.FrameInfo(src, "Source")
folder = "/path/to/settings_folder"
out = awf.InterleaveDir(src, folder, PrintInfo=True, first=extract,
repeat=True

如果你正在使用yuuno,可以使用下面的iPython技巧来获得预览,将鼠标悬停在预览屏幕上就可以在两个源之间切换:

%vspreview --diff
clip_A = core.ffms2.Source("settings/crf/17.0")
clip_A.set_output()
clip_B = core.ffms2.Source("settings/crf/17.5")
clip_B.set_output(1)

通常,需要首先测试码率。只需对两个不同的CRF进行编码,并将它们与源进行比较,就可以找到接近于源质量的最高CRF值。现在,四舍五入这个值——最好是向下,并切换到2-pass。对于标准测试,测试qcomp(间隔0.05),aq-mode, aq-strength(间隔0.05),merange (32, 48, 64),psyrd(间隔0.05),ipratio/pbratio(间隔0.05并保持两者距离为0.10),然后deblock(间隔为1)。如果你觉得mbtree有帮助(比如你在编码动画),启用mbtree重做这个过程。你可能不想过多地更改顺序,但确实可以这样做。

对于x265,顺序应该是qcomp、aq-mode、aq-strength、psy-rd、psy-rdoq、ipratio和pbratio,然后是deblock。

如果你想要更多一些效益,可以围绕着你为每个参数决定的值,用更小的间隔重新做测试。建议在对每个参数都进行了测试之后再进行此操作,因为它们之间都存在轻微的相互影响。

一旦你完成了2-pass下的参数测试,切换回CRF并重复寻找最高视觉无损的CRF值的过程。

4 音频

4.1 SoX

如果你的源音频是高解析度(24位或>48kHz),你可能想要更改这一点以节省空间,因为如果转换能够恰当的完成的话,高解析度音频对消费者来说很大程度上只是心理作用。为了做到这一点,我们推荐SoX。如果你想转换为16位48 kHz,你可以使用以下输入: $ sox foo.wav -G -b 16 bar.wav -v -L 48000 dither -s -f gesemann 如果源音频的采样率是48 kHz的倍数(例如96 kHz),请使用这个(输入)。对于44.1 kHz的倍数,你可以简单地将48000替换为44100。 我们建议对编码进行这些转换,因为较高的采样率通常比其他任何方法都更具破坏性,而且24位和适当的16位之间的差异非常小,只有在非常大量的情况下才会明显。

4.2 有损编码

4.2.1 Opus

Opus作为协奏曲上最有效的和首选的有损编解码器,几乎没有硬件兼容性。这对使用HTPC、台式机、笔记本电脑甚至手机的人来说都不是问题,但试图使用蓝光播放器回放的用户将无法回放。由于我们不太关心硬件兼容性,这可能是最简单的方法。除了是最有效的编解码器,它也是完全开源的,就像FLAC一样,并且在每个操作系统上都能完美地工作。Opus最大的问题是它不适合matroska的有序章节;但是,除非您编码的内容有很多相同的intro/ outros重复,否则这并不重要。为了对opus进行编码,您需要安装opus-tools,从这里开始只需要 $ opusenc foo.wav bar.opus 。此外,如果您不想使用缺省值(公认较低),您可以指定--比特率,尽管Opus会自动检测它的最佳目标比特率。

4.2.2 qAAC and fdkaac

作为效率仅次于Opus的第二名,qAAC也是一个可行的选择。与Opus不同,AAC有更多的硬件兼容性,特别是在立体声方面。它也没有顺序章节的问题。然而,qAAC需要iTunes,在Unix下需要wine。为了编码最高质量的qAAC,您的输入将是: qac .exe -V - 127英尺wav bar.m4a v值表示质量,,数值越大越好,最大值为127。

4.2.3 Dolby Digital aka. Audio Codec 3

硬件兼容性之王是AC-3。因为它是dvd的编解码器,它在大多数播放设备上有很好的支持。然而,它的效率比MP3还差。如果你想在hoome上为硬件兼容性编码(比如你想为AHD/HDB/etc编码),推荐的方法是通过Sound Forge (Windows)或Dolby Media Producer (OS X)。 为了在Sound Forge中编码AC-3,打开文件(w64格式),等待它建索引,然后转到Save As选项。从这里,选择杜比数字AC-3 Pro。然后,选择以下选项(复制自AHD指南):

  • Bitstream mode: Main audio service: Complete main
    • Audio coding mode: 3/2 (L, C, R, Ls, Rs) with LFE enabled- Sample rate: 48 kHz
  • Data rate: 640 kbps
  • Dialog normalization: -32 dB
  • Do not select Save data in Intel byte order
  • Center mix level: -3 dB
  • Surround mix level: -3 dB
  • Do not set copyright bit
  • do not mark as original bitstream
  • Do not include audio production information
  • Do not enable extended bitstream information
  • Do not enable digital de-emphasis
  • Enable DC high-pass flter
  • Enable Bandwidth low-pass flter
  • Enable LFE low-pass flter
  • Do not enable 90-degree phase shift
  • Do not enable 3 dB attenuation
  • Line mode profle: None
  • RF mode profle: None
  • Do not enable RF overmodulation protection

The new E-AC-3 codec has already risen in popularity lately. What this brings to the table is support for Atmos andmore channels. However, the only crack for the encoder doesn’t include Atmos support... 新的E-AC-3编解码器最近已经流行起来。这带来了对Atmos和更多通道的支持。然而,编码器的唯一缺陷不包括Atmos支持,它的效率和AC-3是一样的,因为它只是另一层封装在上面。由于这些原因,我们觉得除了声明它需要杜比媒体生成器,没有必要详细说明如何编码。

4.2.4 DTS

这个编解码器是非常常见的用于1080p的PT编码。坦率地说,这只是因为膨胀的比特率。实际上,DTS在1509 kbps时比AC-3在448 kbps时还要差。只是不要用这个。考虑它的唯一原因是懒惰,因为几乎每部电影源都有DTS Core音轨。

4.3 无损编码

4.3.1 FLAC

它是免费的,它是开源的,它适用于任何地方,它是最有效的。如果你在用立体声或单声道,它在硬件兼容性方面应该没问题。否则,它可能与一些接收器有兼容问题。 $ flac --compression-level-8 foo.wav bar.flac

4.3.2 TrueHD

由于较差的硬件支持,这种编解码器不如DTS-hdMA流行,但它更有效一些,特别是在较低的比特率——这时DTS core的膨胀不增加码率。遗憾的是,由于它的硬件兼容性较差,很多小组坚持要包括一个单独的AC-3兼容轨道。在协奏曲(音乐会?)中,我们希望你不要这样做,但要明白这可能是上传到其他地方所必需的,或者只是为了硬件兼容性。我不知道怎么做这样的编码。问吝啬鬼(守财奴)。

4.3.3 DTS-HD MA

由于更好地支持使用core作为后备,这种编解码器在环绕声remuxes中非常流行。你可以通过提取你的轨道到多个wav来编码,每个频道一个,然后拖动他们到编码套件,点击大红色编码按钮。在协奏曲(音乐会?)中,我们不喜欢这种编解码器,更喜欢使用TrueHD或FLAC。

5 Muxing

5.1 附加文件

有时候,你的编码会崩溃,或者你想编辑一个编码的一小部分。在这样的情况下,我们可以简单地在mkvtoolnix中追加文件。如果你只是想在另一个文件之后添加一些东西,只需在mkvtoolnix的原文件区域右键点击文件。对于视频编辑,首先在末尾找到适当的I-frame,这样你就可以进行足够的编码,然后编码你的片段,在mkvtoolnix中打开每个文件,转到output选项卡中的split选项,然后为视频的部分设置帧。记住计数从1开始(不像Python从0开始)。在你想要添加更改之前和之后的部分执行此操作,然后像以前一样添加它们。您还可以通过这样做来创建一个混合视频remux。确保在muxing之后检查你有所有必要的帧,因为如果你不注意的话,你很容易就会因为糟糕的trimming 导致错误的i帧被使用或错误的帧计数。

5.2 无缝链接

请阅读这篇论坛文章:https://forums.animesuki.com/showthread.php?t=66444。没有什么需要追加的了。

6 教程和资源推荐

• Irrational Encoding Wizardry’s fansubbing guide: https://guide.encode.moe • Kageru’s blog: https://kageru.moe/blog/ • eXmendiC’s fltering guide: https://iamscum.wordpress.com/_test1/_test2/ • AHD Guides

7 做出贡献的人——按字母排序

• Aicha @ D-Z0N3, NSDAB, REEEEEEEE • Anon @ Concertos, NSDAB • Q2KTyrant @ Concertos • Ryuu @ Concertos, NSDAB • Scrooge @ Concertos, COC, FidelityTM • xCreamEnte @ EPSiLON, HDBEE, SiGMA

8 附录

图16:缩小大小的比较。源从1080p缩小到720p,然后被裁剪。所有参数都保留默认值。

图17:放大以后的大小。源(Commie encode of Your Name(2016))被裁剪,然后被放大了2倍。

图18:不同参数的双三次放大比较。记住,这些只是视频的小片段,双三次放大总是会导致伪影,特别是混叠。尖锐的双三次曲线(b=0, c=1)特别容易产生混叠现象。

图19:缩小和放大到原始分辨率的大小比较。所有内容都被缩小到源分辨率的一半,然后使用nnedi3_rpow2放大回去。所有参数都保留默认值。

图20:通过缩小和放大回到原始分辨率的大小比较。所有内容都被缩小到源分辨率的一半,然后用相同的缩放器放大回去。所有参数都保留默认值。

图21:抖动类型的比较。输入源为8位视频,向上抖动并用f3kdb去色带,输出为16位,然后抖动为8位。

图22:一个例子,说明为什么不适当的对角边界fx是个坏主意。上面是源,下面是极客过滤的。来自极客的《精灵旅社2》编码。

图23:16位(右)和8位(左)gamma bug fx的比较

图24:不同edgemask的比较。从左上角到右下角:source, Prewitt, Sobel, Kirsch, TCanny,和retinex_edgemask。所有设置都保留默认值。

图25:更精确的edgemask的比较。从左上角到右下角:source,retinex_edgemask, Kirsch, Sobel。请注意,这是在一个有个糟糕光照的质量非常差的源上,因此retinex和Kirsch 两种遮罩的区别比通常更大。

图26:使用和不使用edgemask时去色带的比较。从左上到右下:源,标记了裁剪区域的源,裁剪区域,裁剪区域上的遮罩(kgf.retinex_edgemask(src).std. binated(5000).std. maximum ().std.Inflate()),无遮罩f3kdb.Deband(y=90, grainy=32,grainc=16)),和遮蔽了的色带。通过平滑级别,伽马值提高了50%。