mpv 算是跨平台的最强播放器了,有还算活跃的社区,也有很多可以用来给画面加料的滤镜。
FSRCNNX 和 Anime4K 算是比较热门的两个滤镜,前者能借助卷积神经网络修复部分纹理,后者则能够提供优秀的锐化效果。
但遗憾的是,这两个滤镜并不能混合使用。本文即是以此为题展开的探索。
更新
最新版本的 Anime4K 已经将 shader 分开,不再需要手动修改代码,下文仅作存档参考。
mpv shader 使用的编程语言被称为 GLSL。
起初咱以为和 OpenGL 的语法结构应该是完全相同的,但实际发现 mpv 只是借用了 GLSL 框架,代码中有大量关键字是新增的。这一部分关键字被详细记录在 mpv 官方文档 中(所以实际上咱是绕了个大弯)。
通读一遍官方文档中相关的部分之后再回到 shader 代码就很好理解为何使用一个滤镜后另一个滤镜并不会被完全激活的原因了。接触过 iptables 的话,应该能很容易理解 shader 的工作流程。
首先,可以把 mpv 内图像的处理想象成工厂流水线,而滤镜就是流水线上的工人。
滤镜内通过特殊的关键字指定流水线上的哪一个位置需要布置「工人」以及干活需要哪些「工具」。
分析:
以 Anime4K 为例,用文本编辑器打开 GLSL 文件可以看见每一段花括弧包裹的代码块前都有一段使用「//
」开头的文字说明,这实际上就是代码块工作条件的说明。
//!DESC Anime4K-ThinLines-v1.0RC2
//!HOOK SCALED
//!BIND HOOKED
//!BIND LUMA
//!WHEN OUTPUT.w LUMA.w / 1.400 > OUTPUT.h LUMA.h / 1.400 > *
//!BIND LUMAG
以 !HOOK
开头的即为代码块在流水线上的位置,例如此处的 SCALED
代表的是在画面完成放大后颜色管理被应用前(The final upscaled image, before color management
)。
同样的 !BIND
代表的是代码除了流水上的资料外其他会用到的资料。
当代码执行完成后,默认情况下处理完成的资料会被放回流水线原来的位置,但也可以放到其他的位置上,此时会使用到 !SAVE
这个关键字。
最后是本文的关键,即 !WHEN
关键字。
类似于一般编程语言的 IF
,原文中是这么说的「Specifies a condition that needs to be true (non-zero) for the shader stage to be evaluated. If it fails, it will silently be omitted
」,即指定一个触发滤镜工作的条件。
而 !WHEN
后边的部分则在文档中 WIDTH
的部分做出了说明:使用逆波兰表达式编写。稍微了解过数据结构的应该都知道这是树的后序遍历结果,只要还原为中序遍历即可回到常用的算术逻辑表达式。那么同样的,只要将一般的条件语句反过来即可变成这里的表达式。
在改写之前,首先理解一下例子中这个条件的含义。
转换为中序的形式应该是:
(OUTPUT.w / LUMA.w > 1.4) * (OUTPUT.h / LUMA.h > 1.4)
将乘号换成常用的逻辑表达式:
(OUTPUT.w / LUMA.w > 1.4) && (OUTPUT.h / LUMA.h > 1.4)
这样一来意思就很明显了:如果放大后的任意一边尺寸是放大前的 1.4 倍不到,那么滤镜会被跳过。
那么两个滤镜无法同时工作的问题就很容易解释了:有一个滤镜的工作条件没有满足。
解决:
知道了原因,解决办法也很简单,
第一个方法:去掉前一个滤镜声明大小的关键字让后边的以为画面没有被放大;
第二个方法:去掉后一个滤镜 !WHEN
的条件,让滤镜强制工作。
以第一个方法为例,假设 FSRCNNX 滤镜在前工作,那么删掉 GLSL 内最后一个函数前的这两句就好了:
//!WIDTH LUMA.w 2 *
//!HEIGHT LUMA.h 2 *
第二个方法就更简单了,暴力删掉后一个滤镜所有的 !WHEN
行即可。
一些有的没的:
在咱自己的土豆机器(Vega 8)上使用 FSRCNNX 8-0-4-1
配合 Anime4K 完整版可以基本 2.5K 全屏流畅观看 1080P 视频,且观感比较显著。(使用第一种方法)
据某群友(Vega 56)测试反馈,使用 NNEDI3 配合 Anime4K 效果最佳。(使用第二种方法)
希望看到本篇组合出更好效果的选手可以偷偷告诉咱一下。