反走样(抗锯齿)
作者:互联网
(1)采样与走样
采样不只是发生在不同的位置,也可以是不同的时间,比如视频、动画,把一系列的图按一定的时间放映出来,动画实际上就是在时间中进行的采样,因为本质上说,我们没有连续意义上的动画。
采样是广泛存在的,同样,采样造成的问题也是广泛存在的,叫做 Artifacts(图形学中的黑话,不希望看到的东西,可以理解为失真或瑕疵)。
上图是采样所产生的另外一个 artifacts,叫摩尔纹。将左边这幅图的奇数行和奇数列都去掉,然后得到的一个小的图,显示还是一样大,就是右边图片的效果。日常生活中也能见到,比如拿手机拍显示器的屏幕。
有时看高速行驶的汽车的轮子,感觉是在倒转,这也是采样造成的现象,人眼在时间中的采样跟不上运动的速度。
可以看到,采样造成的 artifacts 本质都是信号变化太快了以至于采样跟不上它。
(2)反走样的初步思路
反走样的思路:在采样之前先做一个模糊处理。
实际中的效果也是不错的:
普通采样 VS 反走样:
那么我反过来,先做采样再做模糊呢?
效果不行。先做一次采样本身丢失大量信息,之后又被模糊了。
(3)频域角度解释走样
从频域的角度思考:
上面两个函数不同是因为它们的频率不同,下面那个频率大的明显变化要快。
傅里叶级数展开(上图)与傅里叶变换(下图)的概念是不一样的。
傅里叶变换将函数变换成不同频率的段
我们可以每间隔一段对这些函数进行一次采样。采样第一个函数,基本上我们能发现函数大概长什么样;但是到最后就完全不行了,和实际的 f5(x) 完全不一样了。
从这里看,函数有一定的什么样的频率,我们采样也应该有一定的什么样的频率。而如果函数的频率很高,而采样的频率很低,我就跟不上函数的变化,没办法把原始的信号恢复出来。
那么现在假如说我有一段蓝色的函数,一段黑色的函数,如果我用上面这样采样的方法,得到的结果是,我用同样的采样方法,采样两种频率截然不同的信号,但是我采样的结果是完全相同的。像这样一种现象,就是走样的正规的定义。
(4)频域上的滤波
滤波(Filtering)从频域的角度上说,就是把某个特定的频段给去除掉。
傅里叶变换能将图像从时域变换到频域。虽然图像不带任何时间的信息,但在空间上的位置也算作时域,这只是一个名字。
中心定义为低频的区域,周围定义为高频的区域。中心到周围,它的频率会越来越高。到底有多少信息在不同频率的位置上,我们用亮度来表示。
可以看到,图像的大多数信息都集中在低频上的,对于自然的图片来说基本都是这样的。
为什么会有水平的和垂直的两条线?我们在分析一个信号的时候,我们会认为它是一个周期性重复的信号,那么对于不周期性重复的信息,比如这张照片,我们会认为,到了右边界它又会重复它左边的内容,就好像这张图它在水平方向上叠了无限多个,竖直方向上也叠了无限多个。很少有图像左边界和右边界完全一致的,那么在边界上会发生剧烈的信号变化,也就会产生一个极其高的高频。
高通滤波,将低频的信号完全抹掉了,显示出来的就是图像的轮廓细节信息。信号变化特别大,那就是高频信息。
低通滤波,得到了一张相对模糊的图,绝大部分细节信息都看不见了。
带通滤波,不是那么明显的边界特征。
将边扩大,就更像是高频的信息了。
(5)卷积操作
卷积操作(是图形学上简化了的定义,不是数学上的定义)。
卷积定理说明,时域上如果我想对两个信号进行卷积,那么其实对应到两个信号各自的频域上,是两个信号频域的乘积。相反同理,时域上的乘积就是频域上的卷积。
那么通过卷积定理我们知道可以通过两种办法做一个卷积,拿到一幅图,我可以直接拿一个滤波器做一个卷积操作,也可以将这幅图先傅里叶变换到频域上,把卷积的滤波器也变换到频域上,把两者相乘,然后再通过逆傅里叶变换到时域上,也完成了一个卷积操作。图示如下:
实际上就好像对左边的频域图做了一个低通滤波,最后留下的低频内容,就得到了一个相对模糊的图。
下面四张图该怎么解释呢?
我用更大的滤波器去做卷积操作,得到的图像肯定越来越模糊,在频域上也表现为越来越倾向低频。
(6)频谱角度解释走样
在频谱的角度上怎么理解采样?
采样就是在重复频谱上的内容。
图 (a) 是待采样的函数,那么我想对其进行采样,就乘以另外一个函数 (c),只在一些固定位置上有值,而其他位置上结果为 0。那么得到的就是函数 (a) 上面离散的点。这里的函数 (c) 叫做冲激函数。而时域上的乘积对应到频域上就是卷积,对应的右边三幅图。
那么有一个函数,我把它采样后变成了一系列离散的点,其实我在频域上做的操作就是把原始的函数,它的频谱给复制粘贴了很多份。
采样就是在重复原始信号的频谱。
那么这就能解释为什么会产生走样现象。
假设我原始信号是一个如上图梯形的形状,如果我按照良好的步长进行复制粘贴,就会得到第一幅图那样;而如果我采样率不足,或者说我采样得不够快,那么我复制粘贴的间隔就会非常小,现在我原始的信号和我复制粘贴的信号混在一块了,在这种情况下,就发生了走样。
(7)反走样
如何减少走样现象呢?有两种思路:
1)增加采样率
比如原先我显示器分辨率很差,一个像素非常大,很明显可以看出来,那我用它去光栅化一个三角形,当然看起来锯齿很严重;而如果我用现在的高分辨率显示器,像素小一些,意味着像素跟像素之间间隔小,意味着采样频率高,在频谱上的搬移间隔大,就不容易出现频谱的混叠。
这个当然是一个解决办法,但这个并不是反走样要做的事情,因为我要在同一块屏幕上进行操作,不可能说我开启某个反走样的选项,我分辨率就能增加很多。受制于物理限制。
2)反走样
通过前面的频率分析,我们就能知道最开始我们先做模糊再做采样是有意义的。
我们首先做一个模糊,将它的高频信号砍掉,那么剩下的信号就是第二幅图的形状,然后我再以一个稀疏的采样率来进行采样,现在就能发现对应的信号在原先的位置不再发生混叠了。
那么这个就是我们的解决方法,叫做反走样。
那么接下来的问题就是我怎么做这一步滤波操作,把三角形变成模糊三角形。
就用一定大小的低通滤波器,对它进行卷积,就可以了。
对每一个像素点用这个滤波器进行一个取平均操作。
三角形覆盖面,对其求一个平均,比如第一副图,三角形覆盖了 1 / 8,那我就赋予一个 7 / 8 的亮度。
要将每一个像素,三角形在里面覆盖的面积算出来,这件事说着容易做着难,那么在实际中如何操作?
(8)MSAA(Multi-Sample Anti-Aliasing)
它是一个对反走样的近似,它并不能严格意义上解决反走样问题。
原本我们只看一个像素点,那么我们将这一个像素划分成 4 * 4 的一个个小的像素,每一个小的像素有一个中心,我可以判断这些点是不是在三角形内,然后我们再把这些判断的结果平均起来,就可以看作三角形对这个大的像素区域的一个近似。
比如我们先划分成 2 * 2 的子采样点,然后每个点都能判断是否在三角形内。
对每一个像素内的子采样点求平均,设定像素的亮度
模糊做完了,那采样将变得无比简单,格子是什么样采样就什么样了。(强调一下,MSAA 是做模糊的操作,MSAA 也绝不是靠提高分辨率直接解决的走样问题,只是为了得到近似的三角形的覆盖)
原始效果:
4 * 4 子采样:
但是 No Free Lunch!
我们为了达到 MSAA 的效果,牺牲了什么呢?
计算量。比如 2 * 2 就是 4 倍的计算量;4 * 4 就是 16 倍的计算量。
但是人们是贪婪的,人们不希望通过这么大的采样量就达到这么一点点的效果,人们希望只用很少的采样点,也能够达到这样的效果。
在实际的工业界,人们也不会用这么规则的划分成比如说 4 * 4 个点,人们会用一些更加有效的图案来分布这些不同的点,有一些点还会被临近的像素所复用。所以如果打游戏时如果开启抗锯齿,比如 2 * 2 的 MSAA,帧率不会掉到原来的 1 / 4,不会掉得那么多,有很多样本得到了复用。
在这里只提到了一种抗锯齿,显然无法代表现代图形学的发展,抗锯齿的方案当然有很多。比如 FXAA 是对图像的后期处理,我先得到有锯齿的图,我再对它进行操作(前面我们就使用先采样后模糊的方法),先将有锯齿的边界找到,然后再进行匹配,用没有锯齿的边界换上,而且非常非常快,它是和采样无关的图像方面做的抗锯齿,效果挺好的。TAA 是最近几年兴起的抗锯齿的方法,非常简单高效,Temporal 就表示和时间相关的,我去找上一帧的信息,假如说不是一个运动的场景,是一个静止的场景,相邻两帧显示的东西一样,TAA 它会说我复用上一帧感知到的结果,就是说上一帧像素的值在这一帧还会发生作用,相当于 MSAA 对应的这些样本给分布在时间上。
另外再提一个超分辨率(超采样)的概念,比如一张 512 * 512 的图,把它拉大成 1024 * 1024 的图,那看到的全是锯齿。我想把这张图拉大,但我又不想看到锯齿,就跟抗锯齿很相似。DLSS 深度学习的方法,图片拉大后有些细节缺失,那我猜出来就最好了,一切涉及猜测的技术都是目前深度学习非常擅长的。
标签:采样,抗锯齿,走样,卷积,频域,像素,信号 来源: https://www.cnblogs.com/hanselhuang/p/15754334.html