http://blog.csdn.net/u011867581/article/details/45600749
原文转自:http://www.cnblogs.com/xkfz007/archive/2012/07/27/2612006.html
在JM8.6中帧内与帧间模式的选择是其中非常重要的一部分, 模式选择的过程其实涵盖了H.264编码中的大部分内容. 从代码看来, 这一部分其实和码率控制中的代价函数计算是重叠在一起的, 在进行代价函数的计算过程中也就实现了模式的选择, 代价计算完毕, 最优模式也就选择完毕.
1. 下面, 我们先回顾一下H.264中的帧内预测算法:
2. H.264中的帧间模式选择算法:
在帧间模式选中, 要进行的一个重要的过程就是运动估计(运动搜索)
3. 代价函数
在上面我们也提到过, 帧内帧间模式的选择其实和代价的计算是重叠的. 在具体的JM中实现时, 其实对于代价的计算是分为两种情况的, 在帧内模式选择的过程中也提到过, 主要分为RDO模式和非RDO模式. RDO指率失真优化.
H.264在运动搜索,参考帧择优, 模式决策三个方面运用了不同的RDO代价函数, 同时也将非RDO代价函数列为可选模式, 以满足不同的需要.
4. 具体代码实现
具体的代价函数如下:
代价函数主要包括MV_COST, REF_COST和MODE_COST, 而前两者在帧间模式选择时才存在, 换句话说, 帧内模式选择时主要是计算MODE_COST.
4.1 运动矢量代价MV_ COST
运动矢量代价主要运用于运动搜索模块中, 为搜索点的择优提供了代价依据。H.264 直接运用了RDO 运动搜索代价函数,其主要构成如下:
其中, S AD 是当前块和参考帧搜索位置块的差值, 如果用了哈达马变换则 SA D 转化为 S A TD; MV bi t s 是编码运动矢量MV 的比特数,由 MV bit s 列表给出。
具体在JM8.6中的代码实现:
对比公式可以发现, 代码中MV_COST不包含SAD.只是计算的后面一部分
通过查看代码可以发现, MV_COST计算所使用的地方都是在运动搜索的函数中, 这也是可以理解的: 通过比较MV_COST来得到最佳的预测块.
其实仔细看代码我们可以发现, 在运动搜索时为了寻找最优块其实就是使用了上面的公式, 在代码中有计算SAD:
byte_abs与img->quad是类似,img->quad存储的是-255到255的平方,用于计算SSD,而byte_abs则是绝对值,用于计算SAD
在代码中都有类似的计算SAD的代码(有的地方使用SATD)
所以其实在运动搜索的函数中其实将计算得到的运动搜索储存在了mcost(或mv_cost)中,并且其中是包括SAD的.
在非RDO模式下搜索: 看下面的代码:
其中motion_cost是一个全局变量,记录着每种模式下在每一个参考帧下的每一个8x8块的运动搜索代价
接着看:
在encode_one_macroblock函数中的帧间模式选择中:
经过函数Partition...后motion_cost有每一个参考帧对应的运动搜索代价
在RDO模式下:
在RDO模式下比较特殊, 因为在这种情况下实际上是完成了编码, 直接生成了码流, 可以得到对应的码率(rate)
所以在RDO模式下没有计算运动搜索代价
对于MV_COST, 它搜索后的最优代价结果是保存在motion_cost中的
4.2 参考帧代价REF_ COST
参考帧代价用于多参考帧的择优, 采用RDO 或非RDO 代价函数时,其定义有所差别。
(1) 非RDO 方式时:
其中,参考帧号为参考帧的编号,其他各变量的定义与MV_ COST 中一致。
(2) RDO 方式时:
其中, REF bit s 是编码参考帧号的比特数,由REF bit s 列表给出,其他变量与上述定义一致。\
具体在JM8.6中的实现:
同样也是没有考虑SAD先,
可以看到, 这儿是分开RDO和非RDO的.与上面的 公式相符的.
同时, 我搜索了代码, 可以发现, 只有这一个地方计算REF_COST, 所以这个地方我们可以联系上面的运动搜索代价MV_COST, 可以发现其实两者最后都加到mcost变量中去了,用于参考帧的选择.
REF_ COST用于参考帧的选择, RDO和非RDO模式下选出的参考帧不一样
对于MV_COST, 在RDO模式和非RDO模式下是一样的, 也就是说MV_COST的计算不分RDO模式或非RDO模式.
可以看出, 下面的这个循环根据MV_COST和REF_COST选择出了最佳参考帧:
准确来说上面REF_COST和MV_COST都是对应模式(16x16,16x8….)下针对某一块(8x8)的. 最后对于每一种模式都是要将所有的块累加起来比较大小的.
对于REF_COST, 它搜索后的最优代价(前向参考, 对于B帧还要考虑后向和双向)保存在fw_mcost中
总结一下, 上面的REF_COST和MV_COST都是两方面用处, 首先用于参考帧和运动矢量的搜索, 其次最优的参考帧代价和最优的运动矢量代价累加到模式代价中, 进行模式选择
4.3 模式代价MODE_ COST
模式代价用于块模式的择优中, 在帧内预测块和帧间预测块的各模式选择过程中定义有所不同。
1. 帧内模式选择:
对于帧内, 主要是在I4MB和I16MB之间进行选择.
对于亮度分量择优,非RDO 方式:
其中, SA D 为当前块与预测块的差值,同样,如果用了哈达马变换则转化为S AT D 。
RDO 方式:
其中, SS D 为当前块与重构块的差值; BL OCK bi t s 为某种预测模式下的残差块编码比特数,由熵编码结果给出。
对于色度分量帧内预测模式择优(色度分量只有帧内预测) , 因为对于帧间模式[色度块采用和亮度块同样的分割模式, 只是尺寸减半(水平和垂直方向都减半)], 并且只是在RDO方式下, 非RDO方式下没有考虑色度
SAD 为当前块与预测块的差值, 用了哈达马变换则转化为SATD。
我们来看代码方面的实现:
-
对于I4MB的模式代价计算:
代价的计算主要是在函数中,最后的代价保存在cost中. 具体的计算时,
-
将宏块16x16, 分为4个8x8块,利用计算得到每一个8x8块的代价,然后累加起来得到整个宏块的代价.
-
对于8x8块是分为4个4x4块,利用计算得到每一个4x4块的代价,然后累加起来得到一个8x8块的代价.即
-
对于一个4x4块, 在函数 Mode_Decision_for_4x4IntraBlocks中,利用函数intrapred_luma计算出了9中预测模式下的预测值保存在img->mprr中.
接下来, 对于9种模式分别计算其代价,然后选择最优的模式,此时是要分RDO还是非RDO的:
-
非RDO方式下,
利用上面给出的公式计算代价, 代码如下:
计算中利用SATD函数,在这个函数中会根据配置文件中的设置来选择是否使用SAD还是SATD
最优的代价保存在min_cost中, 这样就可以得到整个宏块16个4x4块的代价之和, 也就是一个宏块在I4MB模式下的的代价.并且最优的I4MB中的模式保存在了best_ipmode中
-
RDO方式下,
先计算了一下, 残差, 保存在img->m7中,然后调用函数RDCost_for_4x4IntraBlocks,
(1) 在这个函数中,调用函数dct_luma对img->m7进行DCT,量化, 反DCT,反量化,将重建后的结果保存在enc_picture->imgY中.
(2) 计算出SSD,即distortion,
(3) 对该4x4块进行编码, 从而得到码流大小rate
(4) 最后利用公公式得到代价:
最后利用RDCost_for_4x4IntraBlocks返回的代价rdcost,在9种模式中比较得到最优的代价保存在min_rdcost中,同时最优模式保存在best_ipmode中.
在函数Mode_Decision_for_4x4IntraBlocks返回前还保存了最优模式在img->ipredmode中
最后根据在Mode_Decision_for_4x4IntraBlocks中求得的代价可以比较I4MB代价是不是比较小,如下:
-
对于I16MB模式代价计算:
-
首先是非RDO方式:
-
(1)调用intrapred_luma_16x16函数,计算I16MB的4种模式下的预测值,保存在img->mprr_2中
(2) 调用函数find_sad_16x16, 计算SAD,将SAD作为最终的代价,根据SAD比较得到最优的I16MB模式
其实这儿与下面的叙述结合起来看,可以发现,如果代码运动到上面的部分,那么非RDO方式下的帧内与帧间最佳模式已经选择出来了.非RDO下的帧内帧间预测选择是随着代码的从上而下的运行不断进行比较选择的.
-
RDO方式:
在RDCost_for_macroblocks函数中调用
查看Intra16x16_Mode_Decision可以看到与非RDO方式是一样的
但是RDO方式下, 还是要计算的
2. 帧间模式选择:
对于亮度分量择优,非RDO 方式:
其中, MV _ COST、 RE F _ COST 分别为运动搜索、 参考帧择优后的最佳代价; MODE bit s 为编码模式号的比特数,由列表给出。
RDO 方式:
其中, SSD 为当前块与重构块的差值; BL OCK bit s 为某种块模式下的块编码比特数, 由熵编码结果给出, 包括残差块、 运动矢量和参考帧编码比特数。
下面看看具体JM8.6中的代码实现:
对于帧间模式需要在(16x16,16x8, 8x16,8x8,8x4,4x8和4x4)7种模式中选择一种, 对于RDO和非RDO差别比较大.
-
在非RDO方式下:
由于帧间的模式选择牵扯到运动搜索和参考帧搜索, 所以在计算模式代价的时候要考虑到这两方面. 在前面对MV_COST和REF_COST进行分析中,我们也提到过了, MV_COST和REF_COST是要作为帧间模式选择时代价函数的一个重要部分的.
-
首先是宏块级的选择: 16x16,16x8, 8x16
看代码我们可以发现, 其实MV_COST和REF_COST的选择是在模式选择的过程中完成的.
在计算一个宏块(16x16)在16x16,16x8, 8x16三种模式下的代价的时候, mode16x16是一个块直接计算完, mode16x8是分两个块的和, mode8x16也是2个8x16块的和. 其中代码中的cost就是一整个宏块的总的代价和.
下面的代码很清晰的显示了在宏块级进行模式选择的过程.
-
然后是亚宏块级的选择: 8x8,8x4,4x8和4x4
亚宏块的选择与宏块级的步骤是类似的.
当前宏块的在亚宏块模式下的代价cost8x8与之前宏块级模式下的最优代价min_cost进行比较, 再选择最优
-
在RDO方式下:
在JM8.6中RDO方式下的模式选择包括帧内和帧间都是在一起进行选择的, 通过在函数RDCost_for_macroblocks中计算对应模式的RDO代价,进行比较得到最优的模式.从下面的代码中可以看出来:
下面我们深入到RDCost_for_macroblock函数中进行, 分析这些模式的比较, 由于对于帧内I4MB和I16MB两种模式已经进行过分析, 所以这儿我们主要专注于帧间模式的选择:
首先, 调用函数设置当前宏块的模式和参考帧信息(其实是设置currMB->b8mode[4])
上面针对不同的模式进行不同的计算.
-
我们先看对于亚宏块级(P8x8):
亚宏块级的RDO计算的代码不在这个地方, 是和非RDO方式混合在一起的, 可以结合上面的对非RDO方式下对亚宏块级的分析来看>对于一个宏块的4个8x8块都要进行进行4种亚宏块级模式的选择:
调用了RDCost_for_8x8blocks函数, 对于RDCost_for_8x8blocks
由于在函数Partition...进行运动搜索时已经计算出了运动矢量mv保存在了img->all_mv中,所以这儿就可以根据mv直接找到预测值,进而在函数LumaPrediction4x4中完成预测值计算,保存在img->mpr中,然后在函数LumaResidualCoding8x8中计算得到残差,保存在img->m7中
接着再dct_luma函数中完成DCT变换,量化, 反DCT,反量化,然后将得到的重建值存在enc_picture->imgY中,同时也计算出了coef_cost, 这个coef_cost作为RDCost_for_8x8blocks函数中的cnt_nonz,表征残差中是否存在不为零的数. 经过这些操作后, 我们现在可以总结一下到底在RDO模式下的代价怎么计算的?
上面是RDCost_for_8x8blocks的返回值,我们可以发现, RDO模式下代价的计算是严格遵守了公式
失真distortion的计算是
是原始值与重建值的差
rate是对很多项进行编码后所占用的码流大小
RDCost_for_8x8blocks函数计算得到的代价保存在rdcost中,然后利用rdcost和min_rdcost比较
进而得到一个8x8块的最优P8x8中一种
这样我们就在RDO方式下, 得到了一个宏块中的4个8x8块的最优帧间模式.
同时我们要在这儿郑重说明一下: RDCost_for_8x8blocks函数与RDCost_for_macroblocks的不同在于, RDCost_for_8x8blocks只是在RDO方式下为一个8x8块在4种亚宏块模式中一种计算其代价,进行选择最佳模式,并且不考虑色度块,而RDCost_for_macroblocks是为一整个宏块(16x16)在(0,1-3,P8x8,I4MB,I16MB)计算其RDO代价,然后选择一个最优模式,并且是考虑色度块的.
所以下RDCost_for_macroblocks中, 要对选择P8x8模式下的整个宏块重新计算其代价的.并且在本函数中只是调用函数设置一下参数
包括AC系数img->cofAC和重建值enc_picture->imgY及模式currMB->intra_pred_modes
-
宏块级(LumaResidualCoding):
对于帧间的模式(1-3)调用了函数LumaResidualCoding,在函数LumaResidualCoding中对宏块残差进行编码, 是将一个宏块分为4个8x8块分别进行编码, 在这个函数中调用了LumaResidualCoding8x8,与RDCost_for_8x8blocks函数类似,
在函数LumaResidualCoding8x8中, 对8x8块的4个4x4块分别进行预测,dct变换等,得到coef_cost
利用函数LumaPrediction4x4根据已经得到的mv来计算每一块的预测值,保存在img->mpr中,进而计算残差保存在img->m7中,然后利用这个残差调用函数dct_luma进行DCT, 量化,反DCT, 反量化, 之后得到重建值保存在enc_picture->imgY中.
其实到现在为止上面的分析都是在模式的选择都是在I4MB的9种中选择最佳,在I16MB的4种中选择最佳, 在P8x8中的4种中选择最佳. 下面我们要在帧内和帧间所有7种模式(0,16x16,16x8,8x16,P8x8,I4MB和I16MB)中选择最优.
先调用函数ChromaResidualCoding, 在这个函数中主要是计算色度的预测值, 保存在enc_picuture->imgUV中
(1) 计算失真distortion(包括亮度和色度)
(2)计算码率rate
最后计算代价
这样就得到了最优的代价及最优的模式.
4.5 JM8.6中模式选择
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
经过上面的分析, 下面我们对JM8.6中的模式选择做一个总结:
总起来说, 模式选择是这么一个过程:
分为RDO和非RDO两种情况, (我们假设帧内和帧间模式都允许)
1. 非RDO方式
(1) 在宏块级模式16x16, 16x8, 8x16中选择最佳模式,min_cost初始化为一个很大数,cost是一种宏块级模式的代价
最终,从上面的3种模式中选择到最佳模式:best_mode保存最佳模式,min_cost保存最佳代价
(2) 对整个宏块中的4个8x8块分别,在亚宏块级模式8x8,8x4,4x8,4x4中选择最佳模式,每一个8x8块的最佳模式保存在best8x8mode[4]中,最佳代价保存在min_cost8x8中, 最后4个8x8块的代价累加得到整个宏块的代价cost8x8中,
这样就可以将宏块的P8x8模式与宏块级模式进行比较了:
由于min_cost保存的是宏块级3种模式中的最佳代价所以和cost8x8比较,得到16x16,16x8,8x16和P8x8中的最优模式
同样best_mode保存最佳模式,min_cost保存最佳代价.
至此我们就得到了帧间预测模式中的最佳模式和对应的最佳代价
(3) 计算Direct的代价,与帧间最佳模式进行比较,得到最有模式:
(4)进行帧内I4MB的9种模式的选择,利用函数Mode_Decision_for_Intra4x4Macroblock,同时得到最优代价cost和整个宏块中16个4x4块对应的最优模式 img->ipredmode[pic_block_x][pic_block_y] = best_ipmode;
然后与Direct和帧间的最佳模式进行比较,得到三者中的最优模式
(5) 进行帧内I16MB的4种模式的选择,先利用函数intrapred_luma_16x16计算得到4种模式下的预测值,保存在img->mprr_2中,然后利用 find_sad_16x16选择得到最佳模式i16mode和对应代价cost,
然后cost与前面所有帧内帧间的最佳代价进行比较,从而得到最终的帧内和帧间的模式中的最佳模式:best_mode
最终利用函数SetModesAndRefframeForBlocks将best_mode 保存到当前宏块的mb_type
2. RDO方式
前提,所需要的运动矢量代价和对应的参考帧代价都计算出来了.
(1)在亚宏块级的4种模式中选择最优的模式,利用的是函数RDCost_for_8x8blocks计算一个8x8块的代价,这样最佳模式保存在了best8x8mode[4]中,这样就可以得到一个宏块的4个8x8块对应的最佳模式,
(2) 接下来在7种模式(0,16x16,16x8,8x16,P8x8,I4MB和I16MB)中选择选择一种最优模式
其中I4MB是利用Mode_Decision_for_Intra4x4Macroblock得到9种I4MB帧内模式中得到最优模式后,作为I4MB
同样I16MB是利用函数Intra16x16_Mode_Decision得到4种I16MB帧内模式中的最优模式后作为I4MB
在函数RDCost_for_macroblocks计算每种模式对应的代价,然后比较得到最优的模式.
由于RDO方式是要先对每种模式进行具体编码一次,得到对应的码率和失真后利用公式来计算得到的, 所以对于上面的7种模式都必须计算失真distortion(包括亮度和色度)和码率rate
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
另外对于P 帧亮度分量的特殊模式SKIP 模式和B 帧亮度分量的特殊模式SKIP、 DIRECT 模式,其非RDO
代价:
SA D 为当前块与参考帧相应位置块的差值, 同样,如果用了哈达马变换则转化为SA T D。
在代码中具体如下:
这个应该是对应B帧的Direct模式
但是我现在没有在代码中发现P帧skip模式下代价的计算