亮度模式决策函数:estIntraPredLumaQT()
函数主要分为RMD(粗选择)过程,MPM获取过程及RDO过程。
1、RMD过程通过SATD进行候选模式的选择,主要预测函数为:predIntraAng()
函数详细分析见:
https://blog.csdn.net/yolo_life/article/details/81736464
2、获取6种MPM(HEVC为3种)的函数为getIntraMPMs()
3、对RMD过程后选出的模式加上MPM选出的模式进行预测及变换量化的RDO过程的主要函数为:xRecurIntraCodingLumaQT()
Void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner )
{
CodingStructure &cs = *cu.cs;
const SPS &sps = *cs.sps;
const UInt uiWidthBit = cs.pcv->rectCUs ? g_aucLog2[partitioner.currArea().lwidth() ] : CU::getIntraSizeIdx(cu);
const UInt uiHeightBit = g_aucLog2[partitioner.currArea().lheight()];
//预测过程中,HEVC_USE_PART_SIZE这个宏没开,CU没有划分为PU,与JEM中QTBT保持一致,下文将此宏下相关内容删除
#if JEM_TOOLS
auto slsCtrl = dynamic_cast<SaveLoadEncInfoCtrl*>( m_modeCtrl );
#endif
// Lambda calculation at equivalent Qp of 4 is recommended because at that Qp, the quantization divisor is 1.
const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda(cu.transQuantBypass) / double(1 << SCALE_BITS);
//******************* QTBT下没有循环进行下列步骤 **********************===== loop over partitions =====
const TempCtx ctxStart ( m_CtxCache, m_CABACEstimator->getCtx() );
const TempCtx ctxStartIntraMode ( m_CtxCache, SubCtx( Ctx::IPredMode[CHANNEL_TYPE_LUMA], m_CABACEstimator->getCtx() ) );
CHECK( !cu.firstPU, "CU has no PUs" );
const bool keepResi = cs.pps->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() || KEEP_PRED_AND_RESI_SIGNALS;
#if JEM_TOOLS
//下列变量:存储多NSST通道的快速帧内模式扫描结果 variables for saving fast intra modes scan results across multiple NSST passes
bool NSSTLoadFlag = sps.getSpsNext().getUseNSST() && cu.nsstIdx != 0;
bool NSSTSaveFlag = sps.getSpsNext().getUseNSST() && cu.nsstIdx == 0 && !cu.pdpc;
NSSTSaveFlag &= sps.getSpsNext().getUseIntraEMT() ? cu.emtFlag == 0 : true;
#endif
#if JEM_TOOLS
//NSST下模式多两个
UInt extraModes = sps.getSpsNext().getUseNSST() ? 2 : 0; // add two extra modes, which would be used after uiMode <= DC_IDX is removed for cu.nsstIdx == 3
#else
UInt extraModes = 0; // add two extra modes, which would be used after uiMode <= DC_IDX is removed for cu.nsstIdx == 3
#endif
#if JEM_TOOLS
const int width = partitioner.currArea().lwidth();
const int height = partitioner.currArea().lheight();
#endif
#if JEM_TOOLS
// Marking EMT usage for faster EMT
// 0: EMT is either not applicable for current CU (cuWidth > EMT_INTRA_MAX_CU or cuHeight > EMT_INTRA_MAX_CU), not active in the config file or the fast decision algorithm is not used in this case
// 1: EMT fast algorithm can be applied for the current CU, and the DCT2 is being checked
// 2: EMT is being checked for current CU. Stored results of DCT2 can be utilized for speedup
UChar emtUsageFlag = 0;
const int maxSizeEMT = cs.pcv->noRQT ? EMT_INTRA_MAX_CU_WITH_QTBT : EMT_INTRA_MAX_CU;
if( width <= maxSizeEMT && height <= maxSizeEMT && sps.getSpsNext().getUseIntraEMT() )
{
emtUsageFlag = cu.emtFlag == 1 ? 2 : 1;
}
Bool isAllIntra = m_pcEncCfg->getIntraPeriod() == 1;
if( cs.pcv->rectCUs )
{
if( ( width * height < 64 && !isAllIntra ) || ( slsCtrl && m_pcEncCfg->getUseSaveLoadEncInfo() && m_pcEncCfg->getIntraEMT() && LOAD_ENC_INFO == slsCtrl->getSaveLoadTag( cu ) /*&& m_modeCtrl->getSaveLoadEmtCuFlag(cu.cs->area)==0*/ ) )
{
emtUsageFlag = 0; //this forces the recalculation of the candidates list. Why is this necessary? (to be checked)
}
//not very sure about this command. It should be further checked when the EMT and the NSST are combined!!!
NSSTSaveFlag |= m_pcEncCfg->getNSST() && m_pcEncCfg->getIntraEMT() && slsCtrl && m_pcEncCfg->getUseSaveLoadEncInfo() && LOAD_ENC_INFO == slsCtrl->getSaveLoadTag( cu );
}
NSSTLoadFlag &= !(m_pcEncCfg->getNSST() && m_pcEncCfg->getUseSaveLoadEncInfo() && (LOAD_ENC_INFO == slsCtrl->getSaveLoadTag(cu)));
#endif
static_vector<UInt, FAST_UDI_MAX_RDMODE_NUM> uiHadModeList;
static_vector<Double, FAST_UDI_MAX_RDMODE_NUM> CandCostList;
static_vector<Double, FAST_UDI_MAX_RDMODE_NUM> CandHadList;
auto &pu = *cu.firstPU;
#if JEM_TOOLS
int puIndex = 0;
#endif
{
CandHadList.clear();
CandCostList.clear();
uiHadModeList.clear();
CHECK(pu.cu != &cu, "PU is not contained in the CU");
//******************************************** 粗选择RMD过程
//===== determine set of modes to be tested (using prediction signal only) =====
Int numModesAvailable = NUM_LUMA_MODE; //67, total number of Intra modes
static_vector< UInt, FAST_UDI_MAX_RDMODE_NUM > uiRdModeList;
Int numModesForFullRD = 3;
if( cs.pcv->rectCUs )
{
//RMD模式的个数与块大小一一对应,uiWidthBit 与uiHeightBit 为log2后值
numModesForFullRD = g_aucIntraModeNumFast_UseMPM_2D[uiWidthBit - MIN_CU_LOG2][uiHeightBit - MIN_CU_LOG2];
}
else
{
numModesForFullRD = m_pcEncCfg->getFastUDIUseMPMEnabled() ? g_aucIntraModeNumFast_UseMPM[uiWidthBit] : g_aucIntraModeNumFast_NotUseMPM[uiWidthBit];
#if JEM_TOOLS
if( cs.sps->getSpsNext().getUseIntra65Ang() )
{
numModesForFullRD -= 1;
}
#endif
}
#if INTRA_FULL_SEARCH
//删除非活动区域代码
#endif
#if JEM_TOOLS
//******************************************** emtUsageFlag != 2
if( emtUsageFlag != 2 )
#endif
{
// this should always be true
CHECK( !pu.Y().valid(), "PU is not valid" );
//===== init pattern for luma prediction =====
//初始过程,未滤波/滤波参考样本
initIntraPatternChType( cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, false, pu ) );
if( numModesForFullRD != numModesAvailable )//67
{
CHECK( numModesForFullRD >= numModesAvailable, "Too many modes for full RD search" );
const CompArea &area = pu.Y();
PelBuf piOrg = cs.getOrgBuf(area);
PelBuf piPred = cs.getPredBuf(area);
DistParam distParam;
const Bool bUseHadamard = cu.transQuantBypass == 0;//变换量化旁路
m_pcRdCost->setDistParam(distParam, piOrg, piPred, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, bUseHadamard);
distParam.applyWeight = false;
bool bSatdChecked[NUM_INTRA_MODE];
memset( bSatdChecked, 0, sizeof( bSatdChecked ) );
#if JEM_TOOLS
if( !NSSTLoadFlag )
#endif
{
//******************************************** HEVC模式的循环
for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ )
{
UInt uiMode = modeIdx;
Distortion uiSad = 0;
#if JEM_TOOLS
if( ( cu.partSize == SIZE_2Nx2N ) && cu.nsstIdx >= ( uiMode <= DC_IDX ? 3 : 4 ) )
{
continue;
}
#endif
//skip扩展的角度模式,即skip JEM 中的新模式 Skip checking extended Angular modes in the first round of SATD
if( uiMode > DC_IDX && ( uiMode & 1 ) )
{
continue;
}
bSatdChecked[uiMode] = true;
pu.intraDir[0] = modeIdx;
if( useDPCMForFirstPassIntraEstimation( pu, uiMode ) )
{
//水平或垂直预测采用DPCM
//水平:预测值的列被参考块覆盖,将参考块其他可获取的值再赋值给预测值
encPredIntraDPCM( COMPONENT_Y, piOrg, piPred, uiMode );
}
else
{
//进入67个模式的预测及参考像素的处理
predIntraAng( COMPONENT_Y, piPred, pu, IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, true, pu ) );
}
// use Hadamard transform here
uiSad += distParam.distFunc(distParam);
// NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
m_CABACEstimator->getCtx() = SubCtx( Ctx::IPredMode[CHANNEL_TYPE_LUMA], ctxStartIntraMode );
//被选模式与非被选模式编码
UInt64 fracModeBits = xFracModeBitsIntra(pu, uiMode, CHANNEL_TYPE_LUMA);
//总的代价
Double cost = ( Double ) uiSad + ( Double ) fracModeBits * sqrtLambdaForFirstPass;
DTRACE( g_trace_ctx, D_INTRA_COST, "IntraHAD: %u, %llu, %f (%d)\n", uiSad, fracModeBits, cost, uiMode );
//更新候选列表
//对numModesForFullRD + extraModes个根据cost进行排序,得到从小到大排序的CandCostList
updateCandList( uiMode, cost, uiRdModeList, CandCostList, numModesForFullRD + extraModes );
//对 3 + extraModes个根据uiSad进行排序,得到从小到大排序的CandHadList
updateCandList( uiMode, uiSad, uiHadModeList, CandHadList, 3 + extraModes );
}
#if JEM_TOOLS
if( NSSTSaveFlag )
{
// save found best modes
m_uiSavedNumRdModesNSST = numModesForFullRD;
m_uiSavedRdModeListNSST = uiRdModeList;
m_dSavedModeCostNSST = CandCostList;
// PBINTRA fast
m_uiSavedHadModeListNSST = uiHadModeList;
m_dSavedHadListNSST = CandHadList;
NSSTSaveFlag = false;
}
#endif
} // NSSTFlag
#if JEM_TOOLS
else
{
// restore saved modes
numModesForFullRD = m_uiSavedNumRdModesNSST;
uiRdModeList = m_uiSavedRdModeListNSST;
CandCostList = m_dSavedModeCostNSST;
// PBINTRA fast
uiHadModeList = m_uiSavedHadModeListNSST;
CandHadList = m_dSavedHadListNSST;
if( cu.nsstIdx == 3 && cu.partSize == SIZE_2Nx2N )
{
// remove uiMode <= DC_IDX
Int cnt = 0;
for( int i = 0; i < numModesForFullRD; i++ )
{
if( uiRdModeList[i] <= DC_IDX )
{
for( UInt j = i; j < numModesForFullRD + 1 - cnt; j++ )
{
uiRdModeList[j] = uiRdModeList[j + 1];
CandCostList[j] = CandCostList[j + 1];
}
cnt++;
i--;
}
}
if( m_pcEncCfg->getUsePbIntraFast() )
{
// PBINTRA fast
cnt = 0;
for( int i = 0; i < 3; i++ )
{
if( uiHadModeList[i] <= DC_IDX )
{
for( UInt j = i; j < 3 + 1 - cnt; j++ )
{
uiHadModeList[j] = uiHadModeList[j + 1];
CandHadList[j] = CandHadList [j + 1];
}
cnt++;
i--;
}
}
}
}
NSSTLoadFlag = false;
} // NSSTFlag
#endif
// forget the extra modes
uiRdModeList.resize( numModesForFullRD );
#if JEM_TOOLS
if( cs.sps->getSpsNext().getUseIntra65Ang() )
{
static_vector<UInt, FAST_UDI_MAX_RDMODE_NUM> uiParentCandList( FAST_UDI_MAX_RDMODE_NUM );
std::copy_n( uiRdModeList.begin(), numModesForFullRD, uiParentCandList.begin() );
//******************************************** JEM中扩展的角度模式循环,继续更新 Second round of SATD for extended Angular modes
for( Int modeIdx = 0; modeIdx < numModesForFullRD; modeIdx++ )
{
UInt uiParentMode = uiParentCandList[modeIdx];
if( uiParentMode > ( DC_IDX + 1 ) && uiParentMode < ( NUM_LUMA_MODE - 1 ) )
{
for( Int subModeIdx = -1; subModeIdx <= 1; subModeIdx += 2 )//隔着HEVC的预测方向即为JEM的
{
//与uiParentMode相邻的模式
UInt uiMode = uiParentMode + subModeIdx;
if( cu.partSize == SIZE_2Nx2N && cu.nsstIdx >= ( ( uiMode <= DC_IDX ) ? 3 : 4 ) )
{
continue;
}
//没算过SATD的模式satd计算
if( !bSatdChecked[uiMode] )
{
pu.intraDir[0] = uiMode;
if( useDPCMForFirstPassIntraEstimation( pu, uiMode ) )
{
encPredIntraDPCM( COMPONENT_Y, piOrg, piPred, uiMode );
}
else
{
predIntraAng( COMPONENT_Y, piPred, pu, IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, true, pu ) );
}
// use Hadamard transform here
Distortion uiSad = distParam.distFunc( distParam );
// NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
m_CABACEstimator->getCtx() = SubCtx( Ctx::IPredMode[CHANNEL_TYPE_LUMA], ctxStartIntraMode );
UInt64 fracModeBits = xFracModeBitsIntra( pu, uiMode, CHANNEL_TYPE_LUMA );
Double cost = ( Double ) uiSad + ( Double ) fracModeBits * sqrtLambdaForFirstPass;
updateCandList( uiMode, cost, uiRdModeList, CandCostList, numModesForFullRD );
updateCandList( uiMode, uiSad, uiHadModeList, CandHadList, 3 );
//为避免计算重复
bSatdChecked[uiMode] = true;
}
}
}
}
}
#endif
//******************************************** 获取MPM
if( m_pcEncCfg->getFastUDIUseMPMEnabled()
{
unsigned numMPMs = pu.cs->pcv->numMPMs;//6种
unsigned *uiPreds = ( unsigned* ) alloca( numMPMs * sizeof( unsigned ) );
//获取MPM的函数,左侧,上方,planar,DC,左下,右上,左上;不足6个,会加上派生的模式:派生模式通过对MPM列表中角度预测模式-1或+1;不足6个,再加入垂直,水平,模式2。上述过程在满足6个时即停止
const Int numCand = PU::getIntraMPMs( pu, uiPreds );
for( Int j = 0; j < numCand; j++ )
{
Bool mostProbableModeIncluded = false;
Int mostProbableMode = uiPreds[j];
#if JEM_TOOLS
if( cu.partSize == SIZE_2Nx2N && cu.nsstIdx >= ( mostProbableMode <= DC_IDX ? 3 : 4 ) )
{
continue;
}
#endif
for( Int i = 0; i < numModesForFullRD; i++ )
{
mostProbableModeIncluded |= ( mostProbableMode == uiRdModeList[i] );
}
//之前选的与MPM合并
if( !mostProbableModeIncluded )
{
numModesForFullRD++;
uiRdModeList.push_back( mostProbableMode );
}
}
}//getFastUDIUseMPMEnabled
//******************************************** 获取MPM end
}// if( numModesForFullRD != numModesAvailable)
else
{
for( Int i = 0; i < numModesForFullRD; i++ )
{
uiRdModeList.push_back( i );
}
}
#if JEM_TOOLS
//******************************************** emtUsageFlag == 1
if( emtUsageFlag == 1 )
{
// Store the modes to be checked with RD
m_savedNumRdModes[puIndex] = numModesForFullRD;
std::copy_n( uiRdModeList.begin(), numModesForFullRD, m_savedRdModeList[puIndex] );
}
#endif
}//if(emtUsageFlag != 2)
#if JEM_TOOLS
//******************************************** emtUsage = 2
else //emtUsage = 2 (here we potentially reduce the number of modes that will be full-RD checked)
{
if( isAllIntra && m_pcEncCfg->getFastIntraEMT() )
{
//确定AMT跳过步骤的门限
double thresholdSkipMode;
if( cs.pcv->noRQT )
{
thresholdSkipMode = 1.0 + 1.4 / sqrt( ( double ) ( width*height ) );
}
else
{
switch( width )
{
case 4: thresholdSkipMode = 1.47; break; // Skip checking 4x4 Intra modes using the R-D cost in the DCT2-pass
case 8: thresholdSkipMode = 1.28; break; // Skip checking 8x8 Intra modes using the R-D cost in the DCT2-pass
case 16: thresholdSkipMode = 1.12; break; // Skip checking 16x16 Intra modes using the R-D cost in the DCT2-pass
case 32: thresholdSkipMode = 1.06; break; // Skip checking 32x32 Intra modes using the R-D cost in the DCT2-pass
default: thresholdSkipMode = 1.06; break; // Skip checking 32x32 Intra modes using the R-D cost in the DCT2-pass
}
}
numModesForFullRD = 0;
// Skip checking the modes with much larger R-D cost than the best mode
for( Int i = 0; i < m_savedNumRdModes[puIndex]; i++ )
{
if( m_modeCostStore[puIndex][i] <= thresholdSkipMode * m_bestModeCostStore[puIndex] )
{
uiRdModeList.push_back( m_savedRdModeList[puIndex][i] );
numModesForFullRD++;
}
}
}
else //this is necessary because we skip the candidates list calculation, since it was already obtained for the DCT-II. Now we load it
{
// Restore the modes to be checked with RD
numModesForFullRD = m_savedNumRdModes[puIndex];
uiRdModeList.resize( numModesForFullRD );
std::copy_n( m_savedRdModeList[puIndex], m_savedNumRdModes[puIndex], uiRdModeList.begin() );
}
}
#endif
CHECK( numModesForFullRD != uiRdModeList.size(), "Inconsistent state!" );
// after this point, don't use numModesForFullRD
//******************************************** PBINTRA fast
#if JEM_TOOLS
if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && cu.partSize == SIZE_2Nx2N && uiRdModeList.size() < numModesAvailable && emtUsageFlag != 2 )
#else
if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && cu.partSize == SIZE_2Nx2N && uiRdModeList.size() < numModesAvailable )
#endif
{
if( CandHadList.size() < 3 || CandHadList[2] > cs.interHad * PBINTRA_RATIO )
{
uiRdModeList.resize( std::min<size_t>( uiRdModeList.size(), 2 ) );
}
if( CandHadList.size() < 2 || CandHadList[1] > cs.interHad * PBINTRA_RATIO )
{
uiRdModeList.resize( std::min<size_t>( uiRdModeList.size(), 1 ) );
}
if( CandHadList.size() < 1 || CandHadList[0] > cs.interHad * PBINTRA_RATIO )
{
cs.dist = MAX_UINT;
cs.interHad = 0;
//===== reset context models =====
m_CABACEstimator->getCtx() = SubCtx( Ctx::IPredMode [CHANNEL_TYPE_LUMA], ctxStartIntraMode );
return;
}
}
//******************************************** RDO过程 check modes (using r-d costs) =====
//ENABLE_RQT_INTRA_SPEEDUP_MOD默认false,将其下内容删除
UInt uiBestPUMode = 0;
CodingStructure *csTemp = m_pTempCS[gp_sizeIdxInfo->idxFrom( cu.lwidth() )][gp_sizeIdxInfo->idxFrom( cu.lheight() )];
CodingStructure *csBest = m_pBestCS[gp_sizeIdxInfo->idxFrom( cu.lwidth() )][gp_sizeIdxInfo->idxFrom( cu.lheight() )];
csTemp->slice = cs.slice;
csBest->slice = cs.slice;
csTemp->initStructData();
csBest->initStructData();
// just to be sure
numModesForFullRD = ( int ) uiRdModeList.size();
for (UInt uiMode = 0; uiMode < numModesForFullRD; uiMode++)
{
// set luma prediction mode
UInt uiOrgMode = uiRdModeList[uiMode];
pu.intraDir[0] = uiOrgMode;
// set context models
m_CABACEstimator->getCtx() = ctxStart;
// determine residual for partition
cs.initSubStructure( *csTemp, partitioner.chType, cs.area, true );
//******************************************** 根据给定模式下的划分,重建,完成变换量化及率失真计算
xRecurIntraCodingLumaQT( *csTemp, partitioner );
#if JEM_TOOLS
if( emtUsageFlag == 1 && m_pcEncCfg->getFastIntraEMT() )
{
m_modeCostStore[puIndex][uiMode] = csTemp->cost; //cs.cost;
}
#endif
DTRACE( g_trace_ctx, D_INTRA_COST, "IntraCost T %f (%d) \n", csTemp->cost, uiOrgMode );
// check r-d cost
if( csTemp->cost < csBest->cost )
{
std::swap( csTemp, csBest );
uiBestPUMode = uiOrgMode;
#if JEM_TOOLS
if( ( emtUsageFlag == 1 ) && m_pcEncCfg->getFastIntraEMT() )
{
m_bestModeCostStore[puIndex] = csBest->cost; //cs.cost;
}
#endif
}
csTemp->releaseIntermediateData();
} // Mode loop
#endif
cs.useSubStructure( *csBest, partitioner.chType, pu.singleChan( CHANNEL_TYPE_LUMA ), KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, keepResi );
csBest->releaseIntermediateData();
//**********************************************=== update PU data ====
pu.intraDir[0] = uiBestPUMode;
}
//**********************************************===== reset context models =====
m_CABACEstimator->getCtx() = ctxStart;
}
色度模式决策函数:estIntraPreChromaQT()
函数的主要过程是先确定6个CCLM模式,再确定5种普通预测模式,最后对11种预测模式进行遍历,选最优模式。
获取候选模式的函数为:getIntraChromaCandModes()
主要的预测函数为:predIntraChromaLM()
,该函数会调用两次,一次预测Cb,一次预测Cr
Void IntraSearch::estIntraPredChromaQT(CodingUnit &cu, Partitioner &partitioner)
{
const ChromaFormat format = cu.chromaFormat;
//HEVC_USE_PART_SIZE 默认false,删掉其下面相关内容
const UInt numberValidComponents = getNumberValidComponents(format);
CodingStructure &cs = *cu.cs;
const TempCtx ctxStart ( m_CtxCache, m_CABACEstimator->getCtx() );
cs.setDecomp( cs.area.Cb(), false );
auto &pu = *cu.firstPU;
{
UInt uiBestMode = 0;
Distortion uiBestDist = 0;
Double dBestCost = MAX_DOUBLE;
//**************************************----- init mode list ----
{
UInt uiMinMode = 0;
UInt uiMaxMode = NUM_CHROMA_MODE;
//**************************************----- check chroma modes -----
UInt chromaCandModes[ NUM_CHROMA_MODE ];
//11种模式,6CCLM+5种传统(DM+后续尝试):
//当前色度块对应亮度块的5个位置,DM,去重,若<5
//+左邻色度块的预测模式,若<5
//+上方相邻色度块的预测模式,若<5
//+左下色度块的预测模式,若<5
//+右上色度块的预测模式,若<5
//+左上色度块的预测模式,若<5
//+Planar,若<5
//+DC,若<5
//+衍生模式,若<5
//+垂直/水平/模式2,若<5
PU::getIntraChromaCandModes( pu, chromaCandModes );
// create a temporary CS
CodingStructure &saveCS = *m_pSaveCS[0];
saveCS.pcv = cs.pcv;
saveCS.picture = cs.picture;
saveCS.area.repositionTo( cs.area );
saveCS.clearTUs();
if( CS::isDualITree( cs ) )
{
#if ENABLE_BMS
if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
{
partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
do
{
cs.addTU( CS::getArea( cs, partitioner.currArea(), partitioner.chType ), partitioner.chType ).depth = partitioner.currTrDepth;
} while( partitioner.nextPart( cs ) );
partitioner.exitCurrSplit();
}
else
#endif
cs.addTU( CS::getArea( cs, partitioner.currArea(), partitioner.chType ), partitioner.chType );
}
std::vector<TransformUnit*> orgTUs;
//CU还划分TU?
// create a store for the TUs
for( const auto &ptu : cs.tus )
{
// for split TUs in HEVC, add the TUs without Chroma parts for correct setting of Cbfs
if( pu.contains( *ptu, CHANNEL_TYPE_CHROMA ) || ( !cs.pcv->noRQT && !ptu->Cb().valid() && !ptu->Cr().valid() ) )
{
saveCS.addTU( *ptu, partitioner.chType );
orgTUs.push_back( ptu );
}
}
#if JEM_TOOLS
UInt auiSATDModeList[LM_FILTER_NUM];
//LM滤波,4种
if( pu.cs->pcv->noRQT && pu.cs->sps->getSpsNext().getUseLMChroma() && PU::isMFLMEnabled(pu))
{
UInt auiSATDSortedcost[LM_FILTER_NUM];
DistParam distParam;
const Bool bUseHadamard = true;
Int iCurLMMFIdx = 0;
//获取重构亮度的下采样
xGetLumaRecPixels(pu, pu.Cb());
initIntraPatternChType( cu, pu.Cb() );
initIntraPatternChType( cu, pu.Cr() );
//SATD checking for LMMF candidates
for (UInt uiMode = LM_CHROMA_F1_IDX; uiMode < LM_CHROMA_F1_IDX + LM_FILTER_NUM; uiMode++)
{
UInt uiSad = 0;
CodingStructure& cs = *(pu.cs);
CompArea areaCb = pu.Cb();
PelBuf piOrgCb = cs.getOrgBuf(areaCb);
PelBuf piPredCb = cs.getPredBuf(areaCb);
m_pcRdCost->setDistParam(distParam, piOrgCb, piPredCb, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, bUseHadamard);
distParam.applyWeight = false;
//************************************** 预测Cb
predIntraChromaLM(COMPONENT_Cb, piPredCb, pu, areaCb, uiMode);//MMLM,LM过程
#if !ENABLE_BMS
//ENABLE_BMS默认true,删除非活动区域代码
#endif
uiSad += distParam.distFunc(distParam);
CompArea areaCr = pu.Cr();
PelBuf piOrgCr = cs.getOrgBuf(areaCr);
PelBuf piPredCr = cs.getPredBuf(areaCr);
m_pcRdCost->setDistParam(distParam, piOrgCr, piPredCr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, bUseHadamard);
//************************************** 预测Cr
predIntraChromaLM(COMPONENT_Cr, piPredCr, pu, areaCr, uiMode);
#if !ENABLE_BMS
PelBuf savePredCr(m_pLMMFPredSaved[(uiMode - LM_CHROMA_F1_IDX) * 2 + 1], areaCr.width, areaCr);
savePredCr.copyFrom(piPredCr);
#endif
uiSad += distParam.distFunc(distParam);
auiSATDSortedcost[iCurLMMFIdx] = uiSad;
auiSATDModeList[iCurLMMFIdx] = uiMode;
for (Int k = iCurLMMFIdx; k > 0 && auiSATDSortedcost[k] < auiSATDSortedcost[k - 1]; k--)
{
UInt tmp = auiSATDSortedcost[k];
auiSATDSortedcost[k] = auiSATDSortedcost[k - 1];
auiSATDSortedcost[k - 1] = tmp;
tmp = auiSATDModeList[k];
auiSATDModeList[k] = auiSATDModeList[k - 1];
auiSATDModeList[k - 1] = tmp;
}
iCurLMMFIdx++;
}
}
#endif
//************************************** save the dist
Distortion baseDist = cs.dist;
//对11种(6个CCLM+5)模式进行遍历,选min
for (UInt uiMode = uiMinMode; uiMode < uiMaxMode; uiMode++)
{
const int chromaIntraMode = chromaCandModes[uiMode];
#if JEM_TOOLS
if( PU::isLMCMode( chromaIntraMode ) && ! PU::isLMCModeEnabled( pu, chromaIntraMode ) )
{
continue;
}
#endif
#if JEM_TOOLS
if( CS::isDualITree( cs ) && cu.nsstIdx == 3 )
{
int intraMode = chromaIntraMode;
#if JEM_TOOLS
if( PU::isLMCMode( chromaIntraMode ) )
{
intraMode = PLANAR_IDX;
}
else
#endif
if( intraMode == DM_CHROMA_IDX )
{
const PredictionUnit* lumaPu = cs.picture->cs->getPU( partitioner.currArea().lumaPos(), CHANNEL_TYPE_LUMA );
intraMode = lumaPu->intraDir[0];
}
if( intraMode <= DC_IDX )
{
continue;
}
}
#endif
#if JEM_TOOLS
if( pu.cs->pcv->noRQT && pu.cs->sps->getSpsNext().isELMModeMFLM())
{
if( chromaIntraMode >= LM_CHROMA_F1_IDX && chromaIntraMode < LM_CHROMA_F1_IDX + LM_FILTER_NUM)
{
if (auiSATDModeList[0] != chromaIntraMode)
{
continue;
}
}
}
#endif
cs.setDecomp( pu.Cb(), false );
cs.dist = baseDist;
//**************************************----- restore context models -----
m_CABACEstimator->getCtx() = ctxStart;
//**************************************----- chroma coding -----
pu.intraDir[1] = chromaIntraMode;
xRecurIntraChromaCodingQT( cs, partitioner );
if (cs.pps->getUseTransformSkip())
{
m_CABACEstimator->getCtx() = ctxStart;
}
UInt64 fracBits = xGetIntraFracBitsQT( cs, partitioner, false, true );
Distortion uiDist = cs.dist;
Double dCost = m_pcRdCost->calcRdCost( fracBits, uiDist - baseDist );
//**************************************----- compare -----
if( dCost < dBestCost )
{
for( UInt i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ )
{
const CompArea &area = pu.blocks[i];
saveCS.getRecoBuf ( area ).copyFrom( cs.getRecoBuf ( area ) );
#if KEEP_PRED_AND_RESI_SIGNALS
//删除非活动区域代码
#endif
cs.picture->getRecoBuf( area ).copyFrom( cs.getRecoBuf( area ) );
for( UInt j = 0; j < saveCS.tus.size(); j++ )
{
saveCS.tus[j]->copyComponentFrom( *orgTUs[j], area.compID );
#if ENABLE_CHROMA_422
//删除非活动区域代码
#endif
}
}
dBestCost = dCost;
uiBestDist = uiDist;
uiBestMode = chromaIntraMode;
}
}
for( UInt i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ )
{
const CompArea &area = pu.blocks[i];
cs.getRecoBuf ( area ).copyFrom( saveCS.getRecoBuf( area ) );
#if KEEP_PRED_AND_RESI_SIGNALS
//删除非活动区域代码
#endif
cs.picture->getRecoBuf( area ).copyFrom( cs. getRecoBuf( area ) );
for( UInt j = 0; j < saveCS.tus.size(); j++ )
{
orgTUs[ j ]->copyComponentFrom( *saveCS.tus[ j ], area.compID );
#if ENABLE_CHROMA_422
//删除非活动区域代码
#endif
}
}
}
pu.intraDir[1] = uiBestMode;
cs.dist = uiBestDist;
}
//**************************************----- restore context models -----
m_CABACEstimator->getCtx() = ctxStart;
}