H.266/VVC专栏传送
上一篇:H.266/VVC-VTM代码学习-帧内预测10-色度预测的CCLM模式(2)xGetLMParameters函数求解CCLM参数α和β
下一篇:H.266/VVC-VTM代码学习-帧内预测12-编码端获取MPM列表getIntraMPMs函数
目录
前言
VTM是H.266/VVC视频编码标准的参考软件,研究VTM代码给研究人员解释了VVC编码标准的详细标准规范与细节。
本文是笔者对VTM代码的一点学习记录,成文于笔者刚开始接触VVC期间,期间很多概念和理论框架还很不成熟,若文中存在错误欢迎批评指正,也欢迎广大视频编码学习者沟通交流、共同进步。
VTM代码的下载及编译请参考博文:
【视频编码学习】H.266/VVC参考软件VTM配置运行(VTM-6.0版本)
本文涉及的代码主要存在于工程下的Lib\EncoderLib\IntraSearch.cpp文件中。
一、函数代码
bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, const double bestCostSoFar, bool mtsCheckRangeFlag, int mtsFirstCheckId, int mtsLastCheckId, bool moreProbMTSIdxFirst, CodingStructure* bestCS)
{
/*****************************************************/
/******************* 初始化参数 **********************/
/*****************************************************/
//获取当前编码块的编码结构
CodingStructure &cs = *cu.cs;
//获取该编码结构的语法元素
const SPS &sps = *cs.sps;
//亮度块宽取对数(即亮度块宽需要多少bits)
const uint32_t uiWidthBit = floorLog2(partitioner.currArea().lwidth() );
//亮度块高取对数(即亮度块高需要多少bits)
const uint32_t uiHeightBit = floorLog2(partitioner.currArea().lheight());
//Lambda calculation at equivalent Qp of 4 is recommended because at that Qp, the quantization divisor is 1.
//建议在等效Qp(量化参数)为4时进行lambda的计算,因为在此时,用于量化的除数为1
const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda( ) * FRAC_BITS_SCALE;
//===== loop over partitions =====
//===== 循环分区 =====
//上下文模型的开始
const TempCtx ctxStart ( m_CtxCache, m_CABACEstimator->getCtx() );
//MIP模式的flag上下文模型
const TempCtx ctxStartMipFlag ( m_CtxCache, SubCtx( Ctx::MipFlag, m_CABACEstimator->getCtx() ) );
//ISP模式的上下文
const TempCtx ctxStartIspMode ( m_CtxCache, SubCtx( Ctx::ISPMode, m_CABACEstimator->getCtx() ) );
//Planar模式的flag上下文
const TempCtx ctxStartPlanarFlag ( m_CtxCache, SubCtx( Ctx::IntraLumaPlanarFlag, m_CABACEstimator->getCtx() ) );
//其他帧内模式的上下文
const TempCtx ctxStartIntraMode(m_CtxCache, SubCtx(Ctx::IntraLumaMpmFlag, m_CABACEstimator->getCtx()));
//多参考行索引上下文
const TempCtx ctxStartMrlIdx ( m_CtxCache, SubCtx( Ctx::MultiRefLineIdx, m_CABACEstimator->getCtx() ) );
CHECK( !cu.firstPU, "CU has no PUs" );
//variables for saving fast intra modes scan results across multiple LFNST passes
//用于保存对多个LFNST快速帧内模式扫描结果的变量。
//这两个变量主要用于使用LFNST时,只需要进行一遍一轮和二轮的SATD扫描。
//当LfnstIdx==0时,不使用LFNST。此时使用LFNSTSaveFlag将一轮和二轮扫描结果保存下来。
//当LfnstIdx==1、2时,使用LFNST,分别索引变换矩阵集中的第一个矩阵和第二个矩阵。此时使用LFNSTLoadFlag直接加载一轮和二轮的扫描结果
//SPS层开启LFNST,使用LFNST(此时加载一轮和二轮的扫描结果)
bool LFNSTLoadFlag = sps.getUseLFNST() && cu.lfnstIdx != 0;
//SPS层开启LFNST,且不使用LFNST(此时将一轮和二轮扫描结果保存)
bool LFNSTSaveFlag = sps.getUseLFNST() && cu.lfnstIdx == 0;
//若SPS开启帧内MTS,判断当前是否检查的是DCT-Ⅱ;
//若SPS没有开启帧内MTS,则保持原SaveFlag
LFNSTSaveFlag &= sps.getUseIntraMTS() ? cu.mtsFlag == 0 : true;
//LFNST索引
const uint32_t lfnstIdx = cu.lfnstIdx;
//帧间CU的cost
double costInterCU = findInterCUCost( cu );
//当前亮度CU的宽
const int width = partitioner.currArea().lwidth();
//当前亮度CU的高
const int height = partitioner.currArea().lheight();
//Marking MTS usage for faster MTS
//标记MTS的使用情况以加快MTS
//0: MTS is either not applicable for current CU (cuWidth > MTS_INTRA_MAX_CU_SIZE or cuHeight > MTS_INTRA_MAX_CU_SIZE), not active in the config file or the fast decision algorithm is not used in this case
//0:MTS不适用于当前CU(cuWidth>MTS_INTRA_MAX_CU_SIZE或cuHeight>MTS_INTRA_MAX_CU_SIZE),在配置文件中不激活,或者不使用快速决策算法的情况
//1: MTS fast algorithm can be applied for the current CU, and the DCT2 is being checked
//1:当前CU可以采用MTS快速算法,正在检查DCT2
//2: MTS is being checked for current CU. Stored results of DCT2 can be utilized for speedup
//2:正在检查当前CU的MTS,DCT2的存储结果可用于加速
uint8_t mtsUsageFlag = 0;
const int maxSizeEMT = MTS_INTRA_MAX_CU_SIZE;
//若SPS开启LFNST,且一次变换不为DCT-2(mtsFlag=0时为DCT-2),则mtsUsageFlag为2;否则mtsUsageFlag为1
if( width <= maxSizeEMT && height <= maxSizeEMT && sps.getUseIntraMTS() )
{
mtsUsageFlag = ( sps.getUseLFNST() && cu.mtsFlag == 1 ) ? 2 : 1;
}
//宽度*高度<64,且没有开启LFNST快速算法,则mtsUsageFlag为0
if( width * height < 64 && !m_pcEncCfg->getUseFastLFNST() )
{
mtsUsageFlag = 0;
}
//是否启用ACT变换
const bool colorTransformIsEnabled = sps.getUseColorTrans() && !CS::isDualITree(cs);
//是否是第一种颜色空间
const bool isFirstColorSpace = colorTransformIsEnabled && ((m_pcEncCfg->getRGBFormatFlag() && cu.colorTransform) || (!m_pcEncCfg->getRGBFormatFlag() && !cu.colorTransform));
//是否是第二种颜色空间
const bool isSecondColorSpace = colorTransformIsEnabled && ((m_pcEncCfg->getRGBFormatFlag() && !cu.colorTransform) || (!m_pcEncCfg->getRGBFormatFlag() && cu.colorTransform));
//定义当前最优代价
double bestCurrentCost = bestCostSoFar;
//是否可用ISP(SPS确定使用ISP,且MTSFLAG为0(当前正在检查DCT-2变换),且lfnstIdx == 0(没有检查LFNST),且CU可以使用ISP(当前块尺寸符合ISP尺寸限制))
bool ispCanBeUsed = sps.getUseISP() && cu.mtsFlag == 0 && cu.lfnstIdx == 0 && CU::canUseISP(width, height, cu.cs->sps->getMaxTbSize());
//为ISP保存数据(使用ISP,且不启用ACT变换或者是第一种颜色空间)
bool saveDataForISP = ispCanBeUsed && (!colorTransformIsEnabled || isFirstColorSpace);
//测试ISP(使用ISP,且不启用ACT变换)
bool testISP = ispCanBeUsed && (!colorTransformIsEnabled || !cu.colorTransform);
//为ISP保存数据
if ( saveDataForISP )
{
//reset the intra modes lists variables
//重置ISP帧内模式列表变量
m_ispCandListHor.clear();
m_ispCandListVer.clear();
}
//测试ISP
if( testISP )
{
//reset the variables used for the tests
//重置ISP测试需要用到的变量
m_regIntraRDListWithCosts.clear();//选择的常规帧内模式
//水平方向分块的总数
int numTotalPartsHor = (int)height >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_HORZ_SPLIT));
//垂直方向分块的总数
int numTotalPartsVer = (int)width >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_VERT_SPLIT));
//载入分区数
m_ispTestedModes[0].init( numTotalPartsHor, numTotalPartsVer );
//the total number of subpartitions is modified to take into account the cases where LFNST cannot be combined with ISP due to size restrictions
//考虑到由于尺寸限制无法将LFNST与ISP组合的情况,修改子分区的总数
//水平分区数
numTotalPartsHor = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), HOR_INTRA_SUBPARTITIONS) ? numTotalPartsHor : 0;
//垂直分区数
numTotalPartsVer = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), VER_INTRA_SUBPARTITIONS) ? numTotalPartsVer : 0;
//LFNST有4个set 每个set有2个变换核
//set0是为planar(0)和DC(1)准备的
//set1是为 mode<0 和 2 <= mode <= 12 准备的(左下方)
//set2是为 13 <= mode <=23 和 45 <= mode <= 55 准备的(近水平和近垂直)
//set3是为 24 <= mode <= 44 准备的(右上方)
//NUM_LFNST_NUM_PER_SET = 3
for (int j = 1; j < NUM_LFNST_NUM_PER_SET; j++)
{
//根据ISP分块,初始化m_ispTestedModes[1]和m_ispTestedModes[2]
m_ispTestedModes[j].init(numTotalPartsHor, numTotalPartsVer);
}
}
//BDPCM测试标志
//SPS中BDPCM标志位有效,且当前CU块允许BDPCM,且cu.mtsFlag == 0 (当前正在检查DCT-2变换),且cu.lfnstIdx == 0(没有检查LFNST)
const bool testBDPCM = sps.getBDPCMEnabledFlag() && CU::bdpcmAllowed(cu, ComponentID(partitioner.chType)) && cu.mtsFlag == 0 && cu.lfnstIdx == 0;
//哈达玛变换(即SATD)粗选后的候选列表
static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> uiHadModeList;
//候选代价列表
static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandCostList;
//哈达玛变换(SATD/HAD)后的候选Had列表
static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandHadList;
auto &pu = *cu.firstPU;
bool validReturn = false;
//代码块开始
{
CandHadList.clear();
CandCostList.clear();
uiHadModeList.clear();
CHECK(pu.cu != &cu, "PU is not contained in the CU");
//===== determine set of modes to be tested (using prediction signal only) =====
//确定要测试的模式集(仅使用预测信号)
//传统帧内模式的数量(67),不包括MIP模式
int numModesAvailable = NUM_LUMA_MODE;
//是否进行MIP快速算法
const bool fastMip = sps.getUseMIP() && m_pcEncCfg->getUseFastMIP();
//MIP允许使用标志(SPS中允许使用MIP,且当前为亮度块,且lfnstIdx == 0(没有检查LFNST)或当前块大小允许LFNST with MIP)
const bool mipAllowed = sps.getUseMIP() && isLuma(partitioner.chType) && ((cu.lfnstIdx == 0) || allowLfnstWithMip(cu.firstPU->lumaSize()));
//MIP测试标志(允许MIP,且宽高比不能超过1/8或8/1)
const bool testMip = mipAllowed && !(cu.lwidth() > (8 * cu.lheight()) || cu.lheight() > (8 * cu.lwidth()));
//MIP支持最大块尺寸为64*64
const bool supportedMipBlkSize = pu.lwidth() <= MIP_MAX_WIDTH && pu.lheight() <= MIP_MAX_HEIGHT;
static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> uiRdModeList;
//最终RDcost环节最优模式的数量
int numModesForFullRD = 3;
//根据CU的尺寸选择模式数量
numModesForFullRD = g_aucIntraModeNumFast_UseMPM_2D[uiWidthBit - MIN_CU_LOG2][uiHeightBit - MIN_CU_LOG2];
#if INTRA_FULL_SEARCH
//如果是帧内预测全搜索则将最优模式数量设置为可用模式数量
numModesForFullRD = numModesAvailable;
#endif
//第二种颜色空间
if (isSecondColorSpace)
{
uiRdModeList.clear();
//如果第一个颜色空间的模式列表不为空,直接从第一个颜色空间的模式列表中加载测试的模式列表
if (m_numSavedRdModeFirstColorSpace[m_savedRdModeIdx] > 0)
{
for (int i = 0; i < m_numSavedRdModeFirstColorSpace[m_savedRdModeIdx]; i++)
{
uiRdModeList.push_back(m_savedRdModeFirstColorSpace[m_savedRdModeIdx][i]);
}
}
//第一颜色空间模式列表为空
else
{
return false;
}
}
//不是第二种颜色空间
else
{
//mtsUsageFlag = 0 or 1,此时一次变换为DCT-2
if (mtsUsageFlag != 2)
{
CHECK(!pu.Y().valid(), "PU is not valid");
//是否是CTU的第一行
bool isFirstLineOfCtu = (((pu.block(COMPONENT_Y).y) & ((pu.cs->sps)->getMaxCUWidth() - 1)) == 0);
//使用的扩展参考行数量(若为CTU第一行或者SPS层关闭了MRL,则最多只能用1行,否则最多可以用3行)
int numOfPassesExtendRef = ((!sps.getUseMRL() || isFirstLineOfCtu) ? 1 : MRL_NUM_REF_LINES);
pu.multiRefIdx = 0;
//如果最终RDcost环节最优模式的数量不等于传统帧内模式数量(67)(正常都是这个情况,所以这里是主要的RD入口)
if (numModesForFullRD != numModesAvailable)
{
CHECK(numModesForFullRD >= numModesAvailable, "Too many modes for full RD search");
//获取当前 luma PU 的区域
const CompArea &area = pu.Y();
//获取当前 luma PU 的亮度原始值
PelBuf piOrg = cs.getOrgBuf(area);
//获取当前 luma PU 的亮度预测值
PelBuf piPred = cs.getPredBuf(area);
//定义一个使用SAD的失真参数
DistParam distParamSad;
//定义一个使用HAD/SATD(加了哈达玛变换)的失真参数
DistParam distParamHad;
//Luma mapping with chroma scaling (LMCS)
if (cu.slice->getLmcsEnabledFlag() && 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());
// Use SAD cost 设置SAD参数
m_pcRdCost->setDistParam(distParamSad, tmpOrg, piPred, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y,
false);
// Use HAD (SATD) cost 设置SATD参数
m_pcRdCost->setDistParam(distParamHad, tmpOrg, piPred, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y,
true);
}
else
{
// Use SAD cost 设置SAD参数
m_pcRdCost->setDistParam(distParamSad, piOrg, piPred, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y,
false);
// Use HAD (SATD) cost 设置SATD参数
m_pcRdCost->setDistParam(distParamHad, piOrg, piPred, sps.getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y,
true);
}
distParamSad.applyWeight = false;
distParamHad.applyWeight = false;
//若MIP测试标志有效,且为MIP支持尺寸,则将MIP加入候选模式中
if (testMip && supportedMipBlkSize)
{
numModesForFullRD += fastMip
? std::max(numModesForFullRD, floorLog2(std::min(pu.lwidth(), pu.lheight())) - 1)
: numModesForFullRD;
}
//定义SATD变换候选的数量
const int numHadCand = (testMip ? 2 : 1) * 3;
//以下是使用哈达玛变换的推导(常规)角度候选
/*****************************************************/
/****************** 常规角度候选 *********************/
/*****************************************************/
cu.mipFlag = false;
//===== init pattern for luma prediction =====
//初始化亮度预测模式
initIntraPatternChType(cu, pu.Y(), true);
//用于存放对应模式是否被检查
bool bSatdChecked[NUM_INTRA_MODE];
memset(bSatdChecked, 0, sizeof(bSatdChecked));
//若LFNSTLoadFlag无效,即不使用LFNST,不能直接加载一轮和二轮的扫描结果
if (!LFNSTLoadFlag)
{
//首先对67种传统的亮度帧内模式进行第一轮SATD循环粗选
//遍历传统帧内模式(67)
for (int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++)
{
uint32_t uiMode = modeIdx;
Distortion minSadHad = 0;
/********************* 第一轮SATD粗选 ************************/
// Skip checking extended Angular modes in the first round of SATD
//在第一轮SATD中跳过检查扩展角模式(即主要检查0、1、2、4、6...66这些角度模式【即对Planar、DC、HEVC中的角度模式】)
//跳过扩展角度模式
if (uiMode > DC_IDX && (uiMode & 1))
{
continue;
}
//标记当前模式被检查过
bSatdChecked[uiMode] = true;
//该数组中存放的是当前CU最终选中的预测模式
pu.intraDir[0] = modeIdx;
//初始化帧内预测参数
initPredIntraParams(pu, pu.Y(), sps);
//传统角度预测模式函数入口,进行传统的角度预测
predIntraAng(COMPONENT_Y, piPred, pu);
//Use the min between SAD and HAD as the cost criterion
//使用SAD和HAD之间的最小值作为代价标准(HAD即SATD,是加了哈达玛变换后的SAD)
//SAD is scaled by 2 to align with the scaling of HAD
//SAD的缩放比例为2,与HAD的缩放比例一致
minSadHad += std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
//NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
//NB xFracModeBitsIntra不会影响可能已经预先估计的色度模式
//载入MIP Flag
m_CABACEstimator->getCtx() = SubCtx( Ctx::MipFlag, ctxStartMipFlag );
//载入ISP Mode
m_CABACEstimator->getCtx() = SubCtx( Ctx::ISPMode, ctxStartIspMode );
//载入PLANAR Flag
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaPlanarFlag, ctxStartPlanarFlag);
//载入MPM Flag
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
//载入MRL Idx
m_CABACEstimator->getCtx() = SubCtx( Ctx::MultiRefLineIdx, ctxStartMrlIdx );
//估计编码帧内模式所需比特数
uint64_t fracModeBits = xFracModeBitsIntra(pu, uiMode, CHANNEL_TYPE_LUMA);
//计算每种模式的cost = minSadHad + 当前模式所需比特数 * lambda
double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
DTRACE(g_trace_ctx, D_INTRA_COST, "IntraHAD: %u, %llu, %f (%d)\n", minSadHad, fracModeBits, cost, uiMode);
//更新SAD之后的RD候选模式列表以及代价列表
updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiMode), cost, uiRdModeList,
CandCostList, numModesForFullRD);
//更新SATD之后的uiHadModeList、CandHadList列表
updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiMode), double(minSadHad),
uiHadModeList, CandHadList, numHadCand);
}//遍历完67种传统角度模式
//如果SPS层关闭MIP,且需要为LFNST保存粗选结果
if (!sps.getUseMIP() && LFNSTSaveFlag)
{
// save found best modes
//保存找到的最佳模式
m_uiSavedNumRdModesLFNST = numModesForFullRD;
m_uiSavedRdModeListLFNST = uiRdModeList;
m_dSavedModeCostLFNST = CandCostList;
// PBINTRA fast
m_uiSavedHadModeListLFNST = uiHadModeList;
m_dSavedHadListLFNST = CandHadList;
LFNSTSaveFlag = false;
}
}//if (!LFNSTLoadFlag)
//如果SPS层关闭MIP,且使用LFNST,即可为LFNST直接加载之前的粗选结果
if (!sps.getUseMIP() && LFNSTLoadFlag)
{
// restore saved modes
numModesForFullRD = m_uiSavedNumRdModesLFNST;
uiRdModeList = m_uiSavedRdModeListLFNST;
CandCostList = m_dSavedModeCostLFNST;
// PBINTRA fast
uiHadModeList = m_uiSavedHadModeListLFNST;
CandHadList = m_dSavedHadListLFNST;
}
//如果SPS层关闭MIP,或者不使用LFNST(不能为LFNST加载粗选结果)
if (!(sps.getUseMIP() && LFNSTLoadFlag))
{
//将第一轮选出的最优模式放入父模式中
static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> parentCandList = uiRdModeList;
/******************** 第二轮对扩展角度模式进行SATD粗选 ***********************/
//Second round of SATD for extended Angular modes
//开始第二轮对剩下的扩展角度模式的SATD粗选
//遍历第一轮选出的最优模式列表
for (int modeIdx = 0; modeIdx < numModesForFullRD; modeIdx++)
{
//定义父模式,父模式放入的是第一轮选出的最优模式列表
unsigned parentMode = parentCandList[modeIdx].modeId;
if (parentMode > (DC_IDX + 1) && parentMode < (NUM_LUMA_MODE - 1))
{
//从扩展模式开始循环处理 模式号3-65
for (int subModeIdx = -1; subModeIdx <= 1; subModeIdx += 2)
{
//父模式减一和加一,检查第一轮非扩展模式相邻的扩展模式
unsigned mode = parentMode + subModeIdx;
//如果当前模式未经历第一轮SATD粗选
if (!bSatdChecked[mode])
{
pu.intraDir[0] = mode;
//初始化帧内预测参数
initPredIntraParams(pu, pu.Y(), sps);
//传统角度预测模式函数入口,进行传统的角度预测
predIntraAng(COMPONENT_Y, piPred, pu);
//Use the min between SAD and SATD as the cost criterion
//使用SAD和SATD之间的最小值作为代价标准
//SAD is scaled by 2 to align with the scaling of HAD
//SAD的缩放比例为2,与HAD的缩放比例一致
Distortion minSadHad =
std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
//NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
//NB xFracModeBitsIntra不会影响可能已经预先估计的色度模式
m_CABACEstimator->getCtx() = SubCtx(Ctx::MipFlag, ctxStartMipFlag);
m_CABACEstimator->getCtx() = SubCtx(Ctx::ISPMode, ctxStartIspMode);
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaPlanarFlag, ctxStartPlanarFlag);
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
m_CABACEstimator->getCtx() = SubCtx(Ctx::MultiRefLineIdx, ctxStartMrlIdx);
//估计编码帧内模式所需比特数
uint64_t fracModeBits = xFracModeBitsIntra(pu, mode, CHANNEL_TYPE_LUMA);
//计算每种模式的cost = minSadHad + 当前模式所需比特数 * lambda
double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
//更新SAD之后的RD候选模式列表以及代价列表
updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
CandCostList, numModesForFullRD);
//更新SATD之后的uiHadModeList、CandHadList列表
updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, mode), double(minSadHad),
uiHadModeList, CandHadList, numHadCand);
//记录当前模式已被检查过
bSatdChecked[mode] = true;
}//if (!bSatdChecked[mode])
}//for (int subModeIdx = -1; subModeIdx <= 1; subModeIdx += 2)
}//if (parentMode > (DC_IDX + 1) && parentMode < (NUM_LUMA_MODE - 1))
}//for (int modeIdx = 0; modeIdx < numModesForFullRD; modeIdx++)
//为之后测试ISP保存粗选出的传统帧内模式
if (saveDataForISP)
{
//we save the regular intra modes list
//保存常规帧内模式列表
m_ispCandListHor = uiRdModeList;
}
pu.multiRefIdx = 1;
//定义最可能模式的数量,即MPM列表大小(设置为6)
const int numMPMs = NUM_MOST_PROBABLE_MODES;
//定义多参考行的MPM列表
unsigned multiRefMPM[numMPMs];
//获取MPM列表的函数入口
PU::getIntraMPMs(pu, multiRefMPM);
//遍历多参考行
/*****************************************************/
/*********** 遍历MPM中的模式进行多参考行候选 **********/
/*****************************************************/
for (int mRefNum = 1; mRefNum < numOfPassesExtendRef; mRefNum++)
{
//当前多参考行索引
int multiRefIdx = MULTI_REF_LINE_IDX[mRefNum];
//设置为当前遍历到的多参考行索引
pu.multiRefIdx = multiRefIdx;
{
//获取参考像素长度、获取参考像素、参考像素滤波
initIntraPatternChType(cu, pu.Y(), true);
}
//对MPM列表中的6个模式进行SATD比较并选择
for (int x = 1; x < numMPMs; x++)
{
uint32_t mode = multiRefMPM[x];
{
pu.intraDir[0] = mode;
//初始化帧内预测参数
initPredIntraParams(pu, pu.Y(), sps);
//传统角度预测模式函数入口,进行传统的角度预测
predIntraAng(COMPONENT_Y, piPred, pu);
//Use the min between SAD and SATD as the cost criterion
//使用SAD和SATD之间的最小值作为代价标准
//SAD is scaled by 2 to align with the scaling of HAD
//SAD的缩放比例为2,与HAD的缩放比例一致
Distortion minSadHad =
std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
//NB xFracModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
//NB xFracModeBitsIntra不会影响可能已经预先估计的色度模式
m_CABACEstimator->getCtx() = SubCtx(Ctx::MipFlag, ctxStartMipFlag);
m_CABACEstimator->getCtx() = SubCtx(Ctx::ISPMode, ctxStartIspMode);
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaPlanarFlag, ctxStartPlanarFlag);
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
m_CABACEstimator->getCtx() = SubCtx(Ctx::MultiRefLineIdx, ctxStartMrlIdx);
//估计编码帧内模式所需比特数
uint64_t fracModeBits = xFracModeBitsIntra(pu, mode, CHANNEL_TYPE_LUMA);
//计算每种模式的cost = minSadHad + 当前模式所需比特数 * lambda
double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
//更新SAD之后的RD候选模式列表以及代价列表
updateCandList(ModeInfo(false, false, multiRefIdx, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
CandCostList, numModesForFullRD);
//更新SATD之后的uiHadModeList、CandHadList列表
updateCandList(ModeInfo(false, false, multiRefIdx, NOT_INTRA_SUBPARTITIONS, mode), double(minSadHad),
uiHadModeList, CandHadList, numHadCand);
}
}//for (int x = 1; x < numMPMs; x++)
}//for (int mRefNum = 1; mRefNum < numOfPassesExtendRef; mRefNum++)
CHECKD(uiRdModeList.size() != numModesForFullRD, "Error: RD mode list size");
//save a different set for the next run
//为下次运行保存不同的集
//如果LFNST保存标志和MIP测试标志同时有效,且允许LFNST与MIP
if (LFNSTSaveFlag && testMip
&& !allowLfnstWithMip(cu.firstPU->lumaSize()))
{
// save found best modes
//保存找到的最优模式
m_uiSavedRdModeListLFNST = uiRdModeList;
m_dSavedModeCostLFNST = CandCostList;
// PBINTRA fast
m_uiSavedHadModeListLFNST = uiHadModeList;
m_dSavedHadListLFNST = CandHadList;
m_uiSavedNumRdModesLFNST =
g_aucIntraModeNumFast_UseMPM_2D[uiWidthBit - MIN_CU_LOG2][uiHeightBit - MIN_CU_LOG2];
m_uiSavedRdModeListLFNST.resize(m_uiSavedNumRdModesLFNST);
m_dSavedModeCostLFNST.resize(m_uiSavedNumRdModesLFNST);
// PBINTRA fast
m_uiSavedHadModeListLFNST.resize(3);
m_dSavedHadListLFNST.resize(3);
LFNSTSaveFlag = false;
}
//*** Derive MIP candidates using Hadamard
//在两轮传统角度模式的SATD以及MPM的SATD结束以后,对使用哈达玛的MIP模式单独进行候选模式的推导
/*****************************************************/
/****************** MIP模式候选 **********************/
/*****************************************************/
//如果MIP测试标志有效,且当前块大小不支持MIP
if (testMip && !supportedMipBlkSize)
{
//avoid estimation for unsupported blk sizes
//避免对不支持的块大小进行估计
//根据当前块sizeId,获取对应MIPmode的数量 0:16 1:8 2:6
const int transpOff = getNumModesMip(pu.Y());
//包括转置的总模式数(即transpOff的两倍)
const int numModesFull = (transpOff << 1);
//遍历所有MIPmode(包括转置)
for (uint32_t uiModeFull = 0; uiModeFull < numModesFull; uiModeFull++)
{
//所有MIPmode中(包括转置),前transOff个为转置的模式,后面为非转置模式
const bool isTransposed = (uiModeFull >= transpOff ? true : false);
//当前真实MIPmode号
const uint32_t uiMode = (isTransposed ? uiModeFull - transpOff : uiModeFull);
//最终RDcost环节最优模式的数量 + 1
numModesForFullRD++;
//将MIP模式加入最优模式列表
uiRdModeList.push_back(ModeInfo(true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, uiMode));
//加入对应代价为0
CandCostList.push_back(0);
}
}
//如果MIP测试标志有效,且当前块大小支持MIP
else if (testMip)
{
cu.mipFlag = true;
pu.multiRefIdx = 0;
double mipHadCost[MAX_NUM_MIP_MODE] = { MAX_DOUBLE };
//获取参考像素长度、获取参考像素、参考像素滤波
initIntraPatternChType(cu, pu.Y());
//调用函数初始化MIP(求参考像素下采样的向量)
initIntraMip(pu, pu.Y());
const int transpOff = getNumModesMip(pu.Y());
const int numModesFull = (transpOff << 1);
//循环遍历当前sizeId对应的全部MIPmode(包括转置)
for (uint32_t uiModeFull = 0; uiModeFull < numModesFull; uiModeFull++)
{
//判断是否转置
const bool isTransposed = (uiModeFull >= transpOff ? true : false);
//当前MIPmode
const uint32_t uiMode = (isTransposed ? uiModeFull - transpOff : uiModeFull);
pu.mipTransposedFlag = isTransposed;
pu.intraDir[CHANNEL_TYPE_LUMA] = uiMode;
//MIP预测函数入口,进行MIP预测
predIntraMip(COMPONENT_Y, piPred, pu);
// Use the min between SAD and HAD as the cost criterion
//使用SAD和SATD之间的最小值作为代价标准
// SAD is scaled by 2 to align with the scaling of HAD
//SAD的缩放比例为2,与HAD的缩放比例一致
Distortion minSadHad =
std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
m_CABACEstimator->getCtx() = SubCtx(Ctx::MipFlag, ctxStartMipFlag);
//估计编码帧内模式所需比特数
uint64_t fracModeBits = xFracModeBitsIntra(pu, uiMode, CHANNEL_TYPE_LUMA);
//计算每种模式的cost = minSadHad + 当前模式所需比特数 * lambda
double cost = double(minSadHad) + double(fracModeBits) * sqrtLambdaForFirstPass;
mipHadCost[uiModeFull] = cost;
DTRACE(g_trace_ctx, D_INTRA_COST, "IntraMIP: %u, %llu, %f (%d)\n", minSadHad, fracModeBits, cost,
uiModeFull);
//更新SAD之后的RD候选模式列表以及代价列表
updateCandList(ModeInfo(true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, uiMode), cost, uiRdModeList,
CandCostList, numModesForFullRD + 1);
//更新SATD之后的uiHadModeList、CandHadList列表
updateCandList(ModeInfo(true, isTransposed, 0, NOT_INTRA_SUBPARTITIONS, uiMode),
0.8 * double(minSadHad), uiHadModeList, CandHadList, numHadCand);
}//for (uint32_t uiModeFull = 0; uiModeFull < numModesFull; uiModeFull++)
//在总共四轮的STAD之后,最终更新RDcost列表之后,还要对该列表进行缩减
const double thresholdHadCost = 1.0 + 1.4 / sqrt((double) (pu.lwidth() * pu.lheight()));
//缩减HAD候选列表
reduceHadCandList(uiRdModeList, CandCostList, numModesForFullRD, thresholdHadCost, mipHadCost, pu,
fastMip);
}//else if (testMip)
//如果SPS层开启MIP,且要为LFNST保存粗选结果
if (sps.getUseMIP() && LFNSTSaveFlag)
{
// save found best modes
m_uiSavedNumRdModesLFNST = numModesForFullRD;
m_uiSavedRdModeListLFNST = uiRdModeList;
m_dSavedModeCostLFNST = CandCostList;
// PBINTRA fast
m_uiSavedHadModeListLFNST = uiHadModeList;
m_dSavedHadListLFNST = CandHadList;
LFNSTSaveFlag = false;
}
}//if (!(sps.getUseMIP() && LFNSTLoadFlag))
//if( sps.getUseMIP() && LFNSTLoadFlag)
//如果SPS层开启MIP,且需要为LFNST加载粗选结果
else
{
// restore saved modes
numModesForFullRD = m_uiSavedNumRdModesLFNST;
uiRdModeList = m_uiSavedRdModeListLFNST;
CandCostList = m_dSavedModeCostLFNST;
// PBINTRA fast
uiHadModeList = m_uiSavedHadModeListLFNST;
CandHadList = m_dSavedHadListLFNST;
}
//获取FastUDI的MPM列表,如果满足条件也将其加入RD候选列表
if (m_pcEncCfg->getFastUDIUseMPMEnabled())
{
const int numMPMs = NUM_MOST_PROBABLE_MODES;
unsigned uiPreds[numMPMs];//MPM list
pu.multiRefIdx = 0;
/******** 获取MPM列表中最可能的模式,只对MPM中最可能的几个模式进行SATD粗选,其余模式不进行 ********/
//MPM中的候选个数
const int numCand = PU::getIntraMPMs(pu, uiPreds);
//循环遍历MPM list
for (int j = 0; j < numCand; j++)
{
bool mostProbableModeIncluded = false;
ModeInfo mostProbableMode( false, false, 0, NOT_INTRA_SUBPARTITIONS, uiPreds[j] );
//遍历RdModeList,判断最可能模式是否存在
for (int i = 0; i < numModesForFullRD; i++)
{
mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);
}
//如果最可能模式不存在于RdModeList,添加最可能模式
if (!mostProbableModeIncluded)
{
numModesForFullRD++;
uiRdModeList.push_back(mostProbableMode);
CandCostList.push_back(0);
}
}
//为ISP保存数据
if (saveDataForISP)
{
// we add the MPMs to the list that contains only regular intra modes
//我们将MPMs添加到仅包含常规帧内模式的列表中
//循环遍历MPM
for (int j = 0; j < numCand; j++)
{
bool mostProbableModeIncluded = false;
ModeInfo mostProbableMode(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiPreds[j]);
//遍历候选列表检查最可能模式是否包含
for (int i = 0; i < m_ispCandListHor.size(); i++)
{
mostProbableModeIncluded |= (mostProbableMode == m_ispCandListHor[i]);
}
//若当前最可能模式未被候选列表包含,则添加到候选列表中
if (!mostProbableModeIncluded)
{
m_ispCandListHor.push_back(mostProbableMode);
}
}
}
}//if (m_pcEncCfg->getFastUDIUseMPMEnabled()) 获取FastUDI可能的MPM列表,如果满足条件也将其加入RD候选列表
}//if (numModesForFullRD != numModesAvailable)//如果最终RDcost环节最优模式的数量不等于传统帧内模式数量(67)(正常都是这个情况,所以这里是主要的RD入口)
//numModesForFullRD == numModesAvailable
//即如果最终RDcost环节最优模式的数量等于传统帧内模式数量(67)
else
{
THROW("Full search not supported for MIP");
}
//若SPS层可用LFNST,且当前CU可用MTS
//为进行MTS变换检查保存RD模式列表
if (sps.getUseLFNST() && mtsUsageFlag == 1)
{
// Store the modes to be checked with RD
//存储要进行RD cost 检查的模式
//最终RD cost 最优模式的数量
m_savedNumRdModes[lfnstIdx] = numModesForFullRD;
//保存RD模式列表
std::copy_n(uiRdModeList.begin(), numModesForFullRD, m_savedRdModeList[lfnstIdx]);
}
}//if (mtsUsageFlag != 2)
// mtsUsage = 2 (here we potentially reduce the number of modes that will be full-RD checked)
//我们可能减少将进行全RD检查的模式数量
else
{
//LFNST快速算法或者不是I帧(是B帧或P帧),且最佳模式cost有效
if ((m_pcEncCfg->getUseFastLFNST() || !cu.slice->isIntra()) && m_bestModeCostValid[lfnstIdx])
{
numModesForFullRD = 0;
//跳过检查模式的cost与最佳模式cost倍数的阈值
double thresholdSkipMode = 1.0 + ((cu.lfnstIdx > 0) ? 0.1 : 1.0) * (1.4 / sqrt((double) (width * height)));
// Skip checking the modes with much larger R-D cost than the best mode
//跳过检查RD cost比最佳模式大得多的模式,
//遍历RD cost所有模式,将mtsUsage=1时与最优模式相近的候选模式放入当前候选列表
for (int i = 0; i < m_savedNumRdModes[lfnstIdx]; i++)
{
//若当前遍历模式cost未大于阈值倍最佳模式cost,则检查
if (m_modeCostStore[lfnstIdx][i] <= thresholdSkipMode * m_bestModeCostStore[lfnstIdx])
{ //这里的m_modeCostStore和m_bestModeCostStore是之前mtsUsage=1时保存的候选模式和最优模式
//将当前模式加入RdModeList
uiRdModeList.push_back(m_savedRdModeList[lfnstIdx][i]);
numModesForFullRD++;
}
}
}
// this is necessary because we skip the candidates list calculation, since it was already obtained for the DCT-II. Now we load it
//这里的操作是必要的,因为已经从DCT-2中获得了计算数据,因此这里跳过了候选列表的计算,现在直接加载它
//是I帧,或最佳模式cost无效
else
{
// Restore the modes to be checked with RD
numModesForFullRD = m_savedNumRdModes[lfnstIdx];
uiRdModeList.resize(numModesForFullRD);
//直接加载候选列表
std::copy_n(m_savedRdModeList[lfnstIdx], m_savedNumRdModes[lfnstIdx], uiRdModeList.begin());
CandCostList.resize(numModesForFullRD);
}
}// mtsUsage = 2
CHECK(numModesForFullRD != uiRdModeList.size(), "Inconsistent state!");
// after this point, don't use numModesForFullRD
//在这一点后,不再使用numModesForFullRD
// PBINTRA fast
//快速帧内模式
//如果设置使用快速帧内模式,且不是I帧(B帧或P帧),且RdModeList长度小于67,且SATD对RD可用,且mstUsageFlag != 2 || lfnstIdx > 0(使用LFNST)
if (m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && uiRdModeList.size() < numModesAvailable
&& !cs.slice->getDisableSATDForRD() && (mtsUsageFlag != 2 || lfnstIdx > 0))
{
double pbintraRatio = (lfnstIdx > 0) ? 1.25 : PBINTRA_RATIO;
int maxSize = -1;
ModeInfo bestMipMode;
int bestMipIdx = -1;
//遍历MPM,找到最优MIP模式
for (int idx = 0; idx < uiRdModeList.size(); idx++)
{
if (uiRdModeList[idx].mipFlg)
{
//记录最优MIP模式
bestMipMode = uiRdModeList[idx];
//记录最优MIP模式在MPM中的索引
bestMipIdx = idx;
break;
}
}
const int numHadCand = 3;
//遍历HAD候选列表
for (int k = numHadCand - 1; k >= 0; k--)
{
//候选Had列表尺寸 >= k,或CandHadList[k] > cs.interHad * pbintraRatio
if (CandHadList.size() < (k + 1) || CandHadList[k] > cs.interHad * pbintraRatio)
{
//记录候选Had列表最大尺寸
maxSize = k;
}
}
//若候选Had列表最大尺寸不为0
if (maxSize > 0)
{
//MPM长度设置为原长度和候选Had列表最大尺寸中的较小值
uiRdModeList.resize(std::min<size_t>(uiRdModeList.size(), maxSize));
//如果MPM中有最佳Mip模式
if (bestMipIdx >= 0)
{
if (uiRdModeList.size() <= bestMipIdx)
{
//若当前MPM尺寸未包括最佳MIP模式,则将最佳MIP模式加入MPM中
uiRdModeList.push_back(bestMipMode);
}
}
//如果为ISP保存数据
if (saveDataForISP)
{
//ISP候选列表长度设置为原长度和Had列表最大尺寸中的较小值
m_ispCandListHor.resize(std::min<size_t>(m_ispCandListHor.size(), maxSize));
}
}
//若候选Had列表最大尺寸为0
if (maxSize == 0)
{
cs.dist = std::numeric_limits<Distortion>::max();
cs.interHad = 0;
//===== reset context models =====
//重置上下文模型
m_CABACEstimator->getCtx() = SubCtx(Ctx::MipFlag, ctxStartMipFlag);
m_CABACEstimator->getCtx() = SubCtx(Ctx::ISPMode, ctxStartIspMode);
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaPlanarFlag, ctxStartPlanarFlag);
m_CABACEstimator->getCtx() = SubCtx(Ctx::IntraLumaMpmFlag, ctxStartIntraMode);
m_CABACEstimator->getCtx() = SubCtx(Ctx::MultiRefLineIdx, ctxStartMrlIdx);
return false;
}
}//if (m_pcEncCfg->getUsePbIntraFast() && !cs.slice->isIntra() && uiRdModeList.size() < numModesAvailable && !cs.slice->getDisableSATDForRD() && (mtsUsageFlag != 2 || lfnstIdx > 0))
}//不是第二种颜色空间
//非ISP模式的数量
int numNonISPModes = (int)uiRdModeList.size();
//若测试ISP
if ( testISP )
{
// we reserve positions for ISP in the common full RD list
//为ISP在通用的完整RD列表中保留位置
//ISP在RD列表中的最大模式数量(使用LFNST时:16*3,不使用LFNST:16)
const int maxNumRDModesISP = sps.getUseLFNST() ? 16 * NUM_LFNST_NUM_PER_SET : 16;
m_curIspLfnstIdx = 0;
//将ISP模式加入候选模式列表
for (int i = 0; i < maxNumRDModesISP; i++)
{
uiRdModeList.push_back( ModeInfo( false, false, 0, INTRA_SUBPARTITIONS_RESERVED, 0 ) );
}
}
//===== check modes (using r-d costs) =====
//使用RDCost检查预测模式
//定义当前PU最优的亮度帧内预测模式
ModeInfo uiBestPUMode;
//最佳BDPCM模式
int bestBDPCMMode = 0;
//不使用BDPCM时的最优代价
double bestCostNonBDPCM = MAX_DOUBLE;
//临时缓存CS
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() )];CS
csTemp->slice = cs.slice;
csBest->slice = cs.slice;
csTemp->initStructData();
csBest->initStructData();
csTemp->picture = cs.picture;
csBest->picture = cs.picture;
// just to be sure
//RdModeList中的模式数量
numModesForFullRD = ( int ) uiRdModeList.size();
TUIntraSubPartitioner subTuPartitioner( partitioner );
//如果测试ISP
if ( testISP )
{
//先设置最优ISP cost 为正无穷
m_modeCtrl->setIspCost( MAX_DOUBLE );
//先设置无ISP的MTS最优cost为正无穷
m_modeCtrl->setMtsFirstPassNoIspCost( MAX_DOUBLE );
}
int bestLfnstIdx = cu.lfnstIdx;
/******** 进入最终对RD候选列表中的模式进一步精选RDcost ********/
//遍历RD候选列表
for (int mode = isSecondColorSpace ? 0 : -2 * int(testBDPCM); mode < (int)uiRdModeList.size(); mode++)
{
// set CU/PU to luma prediction mode
//设置亮度CU/PU的预测模式
ModeInfo uiOrgMode;
//若使用了ColorTrans,且不是RGB形式,且是第二色彩空间,且当前模式在RD候选列表中下标不为0
if (sps.getUseColorTrans() && !m_pcEncCfg->getRGBFormatFlag() && isSecondColorSpace && mode)
{
continue;//跳过当前候选模式
}
//若当前模式在RD候选列表中下标小于0,或是第二色彩空间且在第一色彩空间保存了BDPCM模式 即如果是BDPCM模式
if (mode < 0 || (isSecondColorSpace && m_savedBDPCMModeFirstColorSpace[m_savedRdModeIdx][mode]))
{
//记录BDPCM模式,若模式号小于0则记录绝对值,否则记录对应第一色彩空间的BDPCM模式
cu.bdpcmMode = mode < 0 ? -mode : m_savedBDPCMModeFirstColorSpace[m_savedRdModeIdx][mode];
uiOrgMode = ModeInfo( false, false, 0, NOT_INTRA_SUBPARTITIONS, cu.bdpcmMode == 2 ? VER_IDX : HOR_IDX );
}
//不是BDPCM模式
else
{
cu.bdpcmMode = 0;
uiOrgMode = uiRdModeList[mode];
}
//若不适用BDPCM,且当前模式ISP的模式为4 即如果是ISP模式
if (!cu.bdpcmMode && uiRdModeList[mode].ispMod == INTRA_SUBPARTITIONS_RESERVED)
{
// the list needs to be sorted only once
//列表只需要排序一次
//若当前模式号为非ISP模式的数量(即候选列表中第一个ISP模式)
if (mode == numNonISPModes)
{
//若使用快速ISP
if (m_pcEncCfg->getUseFastISP())
{
m_modeCtrl->setBestPredModeDCT2(uiBestPUMode.modeId);
}
//排列ISP模式列表
if (!xSortISPCandList(bestCurrentCost, csBest->cost, uiBestPUMode))
{
break;
}
}
//获取下一个ISP模式
xGetNextISPMode(uiRdModeList[mode], (mode > 0 ? &uiRdModeList[mode - 1] : nullptr), Size(width, height));
//若当前模式的ISP模式是4
if (uiRdModeList[mode].ispMod == INTRA_SUBPARTITIONS_RESERVED)
{
continue;
}
cu.lfnstIdx = m_curIspLfnstIdx;
uiOrgMode = uiRdModeList[mode];
}
//当前候选模式是否是MIP
cu.mipFlag = uiOrgMode.mipFlg;
//当前候选模式是否是MIP转置模式
pu.mipTransposedFlag = uiOrgMode.mipTrFlg;
//当前候选模式是否是ISP
cu.ispMode = uiOrgMode.ispMod;
//当前候选模式是否使用MRL
pu.multiRefIdx = uiOrgMode.mRefId;
//当前候选模式放入intraDir
pu.intraDir[CHANNEL_TYPE_LUMA] = uiOrgMode.modeId;
CHECK(cu.mipFlag && pu.multiRefIdx, "Error: combination of MIP and MRL not supported");
CHECK(pu.multiRefIdx && (pu.intraDir[0] == PLANAR_IDX), "Error: combination of MRL and Planar mode not supported");
CHECK(cu.ispMode && cu.mipFlag, "Error: combination of ISP and MIP not supported");
CHECK(cu.ispMode && pu.multiRefIdx, "Error: combination of ISP and MRL not supported");
CHECK(cu.ispMode&& cu.colorTransform, "Error: combination of ISP and ACT not supported");
pu.intraDir[CHANNEL_TYPE_CHROMA] = cu.colorTransform ? DM_CHROMA_IDX : pu.intraDir[CHANNEL_TYPE_CHROMA];
// set context models
//设置上下文模型
m_CABACEstimator->getCtx() = ctxStart;
// determine residual for partition
//确定分区的残差
cs.initSubStructure( *csTemp, partitioner.chType, cs.area, true );
bool tmpValidReturn = false;
//如果是ISP模式
if( cu.ispMode )
{
//如果使用快速ISP
if ( m_pcEncCfg->getUseFastISP() )
{
//记录ISP已被测试
m_modeCtrl->setISPWasTested(true);
}
//记录ISP是否有效
tmpValidReturn = xIntraCodingLumaISP(*csTemp, subTuPartitioner, bestCurrentCost);
//如果TU未分块
if (csTemp->tus.size() == 0)
{
// no TUs were coded
//没有TU被编码
csTemp->cost = MAX_DOUBLE;
continue;
}
// we save the data for future tests
//保存数据以备将来的测试
m_ispTestedModes[m_curIspLfnstIdx].setModeResults((ISPType)cu.ispMode, (int)uiOrgMode.modeId, (int)csTemp->tus.size(), csTemp->cus[0]->firstTU->cbf[COMPONENT_Y] ? csTemp->cost : MAX_DOUBLE, csBest->cost);
csTemp->cost = !tmpValidReturn ? MAX_DOUBLE : csTemp->cost;
}
//不是ISP模式
else
{
//如果colorTransform有效,做ACT变换
if (cu.colorTransform)
{
tmpValidReturn = xRecurIntraCodingACTQT(*csTemp, partitioner, mtsCheckRangeFlag, mtsFirstCheckId, mtsLastCheckId, moreProbMTSIdxFirst);
}
//colorTransform无效
else
{
//使用选中的预测模式,通过调用xIntraCodingTUBlock计算相应的失真,从而计算RD Cost,从而选出最佳变换。
tmpValidReturn = xRecurIntraCodingLumaQT(
*csTemp, partitioner, uiBestPUMode.ispMod ? bestCurrentCost : MAX_DOUBLE, -1, TU_NO_ISP,
uiBestPUMode.ispMod, mtsCheckRangeFlag, mtsFirstCheckId, mtsLastCheckId, moreProbMTSIdxFirst);
}
}
if (!cu.ispMode && !cu.mtsFlag && !cu.lfnstIdx && !cu.bdpcmMode && !pu.multiRefIdx && !cu.mipFlag && testISP)
{
m_regIntraRDListWithCosts.push_back( ModeInfoWithCost( cu.mipFlag, pu.mipTransposedFlag, pu.multiRefIdx, cu.ispMode, uiOrgMode.modeId, csTemp->cost ) );
}
//如果当前CU是ISP模式,且第一块TU的CBF为0(无残差)
if( cu.ispMode && !csTemp->cus[0]->firstTU->cbf[COMPONENT_Y] )
{
csTemp->cost = MAX_DOUBLE;
csTemp->costDbOffset = 0;
tmpValidReturn = false;
}
validReturn |= tmpValidReturn;
//如果SPS层开启LFNST,且可以使用MTS并正在检查DCT-2,且不用ISP。且当前模式号>=0
if( sps.getUseLFNST() && mtsUsageFlag == 1 && !cu.ispMode && mode >= 0 )
{
//将当前RD cost保存以在检查MTS时使用快速算法
m_modeCostStore[lfnstIdx][mode] = tmpValidReturn ? csTemp->cost : (MAX_DOUBLE / 2.0); //(MAX_DOUBLE / 2.0) ??
}
DTRACE(g_trace_ctx, D_INTRA_COST, "IntraCost T [x=%d,y=%d,w=%d,h=%d] %f (%d,%d,%d,%d,%d,%d) \n", cu.blocks[0].x,
cu.blocks[0].y, (int) width, (int) height, csTemp->cost, uiOrgMode.modeId, uiOrgMode.ispMod,
pu.multiRefIdx, cu.mipFlag, cu.lfnstIdx, cu.mtsFlag);
//如果是有效返回值,比较最优模式代价和当前模式代价
if( tmpValidReturn )
{
//如果是第一色彩空间
if (isFirstColorSpace)
{
//如果是RGB格式,或者不是ISP模式
if (m_pcEncCfg->getRGBFormatFlag() || !cu.ispMode)
{
//对RdModeList排序
sortRdModeListFirstColorSpace(uiOrgMode, csTemp->cost, cu.bdpcmMode, m_savedRdModeFirstColorSpace[m_savedRdModeIdx], m_savedRdCostFirstColorSpace[m_savedRdModeIdx], m_savedBDPCMModeFirstColorSpace[m_savedRdModeIdx], m_numSavedRdModeFirstColorSpace[m_savedRdModeIdx]);
}
}
// check r-d cost
//检查RD cost
//如果当前模式的cost小于目前最优模式cost
if( csTemp->cost < csBest->cost )
{
//当前模式成为新的最优模式
std::swap( csTemp, csBest );
uiBestPUMode = uiOrgMode;
bestBDPCMMode = cu.bdpcmMode;
//如果使用LFNST
if( sps.getUseLFNST() && mtsUsageFlag == 1 && !cu.ispMode )
{
m_bestModeCostStore[ lfnstIdx ] = csBest->cost; //cs.cost;
m_bestModeCostValid[ lfnstIdx ] = true;
}
if( csBest->cost < bestCurrentCost )
{
bestCurrentCost = csBest->cost;
}
//如果是ISP模式
if ( cu.ispMode )
{
m_modeCtrl->setIspCost(csBest->cost);
bestLfnstIdx = cu.lfnstIdx;
}
//如果使用ISP,且不启用ACT变换
else if ( testISP )
{
m_modeCtrl->setMtsFirstPassNoIspCost(csBest->cost);
}
}//if( csTemp->cost < csBest->cost )
//如果不是ISP模式,且不是BDPCM模式,且当前cost小于不用BDPCM的最佳cost
if( !cu.ispMode && !cu.bdpcmMode && csBest->cost < bestCostNonBDPCM )
{
//更新不用BDPCM的最佳cost
bestCostNonBDPCM = csBest->cost;
}
}//if( tmpValidReturn )
//清空CU,PU,TU
csTemp->releaseIntermediateData();
if( m_pcEncCfg->getFastLocalDualTreeMode() )
{
if( cu.isConsIntra() && !cu.slice->isIntra() && csBest->cost != MAX_DOUBLE && costInterCU != COST_UNKNOWN && mode >= 0 )
{
if( m_pcEncCfg->getFastLocalDualTreeMode() == 2 )
{
//Note: only try one intra mode, which is especially useful to reduce EncT for LDB case (around 4%)
//仅尝试一种帧内模式,这对于减少LDB情况下的EncT尤其有用(大约4%)
break;
}
else
{
if( csBest->cost > costInterCU * 1.5 )
{
break;
}
}
}
}
//启用ACT变换
if (sps.getUseColorTrans() && !CS::isDualITree(cs))
{
if ((m_pcEncCfg->getRGBFormatFlag() && !cu.colorTransform) && csBest->cost != MAX_DOUBLE && bestCS->cost != MAX_DOUBLE && mode >= 0)
{
if (csBest->cost > bestCS->cost)
{
break;
}
}
}
} // Mode loop RD候选模式的循环遍历结束,选出最优模式
//设置最优模式
cu.ispMode = uiBestPUMode.ispMod;
cu.lfnstIdx = bestLfnstIdx;
if( validReturn )
{
//如果色彩转换
if (cu.colorTransform)
{
cs.useSubStructure(*csBest, partitioner.chType, pu, true, true, KEEP_PRED_AND_RESI_SIGNALS, KEEP_PRED_AND_RESI_SIGNALS, true);
}
else
{
cs.useSubStructure(*csBest, partitioner.chType, pu.singleChan(CHANNEL_TYPE_LUMA), true, true, KEEP_PRED_AND_RESI_SIGNALS,
KEEP_PRED_AND_RESI_SIGNALS, true);
}
}
csBest->releaseIntermediateData();
if( validReturn )
{
//=== update PU data ====
//更新PU数据
cu.mipFlag = uiBestPUMode.mipFlg;
pu.mipTransposedFlag = uiBestPUMode.mipTrFlg;
pu.multiRefIdx = uiBestPUMode.mRefId;
pu.intraDir[ CHANNEL_TYPE_LUMA ] = uiBestPUMode.modeId;
cu.bdpcmMode = bestBDPCMMode;
if (cu.colorTransform)
{
CHECK(pu.intraDir[CHANNEL_TYPE_CHROMA] != DM_CHROMA_IDX, "chroma should use DM mode for adaptive color transform");
}
}
}
//===== reset context models =====
//重置上下文模型
m_CABACEstimator->getCtx() = ctxStart;
return validReturn;
}
二、逻辑结构
1.[2]-[183]:初始化相关参数(包括确认lambda、加载上下文、确定LFNST标志、确定MTS标志等)
2.[185]-[201]:若当前为ACT变换后的颜色空间,则直接从原颜色空间的模式列表中加载测试的模式列表
3.[202]-[851]:若当前不是ACT变换后的颜色空间,则开始选择测试模式
(1) [206]-[732]:mtsUsageFlag != 2
若帧内全搜索,则提示MIP不支持帧内全搜索。若非帧内全搜索,则:
- [208]-[268]:检查确定相关参数,定位当前区域,并设置SAD、HAD参数,若测试MIP,则在最终RD cost列表中预留对应数量位置。
- [284]-[362]:先对非扩展角度模式进行第一轮粗选([288]-[347]),若不使用MIP且LFNST保存标志有效,则将粗选结果保存([350]-[361])。
- [365]-[374]:若SPS层不使用MIP且LFNST加载标志有效,则直接加载之前保存的粗选结果。
- [377]-[642]:若SPS层不使用MIP,或者LFNST加载标志无效,则先对第一轮选出的候选角度模式的扩展角度模式进行第二轮粗选([386]-[441])。再判断为ISP保存数据的标志位,若有效,则将此时候选列表保存([444]-[449])。再对候选列表中的模式遍历多参考行,更新候选模式列表([461]-[512])。若LFNST保存标志和MIP测试标志同时有效,且不允许LFNST与MIP同时存在,则保存候选列表([518]-[536])。下面开始添加MIP模式:若MIP测试标志有效,但当前块大小不支持MIP,则直接将所有MIP模式加入候选模式列表([543]-[566])。若需要测试MIP模式(块大小也支持MIP),则遍历所有可能MIP模式,更新候选模式列表([568]-[627])。若SPS允许使用MIP,且LFNST保存标志有效,则保存候选列表([631]-[641])。
- [646]-[655]:若SPS层使用MIP,且需要为LFNST加载候选,则直接加载候选模式列表。
- [659]-[711] :获取FASTUDI的MPM列表,MPM中的模式若不存在于候选模式列表,则添加。同样为ISP添加候选模式。
- [723]-[731]:若SPS层可用LFNST,且当前CU可用MTS,为进行MTS变换检查保存RD模式列表。
(2)[736]-[773]:mtsUsageFlag == 2 时
- [739]-[759]:LFNST快速算法或者不是I帧(是B帧或P帧),且最佳模式cost有效时,检查RD候选模式中,与根据LFNSTIndex索引的最优模式代价比小于阈值的模式,将其加入RD候选模式中。
- [764]-[772]:是I帧,或最佳模式cost无效时,直接复制之前保存的RD候选模式列表。
(3) [783]-[850]:快速帧内模式(PBINTRA fast)
- [791]-[801]:遍历RDmodelist找到最优MIP模式。
- [804]-[850]:记录HAD候选列表最大长度。最大长度不为0时,将RDmodelist长度设置为原长度和HAD候选列表最大长度中的较小值,再判断最佳MIP模式是否包含在缩小后的RDmodelist中,若不,则添加进RDmodelist。若为ISP保存数据,则将ISP候选列表也设置为原长度和HAD候选列表最大长度中的较小值。重置上下文模型。
4.[857]-[869]:为ISP在RD列表中保留位置
5.[907]-[1141]:遍历RD列表精选,选出最佳模式
6.[1143]-[1180]:结尾工作(设置最优模式、更新PU参数、重置上下文模型等)
上一篇:H.266/VVC-VTM代码学习-帧内预测10-色度预测的CCLM模式(2)xGetLMParameters函数求解CCLM参数α和β
下一篇:H.266/VVC-VTM代码学习-帧内预测12-编码端获取MPM列表getIntraMPMs函数