亮度模式決策函數: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;
}