关于帧内亮度预测的过程在之前 H.266/VVC代码学习1:帧内预测框架 中已简要讲述.
主要分为以下几个重要步骤:
1.相邻参考像素的获取及滤波;H.266/VVC代码学习22:帧内预测的初始化(initIntraPatternChType)
2.遍历H.265中35种预测模式,选出种子模式;(预测过程:H.266/VVC代码学习20:角度预测入口)
3.更新种子模式;
4.增加MPM操作;
5.进行变换量化,求失真等步骤:H.266/VVC代码学习8:xRecurIntraLumaCodingQT函数;
6.计算RD代价,更新代价值。
最新的代码增加了ISP技术,也就是对块的进一步划分技术。以下是VTM4.0帧内亮度预测代码:
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, const double bestCostSoFar )
#else
void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner )
#endif
{
CodingStructure &cs = *cu.cs;
const SPS &sps = *cs.sps;
const uint32_t uiWidthBit = g_aucLog2[partitioner.currArea().lwidth() ];
const uint32_t uiHeightBit = g_aucLog2[partitioner.currArea().lheight()];
// 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);
//===== loop over partitions =====
const TempCtx ctxStart ( m_CtxCache, m_CABACEstimator->getCtx() );
const TempCtx ctxStartIntraMode(m_CtxCache, SubCtx(Ctx::IntraLumaMpmFlag, m_CABACEstimator->getCtx()));
const TempCtx ctxStartMHIntraMode ( m_CtxCache, SubCtx( Ctx::MHIntraPredMode, m_CABACEstimator->getCtx() ) );
const TempCtx ctxStartMrlIdx ( m_CtxCache, SubCtx( Ctx::MultiRefLineIdx, m_CABACEstimator->getCtx() ) );
CHECK( !cu.firstPU, "CU has no PUs" );
const bool keepResi = cs.pps->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() || KEEP_PRED_AND_RESI_SIGNALS;
uint32_t extraModes = 0; // add two extra modes, which would be used after uiMode <= DC_IDX is removed for cu.nsstIdx == 3
#if !JVET_M0464_UNI_MTS
const int width = partitioner.currArea().lwidth();
const int height = partitioner.currArea().lheight();
// 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
uint8_t emtUsageFlag = 0;
const int maxSizeEMT = EMT_INTRA_MAX_CU_WITH_QTBT;
if( width <= maxSizeEMT && height <= maxSizeEMT && sps.getUseIntraEMT() )
{
emtUsageFlag = cu.emtFlag == 1 ? 2 : 1;
}
bool isAllIntra = m_pcEncCfg->getIntraPeriod() == 1;
if( width * height < 64 && !isAllIntra )
{
emtUsageFlag = 0; //this forces the recalculation of the candidates list. Why is this necessary? (to be checked)
}
#endif
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
#if JVET_M0464_UNI_MTS
const int width = partitioner.currArea().lwidth();
const int height = partitioner.currArea().lheight();
int nOptionsForISP = NUM_INTRA_SUBPARTITIONS_MODES;//默认为3,后面如果没划分改为1
#else
int nOptionsForISP = cu.emtFlag == 0 ? NUM_INTRA_SUBPARTITIONS_MODES : 1;
#endif
double bestCurrentCost = bestCostSoFar;//至今最好的代价
int ispOptions[NUM_INTRA_SUBPARTITIONS_MODES] = { 0 };//3个值,使用何种ISP
if( nOptionsForISP > 1 )
{
auto splitsThatCanBeUsedForISP = CU::canUseISPSplit( width, height, cu.cs->sps->getMaxTrSize() );//ISP的类型,0是4*4不划分,1是4*8或8*4水平划,2是垂直划,4是划成四份
if( splitsThatCanBeUsedForISP == CAN_USE_VER_AND_HORL_SPLITS )//如果为4,即分成四份
{
const CodingUnit* cuLeft = cu.ispMode != NOT_INTRA_SUBPARTITIONS ? cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( -1, 0 ), partitioner.chType ) : nullptr;
const CodingUnit* cuAbove = cu.ispMode != NOT_INTRA_SUBPARTITIONS ? cs.getCU( cs.area.blocks[partitioner.chType].pos().offset( 0, -1 ), partitioner.chType ) : nullptr;
bool ispHorIsFirstTest = CU::firstTestISPHorSplit( width, height, COMPONENT_Y, cuLeft, cuAbove );
if( ispHorIsFirstTest )
{
ispOptions[1] = HOR_INTRA_SUBPARTITIONS;
ispOptions[2] = VER_INTRA_SUBPARTITIONS;
}
else
{
ispOptions[1] = VER_INTRA_SUBPARTITIONS;
ispOptions[2] = HOR_INTRA_SUBPARTITIONS;
}
}
else if( splitsThatCanBeUsedForISP == HOR_INTRA_SUBPARTITIONS )//如果为1,即水平划分
{
nOptionsForISP = 2;
ispOptions[1] = HOR_INTRA_SUBPARTITIONS;
}
else if( splitsThatCanBeUsedForISP == VER_INTRA_SUBPARTITIONS )//如果为2,即垂直划分
{
nOptionsForISP = 2;
ispOptions[1] = VER_INTRA_SUBPARTITIONS;
}
else//如果为3,即不进行划分
{
nOptionsForISP = 1;
}
}
if( nOptionsForISP > 1 )//如果进行ISP划分了
{
//variables for the full RD list without MRL modes 没有MRL模式的完整RD列表的变量
m_rdModeListWithoutMrl .clear();
m_rdModeListWithoutMrlHor .clear();
m_rdModeListWithoutMrlVer .clear();
//variables with data from regular intra used to skip ISP splits 具有来自常规内部的数据的变量用于跳过ISP分裂
m_intraModeDiagRatio .clear();
m_intraModeHorVerRatio .clear();
m_intraModeTestedNormalIntra.clear();
}
#endif
static_vector<uint32_t, FAST_UDI_MAX_RDMODE_NUM> uiHadModeList;
static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandCostList;
static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandHadList;
static_vector<int, FAST_UDI_MAX_RDMODE_NUM> extendRefList;
static_vector<int, FAST_UDI_MAX_RDMODE_NUM>* nullList = NULL;
auto &pu = *cu.firstPU;
#if !JVET_M0464_UNI_MTS
int puIndex = 0;
#endif
{
CandHadList.clear();
CandCostList.clear();
uiHadModeList.clear();
extendRefList.clear();
CHECK(pu.cu != &cu, "PU is not contained in the CU");
//===== determine set of modes to be tested (using prediction signal only) =====
int numModesAvailable = NUM_LUMA_MODE; // total number of Intra modes
static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > uiRdModeList;
int numModesForFullRD = 3;
numModesForFullRD = g_aucIntraModeNumFast_UseMPM_2D[uiWidthBit - MIN_CU_LOG2][uiHeightBit - MIN_CU_LOG2];
#if INTRA_FULL_SEARCH
numModesForFullRD = numModesAvailable;
#endif
#if !JVET_M0464_UNI_MTS
if( emtUsageFlag != 2 )
#endif
{
// this should always be true
CHECK( !pu.Y().valid(), "PU is not valid" );
#if ENABLE_JVET_L0283_MRL//多参考行
bool isFirstLineOfCtu = (((pu.block(COMPONENT_Y).y)&((pu.cs->sps)->getMaxCUWidth() - 1)) == 0);
int numOfPassesExtendRef = (isFirstLineOfCtu ? 1 : MRL_NUM_REF_LINES);//多参考行
#endif
pu.multiRefIdx = 0;
//===== init pattern for luma prediction 亮度模式预测的初始化 =====
initIntraPatternChType( cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, false, pu ) );//对参考像素进行滤波
if( numModesForFullRD != numModesAvailable )
{
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;
#if JVET_M0427_INLOOP_RESHAPER
if (cu.slice->getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag())
{
CompArea tmpArea(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size());
PelBuf tmpOrg = m_tmpStorageLCU.getBuf(tmpArea);
tmpOrg.copyFrom(piOrg);
tmpOrg.rspSignal(m_pcReshape->getFwdLUT());
m_pcRdCost->setDistParam(distParam, tmpOrg, piPred, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, bUseHadamard);
}
else
#endif
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 ) );
/**********************************第一轮SATD的选择,对原始的35种模式进行简单的SATD的计算*********************************/
{
for( int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ )
{
uint32_t uiMode = modeIdx;
Distortion uiSad = 0;
// Skip checking extended Angular modes in the first round of SATD
if( uiMode > DC_IDX && ( uiMode & 1 ) )//在第一轮SATD中跳过266新加的32种角度模式
{
continue;
}
bSatdChecked[uiMode] = true;
pu.intraDir[0] = modeIdx;
if( useDPCMForFirstPassIntraEstimation( pu, uiMode ) )//DPCM模式??
{
encPredIntraDPCM( COMPONENT_Y, piOrg, piPred, uiMode );
}
else//角度模式
{
predIntraAng( COMPONENT_Y, piPred, pu, IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, true, pu ) );
}
// use Hadamard transform here
uiSad += distParam.distFunc(distParam);//在这里使用Hadamard变换
// NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
m_CABACEstimator->getCtx() = SubCtx( Ctx::MHIntraPredMode, ctxStartMHIntraMode );
m_CABACEstimator->getCtx() = SubCtx( Ctx::MultiRefLineIdx, ctxStartMrlIdx );
uint64_t 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 );
updateCandList( uiMode, cost, uiRdModeList, CandCostList
, extendRefList, 0
, numModesForFullRD + extraModes );
updateCandList(uiMode, (double) uiSad, uiHadModeList, CandHadList
, *nullList, -1
, 3 + extraModes);
}
} // NSSTFlag
// forget the extra modes
uiRdModeList.resize( numModesForFullRD );
CandCostList.resize(numModesForFullRD);
extendRefList.resize(numModesForFullRD);
static_vector<unsigned, FAST_UDI_MAX_RDMODE_NUM> parentCandList(FAST_UDI_MAX_RDMODE_NUM);
std::copy_n(uiRdModeList.begin(), numModesForFullRD, parentCandList.begin());
/******************第二轮SATD选择,将numModesForFullRD种模式在2-66的相邻两个拿出来一起比较SATD**********************/
// Second round of SATD for extended Angular modes
for (int modeIdx = 0; modeIdx < numModesForFullRD; modeIdx++)
{
unsigned parentMode = parentCandList[modeIdx];
if (parentMode > (DC_IDX + 1) && parentMode < (NUM_LUMA_MODE - 1))
{
for (int subModeIdx = -1; subModeIdx <= 1; subModeIdx += 2)
{
unsigned mode = parentMode + subModeIdx;
if (!bSatdChecked[mode])
{
pu.intraDir[0] = mode;
if (useDPCMForFirstPassIntraEstimation(pu, mode))
{
encPredIntraDPCM(COMPONENT_Y, piOrg, piPred, mode);
}
else
{
predIntraAng(COMPONENT_Y, piPred, pu,
IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Y, pu, true, pu));
}
// use Hadamard transform here
Distortion sad = distParam.distFunc(distParam);
// NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
m_CABACEstimator->getCtx() = SubCtx( Ctx::MHIntraPredMode, ctxStartMHIntraMode );
m_CABACEstimator->getCtx() = SubCtx( Ctx::MultiRefLineIdx, ctxStartMrlIdx );
uint64_t fracModeBits = xFracModeBitsIntra(pu, mode, CHANNEL_TYPE_LUMA);
double cost = (double) sad + (double) fracModeBits * sqrtLambdaForFirstPass;
updateCandList(mode, cost, uiRdModeList, CandCostList
, extendRefList, 0
, numModesForFullRD);
updateCandList(mode, (double)sad, uiHadModeList, CandHadList
, *nullList, -1
, 3);
bSatdChecked[mode] = true;
}
}
}
}
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
if( nOptionsForISP > 1 )//如果ISP划分了
{//我们保存列表没有mrl模式只保留Hadamard选择的模式(没有mpms)
//we save the list with no mrl modes to keep only the Hadamard selected modes (no mpms)
m_rdModeListWithoutMrl.resize( numModesForFullRD );
std::copy_n( uiRdModeList.begin(), numModesForFullRD, m_rdModeListWithoutMrl.begin() );
}
#endif
/********************************** 第三轮:进行MPMS的选择并更新列表 ************************************/
#if ENABLE_JVET_L0283_MRL
pu.multiRefIdx = 1;
const int numMPMs = NUM_MOST_PROBABLE_MODES;
unsigned multiRefMPM [numMPMs];
PU::getIntraMPMs(pu, multiRefMPM);//获取6个MPM模式
for (int mRefNum = 1; mRefNum < numOfPassesExtendRef; mRefNum++)//遍历选择第几行。(MPM的使用和参考行的数量有关)
{
int multiRefIdx = MULTI_REF_LINE_IDX[mRefNum];
pu.multiRefIdx = multiRefIdx;
{
initIntraPatternChType(cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Y, pu, false, pu));
}
for (int x = 0; x < numMPMs; x++)//遍历选择第几个模式(6种模式的哪一种)
{
uint32_t mode = multiRefMPM[x];
{
pu.intraDir[0] = mode;
if (useDPCMForFirstPassIntraEstimation(pu, mode))
{
encPredIntraDPCM(COMPONENT_Y, piOrg, piPred, mode);
}
else
{
predIntraAng(COMPONENT_Y, piPred, pu, IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Y, pu, true, pu));
}
// use Hadamard transform here
Distortion sad = distParam.distFunc(distParam);
// NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
m_CABACEstimator->getCtx() = SubCtx( Ctx::MHIntraPredMode, ctxStartMHIntraMode );
m_CABACEstimator->getCtx() = SubCtx( Ctx::MultiRefLineIdx, ctxStartMrlIdx );
uint64_t fracModeBits = xFracModeBitsIntra(pu, mode, CHANNEL_TYPE_LUMA);
double cost = (double)sad + (double)fracModeBits * sqrtLambdaForFirstPass;
updateCandList(mode, cost, uiRdModeList, CandCostList, extendRefList, multiRefIdx, numModesForFullRD);//用MPM的模式更新RD列表
}
}
}
#endif
CandCostList.resize(numModesForFullRD);
extendRefList.resize(numModesForFullRD);
if( m_pcEncCfg->getFastUDIUseMPMEnabled() )
{
const int numMPMs = NUM_MOST_PROBABLE_MODES;
unsigned uiPreds[numMPMs];
pu.multiRefIdx = 0;
const int numCand = PU::getIntraMPMs( pu, uiPreds );//使用MPM的数量
for( int j = 0; j < numCand; j++ )
{
bool mostProbableModeIncluded = false;
int mostProbableMode = uiPreds[j];
for( int i = 0; i < numModesForFullRD; i++ )//看MPM得到的模式,与之前列表中的numModesForFullRD个模式是否有一样的
{
mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i] && extendRefList[i] == 0);
}
if( !mostProbableModeIncluded )//如果都不一样
{
extendRefList.push_back(0);//额外的模式(MPM得到的模式)增加一个
numModesForFullRD++;//要检测的列表增加一个
uiRdModeList.push_back( mostProbableMode );//把这个MPM得到的放在后面
}
}
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
if( nOptionsForISP > 1 )
{//我们将ISP的MPM添加到列表中而不使用mrl模式
//we add the ISP MPMs to the list without mrl modes
m_rdModeListWithoutMrlHor = m_rdModeListWithoutMrl;
m_rdModeListWithoutMrlVer = m_rdModeListWithoutMrl;
static_vector<uint32_t, FAST_UDI_MAX_RDMODE_NUM>* listPointer;
for( int k = 1; k < nOptionsForISP; k++ )//如果是水平或垂直划分,对每一块同上操作
{
cu.ispMode = ispOptions[k];
listPointer = &( cu.ispMode == HOR_INTRA_SUBPARTITIONS ? m_rdModeListWithoutMrlHor : m_rdModeListWithoutMrlVer );
const int numCandISP = PU::getIntraMPMs( pu, uiPreds );
for( int j = 0; j < numCandISP; j++ )
{
bool mostProbableModeIncluded = false;
int mostProbableMode = uiPreds[j];
for( int i = 0; i < listPointer->size(); i++ )
{
mostProbableModeIncluded |= ( mostProbableMode == listPointer->at( i ) );
}
if( !mostProbableModeIncluded )
{
listPointer->push_back( mostProbableMode );
}
}
}
cu.ispMode = NOT_INTRA_SUBPARTITIONS;//停止划分
}
#endif
}
}
else
{
for( int i = 0; i < numModesForFullRD; i++ )
{
uiRdModeList.push_back( i );
}
}
#if !JVET_M0464_UNI_MTS
if( emtUsageFlag == 1 )
{
// Store the modes to be checked with RD
m_savedNumRdModes[puIndex] = numModesForFullRD;
std::copy_n( uiRdModeList.begin(), numModesForFullRD, m_savedRdModeList[puIndex] );
std::copy_n(extendRefList.begin(), numModesForFullRD, m_savedExtendRefList[puIndex]);
}
#endif
}
#if !JVET_M0464_UNI_MTS
else //emtUsage = 2 (here we potentially reduce the number of modes that will be full-RD checked)
{
if( isAllIntra && m_pcEncCfg->getFastIntraEMT() )
{
double thresholdSkipMode = 1.0 + 1.4 / sqrt( ( double ) ( width*height ) );
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] );
extendRefList.push_back(m_savedExtendRefList[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() );
CandCostList.resize(numModesForFullRD);
extendRefList.resize(numModesForFullRD);
std::copy_n(m_savedExtendRefList[puIndex], m_savedNumRdModes[puIndex], extendRefList.begin());
}
}
#endif
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
if( nOptionsForISP > 1 ) // we remove the non-MPMs from the ISP lists 我们从ISP列表中删除非MPM
{
static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > uiRdModeListCopyHor = m_rdModeListWithoutMrlHor;
m_rdModeListWithoutMrlHor.clear();
static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > uiRdModeListCopyVer = m_rdModeListWithoutMrlVer;
m_rdModeListWithoutMrlVer.clear();
static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > *listPointerCopy, *listPointer;
for( int ispOptionIdx = 1; ispOptionIdx < nOptionsForISP; ispOptionIdx++ )//对ISP水平划分或垂直划分操作
{
cu.ispMode = ispOptions[ispOptionIdx];
//we get the mpm cand list 获得MPM列表
const int numMPMs = NUM_MOST_PROBABLE_MODES;
unsigned uiPreds[numMPMs];
pu.multiRefIdx = 0;
PU::getIntraMPMs( pu, uiPreds );
//we copy only the ISP MPMs 复制ISP的MPM
listPointerCopy = &( cu.ispMode == HOR_INTRA_SUBPARTITIONS ? uiRdModeListCopyHor : uiRdModeListCopyVer );
listPointer = &( cu.ispMode == HOR_INTRA_SUBPARTITIONS ? m_rdModeListWithoutMrlHor : m_rdModeListWithoutMrlVer );
for( int k = 0; k < listPointerCopy->size(); k++ )
{
for( int q = 0; q < numMPMs; q++ )
{
if( listPointerCopy->at( k ) == uiPreds[q] )
{
listPointer->push_back( listPointerCopy->at( k ) );
break;
}
}
}
}
cu.ispMode = NOT_INTRA_SUBPARTITIONS;//停止划分
}
#endif
CHECK( numModesForFullRD != uiRdModeList.size(), "Inconsistent state!" );
// after this point, don't use numModesForFullRD
// PBINTRA fast
#if JVET_M0464_UNI_MTS
if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && uiRdModeList.size() < numModesAvailable )
#else
if( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && uiRdModeList.size() < numModesAvailable && emtUsageFlag != 2 )
#endif
{
if( CandHadList.size() < 3 || CandHadList[2] > cs.interHad * PBINTRA_RATIO )
{
uiRdModeList.resize( std::min<size_t>( uiRdModeList.size(), 2 ) );
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
extendRefList.resize( std::min<size_t>( extendRefList.size(), 2 ) );
if( nOptionsForISP > 1 )//如果划分了,对HOR和VER进行操作
{
m_rdModeListWithoutMrlHor.resize( std::min<size_t>( m_rdModeListWithoutMrlHor.size(), 2 ) );
m_rdModeListWithoutMrlVer.resize( std::min<size_t>( m_rdModeListWithoutMrlVer.size(), 2 ) );
}
#endif
}
if( CandHadList.size() < 2 || CandHadList[1] > cs.interHad * PBINTRA_RATIO )
{
uiRdModeList.resize( std::min<size_t>( uiRdModeList.size(), 1 ) );
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
extendRefList.resize( std::min<size_t>( extendRefList.size(), 1 ) );
if( nOptionsForISP > 1 )//如果划分了,对HOR和VER进行操作
{
m_rdModeListWithoutMrlHor.resize( std::min<size_t>( m_rdModeListWithoutMrlHor.size(), 1 ) );
m_rdModeListWithoutMrlVer.resize( std::min<size_t>( m_rdModeListWithoutMrlVer.size(), 1 ) );
}
#endif
}
if( CandHadList.size() < 1 || CandHadList[0] > cs.interHad * PBINTRA_RATIO )
{
cs.dist = std::numeric_limits<Distortion>::max();
cs.interHad = 0;
//===== reset context models =====
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
m_CABACEstimator->getCtx() = SubCtx( Ctx::MHIntraPredMode, ctxStartMHIntraMode );
m_CABACEstimator->getCtx() = SubCtx( Ctx::MultiRefLineIdx, ctxStartMrlIdx );
return;
}
}
/**********************************对比RDCOST选最好的模式(1),准备阶段********************************/
//===== check modes (using r-d costs) =====
uint32_t uiBestPUMode = 0;
int bestExtendRef = 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();
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
PartSplit intraSubPartitionsProcOrder = TU_NO_ISP;
int bestNormalIntraModeIndex = -1;
uint8_t bestIspOption = NOT_INTRA_SUBPARTITIONS;
TUIntraSubPartitioner subTuPartitioner( partitioner );
#if !JVET_M0464_UNI_MTS
if ( !cu.ispMode && !cu.emtFlag )
{
m_modeCtrl->setEmtFirstPassNoIspCost( MAX_DOUBLE );
}
#endif
for( uint32_t ispOptionIdx = 0; ispOptionIdx < nOptionsForISP; ispOptionIdx++ )
{
cu.ispMode = ispOptions[ispOptionIdx];
int numModesForFullRDispOption = cu.ispMode == NOT_INTRA_SUBPARTITIONS ? numModesForFullRD : cu.ispMode == HOR_INTRA_SUBPARTITIONS ? (int)m_rdModeListWithoutMrlHor.size() : (int)m_rdModeListWithoutMrlVer.size();
for( uint32_t uiMode = 0; uiMode < numModesForFullRDispOption; uiMode++ )
{
// set luma prediction mode
uint32_t uiOrgMode = cu.ispMode == NOT_INTRA_SUBPARTITIONS ? uiRdModeList[uiMode] : cu.ispMode == HOR_INTRA_SUBPARTITIONS ? m_rdModeListWithoutMrlHor[uiMode] : m_rdModeListWithoutMrlVer[uiMode];
pu.intraDir[0] = uiOrgMode;
int multiRefIdx = 0;
pu.multiRefIdx = multiRefIdx;
if( cu.ispMode )
{
intraSubPartitionsProcOrder = CU::getISPType( cu, COMPONENT_Y );
bool tuIsDividedInRows = CU::divideTuInRows( cu );
if( m_intraModeDiagRatio.at( bestNormalIntraModeIndex ) > 1.25 )
{
continue;
}
if( ( m_intraModeHorVerRatio.at( bestNormalIntraModeIndex ) > 1.25 && tuIsDividedInRows ) || ( m_intraModeHorVerRatio.at( bestNormalIntraModeIndex ) < 0.8 && !tuIsDividedInRows ) )
{
continue;
}
}
else
{
multiRefIdx = extendRefList[uiMode];
pu.multiRefIdx = multiRefIdx;
CHECK( pu.multiRefIdx && ( pu.intraDir[0] == DC_IDX || pu.intraDir[0] == PLANAR_IDX ), "ERL" );
}
#else
for (uint32_t uiMode = 0; uiMode < numModesForFullRD; uiMode++)
{
// set luma prediction mode
uint32_t uiOrgMode = uiRdModeList[uiMode];
pu.intraDir[0] = uiOrgMode;
int multiRefIdx = extendRefList[uiMode];
pu.multiRefIdx = multiRefIdx;
CHECK(pu.multiRefIdx && (pu.intraDir[0] == DC_IDX || pu.intraDir[0] == PLANAR_IDX), "ERL");
#endif
// set context models 设置上下文模型
m_CABACEstimator->getCtx() = ctxStart;
// determine residual for partition 确定划分的残差
cs.initSubStructure( *csTemp, partitioner.chType, cs.area, true );
/*******************************进行进一步的变换,熵编码,重建等过程*****************************/
#if JVET_M0102_INTRA_SUBPARTITIONS
if( cu.ispMode )
{
xRecurIntraCodingLumaQT( *csTemp, subTuPartitioner, bestCurrentCost, 0, intraSubPartitionsProcOrder );
}
else
{
xRecurIntraCodingLumaQT( *csTemp, partitioner, MAX_DOUBLE, -1 );
}
if( cu.ispMode && !csTemp->cus[0]->firstTU->cbf[COMPONENT_Y] )
{
csTemp->cost = MAX_DOUBLE;
}
#else
xRecurIntraCodingLumaQT( *csTemp, partitioner );
#endif
#if !JVET_M0464_UNI_MTS
#if JVET_M0102_INTRA_SUBPARTITIONS
if (emtUsageFlag == 1 && m_pcEncCfg->getFastIntraEMT() && !cu.ispMode)
#else
if( emtUsageFlag == 1 && m_pcEncCfg->getFastIntraEMT() )
#endif
{
m_modeCostStore[puIndex][uiMode] = csTemp->cost; //cs.cost;
}
#endif
DTRACE( g_trace_ctx, D_INTRA_COST, "IntraCost T %f (%d) \n", csTemp->cost, uiOrgMode );
/**********************************对比RDCOST选最好的模式(2),比较阶段********************************/
// check r-d cost
if( csTemp->cost < csBest->cost )
{
std::swap( csTemp, csBest );
uiBestPUMode = uiOrgMode;
bestExtendRef = multiRefIdx;
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
bestIspOption = cu.ispMode;
#endif
#if !JVET_M0464_UNI_MTS
#if JVET_M0102_INTRA_SUBPARTITIONS
if (emtUsageFlag == 1 && m_pcEncCfg->getFastIntraEMT() && !cu.ispMode)
#else
if( ( emtUsageFlag == 1 ) && m_pcEncCfg->getFastIntraEMT() )
#endif
{
m_bestModeCostStore[puIndex] = csBest->cost; //cs.cost;
}
#endif
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
if( csBest->cost < bestCurrentCost )
{
bestCurrentCost = csBest->cost;
}
if( !cu.ispMode )
{
bestNormalIntraModeIndex = uiMode;
}
#endif
}
csTemp->releaseIntermediateData();
} // Mode loop
#if JVET_M0102_INTRA_SUBPARTITIONS//ISP技术
#if !JVET_M0464_UNI_MTS
if (!cu.ispMode && !cu.emtFlag)
{
m_modeCtrl->setEmtFirstPassNoIspCost(csBest->cost);
}
#endif
}
cu.ispMode = bestIspOption;
#endif
#if JVET_M0427_INLOOP_RESHAPER
cs.useSubStructure(*csBest, partitioner.chType, pu.singleChan(CHANNEL_TYPE_LUMA), true, true, keepResi, keepResi);
#else
cs.useSubStructure( *csBest, partitioner.chType, pu.singleChan( CHANNEL_TYPE_LUMA ), KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, keepResi );
#endif
csBest->releaseIntermediateData();
//=== update PU data 更新PU数据 ====
pu.intraDir[0] = uiBestPUMode;
pu.multiRefIdx = bestExtendRef;
}
//===== reset context models 重建上下文模型=====
m_CABACEstimator->getCtx() = ctxStart;
}