JM模型I帧帧内预测流程
I帧只存在帧内编码,没有帧间运动估计,不用参考其他的帧,所以I帧具有同步作用,.付出的代价就是效率稍差,不过也十分必要的。
I帧帧内编码分为亮度编码和色度编码,需要完成预测,计算RD代价,来判别宏块分块模式.
I帧亮度度分块模式分为16X16,8X8,4X4三种模式,色度分块模式只有一种8X8模式,每种分块模式,又有不同的
预测方式,在JM模型中,需要对这些模式进行RD代价计算,选择其中最小值作为最优模式。
下面对涉及分块模式的数据进行说明:
const int mb_mode_table[9] = {0, 1, 2, 3, P8x8, I16MB, I4MB, I8MB, IPCM}; // DO NOT CHANGE ORDER !!!
0:16X16 Direct模式,在B帧中有效
1:Inter16X16,在帧间有效
2:Inter16X8,在帧间有效
3:Inter8X16,在帧间有效
P8X8:帧间有效
I16MB:Intra16X16帧内有效
I4MB:Intra有效
I8MB:Intra有效
IPCM:Intra有效,不要预测,直接对RAW数据编码.
其中P8X8模式和下面的数据又有关系:
const int b8_mode_table[6] = {0, 4, 5, 6, 7}; // DO NOT CHANGE ORDER !!!
上面的5种模式都归入P8X8模式,叫做亚宏块级,在码流TRACE文件有一个语法元素叫做b8mode,说的就是这个。
0:8X8 Direct模式,在B帧中有效。
4:Inter8X8,在帧间有效
5:Inter8X4,在帧间有效
6:Inter4X8,在帧间有效
7:Inter4X4,在帧间有效
下面举个例子,TRACE文件里面:
@45800 mb_type (B_SLICE) ( 7, 4) = 8 0000 ( 22)
@45804 8x8 mode/pdir( 0) = 4/0 000 ( 1)
@45807 8x8 mode/pdir( 1) = 6/1 0000 ( 7)
@45811 8x8 mode/pdir( 2) = 4/0 00 ( 1)
@45813 8x8 mode/pdir( 3) = 5/1 0000000 ( 6)
意思就是位置(7,4)宏块是P8X8分块,
其中第一个8X8块是8X8,list0预测,第二个8X8块,是两个4X8块,list1方向预测,第3个是8X8,list0预测,第四个是两个8X4,list1预测,
这个好象有点跑题了,因为b8mode不是I帧分块模式,是P,B帧中分块模式。
以上基本包括了264帧内,帧间要用到的所有分块模式.
下面开始分析帧内预测流程:
色度预测(计算所有可能的预测模式的预测值)---->亮度预测(所有的分块模式,要对3种分块)---->计算每种模式的RD值(包括各种预测方式)--->获得最优。
下面可是一步一步进行说明:
1:色度预测
色度预测基于8X8块预测,8X8预测有4种预测模式,包括水平,垂直,DC预测,平坦预测。
对色度帧内预测,必须要提到几个表格:
一个表格是:
static int block_pos[3][4][4]= //[yuv][b8][b4]
{
{ {0, 1, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0},{0, 0, 0, 0}},
{ {0, 1, 2, 3},{2, 3, 2, 3},{0, 0, 0, 0},{0, 0, 0, 0}},
{ {0, 1, 2, 3},{1, 1, 3, 3},{2, 3, 2, 3},{3, 3, 3, 3}}
};
其中yuv就是YUV采样比例,yuv = YUV format -1,因此YUV420,yuv值等于0,b8为宏块色度8X8块序号,YUV420,8X8块只有一个,b8为0,
b4有0,1,2,3,因此,很容易看明白上面的表格.
第二个表格是
const unsigned char subblk_offset_x[3][8][4] = //[yuv][b8][b4]
{
{ {0, 4, 0, 4},
{0, 4, 0, 4},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0}, },
{ {0, 4, 0, 4},
{0, 4, 0, 4},
{0, 4, 0, 4},
{0, 4, 0, 4},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0}, },
{ {0, 4, 0, 4},
{8,12, 8,12},
{0, 4, 0, 4},
{8,12, 8,12},
{0, 4, 0, 4},
{8,12, 8,12},
{0, 4, 0, 4},
{8,12, 8,12} }
};
const unsigned char subblk_offset_y[3][8][4] =//[yuv][b8][b4]
{ { {0, 0, 4, 4},
{0, 0, 4, 4},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0}, },
{ {0, 0, 4, 4},
{8, 8,12,12},
{0, 0, 4, 4},
{8, 8,12,12},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0} },
{ {0, 0, 4, 4},
{0, 0, 4, 4},
{8, 8,12,12},
{8, 8,12,12},
{0, 0, 4, 4},
{0, 0, 4, 4},
{8, 8,12,12},
{8, 8,12,12} }
};
这两个表格和上面的那个block_pos含义差不多,但是对U,V两个分量都包括进去了,这个含义是帧内坐标偏移,不是上面序号的意思
色度帧内预测要用到临块A,B,C宏块情况做出判断,色度块预测.
由于写这篇文章的时候,是边对照代码,边写的,所以是思路有些不对,有点写文章中倒叙的方式
ImageParameters结构里面有一个成员mprr_c[2][4][16][16];//[uv][DC_PRED_8/HOR_PRED_8/VERT_PRED_8/PLANE_8][16][16]这个成员,
放置了各种模式下的预测值,问为什么要16X16呢,而不是8X8,当YUV444模式时,当然是16X16了,这个是预留得
1:DC预测,DC预测又是以4X4块为基本单位,对A,B,C块的象素取平均值,假如没有A,B,C块的时候,取128了
2:垂直预测,假如B宏块存在,直接把B宏块最下边的象素Copy
3:水平预测,假如A宏块存在,直接把A宏块最右边的象素Copy
4:平坦模式,需要A,B,C同时存在,平坦模式的预测象素值算法比较复杂,这里不讲了.
预测完了色度值,暂时保存在mprr_c数组里面,等待后面使用,
2:对亮度的预测和代价值的计算都在函数compute_mode_RD_cost中,该函数又用RDCost_for_macroblocks来完成主要计算
该函数说明如下:
int RDCost_for_macroblocks (double lambda, // <-- lagrange multiplier,拉格朗日因子
int mode, // <-- modus (0-COPY/DIRECT, 1-16x16, 2-16x8, 3-8x16, 4-8x8(+), 5-Intra4x4, 6-Intra16x16)
// mode 9种分块模式.
double* min_rdcost, // <-> minimum rate-distortion cost
double* min_rate, // --> bitrate of mode which has minimum rate-distortion cost.
int i16mode ) //16X16预测模式,仅对帧间16X16有效。
该函数对mode取不同值选择不同的模式决策函数
I4MB-------------Mode_Decision_for_Intra4x4Macroblock
I16MB------------Intra16x16_Mode_Decision
I8MB-------------Mode_Decision_for_new_Intra8x8Macroblock
下面对I4MB进行分析
I4MB又调用Mode_Decision_for_8x8IntraBlocks函数,故名思义,该函数对8X8块进行模式选择,
该函数又调用Mode_Decision_for_4x4IntraBlocks,该函数完成4X4帧内预测,残差计算,参差DCT,Zig排序,量化,RL编码,IDCT,反量化,
存储重建象素,便于以后使用.
二。264编一个I帧多麻烦---JM 9.7中I帧编码分析
编码一帧,首先当然得知道从哪儿开始编,自打有了容错这种东西之后,FMO(Flexible macroblock ordering)就成了选择编码块的
第一步了.当然,最平常的编码方法中,一帧就是一个slice,一个slice从一帧的第一个宏块开始算,所以得到一帧的最左上角的宏块
地址,I帧编码开始.
在初始化编码参数的时候,大都都是在configure文件中设置好的编码参数,比如说在Intra中4x4的某个方向的预测是不是被禁止的
,8x8是可选的,还是说压根儿就不用等等.这些都被初始化到enc_mb->valid[]这个诡异的小东西里面了,所以说如果要做单个block
mode的时候,对里面的数据一定是要非常小心的.
对于一个I帧来说,编码总是先把一帧的U,V部分先进行Intra prediction,然后才考虑在Chroma最优之后,Luma的最优解.之后通过计
算编码这一帧来说最小的RD cost的mode来得到当前luma分量的编码方法.
核心函数:rdopt.c: int RDCost_for_macroblocks(.....)
在该函数中,对于不同种的分块模式以及当前的编码status来分析,涉及到Intra coding的有I4MB,I8MB,I16MB以及IPCM(IPCM是个
关于H.264中DCT变换问题 |
以前写的一篇文章,现在放上来。解码器现在作到Huffman解码了。
窗外的烟花声音渐渐沉寂,开始工作了.
H.264标准DCT8X8变换问题,据流媒体MPEG4/H264版面斑竹,X264/MPEG4开发小组成员的chenm001讲已经废弃,所有 ABT(自适应块尺寸变换)部分,都从标准中拿掉.但是我从JM97测试模型中依然开始看到,这一部分还在工作,码流依然输出 了相关的DCT8X8 flag信息,所谓ABT(adaptive block transform),就是讲在DCT变换的时候,选择4X4,8X8,16X16是可以自适应 的,自动选择其最合理的方式,听起来的挺好的事情,任何好的东西都是要付出代价的,天下没有掉下的馅饼。自适应是要 付出计算代价的。H.264 80%以上(对不起,没有数据参考,纯属个人估计)的DCT变换是基于4X4的,对I16MB模式下用16X16 变换.其实DCT8X8变换是很多压缩标准使用的变换方式,比如JPEG. DCT8X8模式是JM的一个可选项.哪些项可以启用8X8DCT呢? 我们看JM对此决策: if ((mode >= 1 && mode <= 3) && currMB->luma_transform_size_8x8_flag == 0) { //try with 8x8 transform size } //=========== try DIRECT-MODE with 8x8 transform =========== else if (mode == 0 && bslice && active_sps->direct_8x8_inference_flag && currMB->luma_transform_size_8x8_flag == 0) { //try with 8x8 transform size continue; } //=========== try mb_type P8x8 for mode 4 with 4x4/8x8 transform =========== else if ((mode == P8x8) && (enc_mb.valid[4]) && (currMB->luma_transform_size_8x8_flag == 0)) { //check 8x8 partition for transform size 8x8 } 可以看得出来当我们帧间预测的时候,模式为16X8,8X16,16X16,P8X8,I8MB时候,我们都要需要测试计算DCT8X8的RD大小. 为何其他的尺寸不做DCT8X8,比如I4MB,这是理所当然的,我们作DCT是为了块数据的去相关性,我们对I4MB是做4X4预测的 我们却对四个4X4的block合并一起作DCT8X8,数据之间的相关性不大,可以想象效果不会好.这里要指出,对宏块作I8MB子块预测 也是新加入标准的,据我对JM的了解,I8MB也和I4MB一样,有9种预测模式.值的注意的是,I8MB和其它的帧内子块有所不同,由于 DCT8X8变换尺寸比较大,大尺寸变换虽然去相关效果不错,但是会尺寸比较严重的块效应,尤其两块之间的边缘会形成比较大的梯度 所以在预测之前,I8MB子预测要做低通滤波,人工去除两块之间变换后引入的高频噪声,这样块边界看起来比较平滑。 |
诡异的东西,回头再说)
对于I16MB来说:
首先进行16x16块的Intra prediction.当然这是整个块的预测了,所以不涉及到ABT的问题,直接用DC,水平,垂直,Plane四种mode进
行编码,就可以得到四个mode的预测结果.
得到预测结果之后,我们用orignal的数据减去我们预测的数据,得到的就是residual,不同的mode得到的residual的值是不同的,结
果就是他们所含的能量是不同的,它的结果是编码所消耗的码字是不同的,于是乎结果就是不同的.多少年来,做编码的人们为了这么
几个bits费劲脑力,为了省bits,对比结果的这么些个功还是得做的.比较的内容就是他们各自的sad(sum of absolute difference)
,SAD最小的那个mode被称为I16MB分块方式中最佳的编码模式.
预测结束之后,我们就使用这个编码模式,对residual进行DCT与Quantization,结果直接送给熵编码器进行处理,这样一个I16MB编码
完毕.
对于I4MB来说:
将16x16的宏块先分成4个8x8的子块,再把各个子块各分成4个4x4的子块,对每一个4x4的块进行intra prediction,当然4x4的块进行
预测有9个mode,是根据它们可能的纹理走向进行预测的,最符合纹理方向的mode得到的residual比较小,才会被选为是最佳编码的
mode.用最佳的mode得到的最佳intra prediction的值求得相应的residual,计算各自的RD.方法大同小异,计算DCT,送给Quantization,
结果送给熵编码器,编码收工.
这样一直等到16个4x4的块编码完成算是完事.
对于I8MB来说:
把一个16x16的宏块分成4个8x8的子块,对于每一个子块都进行8x8的prediction.计算的结果,算得residual,将每一个residual利用
Hadamard变换求得SATD,比较大小,小的那个为最佳的预测mode.保留最佳residual,进行8x8的dct变换,将变换系数保留,编码结束.
这样一直进行操作,使得整个帧都被编码,这时I帧编码结束.写出MB Layer数据,写出Macroblock编码数据,I帧编码结束.
可以看到对于I帧编码很容易想到更复杂的算法,比如说在一个宏块里面使用多种子块方法等,但是这样的做法可能会使得cbp的bits
数过多,使得bitrate与PSNR的比值不划算,所以编码的一个中心的思想还是很重要的,就是复杂的不见得最优,数学理论与实验科学还
是有一些区别的.这也为继续的研究给出来一个启示,多想想简单而出新的想法,不要把一切都归给数学,警示.
导读:
本文转自
http://intozgc.zhuaxia.com/pre_channel/4971106