HM编码器代码阅读(35)——帧内预测(二)帧内预测总流程

来源:互联网 发布:linux书籍 编辑:程序博客网 时间:2024/05/18 02:46

亮度块的帧内预测


帧内预测的流程

注意:如果没有特别说明,操作的对象都是亮度块(Luma)

1、遍历所有的预测模式,得到每种模式下的残差信号,再对残差信号进行Hadamard变换计算SATD值

2、利用SATD值计算每种预测模式的率失真代价,选取率失真代价最小的几种模式(与PU大小相关)为预测模式集
3、将已编码相邻块的预测模式补充到预测模式集中
4、遍历模式集合中的所有模式,并对残差信号进行正常编码(熵编码),计算率失真代价
5、选取最优的预测模式作为该PU的最优模式

6、当亮度块的模式确定之后,把该模式以及DC、planar、水平方向模式、垂直方向模式作为色度块的候选模式,选取最优的模式即可


亮度块的帧内预测的入口函数

入口函数是estIntraPredQT,流程如下:
1、遍历CU下面的每一个PU,对于每一个PU,进行下面的操作
2、先初始化访问相邻像素块的工具类
3、调用initAdiPattern,对参考像素值进行预处理和滤波
4、首先计算需要进行完整RD率失真优化操作的模式的数量numModesForFullRD,可以根据PU的宽度来得到
5、检测快速搜索标识doFastSearch,通过比较numModesForFullRD和帧内预测模式的总数量(35种)是否相等得到
,如果不相等,那么使用快速搜索模式,否则使用普通模式;(一般来说doFastSearch都是true)
6、如果doFastSearch是true,那么进行下面的操作:
    (1)遍历帧内预测的35种模式,对于每一种模式进行帧内预测,然后使用Hadamard变换计算SATD值,
利用SATD的值计算每种模式的率失真代价
    (2)从35个模式中选取numModesForFullRD个代价比较优的模式,组成模式候选列表
    (3)调用getIntraDirLumaPredictor,根据相邻块的预测模式来对当前块的模式进行预测,若干预测模式
    (4)遍历预测模式,如果它不在候选模式列表中,那么把它添加到候选模式列表中
7、如果doFastSearch是false,那么表示numModesForFullRD的数量是35,那么所有的帧内预测模式都被添加到候选模式列表中
8、用一句话表述步骤6和7,那就是,快速搜索模式下,只选取几种最优可能的模式作为候选模式,普通模式下,所有的帧内预测模式都是候选模式
9、遍历候选模式列表,对于其中的每一模式,进行下面的操作:
    (1)调用xRecurIntraCodingQT,进行预测变换量化,注意该函数倒数第二个参数是true,表示会按照四叉树的方式继续向下划分
    (2)根据率失真代价选取最优的模式
10、经过步骤8,我们已经选取了最优的模式,但是该模式下的编码块是继续向下划分的,因此,我们还要计算该模式下,编码块不向下划分的时候的代价(调用xRecurIntraCodingQT,倒数第二个参数设置为false),通过比较编码块划分和不划分两种情况,得到最优的参数和模式
12、经过了上面的步骤之后,我们已经得到最优模式和变换系数了,此时我们应该重建当前块,因为后面的PU需要使用该重建块作为预测块。

/*** 亮度块的帧内预测的入口函数*/Void TEncSearch::estIntraPredQT( TComDataCU* pcCU, TComYuv*    pcOrgYuv, TComYuv*    pcPredYuv, TComYuv*    pcResiYuv, TComYuv*    pcRecoYuv,UInt&       ruiDistC,Bool        bLumaOnly ){// 删除无关代码*****// 候选的cost列表Double  CandCostList[ FAST_UDI_MAX_RDMODE_NUM ];//===== set QP and clear Cbf =====// 设置QP参数,清理Cbfif ( pcCU->getSlice()->getPPS()->getUseDQP() == true){pcCU->setQPSubParts( pcCU->getQP(0), 0, uiDepth );}else{// 进入此处pcCU->setQPSubParts( pcCU->getSlice()->getSliceQp(), 0, uiDepth );}//===== loop over partitions =====UInt uiPartOffset = 0;// 循环处理CU下的每一个预测块PUfor( UInt uiPU = 0; uiPU < uiNumPU; uiPU++, uiPartOffset += uiQNumParts ){//===== init pattern for luma prediction =====Bool bAboveAvail = false; // 上面的块是否有效Bool bLeftAvail  = false;// 左边的块是否有效// 初始化访问相邻块的工具类pcCU->getPattern()->initPattern   ( pcCU, uiInitTrDepth, uiPartOffset );// 这个函数很重要:主要是在着帧内预测之前,使用重建后的YUV图像对当前PU的相邻样点进行滤波,为接下来的进行的角度预测// 提供参考样点值pcCU->getPattern()->initAdiPattern( pcCU, uiPartOffset, uiInitTrDepth, m_piYuvExt, m_iYuvExtStride, m_iYuvExtHeight, bAboveAvail, bLeftAvail );//===== determine set of modes to be tested (using prediction signal only) =====// 35种帧内预测模式Int numModesAvailable     = 35; //total number of Intra modes// 在原始的YUV中获取获亮度的地址Pel* piOrg         = pcOrgYuv ->getLumaAddr( uiPU, uiWidth );// 在预测的YUV中获取亮度的地址Pel* piPred        = pcPredYuv->getLumaAddr( uiPU, uiWidth );// 偏移UInt uiStride      = pcPredYuv->getStride(); // 8UInt uiRdModeList[FAST_UDI_MAX_RDMODE_NUM];Int numModesForFullRD = g_aucIntraModeNumFast[ uiWidthBit ];//8Bool doFastSearch = (numModesForFullRD != numModesAvailable);//true// 使用快速搜索模式if (doFastSearch){assert(numModesForFullRD < numModesAvailable);for( Int i=0; i < numModesForFullRD; i++ ) {// 用于存储每一种模式的消耗CandCostList[ i ] = MAX_DOUBLE;}CandNum = 0;// 遍历35种帧内预测模式,选取若干个代价比较小的模式作为后续处理的模式for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ ) // 总共有35种模式,numModesAvailable = 35{UInt uiMode = modeIdx;// 对亮度块进行预测predIntraLumaAng( pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, bAboveAvail, bLeftAvail );// use hadamard transform here// 使用hadamard变换,计算SATD的值UInt uiSad = m_pcRdCost->calcHAD(g_bitDepthY, piOrg, uiStride, piPred, uiStride, uiWidth, uiHeight );UInt   iModeBits = xModeBitsIntra( pcCU, uiMode, uiPU, uiPartOffset, uiDepth, uiInitTrDepth );// 计算此种模式的代价Double cost      = (Double)uiSad + (Double)iModeBits * m_pcRdCost->getSqrtLambda();// 更新候选列表CandNum += xUpdateCandList( uiMode, cost, numModesForFullRD, uiRdModeList, CandCostList );}#if FAST_UDI_USE_MPMInt uiPreds[3] = {-1, -1, -1};Int iMode = -1;// 根据相邻块的预测模式来对当前块的模式进行预测,得到若干模式(称为预测模式),存放在uiPreds中Int numCand = pcCU->getIntraDirLumaPredictor( uiPartOffset, uiPreds, &iMode );if( iMode >= 0 ){// 将候选列表的索引设置为此模式numCand = iMode;}// 遍历预测的模式,如果它不在模式候选列表中,那么把它添加到模式候选列表中for( Int j=0; j < numCand; j++){Bool mostProbableModeIncluded = false;Int mostProbableMode = uiPreds[j];for( Int i=0; i < numModesForFullRD; i++){mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);}if (!mostProbableModeIncluded){uiRdModeList[numModesForFullRD++] = mostProbableMode;}}#endif // FAST_UDI_USE_MPM}else{for( Int i=0; i < numModesForFullRD; i++){uiRdModeList[i] = i;}}//===== check modes (using r-d costs) =====#if HHI_RQT_INTRA_SPEEDUP_MODUInt   uiSecondBestMode  = MAX_UINT;Double dSecondBestPUCost = MAX_DOUBLE;#endifUInt    uiBestPUMode  = 0;UInt    uiBestPUDistY = 0;UInt    uiBestPUDistC = 0;Double  dBestPUCost   = MAX_DOUBLE;// 遍历候选集中的模式for( UInt uiMode = 0; uiMode < numModesForFullRD; uiMode++ ){// set luma prediction modeUInt uiOrgMode = uiRdModeList[uiMode];pcCU->setLumaIntraDirSubParts ( uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth );// set context models// 设置上下文模型m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] );// determine residual for partitionUInt   uiPUDistY = 0;UInt   uiPUDistC = 0;Double dPUCost   = 0.0;#if HHI_RQT_INTRA_SPEEDUP// 通过多候选模式进行预测、变换、量化等操作来计算代价// 注意倒数第二个参数bCheckFirst是true,表示会继续按照四叉树的方式向下划分xRecurIntraCodingQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, true, dPUCost );// 重要函数end#elsexRecurIntraCodingQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, dPUCost );#endif// check r-d cost// 从候选列表中选取最优的模式if( dPUCost < dBestPUCost ){#if HHI_RQT_INTRA_SPEEDUP_MODuiSecondBestMode  = uiBestPUMode;dSecondBestPUCost = dBestPUCost;#endifuiBestPUMode  = uiOrgMode;uiBestPUDistY = uiPUDistY;uiBestPUDistC = uiPUDistC;dBestPUCost   = dPUCost;xSetIntraResultQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcRecoYuv );UInt uiQPartNum = pcCU->getPic()->getNumPartInCU() >> ( ( pcCU->getDepth(0) + uiInitTrDepth ) << 1 );::memcpy( m_puhQTTempTrIdx,  pcCU->getTransformIdx()       + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempCbf[0], pcCU->getCbf( TEXT_LUMA     ) + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempCbf[1], pcCU->getCbf( TEXT_CHROMA_U ) + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempCbf[2], pcCU->getCbf( TEXT_CHROMA_V ) + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempTransformSkipFlag[0], pcCU->getTransformSkip(TEXT_LUMA)     + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempTransformSkipFlag[1], pcCU->getTransformSkip(TEXT_CHROMA_U) + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempTransformSkipFlag[2], pcCU->getTransformSkip(TEXT_CHROMA_V) + uiPartOffset, uiQPartNum * sizeof( UChar ) );}#if HHI_RQT_INTRA_SPEEDUP_MODelse if( dPUCost < dSecondBestPUCost ){uiSecondBestMode  = uiOrgMode;dSecondBestPUCost = dPUCost;}#endif} // Mode loop#if HHI_RQT_INTRA_SPEEDUP#if HHI_RQT_INTRA_SPEEDUP_MODfor( UInt ui =0; ui < 2; ++ui )#endif{#if HHI_RQT_INTRA_SPEEDUP_MODUInt uiOrgMode   = ui ? uiSecondBestMode  : uiBestPUMode;if( uiOrgMode == MAX_UINT ){break;}#elseUInt uiOrgMode = uiBestPUMode;#endifpcCU->setLumaIntraDirSubParts ( uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth );// set context modelsm_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] );// determine residual for partitionUInt   uiPUDistY = 0;UInt   uiPUDistC = 0;Double dPUCost   = 0.0;// 现在已经知道最优模式了,使用最优模式对PU进行预测,然后变换量化等,计算代价// 注意倒数第二个参数bCheckFirst是false,表示当前PU不再进行划分,即只处理当前深度的PUxRecurIntraCodingQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, false, dPUCost );// 检测同一种模式下,bCheckFirst为true和false的情况下,那个的代价更优if( dPUCost < dBestPUCost ){uiBestPUMode  = uiOrgMode;uiBestPUDistY = uiPUDistY;uiBestPUDistC = uiPUDistC;dBestPUCost   = dPUCost;xSetIntraResultQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcRecoYuv );UInt uiQPartNum = pcCU->getPic()->getNumPartInCU() >> ( ( pcCU->getDepth(0) + uiInitTrDepth ) << 1 );::memcpy( m_puhQTTempTrIdx,  pcCU->getTransformIdx()       + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempCbf[0], pcCU->getCbf( TEXT_LUMA     ) + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempCbf[1], pcCU->getCbf( TEXT_CHROMA_U ) + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempCbf[2], pcCU->getCbf( TEXT_CHROMA_V ) + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempTransformSkipFlag[0], pcCU->getTransformSkip(TEXT_LUMA)     + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempTransformSkipFlag[1], pcCU->getTransformSkip(TEXT_CHROMA_U) + uiPartOffset, uiQPartNum * sizeof( UChar ) );::memcpy( m_puhQTTempTransformSkipFlag[2], pcCU->getTransformSkip(TEXT_CHROMA_V) + uiPartOffset, uiQPartNum * sizeof( UChar ) );}} // Mode loop#endif//--- update overall distortion ---uiOverallDistY += uiBestPUDistY;uiOverallDistC += uiBestPUDistC;//--- update transform index and cbf ---UInt uiQPartNum = pcCU->getPic()->getNumPartInCU() >> ( ( pcCU->getDepth(0) + uiInitTrDepth ) << 1 );::memcpy( pcCU->getTransformIdx()       + uiPartOffset, m_puhQTTempTrIdx,  uiQPartNum * sizeof( UChar ) );::memcpy( pcCU->getCbf( TEXT_LUMA     ) + uiPartOffset, m_puhQTTempCbf[0], uiQPartNum * sizeof( UChar ) );::memcpy( pcCU->getCbf( TEXT_CHROMA_U ) + uiPartOffset, m_puhQTTempCbf[1], uiQPartNum * sizeof( UChar ) );::memcpy( pcCU->getCbf( TEXT_CHROMA_V ) + uiPartOffset, m_puhQTTempCbf[2], uiQPartNum * sizeof( UChar ) );::memcpy( pcCU->getTransformSkip(TEXT_LUMA)     + uiPartOffset, m_puhQTTempTransformSkipFlag[0], uiQPartNum * sizeof( UChar ) );::memcpy( pcCU->getTransformSkip(TEXT_CHROMA_U) + uiPartOffset, m_puhQTTempTransformSkipFlag[1], uiQPartNum * sizeof( UChar ) );::memcpy( pcCU->getTransformSkip(TEXT_CHROMA_V) + uiPartOffset, m_puhQTTempTransformSkipFlag[2], uiQPartNum * sizeof( UChar ) );//--- set reconstruction for next intra prediction blocks ---// 变换量化/反变换反量化都已经处理完成了,那么设置重建块if( uiPU != uiNumPU - 1 ){Bool bSkipChroma  = false;Bool bChromaSame  = false;UInt uiLog2TrSize = g_aucConvertToBit[ pcCU->getSlice()->getSPS()->getMaxCUWidth() >> ( pcCU->getDepth(0) + uiInitTrDepth ) ] + 2;if( !bLumaOnly && uiLog2TrSize == 2 ){assert( uiInitTrDepth  > 0 );bSkipChroma  = ( uiPU != 0 );bChromaSame  = true;}UInt    uiCompWidth   = pcCU->getWidth ( 0 ) >> uiInitTrDepth;UInt    uiCompHeight  = pcCU->getHeight( 0 ) >> uiInitTrDepth;UInt    uiZOrder      = pcCU->getZorderIdxInCU() + uiPartOffset;Pel*    piDes         = pcCU->getPic()->getPicYuvRec()->getLumaAddr( pcCU->getAddr(), uiZOrder );UInt    uiDesStride   = pcCU->getPic()->getPicYuvRec()->getStride();Pel*    piSrc         = pcRecoYuv->getLumaAddr( uiPartOffset );UInt    uiSrcStride   = pcRecoYuv->getStride();for( UInt uiY = 0; uiY < uiCompHeight; uiY++, piSrc += uiSrcStride, piDes += uiDesStride ){for( UInt uiX = 0; uiX < uiCompWidth; uiX++ ){piDes[ uiX ] = piSrc[ uiX ];}}if( !bLumaOnly && !bSkipChroma ){if( !bChromaSame ){uiCompWidth   >>= 1;uiCompHeight  >>= 1;}piDes         = pcCU->getPic()->getPicYuvRec()->getCbAddr( pcCU->getAddr(), uiZOrder );uiDesStride   = pcCU->getPic()->getPicYuvRec()->getCStride();piSrc         = pcRecoYuv->getCbAddr( uiPartOffset );uiSrcStride   = pcRecoYuv->getCStride();for( UInt uiY = 0; uiY < uiCompHeight; uiY++, piSrc += uiSrcStride, piDes += uiDesStride ){for( UInt uiX = 0; uiX < uiCompWidth; uiX++ ){piDes[ uiX ] = piSrc[ uiX ];}}piDes         = pcCU->getPic()->getPicYuvRec()->getCrAddr( pcCU->getAddr(), uiZOrder );piSrc         = pcRecoYuv->getCrAddr( uiPartOffset );for( UInt uiY = 0; uiY < uiCompHeight; uiY++, piSrc += uiSrcStride, piDes += uiDesStride ){for( UInt uiX = 0; uiX < uiCompWidth; uiX++ ){piDes[ uiX ] = piSrc[ uiX ];}}}}//=== update PU data ====pcCU->setLumaIntraDirSubParts     ( uiBestPUMode, uiPartOffset, uiDepth + uiInitTrDepth );pcCU->copyToPic                   ( uiDepth, uiPU, uiInitTrDepth );} // PU loop// 设置一些Cbf、重置熵编码上下文之类的操作// 删除无关代码*****}


1 0