圖像處理與RTL硬件實現|第1章:先從卷積開始
ISP的本質是設計濾波器,要想理解濾波器,得先從卷積開始。
1. 一維卷積
對于一維離散卷積,《信號與系統(tǒng)》是用公式(1)來表述的,x(n)是輸入信號,h(n)是一個線性系統(tǒng),x(n)經過系統(tǒng)h(n)后的輸出是y(n),計算y(n)要先把h(n)翻轉一下,然后每次向右移動一個位置和x(n)相乘后累加,直到移出x(n)的范圍,得到的一系列累加值就是卷積的結果。 ? ?

這個描述直到今天我都覺得抽象,最無法理解的就是為什么一定要把h(n)翻轉一下?后來我找到了自己的答案,現在我把它分享出來,僅供參考。首先忘記“卷積”這兩個字,不要糾結于字面的“卷”和“積”,它只是一個算法的名字而已,其次把輸入信號x(n)和系統(tǒng)h(n)都想象成和時間有關的函數,最后把h(n)想成一個黑盒子,黑盒子的內部對我是開放的,我可以定義它。
為了講出自己的答案,我做個假設,假設我是一個賣房子的銷售,我靠提成賺錢,我每個月的收入并不固定,我把1~12月份每個月的收入作為x(n),n代表月份,我一年的收入x(n)如下:

現在我要計算年薪,計算過程如下:

(2)式中隱藏了一個h(n),我把它寫出來:

寫出的h(n)里面是12個1,列成表格如下所示:

現在我有了x(n)和h(n),如果套用卷積的定義,從1月開始,每個月對應的位置都相乘累加,我會得到什么呢?這個過程如下圖1所示:

圖1顯示的結果是從1月~12月,月收入的累加,它可以理解為“到本月為止,我已經賺到了多少錢”,它顯示了每個月我的收入動態(tài)累加的過程。在每個月的x(n)中我只標識出了以前的收入和當前月的收入,這里主要強調了一下時間的概念,以后的收入我是不知道的。
此外,h(n)中是12個1,它一方面限制了對x(n)的計算方法,本例是算總和,另一方面限制了累加的范圍,最多12個,不滿12個的就當它不存在,有多少加多少。
所以卷積是定義了一種計算方式,這種方式就像乘法或者平方一樣,用乘法或者平方可以靜態(tài)的算出一個結果,而卷積是一種積分算法,它動態(tài)的描述一系列結果。卷積的作用是對于任意的輸入信號x(n),都按照進入系統(tǒng)h(n)的時間先后順序進行計算,具體的計算方式和數據范圍由h(n)來決定,它描述了一個積分的動態(tài)過程。
再舉一個例子來優(yōu)化h(n),雖然我算出了年薪,但是我沒有考慮每個月的支出,所謂開源節(jié)流,到年終能留在卡里的錢才算是我真正賺到的錢,那么我該如何設計h(n)來描述這個既有收入又有支出的過程呢?我決定每個月發(fā)工資的時候,從現有的幾個月的存量中各取出10%作為本月的消費,那當月留下來的錢為x(n)*(1-10%)=x(n)×0.9,然后再加上以前那些月份剩余的錢×0.9,就是迄今為止我存下來的錢。這個時候h(n)=0.9^n,列一下0.9的n次方結果:
再把一年的x(n)和h(n)列出來看一下,到12月份能存下來的錢如下圖2所示,這個圖好像哪里不對勁?按照邏輯,應該是1月份的12k*0.28,12月份的16k*0.9才對,畢竟到了年底,1月份的收入已經被我每個月花10%用的差不多了,12月份的才是最新的收入。

這就是為什么公式上面說h(n)需要翻轉一下,因為在計算儲蓄的這個情景下,我們的計算是沿時間(按月領工資,“月”就是時間)進行的,0.9對應的是“現在”,0.28對應的才是“過去”,我們習慣于把n作為x軸,越往右邊,n應該越大,但是實際上0.9^n要表達的意思是,隨著月份n的增加過去的收入被我不斷消費掉了,所以不是h(n)必須要翻轉,而是在時間這個坐標軸下,“過去”應該在“現在”的左邊才合理,也就是下圖3的樣子。

在《信號與系統(tǒng)》里面,對一個信號的描述,就是寫出它在每個時刻的幅度,所以信號系統(tǒng)的卷積就是對時間進行的累加,只是在累加之前,需要讓“過去”和“過去”匹配,“現在”和“現在”對應。所以“卷積”不能從的字面意思來理解它,它只是一個純粹的數學表達,也不能從公式的行為來理解它,因為在不同的領域,卷積這種算法對應的實際意義是不一樣的,比如在接下來的圖像處理領域,卷積代表的是對空間位置內像素的加權累加,限定的是像素的范圍,和翻轉扯不上關系。
此外,卷積的理解還要結合系統(tǒng)h(n)的具體行為,在我計算年薪的時候,我設置的h(n)是12個1,“過去”和“現在”對積蓄的影響是一樣的,此時時間是否翻轉都無所謂。但是在接下來要考慮消費和收入的這個系統(tǒng)中,我通過h(n)限定了“過去”的收入被不斷支出(從現有的幾個月的存量中各取出10%作為本月的消費),那h(n)中就需要把時間歸位回去才符合邏輯。實際上后面的這個h(n)就等同于《信號與系統(tǒng)》中的衰減系統(tǒng),隨著時間的增加,“過去”輸入的信號影響在逐漸減少,“現在”輸入的部分影響較大,當然我也可以把h(n)設計成遞增系統(tǒng),讓“過去”輸入的信號影響在逐漸增大,“現在”輸入的部分影響較少,這個要看我的具體需求。
說了這么多,其實只想說明一個問題,就是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)用在信號處理領域,它可以被稱為濾波器,我可以用它描述一個復雜的系統(tǒng),讓同行看一眼就知道這個濾波器是干什么的。
2. 二維卷積
有了一維就有二維,數字圖像就是二維信號,按卷積的定義,二維離散形式的卷積公式如(4)所示,參考剛才一維的解釋,二維卷積就是f(u,v)經過了一個系統(tǒng)g(u,v),被變換成了其他的樣子。

假設有下面的這樣一幅圖像f(u,v),像素位寬是8bit(0~255),它經過了一個系統(tǒng)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的均值濾波器,它是一個低通濾波器。此外均值濾波還可以看成是兩個矩陣相乘,公式如下:

這是個非常優(yōu)良的性質,高斯濾波器也有這個性質,但是后續(xù)的中值、雙邊和nlm就沒有這個性質了,這個可拆成兩個矩陣相乘的性質在做RTL硬件實現的時候會帶來額外的驚喜,先列在這里,后面實現的時候再詳細解釋。