整數DCT的作用是變換編碼,提取信號特徵。H264的整數DCT分兩步:
1、DCT整數部分變換;
2、DCT實數部分變換。
實數部分和量化在一起進行,可以統一損失精度,也可以降低運算複雜度。
整數部分用蝶形變換,以最常見的4x4宏塊爲例,代碼如下:
#include <iostream>
using namespace std;
#define BLOCK_SIZE 4
int block[BLOCK_SIZE][BLOCK_SIZE], tblock[BLOCK_SIZE][BLOCK_SIZE];
//蝶形算法
void forward4x4(int pos_y, int pos_x)
{
int i, ii;
int tmp[16] = { 0 };
int *pTmp = tmp, *pblock;
int p0, p1, p2, p3;
int t0, t1, t2, t3;
// Horizontal
for (i = pos_y; i < pos_y + BLOCK_SIZE; i++)
{
pblock = &block[i][pos_x];
p0 = *(pblock++);
p1 = *(pblock++);
p2 = *(pblock++);
p3 = *(pblock);
t0 = p0 + p3;
t1 = p1 + p2;
t2 = p1 - p2;
t3 = p0 - p3;
*(pTmp++) = t0 + t1;
*(pTmp++) = (t3 << 1) + t2;
*(pTmp++) = t0 - t1;
*(pTmp++) = t3 - (t2 << 1);
}
// Vertical
for (i = 0; i < BLOCK_SIZE; i++)
{
pTmp = tmp + i;
p0 = *pTmp;
p1 = *(pTmp += BLOCK_SIZE);
p2 = *(pTmp += BLOCK_SIZE);
p3 = *(pTmp += BLOCK_SIZE);
t0 = p0 + p3;
t1 = p1 + p2;
t2 = p1 - p2;
t3 = p0 - p3;
ii = pos_x + i;
tblock[pos_y][ii] = t0 + t1;
tblock[pos_y + 1][ii] = t2 + (t3 << 1);
tblock[pos_y + 2][ii] = t0 - t1;
tblock[pos_y + 3][ii] = t3 - (t2 << 1);
}
}
void inverse4x4(int pos_y, int pos_x)
{
int i, ii;
int tmp[16] = { 0 };
int *pTmp = tmp, *pblock;
int p0, p1, p2, p3;
int t0, t1, t2, t3;
// Horizontal
for (i = pos_y; i < pos_y + BLOCK_SIZE; i++)
{
pblock = &tblock[i][pos_x];
t0 = *(pblock++);
t1 = *(pblock++);
t2 = *(pblock++);
t3 = *(pblock);
p0 = t0 + t2;
p1 = t0 - t2;
p2 = (t1 >> 1) - t3;
p3 = t1 + (t3 >> 1);
*(pTmp++) = p0 + p3;
*(pTmp++) = p1 + p2;
*(pTmp++) = p1 - p2;
*(pTmp++) = p0 - p3;
}
// Vertical
for (i = 0; i < BLOCK_SIZE; i++)
{
pTmp = tmp + i;
t0 = *pTmp;
t1 = *(pTmp += BLOCK_SIZE);
t2 = *(pTmp += BLOCK_SIZE);
t3 = *(pTmp += BLOCK_SIZE);
p0 = t0 + t2;
p1 = t0 - t2;
p2 = (t1 >> 1) - t3;
p3 = t1 + (t3 >> 1);
ii = i + pos_x;
block[pos_y][ii] = p0 + p3;
block[pos_y + 1][ii] = p1 + p2;
block[pos_y + 2][ii] = p1 - p2;
block[pos_y + 3][ii] = p0 - p3;
}
}
int main()
{
cout << "hello init block and tblock" << endl;
int pos_y = 0, pos_x = 0;
int cnt = 0;
for (int i = 0; i < BLOCK_SIZE; i++)
{
for (int j = 0; j < BLOCK_SIZE; j++)
{
block[i][j] = cnt;
tblock[i][j] = 0;
cnt++;
cout << "(" << block[i][j] << "," << tblock[i][j] << ") ";
}
cout << endl;
}
forward4x4(pos_y, pos_x);
cout << "after DCT" << endl;
for (int i = 0; i < BLOCK_SIZE; i++)
{
for (int j = 0; j < BLOCK_SIZE; j++)
{
block[i][j] = 0;
//tblock[i][j] = rshift_rnd_sf(tblock[i][j],8);
cout << "(" << block[i][j] << "," << tblock[i][j] << ") ";
}
cout << endl;
}
inverse4x4(pos_y, pos_x);
cout << "after IDCT" << endl;
for (int i = 0; i < BLOCK_SIZE; i++)
{
for (int j = 0; j < BLOCK_SIZE; j++)
{
tblock[i][j] = 0;
cout << "(" << block[i][j] << "," << (block[i][j] + 30) / 20 << "," << tblock[i][j] << ") ";
}
cout << endl;
}
getchar();
return 0;
}
其中forward4x4是整數DCT的整數部分,inverse4x4是整數DCT的反變換。
由於forward4x4和inverse4x4只有移位和加減,導致的數據沒有被縮放,反變換要想恢復原數據,我添加了(block[i][j]+30)/20的操作,模擬量化步驟中的縮放,由於測試數據是0,1,2,3 ... 15,所以用30和20實現了無損還原,在實際的H264標準中,是以經驗值代替的。