JM模型I帧帧内预测流程 - huzia的专栏 - CSDNBlog

来源:百度文库 编辑:神马文学网 时间:2024/04/29 09:27:31
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的比值不划算,所以编码的一个中心的思想还是很重要的,就是复杂的不见得最优,数学理论与实验科学还
是有一些区别的.这也为继续的研究给出来一个启示,多想想简单而出新的想法,不要把一切都归给数学,警示.