本篇是該系列的第六篇,承接上篇IZigZag變換,介紹接下來的一個步驟——逆離散余弦變換,即逆零偏置前的一個步驟。
該步驟比較偏理論,其業務是對IZigZag變換后的數據,再進一步的處理,使其恢復DCT變換前的數據。
需要補充一點說明的是,上面的DCT其實是DCT2,因為jpeg編碼下都是對8x8的像素塊進行處理。
1. 理論
1.1. 背景
DCT,即離散余弦變換,常用圖像壓縮算法,步驟如下
1)分割,首先將圖像分割成8x8或16x16的小塊;
2)DCT變換,對每個小塊進行DCT變換;
3)舍棄高頻系數(AC系數),保留低頻信息(DC系數)。高頻系數一般保存的是圖像的邊界、紋理信息,低頻信息主要是保存的圖像中平坦區域信息。
4)圖像的低頻和高頻,高頻區域指的是空域圖像中突變程度大的區域(比如目標邊界區域),通常的紋理豐富區域。
1.2.算法
二維DCT變換就是將二維圖像從空間域轉換到頻率域。形象的說,就是計算出圖像由哪些二維余弦波構成。其算法如下:
其中,F就是變換得到的系數,f是圖像的像素值,A是轉換矩陣,其中i為二維波的水平方向頻率,j為二維波的垂直方向頻率,取值范圍都是0-(N-1),N是圖像塊的大小,其中:
即進行如下步驟的處理可以得到DCT變換后的系數矩陣:
1)求出轉換矩陣A;
2)利用轉換矩陣A,轉換到頻域,即由圖像 f 得到系數矩陣F。
2. 實踐
2.1. 圖像上運用實現
DCT用圖像上的處理,都是對8x8的像素數據塊進行處理,因此,DCT2變換的轉換矩陣A為:
其中,
那么轉換矩陣A為:
計算如這個鏈接:
1 void InitTransMat() 2 { 3 int i,j; 4 float a; 5
6 for(i=0;i<MAT_SIZE;i++) 7 { 8 for(j=0;j<MAT_SIZE;j++) 9 { 10 a = 0; 11 if(i==0) 12 { 13 a=sqrt((float)1/MAT_SIZE); 14 } 15 else
16 { 17 a=sqrt((float)2/MAT_SIZE); 18 } 19 DCT_Mat[i][j]= a*cos((j+0.5)*PI*i/MAT_SIZE); //變換矩陣
20 } 21 } 22 /* only for 8x8 */
23 //float Tmp[100][100] = { 24 // {0.3536, 0.3536, 0.3536, 0.3536, 0.3536, 0.3536, 0.3536, 0.3536,}, 25 // {0.4904, 0.4157, 0.2778, 0.0975, -0.0975, -0.2778, -0.4157, -0.4904,}, 26 // {0.4619, 0.1913, -0.1913, -0.4619, -0.4619, -0.1913, 0.1913, 0.4619,}, 27 // {0.4157, -0.0975, -0.4904, -0.2778, 0.2778, 0.4904, 0.0975, -0.4157,}, 28 // {0.3536, -0.3536, -0.3536, 0.3536, 0.3536, -0.3536, -0.3536, 0.3536,}, 29 // {0.2778, -0.4904, 0.0975, 0.4157, -0.4157, -0.0975, 0.4904, -0.2778,}, 30 // {0.1913, -0.4619, 0.4619, -0.1913, -0.1913, 0.4619, -0.4619, 0.1913,}, 31 // {0.0975, -0.2778, 0.4157, -0.4904, 0.4904, -0.4157, 0.2778, -0.0975,}, 32 //}; 33 //for (int i=0; i<8; i++) 34 // for (int j=0; j<8; j++) 35 // DCT_Mat[i][j] = Tmp[i][j];
36 }
2.2. IDCT2—— 由F求f的過程
前篇文章已介紹了由IZigZag變換后得到的系數矩陣F,那么要恢復原數據,就要進行如下變換:
算法的C語言描述如下:
1 int IDCT2(float (*dst)[8], int (*block)[8], bool dump) 2 { 3 float trans_matrix[8][8] = { 4 {0.3536, 0.3536, 0.3536, 0.3536, 0.3536, 0.3536, 0.3536, 0.3536,}, 5 {0.4904, 0.4157, 0.2778, 0.0975, -0.0975, -0.2778, -0.4157, -0.4904,}, 6 {0.4619, 0.1913, -0.1913, -0.4619, -0.4619, -0.1913, 0.1913, 0.4619,}, 7 {0.4157, -0.0975, -0.4904, -0.2778, 0.2778, 0.4904, 0.0975, -0.4157,}, 8 {0.3536, -0.3536, -0.3536, 0.3536, 0.3536, -0.3536, -0.3536, 0.3536,}, 9 {0.2778, -0.4904, 0.0975, 0.4157, -0.4157, -0.0975, 0.4904, -0.2778,}, 10 {0.1913, -0.4619, 0.4619, -0.1913, -0.1913, 0.4619, -0.4619, 0.1913,}, 11 {0.0975, -0.2778, 0.4157, -0.4904, 0.4904, -0.4157, 0.2778, -0.0975,}, 12 }; 13
14 float tmp[8][8]; 15
16 float t=0; 17 int i,j,k; 18 for(i=0;i<8;i++) //same as A'*I
19 { 20 for(j=0;j<8;j++) 21 { 22 t = 0; 23 for(k=0; k<8; k++) 24 { 25 t += trans_matrix[k][i] * block[k][j]; //trans_matrix's ith column * block's jth column
26 } 27 tmp[i][j] = t; 28 } 29 } 30
31 for(i=0; i<8; i++) //same as tmp*A
32 { 33 for(j=0; j<8; j++) 34 { 35 t=0; 36 for(k=0; k<8; k++) 37 { 38 t += tmp[i][k] * trans_matrix[k][j]; 39 } 40 dst[i][j] = t; 41 } 42 } 43
44 if (dump) { 45 puts("----after idct2----"); 46 for (i=0; i<8; i++) { 47 for (j=0; j<8; j++) { 48 printf("%3.4f ", dst[i][j]); 49 } 50 puts(""); 51 } 52 puts(""); 53 } 54
55 return 0; 56 }
2.3. 例子實踐
根據前篇文章的IZigZag結果恢復數據,如下:
有沒有注意到許多值是近似相等的?這是因為圖像在時域的連續性導致,可以利用其進行數據壓縮。
3. 篇外補充
正是圖像上這種特性,才有了圖像壓縮的這種方法——空間去冗余。
但是,需要說明一點的是,DCT2/IDCT2不是有損失壓縮(壓縮是數據量降低的概念,有損/無損是轉換前后信息是否丟失的概念),因為它可以完全恢復數據,只是這種變換將圖像在空間域/頻率域之間轉換。另外因為本人用的是浮點運算,因此會降低運算速度。一種優化可以把變換矩陣表每個元素都擴大10000倍,進行整形運算。
從DCT正變換角度來說,時域的非0數據量很多,但是經DCT2轉換后,頻域(非0數據)數據量變得非常少,值都普遍縮小,很多較大的非零值轉換為了0,后續可以用其他算子來表示一連串連續的零數據。
在這里,要特別感謝傅里葉這位法國數學家,正是因為他的出色工作,才使得如今的最常見的圖像壓縮技術變成了現實。
正可謂,前人栽樹后人乘涼。前人的理論成果,后人在應用上開花結果。