ISP的本质是设计滤波器,要想理解滤波器,得先从卷积开始。
1. 一维卷积
对于一维离散卷积,《信号与系统》是用公式(1)来表述的,x(n)是输入信号,h(n)是一个线性系统,x(n)经过系统h(n)后的输出是y(n),计算y(n)要先把h(n)翻转一下,然后每次向右移动一个位置和x(n)相乘后累加,直到移出x(n)的范围,得到的一系列累加值就是卷积的结果。
(1)
这个描述直到今天我都觉得抽象,最无法理解的就是为什么一定要把h(n)翻转一下?后来我找到了自己的答案,现在我把它分享出来,仅供参考。首先忘记“卷积”这两个字,不要纠结于字面的“卷”和“积”,它只是一个算法的名字而已,其次把输入信号x(n)和系统h(n)都想象成和时间有关的函数,最后把h(n)想成一个黑盒子,黑盒子的内部对我是开放的,我可以定义它。
为了讲出自己的答案,我做个假设,假设我是一个卖房子的销售,我靠提成赚钱,我每个月的收入并不固定,我把1~12月份每个月的收入作为x(n),n代表月份,我一年的收入x(n)如下:
月份 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
收入(k) | 12 | 8 | 6 | 15 | 7 | 8 | 11 | 10 | 14 | 12 | 9 | 16 |
现在我要计算年薪,计算过程如下:
(2)式中隐藏了一个h(n),我把它写出来:
写出的h(n)里面是12个1,列成表格如下所示:
月份 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
h(n) | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
现在我有了x(n)和h(n),如果套用卷积的定义,从1月开始,每个月对应的位置都相乘累加,我会得到什么呢?这个过程如下图1所示:
图1 月收入累加示意图
图1显示的结果是从1月~12月,月收入的累加,它可以理解为“到本月为止,我已经赚到了多少钱”,它显示了每个月我的收入动态累加的过程。在每个月的x(n)中我只标识出了以前的收入和当前月的收入,这里主要强调了一下时间的概念,以后的收入我是不知道的。
此外,h(n)中是12个1,它一方面限制了对x(n)的计算方法,本例是算总和,另一方面限制了累加的范围,最多12个,不满12个的就当它不存在,有多少加多少。
所以卷积是定义了一种计算方式,这种方式就像乘法或者平方一样,用乘法或者平方可以静态的算出一个结果,而卷积是一种积分算法,它动态的描述一系列结果。卷积的作用是对于任意的输入信号x(n),都按照进入系统h(n)的时间先后顺序进行计算,具体的计算方式和数据范围由h(n)来决定,它描述了一个积分的动态过程。
再举一个例子来优化h(n),虽然我算出了年薪,但是我没有考虑每个月的支出,所谓开源节流,到年终能留在卡里的钱才算是我真正赚到的钱,那么我该如何设计h(n)来描述这个既有收入又有支出的过程呢?我决定每个月发工资的时候,从现有的几个月的存量中各取出10%作为本月的消费,那当月留下来的钱为x(n)*(1-10%)=x(n)×0.9,然后再加上以前那些月份剩余的钱×0.9,就是迄今为止我存下来的钱。这个时候h(n)=0.9^n,列一下0.9的n次方结果:
0.9^1 | 0.9^2 | 0.9^3 | 0.9^4 | 0.9^5 | 0.9^6 | 0.9^7 | 0.9^8 | 0.9^9 | 0.9^10 | 0.9^11 | 0.9^12 |
0.9 | 0.81 | 0.73 | 0.66 | 0.59 | 0.53 | 0.48 | 0.43 | 0.39 | 0.35 | 0.31 | 0.28 |
再把一年的x(n)和h(n)列出来看一下,到12月份能存下来的钱如下图2所示,这个图好像哪里不对劲?按照逻辑,应该是1月份的12k*0.28,12月份的16k*0.9才对,毕竟到了年底,1月份的收入已经被我每个月花10%用的差不多了,12月份的才是最新的收入。
图2 直观感受图
这就是为什么公式上面说h(n)需要翻转一下,因为在计算储蓄的这个情景下,我们的计算是沿时间(按月领工资,“月”就是时间)进行的,0.9对应的是“现在”,0.28对应的才是“过去”,我们习惯于把n作为x轴,越往右边,n应该越大,但是实际上0.9^n要表达的意思是,随着月份n的增加过去的收入被我不断消费掉了,所以不是h(n)必须要翻转,而是在时间这个坐标轴下,“过去”应该在“现在”的左边才合理,也就是下图3的样子。
图3 时间归位图
在《信号与系统》里面,对一个信号的描述,就是写出它在每个时刻的幅度,所以信号系统的卷积就是对时间进行的累加,只是在累加之前,需要让“过去”和“过去”匹配,“现在”和“现在”对应。所以“卷积”不能从的字面意思来理解它,它只是一个纯粹的数学表达,也不能从公式的行为来理解它,因为在不同的领域,卷积这种算法对应的实际意义是不一样的,比如在接下来的图像处理领域,卷积代表的是对空间位置内像素的加权累加,限定的是像素的范围,和翻转扯不上关系。
此外,卷积的理解还要结合系统h(n)的具体行为,在我计算年薪的时候,我设置的h(n)是12个1,“过去”和“现在”对积蓄的影响是一样的,此时时间是否翻转都无所谓。但是在接下来要考虑消费和收入的这个系统中,我通过h(n)限定了“过去”的收入被不断支出(从现有的几个月的存量中各取出10%作为本月的消费),那h(n)中就需要把时间归位回去才符合逻辑。实际上后面的这个h(n)就等同于《信号与系统》中的衰减系统,随着时间的增加,“过去”输入的信号影响在逐渐减少,“现在”输入的部分影响较大,当然我也可以把h(n)设计成递增系统,让“过去”输入的信号影响在逐渐增大,“现在”输入的部分影响较少,这个要看我的具体需求。
说了这么多,其实只想说明一个问题,就是h(n)是可以被设计的。如果我想算平均月收入,那么我设计h(n)=[1,1,1,1,1,1,1,1,1,1,1,1]/12;如果我想知道我的收入在一年内的哪3个月最好(楼市旺季),那么我设计h(n)=[1,1,1];如果我想知道哪个月我的收入断崖式下跌了,那么我设计h(n)=[1,-1],无论我的需求是什么,h(n)都明确的指定了计算方式和数据范围。以此类推,通过设置h(n),我可以对任何一组数据x(n)做指定的操作,我还可以用h(n)批量的处理数据。如果h(n)用在信号处理领域,它可以被称为滤波器,我可以用它描述一个复杂的系统,让同行看一眼就知道这个滤波器是干什么的。
2. 二维卷积
有了一维就有二维,数字图像就是二维信号,按卷积的定义,二维离散形式的卷积公式如(4)所示,参考刚才一维的解释,二维卷积就是f(u,v)经过了一个系统g(u,v),被变换成了其他的样子。
(4)
假设有下面的这样一幅图像f(u,v),像素位宽是8bit(0~255),它经过了一个系统g(u,v),g(u,v)是一个3x3的全1矩阵。
那么f(u,v)*g(u,v)的过程就是g(u,v)从f(u,v)的最左边顶点开始,计算每个3x3窗口内的像素累加,由于g(u,v)限定了空间的范围是3x3个像素,图像边界处缺少像素,所以f(u,v)的上下左右需要复制扩边(虚线部分),卷积的结果如下图所示:
f’(u,v)出现了超过255的数值,那是因为在图像处理领域,需要对权重做归一化,以保证输出的像素范围和输入范围一致,也就是g(u,v)加起来的结果要保证为1,3x3的矩阵实际上是9个1/9,这样加起来才是1,g(u,v)做个归一化后的结果如下所示:
g(u,v)在图像处理中又被称为算子,类比一维的h(n),算子可以理解为一个功能特定的滤波器,上面的这个例子其实就是一个3x3的均值滤波器,它是一个低通滤波器。此外均值滤波还可以看成是两个矩阵相乘,公式如下:
这是个非常优良的性质,高斯滤波器也有这个性质,但是后续的中值、双边和nlm就没有这个性质了,这个可拆成两个矩阵相乘的性质在做RTL硬件实现的时候会带来额外的惊喜,先列在这里,后面实现的时候再详细解释。