H.264帧内预测及整数DCT变换

  • 格式:doc
  • 大小:1.32 MB
  • 文档页数:18

1 一, 帧内预测

帧内预测可以减少空域和频域的冗余度, 获得更高的压缩效率. 当块或宏块做帧内编码时, 基于 4×4的亮点和色度样本有多种预测方式. 对于 Intra 4×4

模式下, 每个 4×4 的小块中的像素将通过邻块中已经解码的17 个像素进行预测(图3(a)中从A 至Q), 预测模式分为9 种(模式0 到模式 8), 其中模式 2 为DC (直流)预测. 其他8 种模式的预测方向如图1及图2所示.

图1

1、模式0:DC预测,此种模式有如下 4 种情形:

(1)若A ,B ,C,D , I,J ,K ,L 均在图像界内, 则从a 到p 均由如下公式计算, (A +

B + C + D + I + J +K + L + 4) > > 3;

(2) 若A ,B ,C,D 在图像外, I,J ,K ,L 在图像之内, 则a 到p 所有像素均如下计算, (I+ J + K + L + 2) > > 2; 2 (3) 与(2) 相反, 若A ,B ,C,D 在图像内而 I,J ,K ,L , 在外, 则为(A + B + C+

D + 2) > > 2;

(4)A ,B ,C,D , I,J ,K ,L 均在图像外,a 到p 均取值 128.

在代码中,表示为:

s0 = 0;

if (block_available_up && block_available_left)

{

s0 = (P_A + P_B + P_C + P_D + P_I + P_J + P_K + P_L + 4)/(2*BLOCK_SIZE);

}

//如当前块上块和左块存在,则块内亮度值为上块参考点值与左块参考点值的均值。

//后面加上4是因为/8是右移(取整)操作,加上4可以转换为四舍五入。

else if (!block_available_up && block_available_left)

{

s0 = (P_I + P_J + P_K + P_L + 2)/BLOCK_SIZE;

}//如上块不存在,则取左块参考点值的均值

else if (block_available_up && !block_available_left)

{

s0 = (P_A + P_B + P_C + P_D + 2)/BLOCK_SIZE;

}//同上

else

{

s0 = img->dc_pred_value;

}//如都不存在,则取128

for (j=0; j < BLOCK_SIZE; j++)

{

for (i=0; i < BLOCK_SIZE; i++)

{

img->mprr[DC_PRED][i][j] = s0;//直流预测,DC_PRED为预测模式。

}

}

2、模式0、1: 垂直/水平预测

垂直预测

a, e, i, m 由A观测得

b, f, j, n 由B预测得

c, g, k, o 由C观测得

d, h, l, p 由D观测得

水平预测

a, b, c, d 由A观测得

e, f, g, h 由B预测得

i, j, k, l 由C观测得

m, n, o, p 由D观测得

3 在代码中实现为:

for (i=0; i < BLOCK_SIZE; i++)

{

img->mprr[VERT_PRED][0][i] =

img->mprr[VERT_PRED][1][i] =

img->mprr[VERT_PRED][2][i] =

img->mprr[VERT_PRED][3][i] = (&P_A)[i];//P_A等实际上是数组里的值(在上面有定义)。(&P_A)是得到数组名。

//以上为垂直预测方式

img->mprr[HOR_PRED][i][0] =

img->mprr[HOR_PRED][i][1] =

img->mprr[HOR_PRED][i][2] =

img->mprr[HOR_PRED][i][3] = (&P_I)[i];

//以上为水平预测方式

}

其他模式和以上的差不多,就不一一列了。

3、当9种模式都预测完后,在rdopt.c中,会遍历这9种预测模式,找到一种误差最小的方案

基本实现代码如下:

for (k=j=0; j<4; j++)

for (i=0; i<4; i++, k++)

{

diff[k] = imgY_org[pic_opix_y+j][pic_opix_x+i] - img->mprr[ipmode][j][i];//计算误差

}

cost = (ipmode == mostProbableMode) ? 0 : (int)floor(4 * lambda );

cost += SATD (diff, input->hadamard);

if (cost < *min_cost)//如COST最小,则当前ipmode为best_ipmode

{

best_ipmode = ipmode;

*min_cost = cost;

}

二、整数DCT变换

为什么要进行变换

空间图像数据通常是很难压缩的:相邻的采样点具有很强的相关性(相互关联的),而且能量一般平均分布在一幅图像中,从而要想丢掉某些数据和降低数据精度而不明显影响图像质量,就要选择合适的变换,方法,使图像易于被压缩。适合压缩的变换方法要有这样几个性质:

1、可以聚集图像的能量(将能量集中到少数有意义的数值上);如下图: 4

举个例子说明,下图是变换前的数据:

以下是变换后的数据:

可以看出,经变换后,数据的能量基本上集中到左上方(低频信号)了,而变换后的数据完全可以通过反变换还原成原来的数据。为了达到压缩文件的目的,我们就可以丢弃掉一些能量低的数据(高频信号),而对图像质量影响很小。

2、可以除去数据之间的相关性(以使丢掉不重要的数据对图像的质量影响很少)。

3、变换方法应该适合用软硬件实现。 5

H.264中的整数变换

H.264与先前的标准相似,对残差采用基于块的变换编码,但变换是整数操作而不是实数运算,其过程和DCT基本相似。这种方法的优点在于:在编码器中和解码器中允许精度相同的变换和反变换,便于使用简单的定点运算方式。也就是说,这里没有"反变换误差"。 变换的单位是4×4块,而不是以往常用的8×8块。由于用于变换块的尺寸缩小,运动物体的划分更精确,这样,不但变换计算量比较小,而且在运动物体边缘处的衔接误差也大为减小。为了使小尺寸块的变换方式对图像中较大面积的平滑区域不产生块之间的灰度差异,可对帧内宏块亮度数据的16个4×4块的DC系数(每个小块一个,共16个)进行第二次4×4块的变换,对色度数据的4个4×4块的DC系数(每个小块一个,共4个)进行2×2块的变换(见图3)。

H.264为了提高码率控制的能力,量化步长的变化的幅度控制在12.5%左右,而不是以不变的增幅变化。变换系数幅度的归一化被放在反量化过程中处理以减少计算的复杂性。为了强调彩色的逼真性,对色度系数采用了较小量化步长。 6

图3

4X4整数变换矩阵公式推导

为了说明标准中整数变换和反变换,先设d=c/b,

我们可以把DCT变换写成下式:

其中,为了保证正交,a ,b, c, d的值可分别如下设置(其实也就是余弦变换的设 7 置?):

根据假设,上式可化为:

把两边的对称矩阵移到左边可得:(下一步运算没搞清楚是怎么回事。)

这一步也有点问题:

此时,d的值为0.4142。这样的话,还是实数运算。如我们令d=1/2,则b=sqrt(2/5).

c=sqrt(1/10),同样,可以保证矩阵的正交,同时,可以把运算变为整数运算。

1/2可以提到矩阵外面,并与右边的点乘合并,得

8

其中,

在JM编码器中,变换过程只包括了

后面的点乘实际上是在量化过程中进行,因为后面的点乘还有实数运算,实数运算将不可避免地产生精度误差,而且运算量巨大。而量化本身就会丢失一些信号,因些,这些实数运算放在量化过程中将大大的降低变换的运算率同时又不明显影响精度?

然而,4X4的矩阵运算如果按常规算法的话仍要进行64次乘法运算和48次加法,这将耗费较多的时间,于是在H.264中,有一种改进的算法(蝶形算法)可以减少运算的次数。这种矩阵运算算法构造非常巧妙,利用构造的矩阵的整数性质和对称性,可完全将乘法运算转化为加法运算。 9

变换过程在JM中代码实现如下:

// Horizontal transform水平变换,其实就是左乘Cf,

for (j=0; j < BLOCK_SIZE && !lossless_qpprime; j++)

{

for (i=0; i < 2; i++)

{

i1=3-i;

m5[i]=img->m7[i][j]+img->m7[i1][j];

m5[i1]=img->m7[i][j]-img->m7[i1][j];

}

img->m7[0][j]=(m5[0]+m5[1]);

img->m7[2][j]=(m5[0]-m5[1]);

img->m7[1][j]=m5[3]*2+m5[2];

img->m7[3][j]=m5[3]-m5[2]*2;

}

// Vertical transform垂直变换,其实就是右乘CfT

for (i=0; i < BLOCK_SIZE && !lossless_qpprime; i++)

{

for (j=0; j < 2; j++)

{

j1=3-j;

m5[j]=img->m7[i][j]+img->m7[i][j1];

m5[j1]=img->m7[i][j]-img->m7[i][j1];

}

img->m7[i][0]=(m5[0]+m5[1]);

img->m7[i][2]=(m5[0]-m5[1]);

img->m7[i][1]=m5[3]*2+m5[2];

img->m7[i][3]=m5[3]-m5[2]*2;

}

反变换: