引言:
采樣是處理圖像必不可少的一個步驟,同時抗鋸齒也是成為當下游戲渲染的一個重要技術,本篇blog的目的便是解析一下它們的原理。
首先,什么是采樣?我們獲取的信號往往是連續的,但實際上我們不可能無間隔地獲取實時信號,因此我們只能獲取離散的信號,這就是離散采樣。而采樣會導致很多問題,例如鋸齒(不平滑不圓潤)等等走樣問題。反走樣技術就是要解決這類問題,不過學習該門技術需要一點信號處理的知識。
傅里葉變換:
之前對於采樣的原理很抗拒,主要源於對傅里葉變換的抵觸。相信大部分搞計算機的同學最開始聽到這個詞時也頭疼不已,但實際上它並沒有想象中那樣復雜。在分析采樣原理的時候,我們無需推導傅里葉變換的公式,只需要了解它代表什么、能做什么即可。
首先引入傅里葉級數展開的概念。對於一個任意的函數,我們都可以用若干個正弦函數來疊加形成。如下圖所示,我們給出一個周期性的方波:
可以看到,為了擬合方波,我們使用了許多不同周期或頻率的正弦波來進行疊加。使用的不同頻率正弦波越多,最后擬合的效果就越好。對於方波而言,可能需要無限各不同頻率的波進行疊加,但這不影響“任意函數都可以由正弦波疊加形成”的結論。那么,為什么是正弦波呢?是因為正弦信號恰好是很多線性時不變系統的特征向量,簡而言之就是三角函數的正弦波的所有組合可以疊加出所有信號波的形狀,而方波和三角波並不具備這樣的特點。
總而言之,對於任意一個函數,都可以由所有頻率的正弦波乘上各自的強度后疊加形成,那么關鍵點在於:各個頻率的正弦波的強度怎么求呢?這就要請出我們的傅里葉變換了。
f(x)指的是空域或時域中的原函數,F(w)對應的就是為了擬合f(x),每個頻率的正弦波對應的強度是多少,即橫坐標是頻率,縱坐標是對應的強度,這個就是我們上文想要求的函數。而f(x)和F(w)之間的變換可以通過圖中的傅里葉變換公式實現(畢竟不是數學blog,所以就不推導公式了)。下圖是一個最簡單的例子:組成方波的各個正弦波在頻域中的強度分布:
現在我們知道,對於任意f(x)都對應着一個頻譜F(w),那么回到我們處理圖像的問題上來。對於圖像而言,頻率代表着什么呢?實際上圖像的頻率是表征圖像中灰度變化劇烈程度的指標,是灰度在平面空間上的梯度。舉個例子,大面積的沙漠在圖像中是一片灰度變化緩慢的區域,對應的頻率值很低;而對於地表屬性變換劇烈的邊緣區域在圖像中是一片灰度變化劇烈的區域,對應的頻率值較高,也就是說圖像(或者說空域)的頻率表示的是灰度值的變化快慢。
那么我們知道圖像是一種二維信號,對於最簡單的灰度圖,我們采樣出來的信息應該是f(x,y),那么對應的頻譜也會擴充到二維,即F(u,v),表示兩個維度上的頻譜分布。
二維的傅里葉變換就更復雜了,所以就不再討論公式了。為了讓f(x,y)和F(u,v)之間的關系更加直觀,我們把F(u,v)這個函數圖樣轉換成一張圖片,用灰度值來代表對應頻率正弦波的強度,這就生成了一張頻譜圖:
左圖是原圖,右圖是對應的頻譜。中間部位非常亮,灰度值非常高,這意味着頻率較低的波對應的強度會非常大;頻率較高的波對應的強度會非常小。那么現在話鋒一轉,在此之前我們思考的方向都是,修改原圖可以修改對應的頻譜圖,但是如果我們要修改頻譜圖來改變圖像,會怎么樣呢?
頻域濾波:
上文提到低頻強度很高,高頻強度很低,現在我們要探究一下通過修改頻譜圖來影響圖像。首先,我們嘗試把中間(低頻區域)挖個洞。怎么挖呢,我們定義一個新的頻譜函數G(u,v),它的特點是u和v比較小時G(u,v)=0,u和v比較大時G(u,v)=1,讓原來的F(u,v)乘一下G(u,v),就完成了低頻挖洞操作。它的效果就是讓原來的F(u,v)即將低頻的波都過濾掉(這個操作叫做高通濾波,即保留高頻率的波段,去除低頻率的波段)那么圖像會變成如下模樣:
現在左圖只剩下一些邊緣了。為什么會這樣呢,因為對於一個圖像而言,若某區域中其灰度值變化很緩,類比到時域就是信號變化很緩,換言之就是信號周期很長。信號頻率與信號周期成反比,因此該區域對應的就是低頻率的波段。將這些波段濾掉,那么灰度值變化不大的區域就會被濾掉;而邊緣最大的特點就是突變,即信號變化非常快,頻率更高。在濾波過程中,高頻波被保留了下來,因此原圖中的邊緣也會被突出了。
那么反過來,如果我們過濾掉高頻波,保留低頻波,就會將原圖變為這樣:
按照上面的邏輯,邊緣這些對應高頻波的部分都會被過濾掉,體現在圖像上就是變平滑了、變模糊了。要注意的是,這就是反走樣的關鍵所在:將尖銳突出的邊緣或鋸齒平滑處理,使得其周圍變得模糊且柔和。
接下來我們要考慮的問題就是如何把圖像這種二維信號變換到頻域當中。對於一張圖像,如果要將它變換到頻域空間,就需要對圖像這種離散的二維信號進行變換,這里我們就要用到大名鼎鼎的DFT(離散傅里葉變換):
二維DFT的算法公式如上圖所示,當然也可以將e^j換個表達方法:
不難想象,dft的算法復雜度是O(n^2),這樣一張1080P的圖片恐怕要跑個幾天甚至幾個月才能出結果,顯然是無法接受的。因此我們可以引入快速傅里葉變換FFT:
它的算法過程其實類似於歸並算法,即每次二分,分而治之,最后再按照逆方向歸並回來。和歸並排序類似,算法復雜度是O(nlogn)。歸並方法如下圖所示:
(實話實說,我是挺驚嘆於這種算法的發明的。自己能根據公式順着能推下來,但無法想象在什么都沒有的情況下是如何發現並研發出這種算法的)
不過還是要說,FFT的時間復雜度雖然比DFT有所優化,但這種代價依然是非常高的,而且濾波之后還要進行反變換,一來一回會耗費非常多的性能。因此,我們必須要將濾波的操作搬到空域來解決。接下來我們就要求一下,空域中要怎么操作才能等效於頻域中的濾波操作。
現在我們假定要完成低通濾波操作,設P是對空域進行濾波操作,Q是對頻域進行濾波操作,那么P(f(x,y))=Q(F(u,v))。現在我們已知f(x,y),可以通過DFT算得F(u,v),Q操作上文已經解釋過了,就是讓F(u,v)乘一下G(u,v),現在我們要求出P操作是什么。這里我們直接引用一個定理:空域卷積等於頻域乘積 空域乘積等於頻域卷積。
這就意味着:f(x,y)卷積{G(u,v)}-1=F(u,v)*G(u,v),即原圖像與低通濾波器的傅里葉逆變換進行卷積,等於圖像的頻域表示與濾波器相乘。這也說明了,P操作就是和濾波器的逆傅里葉變換作卷積操作。事實上,為了節約成本,我們不會先構造出來G(u,v)再作DFT變換為{G(u,v)}-1,而是根據經驗直接構造卷積核,來完成卷積操作。
空域卷積:
上文提到了,在空域進行低通濾波的方法是進行卷積。卷積怎么理解呢,本質上就是讓信號中的孤立點獲取周圍信息。卷積的式子如下:
其中f是原函數,g則是卷積核。圖像處理一般沒有連續函數卷積的情況,因此我們可以直接來看離散卷積。首先舉個一維卷積的例子:
這個大家可以理解一下,對於A卷積B的操作,實際上就是遍歷A上的每一個點,以此為中心,把B整個圖搬進A里,然后逐個點進行乘積,得到的結果寫入該點(這個地方解釋的不太好,如果能畫圖的話比較好說清楚,,,請見諒)如果大家還是比較迷糊的話,可以看下圖:
對於f(x,y)中的每一個點,以此為中心,把h(x,y)貼進來進行乘積加和,最后結果寫入這個點中。舉個例子,對於f中第三行第三列的96,卷積就是0.1*65+0.1*98+0.1*123+0.1*65+0.2*96+0.1*115+0.1*65+0.1*91+0.1*107=92,然后將92寫入原來的這個點。
所以卷積的本質上,就是按照一定的權重將周圍點的信息混合到中心點中。對於非邊緣部分,由於其像素灰度較為接近,因此卷積前后不會有什么變換;對於邊緣部分,它在卷積的時候會把邊緣外的信息糅合進來,從而失掉邊緣的突變性,實現整體平滑和模糊。所以卷積操作比較便於計算,也好理解,因此在做平滑處理的時候我們一般都使用空域卷積的方法。
那么如何構造低通濾波的卷積核呢?首先要做到所有權重都是正數,如果有負數的話會趨向於高通濾波;其次就是卷積核必須要歸一化,即卷積核內的權重之和必須為1,否則卷積后的顏色會嚴重跑偏。如下圖所示:
常見的卷積核有:所有權重都一樣的均值濾波,呈二維正態分布的高斯濾波等。平滑程度取決於權重設置,平滑范圍取決於卷積核的大小。現在已經明確了空域卷積和頻域濾波的關系了,那我直接上一張閆老師的圖來復習一下:
這里大家也能看到中值卷積核在頻域中的濾波器圖樣。
空域和頻域的采樣:
上文講了卷積和濾波操作的原理,它們其實都是反走樣的手段。但是反走樣的前提是先進行采樣,所以接下來我們來看一下采樣的原理。先看下圖中的左半邊:
對於一個連續的信號函數,我們要采樣一些離散的信號點,此時我們需要用到沖激串函數(圖c):它的特點是每隔一段固定時間做一次沖激(這里還會有一段略顯復雜的數學推導,這里直接略掉了)我們只需要將原信號函數和沖激串函數進行相乘,即可得到離散的采樣點。
那么,空域或時域中的采樣,對應到空域中是什么樣子呢?我們前面提到了,空域的相乘也等於頻域的卷積,因此原函數和沖激函數相乘,就等於原函數的頻域表示與沖激串的頻域表示進行卷積。沖激串的頻域如圖d所示,那么按照我們前面提到的卷積原理,就會得到圖f的樣子:多個圖b排列在一起,在坐標軸上兩兩相隔的距離相等。
那么我們從這個角度來理解走樣與反走樣:走樣的本質,就是圖f中的各個圖b之間出現了重疊部分。此時我們若先對圖b進行低通濾波,使得圖b在坐標軸上的寬度削窄,此時再進行采樣,它們便不再發生重疊,解決了走樣問題,這就是反走樣。
如圖a就是典型的頻域中走樣現象,那么為了避免這個現象,我們需要在采樣之前對原頻譜進行低通濾波,變成圖b:
神奇吧?所以我們從空域和頻域兩部分分析了,為什么空域卷積和頻域濾波可以解決走樣問題:空域卷積能夠平滑邊緣,柔化圖像;頻域濾波能夠解決原函數的頻域采樣后發生的重疊問題。這里一定要注意:必須先進行卷積或濾波,再進行采樣。大家可以想一下,對於頻域而言,如果先采樣再濾波,那么重疊的部分依舊會存在,無法完成反走樣;對於空域而言,如果先采樣再卷積,那么在卷積之前鋸齒已經出現了,此時再進行卷積已於事無補。所以先卷積濾波后采樣的順序一定要記住!
由於本篇文章篇幅過長,因此沒有詳細介紹反走樣技術的代表MSAA算法,之后我會在學習完FXAA和TAA后,把三項反走樣技術放在一篇文章中介紹。