離散餘弦變換原理及實現過程【轉載】

背景與原理
1974年,K. R. Rao、N. Ahmed、T. Natarajan三位教授創立了離散餘弦變換(Discrete Cosine Transform, DCT)。在數字信號、數字圖像處理領域,離散餘弦變換的效果能夠接近理論上的最佳變換——Kahunen-Loeve變換(K-L變換)。

以下將介紹DCT的相關背景,並從算法、硬件、應用三個層面進行概述。

1807年,法國數學家、物理學家傅里葉(Jean Baptiste Joseph Fourier)提出了傅里葉變換(Fourier Transform, FT)。傅里葉變換的形式有很多種,歸一化的二維離散傅里葉變換(Discrete Fourier transform, DFT)可以寫成如下形式:
F(u,v)=1NMx=0N1y=0M1f(x,y)e2πiNuxe2πiMvyf(x,y)=1NMu=0N1v=0M1f(u,v)e2πiNuxe2πiMvy F(u,v)=\frac{1}{\sqrt{NM}}\sum^{N-1}_{x=0}\sum^{M-1}_{y=0}f(x,y)e^{-\frac{2\pi i}{N}ux}e^{-\frac{2\pi i}{M}vy}\\ f(x,y)=\frac{1}{\sqrt{NM}}\sum^{N-1}_{u=0}\sum^{M-1}_{v=0}f(u,v)e^{\frac{2\pi i}{N}ux}e^{\frac{2\pi i}{M}vy}
傅里葉變換包含複數運算,其運算複雜度和存儲長度都超過實數運算。爲了簡化上述過程,同時達到更好的變換效果,餘弦變換應運而生。

從傅里葉變換到離散餘弦變換,需要一些數學理論的支持。在給定區間內滿足狄利赫裏條件的連續實對稱函數,可以展開成僅含有餘弦項的傅里葉級數。

對於定義在正實數域上的函數,可以通過偶延拓或奇延拓,滿足上述條件。但如果函數的定義域包含零點,情況則稍有些複雜。

以一個二維離散函數f(x,y)(x,y=0,1,...,N1)f(x,y)(x,y=0,1,...,N-1)爲例,對其進行偶延拓。

假如序列中不包含零點,自然按照以下方式延拓:
f(1,0)=f(1,0),f(0,1)=f(0,1),(0,0)f(1,0)=f(-1,0),f(0,1)=f(0,-1),對稱中心爲(0,0)

由於序列中包括零點,考慮零點後的延拓方式:
f(0,0)=f(1,0),f(0,0)=f(0,1),(1/2,1/2)f(0,0)=f(-1,0),f(0,0)=f(0,-1),對稱中心爲(-1/2,-1/2)

因此,按照上述方法延拓後,歸一化的二維離散餘弦變換可以寫成如下形式:
when(x,y)or(u,v)=(0,0)F(u,v)=1Nx=0N1y=0N1f(x,y)cos[πNu(x+12)]cos[πNv(y+12)]f(u,v)=1Nx=0N1y=0N1F(u,v)cos[πNu(x+12)]cos[πNv(y+12)] when (x,y) or (u,v)=(0,0)\\ F(u,v)=\frac{1}{N}\sum^{N-1}_{x=0}\sum^{N-1}_{y=0}f(x,y)cos[\frac{\pi}{N}u(x+\frac{1}{2})]cos[\frac{\pi}{N}v(y+\frac{1}{2})]\\ f(u,v)=\frac{1}{N}\sum^{N-1}_{x=0}\sum^{N-1}_{y=0}F(u,v)cos[\frac{\pi}{N}u(x+\frac{1}{2})]cos[\frac{\pi}{N}v(y+\frac{1}{2})]

when(x,y)or(u,v)(0,0)F(u,v)=12Nx=0N1y=0N1f(x,y)cos[πNu(x+12)]cos[πNv(y+12)]f(u,v)=12Nx=0N1y=0N1F(u,v)cos[πNu(x+12)]cos[πNv(y+12)] when (x,y) or (u,v)\neq(0,0)\\ F(u,v)=\frac{1}{2N}\sum^{N-1}_{x=0}\sum^{N-1}_{y=0}f(x,y)cos[\frac{\pi}{N}u(x+\frac{1}{2})]cos[\frac{\pi}{N}v(y+\frac{1}{2})]\\ f(u,v)=\frac{1}{2N}\sum^{N-1}_{x=0}\sum^{N-1}_{y=0}F(u,v)cos[\frac{\pi}{N}u(x+\frac{1}{2})]cos[\frac{\pi}{N}v(y+\frac{1}{2})]

在傅里葉變換中,正逆變換的變換核e2πiNuxe2πiMvye^{-\frac{2\pi i}{N}ux}e^{-\frac{2\pi i}{M}vy}e2πiNuxe2πiMvye^{\frac{2\pi i}{N}ux}e^{\frac{2\pi i}{M}vy}相差一個負號;相應的,在餘弦變換中,正逆變換的變換核cos[πNu(x+12)]cos[πNv(y+12)]cos[\frac{\pi}{N}u(x+\frac{1}{2})]cos[\frac{\pi}{N}v(y+\frac{1}{2})]也應相差一個負號。由於 cos(x)=cos(x)cos(x)=cos(-x) ,所以餘弦變換的正逆變換在形式上具有一致性。

順帶一提,在某些教材上,將上述形式的離散餘弦變換寫成類似於下面的樣子:
F(u,v)=1Nx=0N1y=0N1f(x,y)cos[πu(2x+1)2N]cos[πv(2y+1)2N] F(u,v)=\frac{1}{N}\sum^{N-1}_{x=0}\sum^{N-1}_{y=0}f(x,y)cos[\frac{\pi u(2x+1)}{2N}]cos[\frac{\pi v(2y+1)}{2N}]
也就是將12\frac{1}{2} 乘入了整個分式中。雖然這樣寫只是變了一種形式,不影響結果,但破壞了原本的幾何意義(或者說偶延拓過程),誰知道這個 2x+12x+1 對應着什麼東西。

一、DCT在算法層面的實現
爲了簡化離散餘弦變換的計算過程,需要算法上進行優化。優化可以從兩方面入手,一是降低計算次數,一是將浮點運算轉化爲整數運算。

在計算次數方面,對於NN次離散餘弦變換,需要o(N2)o(N^2)次運算,通過快速算法,能夠將運算降低至o(Nlog2N)o(Nlog_2N) 次。

在整數運算方面,通過量化實現整數近似,並且在量化的同時保證變換的可逆性(不然變過去之後,就變不回來了),將離散餘弦變換過程中的浮點運算轉化爲整數運算,大致過程如下:

爲了熟悉和理解上的方便,將離散餘弦變換表達式寫成矩陣形式:
Y=CXCT Y=CXC^T 所謂整數近似,並不是單純地將浮點數四捨五入或者直接截斷得到整數(這樣做後,再進行逆變換就變不回來了),而是將浮點運算歸入量化過程(在逆變換時進行反量化,保證與原始數據的一致性),讓離散餘弦變換過程僅包含整數運算。

這樣做看似是朝三暮四,但在實際應用中,量化和量化中的浮點運算是必不可少的。反正總歸是要量化,這樣可以將兩次浮點預算合併爲一次,對優化算法仍舊是有意義的。

進行量化的整合後,離散餘弦變換表達式爲:
Y=(CfXCfT)Ef Y=(C_fXC^T_f)\bigodot E_f 其中,\bigodot表示矩陣的點乘。完成上述整數化後,不僅將浮點運算歸入量化過程,而且可以並行計算,加快速度。

二、DCT在硬件層面的實現
我們先不談DSP等專用芯片,單說CPUGPU

計算機的運算可以分爲兩類,整數運算浮點運算;浮點運算又可以分爲兩類,單精度浮點運算雙精度浮點運算

GPU擁有大量並行流處理器,但缺少串行邏輯控制機構,擅長進行浮點運算,對於更注重溢出檢查的整數運算,反而是GPU的弱項;CPU作爲中央處理器,則是兩種運算通吃。

進一步考慮浮點運算,單精度浮點運算的複雜度低於雙精度浮點運算。市面上常見的遊戲顯卡和TaiTan X,均是注重單精度浮點運算,而限制了雙精度浮點運算。開普勒架構的遊戲顯卡,雙精度浮點運算能力是單精度浮點運算的1/24;同時代的CPU,這一比值大約是1/2。

一般而言,對於物理建模與模擬(比如流體力學模擬、量子化學計算)、3D建模(其實也是一種意義上的物理建模),需要雙精度浮點運算;對於一般的圖形渲染(包括遊戲渲染、視頻渲染、機器學習需要單精度浮點運算。所以遊戲顯卡削弱雙精度浮點運算,TaiTan X一個勁提升單精度浮點運算,以及1080Ti被視爲平民機器學習神器,都是有其道理的。

回到離散餘弦變換。的確,GPU的並行運算速度超過CPU。經過優化的整數離散餘弦變換算法利於並行計算,更是突出了GPU的並行優勢,但GPU並不擅長整數運算。此外,在實際應用中,離散餘弦變換過程伴隨着串行邏輯,這也GPU不能勝任的。隨着GPU計算的火熱,如何讓GPU完成離散餘弦變換成了學界熱點。但目前來看,讓CPU扮演主要角色,把一部分運算交給GPU實現加速,應當是比較現實的考慮。

三、DCT在應用層面的實現
以視頻編碼爲例。1984年,H.261編碼標準開始研究。1990年,H.261編碼標準由國際電信聯盟(ITU-T)發佈。1993年,MPEG-1編碼標準由國際標準化組織/國際電子學委員會(ISO/IEC)發佈。自此,H.26X系列與MPEG-X系列開始了各自的更新換代,當然期間也有合作交易,比如大家可能比較熟悉的MPEG-2與大家可能不熟悉的H.262,其實是一個東西,又比如MPEG-4,包括了(早期的)MPEG-4、MPEG-4/AVC(=H.264)、MPEG-4/HEVC(=H.265)三個標準。

誒,好像就差H.263與MPEG-3沒有提。前者隨着技術的進步退出了歷史舞臺,後者則被斃掉了…

從H.261開始,離散餘弦變換就被應用於視頻編碼中。當技術發展到H.264與H.265,就具體的數學計算而言,H.261創立的分塊編碼被後續標準沿用,H.264在亮度平面上的基本分塊爲8x8像素塊,於是離散餘弦變換是這個樣子(代入N=8):
F(u,v)=116x=015y=015cos[π8u(x+12)]cos[π8v(y+12)]f(x,y)=116u=015v=015cos[π8u(x+12)]cos[π8v(y+12)] F(u,v)=\frac{1}{16}\sum^{15}_{x=0}\sum^{15}_{y=0}cos[\frac{\pi}{8}u(x+\frac{1}{2})]cos[\frac{\pi}{8}v(y+\frac{1}{2})]\\ f(x,y)=\frac{1}{16}\sum^{15}_{u=0}\sum^{15}_{v=0}cos[\frac{\pi}{8}u(x+\frac{1}{2})]cos[\frac{\pi}{8}v(y+\frac{1}{2})] 在整個編碼過程中,離散餘弦變換的作用如下:

首先,按照圖像處理的一般流程,進行採樣整量,完成連續數據到離散數據的量化。根據前面提到的算法優化,量化過程整合了離散餘弦變換的浮點運算。

接下來,進行整數離散餘弦變換。做這一變換有什麼用?一方面,從圖像處理的整體流程而言,變換後便於後續處理;另一方面,從編碼的角度而言,變換後使圖像信息集中,在數學上體現爲描述關鍵信息的係數變少,相應的,所需存儲空間降低,達到降低視頻體積的目的。


以上博文轉載自:

  1. https://zhuanlan.zhihu.com/p/33845296
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章