此系列是为了记录自己学习VTM10.0的过程,目前正在看编码端。主要的参考文档有JVET-S2001-vH和JVET-S2002-v1。由于本人水平有限,出现的错误恳请大家指正。如果觉得我的文章对您有帮助的话,不妨点赞收藏加关注,谢谢!!
本篇博文就是这个系列的最后一篇了,从开始看VTM到现在差不多9个月,之中断断续续地把VTM的主干大致过了一遍,虽然有很多部分都略过未看,但也对VVC有了大致的认识,算得上是实现开始更新这个系列时的初心。之后如果没有找到想跟大家分享的内容,应该就是无期停更了。欢迎大家跟我讨论有关VTM的问题,一起加深理解。那么接下来就说说有关merge的内容,即函数xCheckRDCostMerge2Nx2N()
1. 预备阶段
const Slice &slice = *tempCS->slice;
tempCS->initStructData( encTestMode.qp );
MergeCtx mergeCtx;
const SPS &sps = *tempCS->sps;
Mv refinedMvdL0[MAX_NUM_PARTS_IN_CTU][MRG_MAX_NUM_CANDS];// 用于保存有关DMVR的变量
setMergeBestSATDCost( MAX_DOUBLE );
mergeCtx: 存储有关merge候选的信息
refinedMvdL0: 用于保存有关DMVR的变量
setMergeBestSATDCost: 设置m_mergeBestSATDCost,表示开启merge时的最佳的化简版RDcost
CodingUnit cu( tempCS->area );
cu.cs = tempCS;
cu.predMode = MODE_INTER;
cu.slice = tempCS->slice;
cu.tileIdx = tempCS->pps->getTileIdx( tempCS->area.lumaPos() );
PredictionUnit pu( tempCS->area );
pu.cu = &cu;
pu.cs = tempCS;
PU::getInterMergeCandidates(pu, mergeCtx
, 0
);// 获取merge候选MVP列表
PU::getInterMMVDMergeCandidates(pu, mergeCtx);// 获取用于MMVD的候选MVP
pu.regularMergeFlag = true;
这段主要是获取merge候选MVP列表和用于MMVD的候选MVP
bool candHasNoResidual[MRG_MAX_NUM_CANDS + MMVD_ADD_NUM];// 为true表示对应merge候选在非skip下不存在有关残差的语法元素
for (uint32_t ui = 0; ui < MRG_MAX_NUM_CANDS + MMVD_ADD_NUM; ui++)
{
candHasNoResidual[ui] = false;
}
bool bestIsSkip = false;// 最佳模式是否是skip
bool bestIsMMVDSkip = true;// 最佳模式是否是MMVD下的skip
// 存储着最终预测值,按merge候选列表排列
PelUnitBuf acMergeBuffer[MRG_MAX_NUM_CANDS];
// 存储着非最终预测值,如果是双向预测值,应当是不经过DMVR、BCW和BDOF处理的,用于CIIP
PelUnitBuf acMergeTmpBuffer[MRG_MAX_NUM_CANDS];
PelUnitBuf acMergeRealBuffer[MMVD_MRG_MAX_RD_BUF_NUM];
// 存储着最终预测值,与RdModeList对应
PelUnitBuf * acMergeTempBuffer[MMVD_MRG_MAX_RD_NUM];
PelUnitBuf * singleMergeTempBuffer;
int insertPos;// 当前merge候选在RdModeList插入的位置
unsigned uiNumMrgSATDCand = mergeCtx.numValidMergeCand + MMVD_ADD_NUM;// RdModeList的最大长度
一些变量,含义参考注释
struct ModeInfo
{
//这里的regular merge和MMVD是区分开来的
uint32_t mergeCand;
bool isRegularMerge;
bool isMMVD;
bool isCIIP;
ModeInfo() : mergeCand(0), isRegularMerge(false), isMMVD(false), isCIIP(false) {
}
ModeInfo(const uint32_t mergeCand, const bool isRegularMerge, const bool isMMVD, const bool isCIIP) :
mergeCand(mergeCand), isRegularMerge(isRegularMerge), isMMVD(isMMVD), isCIIP(isCIIP) {
}
};
static_vector<ModeInfo, MRG_MAX_NUM_CANDS + MMVD_ADD_NUM> RdModeList;// merge候选的Index列表,以化简版RDcost从小到大排列
bool mrgTempBufSet = false;// acMergeTmpBuffer和acMergeTempBuffer是否存在有效值
const int candNum = mergeCtx.numValidMergeCand + (tempCS->sps->getUseMMVD() ? std::min<int>(MMVD_BASE_MV_NUM, mergeCtx.numValidMergeCand) * MMVD_MAX_REFINE_NUM : 0);
for (int i = 0; i < candNum; i++)
{
if (i < mergeCtx.numValidMergeCand)
{
RdModeList.push_back(ModeInfo(i, true, false, false));
}
else
{
RdModeList.push_back(ModeInfo(std::min(MMVD_ADD_NUM, i - mergeCtx.numValidMergeCand), false, true, false));
}
}
这段主要用来设置RdModeList,是merge候选的Index列表,以化简版RDcost从小到大排列,用于存储merge模式粗选的结果
const UnitArea localUnitArea(tempCS->area.chromaFormat, Area(0, 0, tempCS->area.Y().width, tempCS->area.Y().height));
for (unsigned i = 0; i < MMVD_MRG_MAX_RD_BUF_NUM; i++)
{
acMergeRealBuffer[i] = m_acMergeBuffer[i].getBuf(localUnitArea);
if (i < MMVD_MRG_MAX_RD_NUM)
{
acMergeTempBuffer[i] = acMergeRealBuffer + i;
}
else
{
singleMergeTempBuffer = acMergeRealBuffer + i;
}
}
获取缓存
bool isIntrainterEnabled = sps.getUseCiip();// 是否允许测试CIIP
if (bestCS->area.lwidth() * bestCS->area.lheight() < 64 || bestCS->area.lwidth() >= MAX_CU_SIZE || bestCS->area.lheight() >= MAX_CU_SIZE)
{
isIntrainterEnabled = false;
}
// 对应merge候选是否测试过开启skip
bool isTestSkipMerge[MRG_MAX_NUM_CANDS]; // record if the merge candidate has tried skip mode
for (uint32_t idx = 0; idx < MRG_MAX_NUM_CANDS; idx++)
{
isTestSkipMerge[idx] = false;
}
isIntrainterEnabled: 是否允许测试CIIP
isTestSkipMerge: 对应merge候选是否测试过开启skip
2. 用化简版RDcost对merge候选进行筛选
uiNumMrgSATDCand = NUM_MRG_SATD_CAND;
if (isIntrainterEnabled)
{
uiNumMrgSATDCand += 1;
}
bestIsSkip = false;
if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
{
if (slice.getSPS()->getIBCFlag())
{
ComprCUCtx cuECtx = m_modeCtrl->getComprCUCtx();
bestIsSkip = blkCache->isSkip(tempCS->area) && cuECtx.bestCU;
}
else
bestIsSkip = blkCache-