本文主要把实现亮度分量帧内预测的主函数的大体框架通过代码注释的方式介绍一下。
Void TEncSearch::estIntraPredQT( TComDataCU* pcCU, TComYuv* pcOrgYuv, TComYuv* pcPredYuv, TComYuv* pcResiYuv, TComYuv* pcRecoYuv, UInt& ruiDistC, Bool bLumaOnly ) { UInt uiDepth = pcCU->getDepth(0); //!< 当前CU的深度 UInt uiNumPU = pcCU->getNumPartInter(); //!< 当前CU的分割模式,(SIZE_2Nx2N:1, SIZE_2NxN:2, SIZE_Nx2N:2, SIZE_NxN:4 ... ) UInt uiInitTrDepth = pcCU->getPartitionSize(0) == SIZE_2Nx2N ? 0 : 1; //!< 用于计算变换的深度,实际深度为该值+uiDepth UInt uiWidth = pcCU->getWidth (0) >> uiInitTrDepth; //!< 当前PU的宽度,如果又分成4个子块,则宽度除以2 UInt uiHeight = pcCU->getHeight(0) >> uiInitTrDepth; //!< 当前PU的高度,如果又分成4个子块,则高度除以2 UInt uiQNumParts = pcCU->getTotalNumPart() >> 2; // 最小的分区是4x4大小的块,这里计算出以该4x4块为单位的分割数,这么做便于计算当前CU的Zorder坐标 UInt uiWidthBit = pcCU->getIntraSizeIdx(0); UInt uiOverallDistY = 0; UInt uiOverallDistC = 0; UInt CandNum; Double CandCostList[ FAST_UDI_MAX_RDMODE_NUM ]; //===== set QP and clear Cbf ===== if ( 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; //!< 用于记录当前PU的Zorder坐标 for( UInt uiPU = 0; uiPU < uiNumPU; uiPU++, uiPartOffset += uiQNumParts ) //!< 对当前CU中的每个PU进行遍历 { //===== init pattern for luma prediction ===== Bool bAboveAvail = false; Bool bLeftAvail = false; pcCU->getPattern()->initPattern ( pcCU, uiInitTrDepth, uiPartOffset ); // set parameters from CU data for accessing neighbouring pixels // set luma parameters from CU data for accessing ADI data //!< 主要获取当前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) ===== Int numModesAvailable = 35; //total number of Intra modes Pel* piOrg = pcOrgYuv ->getLumaAddr( uiPU, uiWidth ); Pel* piPred = pcPredYuv->getLumaAddr( uiPU, uiWidth ); UInt uiStride = pcPredYuv->getStride(); UInt uiRdModeList[FAST_UDI_MAX_RDMODE_NUM]; Int numModesForFullRD = g_aucIntraModeNumFast[ uiWidthBit ]; //!< MPM数目 //!< g_aucIntraModeNumFast[] = {3, 8, 8, 3, 3, 3, 3}; 2x2, 4x4, 8x8, 16x16, 32x32, 64x64, 128x128 Bool doFastSearch = (numModesForFullRD != numModesAvailable); //!< 此处doFastSearch恒为真 if (doFastSearch) { assert(numModesForFullRD < numModesAvailable); for( Int i=0; i < numModesForFullRD; i++ ) { CandCostList[ i ] = MAX_DOUBLE; } CandNum = 0; for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ ) //!< 遍历35种帧内预测模式 { UInt uiMode = modeIdx; //! 调用亮度帧内预测函数 predIntraLumaAng( pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, pcCU, bAboveAvail, bLeftAvail ); // use hadamard transform here UInt uiSad = m_pcRdCost->calcHAD( 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_MPM // UDI---Unified Directional Intra Int uiPreds[3] = {-1, -1, -1}; Int iMode = -1; //!< 如果三个MPMs的前两个相同,则iMode=1,否则iMode=2 Int numCand = pcCU->getIntraDirLumaPredictor( uiPartOffset, uiPreds, &iMode ); //!< 获取亮度帧内预测模式的三个MPMs if( iMode >= 0 ) //!< iMode = 1 or 2,因此,numCand会被重新赋值为iMode { numCand = iMode; } for( Int j=0; j < numCand; j++) { Bool mostProbableModeIncluded = false; Int mostProbableMode = uiPreds[j]; //!< 取出MPM for( Int i=0; i < numModesForFullRD; i++) { mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]); //!< 检查MPMs是否被uiRdModeList所包含 } if (!mostProbableModeIncluded) //!< 如果没被包含,则将该MPM包含到uiRdModeList里 { uiRdModeList[numModesForFullRD++] = mostProbableMode; } } #endif // FAST_UDI_USE_MPM } //!< if (doFastSearch) else { for( Int i=0; i < numModesForFullRD; i++) { uiRdModeList[i] = i; } } //===== check modes (using r-d costs) ===== //! 帧内预测模式最佳值的确定主要有以下几个步骤:1. 对numModesForFullRD种预测模式进行遍历,即对每种模式计算出 //! 对应的RD costs,但该步骤中,并不会把一个CU的所有分割都算一遍,而仅仅对于至多深度为1的分割进行遍历,这么做 //! 大大减少了运算量,提高速度;2. 在第1个步骤中,会粗略得到最佳预测模式(在HM9.0中会得到包括次优解在内的两个 //! 预测模式),存储下来,以供第3步使用;3. 在第2步的基础上,对最佳(及次优)预测模式的所有分割模式遍历一遍, //! 得到最终的最佳结果 #if HHI_RQT_INTRA_SPEEDUP_MOD UInt uiSecondBestMode = MAX_UINT; Double dSecondBestPUCost = MAX_DOUBLE; #endif UInt uiBestPUMode = 0; //!< 存放最佳预测模式 UInt uiBestPUDistY = 0; //!< 存放最佳预测模式对应的亮度失真值 UInt uiBestPUDistC = 0; //!< 存放最佳预测模式对应的色度失真值 Double dBestPUCost = MAX_DOUBLE; //!< 存放最佳预测模式对应的总代价 for( UInt uiMode = 0; uiMode < numModesForFullRD; uiMode++ ) //!< 遍历存储在uiRdModeList里的模式 { // set luma prediction mode UInt uiOrgMode = uiRdModeList[uiMode]; pcCU->setLumaIntraDirSubParts ( uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth ); // set context models if( m_bUseSBACRD ) { m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] ); } // determine residual for partition UInt uiPUDistY = 0; //!< 存放当前预测模式对应的亮度失真值 UInt uiPUDistC = 0; //!< 存放当前预测模式对应的色度失真值 Double dPUCost = 0.0; //!< 存放当前预测模式对应的代价 #if HHI_RQT_INTRA_SPEEDUP //! 注意这个函数倒数第二个参数,此时为true xRecurIntraCodingQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, true, dPUCost ); #else xRecurIntraCodingQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, dPUCost ); #endif // check r-d cost if( dPUCost < dBestPUCost ) //!< 更新最佳预测模式相关参数 { #if HHI_RQT_INTRA_SPEEDUP_MOD uiSecondBestMode = uiBestPUMode; dSecondBestPUCost = dBestPUCost; #endif 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 ) ); } #if HHI_RQT_INTRA_SPEEDUP_MOD else if( dPUCost < dSecondBestPUCost ) { uiSecondBestMode = uiOrgMode; dSecondBestPUCost = dPUCost; } #endif } // Mode loop #if HHI_RQT_INTRA_SPEEDUP #if HHI_RQT_INTRA_SPEEDUP_MOD for( UInt ui =0; ui < 2; ++ui ) #endif { #if HHI_RQT_INTRA_SPEEDUP_MOD UInt uiOrgMode = ui ? uiSecondBestMode : uiBestPUMode; if( uiOrgMode == MAX_UINT ) { break; } #else UInt uiOrgMode = uiBestPUMode; //!< 设置模式为最佳预测模式 #endif pcCU->setLumaIntraDirSubParts ( uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth ); // set context models if( m_bUseSBACRD ) { m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] ); } // determine residual for partition UInt uiPUDistY = 0; UInt uiPUDistC = 0; Double dPUCost = 0.0; //! 注意该函数倒数第二个参数,此时为false xRecurIntraCodingQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, false, dPUCost ); // check r-d cost 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 if( uiNumPU > 1 ) { // set Cbf for all blocks UInt uiCombCbfY = 0; UInt uiCombCbfU = 0; UInt uiCombCbfV = 0; UInt uiPartIdx = 0; for( UInt uiPart = 0; uiPart < 4; uiPart++, uiPartIdx += uiQNumParts ) { uiCombCbfY |= pcCU->getCbf( uiPartIdx, TEXT_LUMA, 1 ); uiCombCbfU |= pcCU->getCbf( uiPartIdx, TEXT_CHROMA_U, 1 ); uiCombCbfV |= pcCU->getCbf( uiPartIdx, TEXT_CHROMA_V, 1 ); } for( UInt uiOffs = 0; uiOffs < 4 * uiQNumParts; uiOffs++ ) { pcCU->getCbf( TEXT_LUMA )[ uiOffs ] |= uiCombCbfY; pcCU->getCbf( TEXT_CHROMA_U )[ uiOffs ] |= uiCombCbfU; pcCU->getCbf( TEXT_CHROMA_V )[ uiOffs ] |= uiCombCbfV; } } //===== reset context models ===== if(m_bUseSBACRD) { m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]); } //===== set distortion (rate and r-d costs are determined later) ===== ruiDistC = uiOverallDistC; pcCU->getTotalDistortion() = uiOverallDistY + uiOverallDistC; }
(转载请注明出处。)