H.265/HEVC率失真优化(RDO)及其HM代码注解
来源:互联网 发布:国家数据 编辑:程序博客网 时间:2024/06/06 03:53
一、率失真优化(RDO)的目的
选择一个最小失真的编码模式可以带来最好的视频质量, 然而这往往需要很高的编码比特率。如何在有限的编码比特数下,选择一个失真最小的模式是编码中的关键问题。对于给定编码单元,上述求极值问题可将其转化为:在给定码率的情况下,尽可能降低失真D。这也即是 RDO 的目的,率失真代价函数表述如下:
{Para}opt=arg min{Para}(D+λ•R)
其中,R 和D 分别表示编码所消耗的比特数码率以及失真程度,{Para}opt表示最佳的编码参数集,包括模式选择、运动估计以及QP等,λ 为拉格朗日乘子。
二、HEVC中率失真优化方法
HM 采用拉格朗日优化方法为每个编码树单元CTU确定除编码参数QP之外的所有编码参数,主要包括CU划分模式、CU中PU和TU的划分、PU预测等等。每个CTU采用分级方式确定不同层的编码参数,步骤如下:
1. 首先,遍历所有CU,按如下公式对CU(CU从64x64到8x8)进行划分模式进行编码;
min J J=D(Mode)+ λ(Mode)•R(Mode)
2. 然后,在CTU中遍历所有PU(PU是预测的基本单元)模式和TU(TU是变换的基本单元,TU遍历的最小尺寸为4x4)的
组合,选择率失真代价值最小的确定为最优模式;
3. 不论帧内还是帧间,都会存在PU的预测,对PU的预测模式也是遍历所有的预测模式,分别计算每个模式对应的率失真代价值,选取最小的率失真代价值对应的预测模式为最优模式;
三、HEVC参考模型HM中初始QP和拉格朗日乘子初始化
在RDO中拉格朗日乘子作为率失真代价函数计算的关键参数,每帧的初始拉格朗日乘子会根据Slice的类型和在GOP中的位置,根据下式中每帧的初始QP确定对应的拉格朗日乘子:
模式选择过程对应拉格朗日乘子:
λ(Mode)=αW**pow(2,(QP-12)/3.0)
运动估计过程对应拉格朗日乘子:
λ(Motion)=pow(λ(Mode),1/2.0)
W为加权因子,I帧为0.57。根据当前Slice是否最为参考图像,Nb为GOP中B帧的个数(发现LDP配置中P帧在HM中也算作B帧),如果为非参考图像α为1,如果为非参考帧时:
α= 1.0 - Clip3(0.0,0.5,0.05*Nb)
HM在编码GOP之前会对每个Slice的拉格朗日乘子作初始化,并在compressGOP函数中的initEncSlice实现初始化
m_pcSliceEncoder->initEncSlice ( pcPic, iPOCLast, pocCurr, iGOPid, pcSlice, isField );
如果开启多QP优化,会对每个遍历的QP初始lambda,我用的HM版本为HM 16.9,不过不同版本的对应函数应该没有太大变化
// pre-compute lambda and QP values for all possible QP candidates for ( Int iDQpIdx = 0; iDQpIdx < 2 * m_pcCfg->getDeltaQpRD() + 1; iDQpIdx++ ) { // compute QP value dQP = dOrigQP + ((iDQpIdx+1)>>1)*(iDQpIdx%2 ? -1 : 1); // compute lambda value Int NumberBFrames = ( m_pcCfg->getGOPSize() - 1 ); //计算GOP中B帧个数 Int SHIFT_QP = 12;#if FULL_NBIT Int bitdepth_luma_qp_scale = 6 * (rpcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 8);#else Int bitdepth_luma_qp_scale = 0;#endif Double qp_temp = (Double) dQP + bitdepth_luma_qp_scale - SHIFT_QP;#if FULL_NBIT Double qp_temp_orig = (Double) dQP - SHIFT_QP;#endif // Case #1: I or P-slices (key-frame) Double dQPFactor = m_pcCfg->getGOPEntry(iGOPid).m_QPFactor; if ( eSliceType==I_SLICE ) { if (m_pcCfg->getIntraQpFactor()>=0.0 && m_pcCfg->getGOPEntry(iGOPid).m_sliceType != I_SLICE) { dQPFactor=m_pcCfg->getIntraQpFactor(); //如果不是I帧,根据 cfg 配置,对GOP中不同Slice分配不同的拉格朗日乘子权重 } else { //I帧根据GOP中非参考图像的个数分配拉格朗日乘子权重 Double dLambda_scale = 1.0 - Clip3( 0.0, 0.5, 0.05*(Double)(isField ? NumberBFrames/2 : NumberBFrames) ); dQPFactor=0.57*dLambda_scale; } } dLambda = dQPFactor*pow( 2.0, qp_temp/3.0 ); if ( depth>0 ) // I帧的 depth 为0 {#if FULL_NBIT dLambda *= Clip3( 2.00, 4.00, (qp_temp_orig / 6.0) ); // (j == B_SLICE && p_cur_frm->layer != 0 )#else dLambda *= Clip3( 2.00, 4.00, (qp_temp / 6.0) ); // (j == B_SLICE && p_cur_frm->layer != 0 )#endif } // if hadamard is used in ME process if ( !m_pcCfg->getUseHADME() && rpcSlice->getSliceType( ) != I_SLICE ) { dLambda *= 0.95; }#if W0062_RECALCULATE_QP_TO_ALIGN_WITH_LAMBDA Double lambdaRef = 0.57*pow(2.0, qp_temp/3.0); // QP correction due to modified lambda Double qpOffset = floor((3.0*log(dLambda/lambdaRef)/log(2.0)) +0.5); dQP += qpOffset;#endif iQP = max( -rpcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), min( MAX_QP, (Int) floor( dQP + 0.5 ) ) );//iDQpIdx为遍历QP的索引 m_vdRdPicLambda[iDQpIdx] = dLambda; m_vdRdPicQp [iDQpIdx] = dQP; m_viRdPicQp [iDQpIdx] = iQP; } // 如果没有多QP优化时,初始的QP和拉格朗日乘子选择索引为0的PQ和lambda // obtain dQP = 0 case dLambda = m_vdRdPicLambda[0]; dQP = m_vdRdPicQp [0]; iQP = m_viRdPicQp [0];
顺带说一下,在编码CU过程中会递归调用xCompressCU函数,并在完成递归编码完子CU后都会比较率失真代价值
#if AMP_ENC_SPEEDUP DEBUG_STRING_NEW(sChild) if ( !(rpcBestCU->getTotalCost()!=MAX_DOUBLE && rpcBestCU->isInter(0)) ) { xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), NUMBER_OF_PART_SIZES ); } else { xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), rpcBestCU->getPartitionSize(0) ); } DEBUG_STRING_APPEND(sTempDebug, sChild)#else xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth );#endif rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth ); // Keep best part data to current temporary data. xCopyYuv2Tmp( pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth ); } else { pcSubBestPartCU->copyToPic( uhNextDepth ); rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth ); } } m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]); if( !bBoundary ) { m_pcEntropyCoder->resetBits(); m_pcEntropyCoder->encodeSplitFlag( rpcTempCU, 0, uiDepth, true ); rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded(); } rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );
比较率失真代价函数,选择最优编码参数
xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth DEBUG_STRING_PASS_INTO(sDebug) DEBUG_STRING_PASS_INTO(sTempDebug) DEBUG_STRING_PASS_INTO(false) ); // RD compare current larger prediction
- H.265/HEVC率失真优化(RDO)及其HM代码注解
- RDO率失真优化
- RDO率失真优化
- H.264/AVC率失真优化( RDO) 策略研究
- HEVC/H.265理论知识(10)——率失真优化
- HEVC率失真优化
- HM编码器代码阅读(42)——率失真优化
- 率失真优化RDO 及 RD Cost
- 率失真优化RDO 及 RD Cost
- h.264 率失真优化
- HEVC学习(一)HM模型代码下载,HEVC协议下载,HM模型运行
- HEVC代码学习:HM使用方法
- HEVC代码学习26:率失真代价类TComRdCost
- HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析
- HEVC代码学习0:HM使用方法
- HEVC码率控制算法研究与HM相应代码分析(一)——HEVC标准及编码流程介绍
- HEVC/H.265参考代码跟踪
- HEVC/H.265参考代码跟踪
- Android居然采用Kotlin语言作为开发语言
- python 案例 011(猜数值)
- leetcode461位运算的运用
- linux中shell变量$#,$@,$0,$1,$2的含义解释
- 第六届 蓝桥杯 省赛 饮料换购
- H.265/HEVC率失真优化(RDO)及其HM代码注解
- 【数据结构导论】第二遍导图
- Android View的事件分发机制探索
- Urban Elevations UVA
- 简单dp,hdoj2084(数塔)
- String源码.md
- MYSQL学习笔记(七)使用数据处理函数
- 各种模板 长期更新
- linux安装mysql