前一章讲解了VTM帧内预测的主要大致框架,接下来主要学习VTM中相关技术的代码实现。首先,是亮度分量进行帧内预测的入口函数estIntraPredLumaQT()
,这个函数描述了亮度帧内预测的主要流程,包括参考样本生成
,多行预测
,RMD
, MPM
和最后Full RDO最优模式选择
、ISP
等。其主要流程见下图:
代码具体实现如下(VTM-4.0):
进行full RD 搜索的模式个数
const uint8_t g_aucIntraModeNumFast_UseMPM_2D[7 - MIN_CU_LOG2 + 1][7 - MIN_CU_LOG2 + 1] =
{
{3, 3, 3, 3, 2, 2}, // 4x4, 4x8, 4x16, 4x32, 4x64, 4x128,
{3, 3, 3, 3, 3, 2}, // 8x4, 8x8, 8x16, 8x32, 8x64, 8x128,
{3, 3, 3, 3, 3, 2}, // 16x4, 16x8, 16x16, 16x32, 16x64, 16x128,
{3, 3, 3, 3, 3, 2}, // 32x4, 32x8, 32x16, 32x32, 32x64, 32x128,
{2, 3, 3, 3, 3, 2}, // 64x4, 64x8, 64x16, 64x32, 64x64, 64x128,
{2, 2, 2, 2, 2, 3}, // 128x4, 128x8, 128x16, 128x32, 128x64, 128x128,
};
void IntraSearch::estIntraPredLumaQT( CodingUnit &cu, Partitioner &partitioner, const double bestCostSoFar )
{
CodingStructure &cs = *cu.cs;
const SPS &sps = *cs.sps;
const uint32_t uiWidthBit = g_aucLog2[partitioner.currArea().lwidth() ]; // 当前cu的宽度2对数,用于获取进行full RD的模式个数
const uint32_t uiHeightBit = g_aucLog2[partitioner.currArea().lheight()]; // 当前cu的高度2对数,用于获取进行full RD的模式个数
// Lambda calculation at equivalent Qp of 4 is recommended because at that Qp, the quantization divisor is 1.
// 计算lamda,用于帧内RMD中粗略Hadmard cost的计算
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" );
// 是否保存亮度预测残差,用于CCLM色度预测
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_M0102_INTRA_SUBPARTITIONS // ISP参数初始化,获取ISP可能的划分方向
const int width = partitioner.currArea().lwidth(); // CU宽度
const int height = partitioner.currArea().lheight(); // CU高度
int nOptionsForISP = NUM_INTRA_SUBPARTITIONS_MODES; // ISP可选的模式,3种
///< NOT_INTRA_SUBPARTITIONS不进行,HOR_INTRA_SUBPARTITIONS水平ISP,VER_INTRA_SUBPARTITIONS垂直ISP
double bestCurrentCost = bestCostSoFar; // 当前最优cost,用于ISP的提前终止
/// < 根据CU宽度和高度,获取当前CU可能的ISP预测模式(notISP、HorISP、VerISP)
int ispOptions[NUM_INTRA_SUBPARTITIONS_MODES] = { 0 };
if( nOptionsForISP > 1 )
{
auto splitsThatCanBeUsedForISP = CU::canUseISPSplit( width, height, cu.cs->sps->getMaxTrSize() ); // 判断可进行ISP模式类型
if( splitsThatCanBeUsedForISP == CAN_USE_VER_AND_HORL_SPLITS ) // 水平垂直都可以
{
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 ) // 只能进行水平ISP,ISP循环次数变为2(not、 hor)
{
nOptionsForISP = 2;
ispOptions[1] = HOR_INTRA_SUBPARTITIONS;
}
else if( splitsThatCanBeUsedForISP == VER_INTRA_SUBPARTITIONS ) // 只能进行垂直ISP,ISP循环次数变为2(not、 ver)
{
nOptionsForISP = 2;
ispOptions[1] = VER_INTRA_SUBPARTITIONS;
}
else // 不能进行ISP,ISP循环次数变为1(not)
{
nOptionsForISP = 1;
}
}
if( nOptionsForISP > 1 ) // 如果进行ISP,初始化相关参数列表
{
//variables for the full RD list without MRL modes 帧内ISP预测不支持多参考行
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; // hadmard模式列表,用于MPM
static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandCostList; // hadmard cost列表,用于更新选择最优RMD模式
static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandHadList; // hadmard distortion列表,用于帧间模式的intra快速终止判定
static_vector<int, FAST_UDI_MAX_RDMODE_NUM> extendRefList; // 参考行索引列表
static_vector<int, FAST_UDI_MAX_RDMODE_NUM>* nullList = NULL;
auto &pu = *cu.firstPU;
{
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 // 帧内有效预测模式,67种
static_vector< uint32_t, FAST_UDI_MAX_RDMODE_NUM > uiRdModeList; // 进行full RD的模式容器
int numModesForFullRD = 3; // 存储进行full RD搜索的模式个数,根据CU尺寸决定,代码块上部已经列出
numModesForFullRD = g_aucIntraModeNumFast_UseMPM_2D[uiWidthBit - MIN_CU_LOG2][uiHeightBit - MIN_CU_LOG2];
#if INTRA_FULL_SEARCH // 默认关闭帧内模式全搜索
numModesForFullRD = numModesAvailable;
#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); // 如果cu位于ctu(图片)最上部,不能进行多行预测
int numOfPassesExtendRef = (isFirstLineOfCtu ? 1 : MRL_NUM_REF_LINES); // 多行预测可以使用最多三个参考行,0, 1,3行
#endif
pu.multiRefIdx = 0; // 多行预测参考行索引
//===== init pattern for luma prediction =====
//获取相邻样点,并判定是否对其进行平滑滤波,得到最终参考样点,后面会详细解读此函数
initIntraPatternChType( cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, false, pu ) );
//### 下面代码块进行RMD和MPM主题
//### RMD分成两个阶段,第一个阶段对HEVC中三种模式进行RMD选择,对选择到的模式,进行+-1的扩展模式搜索
//### MPM列表分成三份,传统模式的RMD,多行预测的MPM,ISP的MPM,分别生成
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); // 预测信号缓存区
// distortion parameters setting
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 ) );
// 第一轮RMD,测试DC,planar和HEVC中的33个角度预测方向
{
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 ) ) // 如果是角度模式,且是奇数模式,跳过
{
continue;
}
bSatdChecked[uiMode] = true; // 该模式已测试
pu.intraDir[0] = modeIdx;
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
uiSad += distParam.distFunc(distParam); // hadmard变换计算失真
// 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; // hadmard代价
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 ); // 更新full RD 模式列表和多行参考预测列表(用cost为代价)
updateCandList(uiMode, (double) uiSad, uiHadModeList, CandHadList
, *nullList, -1
, 3 + extraModes); // 更新帧内Hadmard cost 列表(用distortion作为代价)
}
} // NSSTFlag
// forget the extra modes
uiRdModeList.resize( numModesForFullRD );
CandCostList.resize(numModesForFullRD);
extendRefList.resize(numModesForFullRD);
// 第二轮RMD,测试第一轮中的前numModesForFullRD个模式中的角度模式的前后相邻角度模式
static_vector<unsigned, FAST_UDI_MAX_RDMODE_NUM> parentCandList(FAST_UDI_MAX_RDMODE_NUM); // 存储第一轮中的模式
std::copy_n(uiRdModeList.begin(), numModesForFullRD, parentCandList.begin()); // 为parentCandList赋值
// Second round of SATD for extended Angular modes 第二轮RMD
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); // 更新RD模式列表,扩展参考行模式列表
updateCandList(mode, (double)sad, uiHadModeList, CandHadList
, *nullList, -1
, 3); // 更新hadmardcost列表
bSatdChecked[mode] = true;
}
}
}
}
#if JVET_M0102_INTRA_SUBPARTITIONS
if( nOptionsForISP > 1 ) // 如果需要做ISP,在多行预测模式之前,将RD模式列表存在m_rdModeListWithoutMrl中
{
//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
#if ENABLE_JVET_L0283_MRL // 多行预测MPM列表建立
pu.multiRefIdx = 1; // 此处参考行索引设置为1,目的为设置一个标志位,在getMPM中获取多行预测的MPM
const int numMPMs = NUM_MOST_PROBABLE_MODES; // MPM数量6
unsigned multiRefMPM [numMPMs];
PU::getIntraMPMs(pu, multiRefMPM); // 获取多参考行预测的MPM列表,MRL仅对MPM列表中的模式进行
for (int mRefNum = 1; mRefNum < numOfPassesExtendRef; mRefNum++)
{
//** MULTI_REF_LINE_IDX[4] = {0, 1, 3, 0} **
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++)
{
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);
// 用参考行索引更新full RD模式列表
double cost = (double)sad + (double)fracModeBits * sqrtLambdaForFirstPass;
updateCandList(mode, cost, uiRdModeList, CandCostList, extendRefList, multiRefIdx, numModesForFullRD);
}
}
}
#endif
CandCostList.resize(numModesForFullRD);
extendRefList.resize(numModesForFullRD);
// **************** MPM normal intra ************************
if( m_pcEncCfg->getFastUDIUseMPMEnabled() )
{
const int numMPMs = NUM_MOST_PROBABLE_MODES;
unsigned uiPreds[numMPMs]; // MPM列表
pu.multiRefIdx = 0; // 参考行0,非多行帧内预测MPM
const int numCand = PU::getIntraMPMs( pu, uiPreds );
// MPM去重加入RD mode list
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] && extendRefList[i] == 0);
}
if( !mostProbableModeIncluded )
{
extendRefList.push_back(0);
numModesForFullRD++;
uiRdModeList.push_back( mostProbableMode );
}
}
#if JVET_M0102_INTRA_SUBPARTITIONS
if( nOptionsForISP > 1 ) // 如果当前块需要进行ISP预测,
{
//** 对水平、垂直ISP候选列表加入MPM模式
//** 此过程结束后,ISP候选模式列表为intra_RMD+ ISP_MPM
//we add the ISP MPMs to the list without mrl modes
m_rdModeListWithoutMrlHor = m_rdModeListWithoutMrl; //没有进行多行预测前的full RD mode list
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 ); // 获取ISP预测的MPM模式列表,返回相邻有效模式数目
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 );
}
}
}
//** 对ISP候选列表去除非ISP_MPM模式 **
//** 刚开始没想明白为什么这里去除非MPM模式了,上面MPM过程还要讲两个列表融合
//** 后来想想可能是为了保持Full RD list中的模式的顺序
#if JVET_M0102_INTRA_SUBPARTITIONS
if( nOptionsForISP > 1 ) // we remove the non-MPMs from the ISP lists
{
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++ )
{
cu.ispMode = ispOptions[ispOptionIdx];
//we get the mpm cand list
const int numMPMs = NUM_MOST_PROBABLE_MODES;
unsigned uiPreds[numMPMs];
pu.multiRefIdx = 0;
PU::getIntraMPMs( pu, uiPreds );
//we copy only the ISP MPMs
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( m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && uiRdModeList.size() < numModesAvailable )
{
if( CandHadList.size() < 3 || CandHadList[2] > cs.interHad * PBINTRA_RATIO )
{
uiRdModeList.resize( std::min<size_t>( uiRdModeList.size(), 2 ) );
#if JVET_M0102_INTRA_SUBPARTITIONS
extendRefList.resize( std::min<size_t>( extendRefList.size(), 2 ) );
if( nOptionsForISP > 1 )
{
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
extendRefList.resize( std::min<size_t>( extendRefList.size(), 1 ) );
if( nOptionsForISP > 1 )
{
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;
}
}
//===== 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();
PartSplit intraSubPartitionsProcOrder = TU_NO_ISP; // ISP子块预测顺序(左->右 or 上->下)
int bestNormalIntraModeIndex = -1; // 最优传统帧内预测模式(reference_idx == 0)
uint8_t bestIspOption = NOT_INTRA_SUBPARTITIONS; // 最优ISP划分方式
// TUIntraSubPartitioner 类似于管理QTMTT划分的Partitioner类,管理ISP sub-partition, 含有ISP分割相关方法
TUIntraSubPartitioner subTuPartitioner( partitioner );
for( uint32_t ispOptionIdx = 0; ispOptionIdx < nOptionsForISP; ispOptionIdx++ ) // 第一层循环,ISP模式,(noISP,hor_ISP,ver_ISP)
{
cu.ispMode = ispOptions[ispOptionIdx]; // 配置cu ISP类型,non_ISP测试RD模式列表中的模式,hor和ver ISP测试相应ISP列表中的模式
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++ ) //第二层循环,依据ISP类型,遍历相应模式列表中的模式,计算RDcost
{
// 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; // 配置pu预测模式
int multiRefIdx = 0;
pu.multiRefIdx = multiRefIdx;
if( cu.ispMode ) //如果是ISP预测,根据non_isp模式信息进行提前终止判定
{
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 //如果不是ISP预测,从extendRefList得到uiOrgMode对应参考行索引
{
multiRefIdx = extendRefList[uiMode];
pu.multiRefIdx = multiRefIdx;
CHECK( pu.multiRefIdx && ( pu.intraDir[0] == DC_IDX || pu.intraDir[0] == PLANAR_IDX ), "ERL" );
}
// 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 ); // ISP RQT
}
else
{
xRecurIntraCodingLumaQT( *csTemp, partitioner, MAX_DOUBLE, -1 ); // non-ISP RQT
}
if( cu.ispMode && !csTemp->cus[0]->firstTU->cbf[COMPONENT_Y] ) // ISP预测的cbf默认不能为0,如果为0,不采用isp预测
{
csTemp->cost = MAX_DOUBLE;
}
#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 ) // 根据RD cost,交换temp和best信息
{
std::swap( csTemp, csBest );
uiBestPUMode = uiOrgMode; // 更新最优模式
bestExtendRef = multiRefIdx; // 更新预测参考行
#if JVET_M0102_INTRA_SUBPARTITIONS
bestIspOption = cu.ispMode; // 更新ISP模式
#endif
#if JVET_M0102_INTRA_SUBPARTITIONS
if( csBest->cost < bestCurrentCost )
{
bestCurrentCost = csBest->cost; // 更新最优RD cost,用于ISP提前终止
}
if( !cu.ispMode )
{
bestNormalIntraModeIndex = uiMode; // 不是ISP预测,更新最优预测模式
}
#endif
}
csTemp->releaseIntermediateData(); // 清除中间编码信息,初始化csTemp
} // Mode loop
}
cu.ispMode = bestIspOption; // 更新最终的最优ISP模式
// InLoop rehsaper
cs.useSubStructure(*csBest, partitioner.chType, pu.singleChan(CHANNEL_TYPE_LUMA), true, true, keepResi, keepResi);
csBest->releaseIntermediateData();
//=== update PU data ====
pu.intraDir[0] = uiBestPUMode;
pu.multiRefIdx = bestExtendRef;
}
//===== reset context models =====
m_CABACEstimator->getCtx() = ctxStart;
}