H.266/VVC代码学习:xCompressCU函数_涵小呆的博客-优快云博客
尝试当前编码器各种可用的模式:如skip,帧间,帧内,PCM等,进行预测及划分。
do
{
} while( m_modeCtrl->nextMode( *tempCS, partitioner ) );
for (int i = compBegin; i < (compBegin + numComp); i++)
{
ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
tempCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
memcpy(tempCS->prevPLT.curPLT[i], curLastPLT[i], curLastPLTSize[comID] * sizeof(Pel));
}
EncTestMode currTestMode = m_modeCtrl->currTestMode();
currTestMode.maxCostAllowed = maxCostAllowed;
for循环:遍历分量,将 PLT 的相关变量填充。当前帧未使用PLT,先跳过
currTestMode:当前要测试的模式
maxCostAllowed:当前测试模式允许的最大cost
if (pps.getUseDQP() && partitioner.isSepTree(*tempCS) && isChroma( partitioner.chType ))
{
const Position chromaCentral(tempCS->area.Cb().chromaPos().offset(tempCS->area.Cb().chromaSize().width >> 1, tempCS->area.Cb().chromaSize().height >> 1));
const Position lumaRefPos(chromaCentral.x << getComponentScaleX(COMPONENT_Cb, tempCS->area.chromaFormat), chromaCentral.y << getComponentScaleY(COMPONENT_Cb, tempCS->area.chromaFormat));
const CodingStructure* baseCS = bestCS->picture->cs;
const CodingUnit* colLumaCu = baseCS->getCU(lumaRefPos, CHANNEL_TYPE_LUMA);
if (colLumaCu)
{
currTestMode.qp = colLumaCu->qp;
}
}
dqp:delta QP
【笔记】HEVC 编码标准(六)——量化_lock。的博客-优快云博客_hevc 量化
这个函数放在后面去看
if( currTestMode.type == ETM_INTER_ME )
{
if( ( currTestMode.opts & ETO_IMV ) != 0 )
{
const bool skipAltHpelIF = ( int( ( currTestMode.opts & ETO_IMV ) >> ETO_IMV_SHIFT ) == 4 ) && ( bestIntPelCost > 1.25 * bestCS->cost );
if (!skipAltHpelIF)
{
tempCS->bestCS = bestCS;
xCheckRDCostInterIMV(tempCS, bestCS, partitioner, currTestMode, bestIntPelCost);
tempCS->bestCS = nullptr;
}
}
else
{
tempCS->bestCS = bestCS;
xCheckRDCostInter( tempCS, bestCS, partitioner, currTestMode );
tempCS->bestCS = nullptr;
}
}
当前测试模式为inter时启用
// 若当前测试模式是 HASH INTER
// 则检查 HashInter 的 RD cost
else if (currTestMode.type == ETM_HASH_INTER)
{
xCheckRDCostHashInter( tempCS, bestCS, partitioner, currTestMode );
}
// 若当前测试模式是 AFFINE
// 则检查 Affine 的 RD cost
else if( currTestMode.type == ETM_AFFINE )
{
xCheckRDCostAffineMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode );
}
#if REUSE_CU_RESULTS
// 若当前测试模式是 RECO_CACHED
// 则检查 Reuse Cached 的 RD cost
else if( currTestMode.type == ETM_RECO_CACHED )
{
xReuseCachedResult( tempCS, bestCS, partitioner );
}
// 若当前测试模式是 MERGE_SKIP
// 则检查 Merge 的 RD cost
else if( currTestMode.type == ETM_MERGE_SKIP )
{
xCheckRDCostMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode );
CodingUnit* cu = bestCS->getCU(partitioner.chType);
if (cu)
{
cu->mmvdSkip = cu->skip == false ? false : cu->mmvdSkip;
}
}
// 若当前测试模式是 MERGE_GEO
// 则检查 Merge Geo 的 RD cost
else if( currTestMode.type == ETM_MERGE_GEO )
{
xCheckRDCostMergeGeo2Nx2N( tempCS, bestCS, partitioner, currTestMode );
}
之后再细看
else if( currTestMode.type == ETM_INTRA )
{
//若使用色彩变换且不为dual tree
if (slice.getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS))
{
//不跳过第二色彩空间
bool skipSecColorSpace = false;
skipSecColorSpace = xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? true : false));
if ((m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()) && !m_pcEncCfg->getRGBFormatFlag())
{
skipSecColorSpace = true;
}
if (!skipSecColorSpace && !tempCS->firstColorSpaceTestOnly)
{
xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? false : true));
}
if (!tempCS->firstColorSpaceTestOnly)
{
if (tempCS->tmpColorSpaceIntraCost[0] != MAX_DOUBLE && tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE)
{
double skipCostRatio = m_pcEncCfg->getRGBFormatFlag() ? 1.1 : 1.0;
if (tempCS->tmpColorSpaceIntraCost[1] > (skipCostRatio*tempCS->tmpColorSpaceIntraCost[0]))
{
tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;
}
}
}
else
{
CHECK(tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE, "the RD test of the second color space should be skipped");
}
}
else
{
xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, false);
}
}
当前测试模式是intra
// 若当前测试模式是 PALETTE
// 则检查 PLT 的 RD cost
else if (currTestMode.type == ETM_PALETTE)
{
xCheckPLT( tempCS, bestCS, partitioner, currTestMode );
}
// 若当前测试模式是 IBC(Intra block copy)
// 则检查 IBC 的 RD cost
else if (currTestMode.type == ETM_IBC)
{
xCheckRDCostIBCMode(tempCS, bestCS, partitioner, currTestMode);
}
// 若当前测试模式是 IBC_MERGE
// 则检查 IBC_MERGE 的 RD cost
else if (currTestMode.type == ETM_IBC_MERGE)
{
xCheckRDCostIBCModeMerge2Nx2N(tempCS, bestCS, partitioner, currTestMode);
}
之后再看
else if( isModeSplit( currTestMode ) )
{
if (bestCS->cus.size() != 0) //size()返回当前序列长度,不为0表示已划分过
{
splitmode = bestCS->cus[0]->splitSeries;
}
assert( partitioner.modeType == tempCS->modeType );
int signalModeConsVal = tempCS->signalModeCons( getPartSplit( currTestMode ), partitioner, modeTypeParent );
int numRoundRdo = signalModeConsVal == LDT_MODE_TYPE_SIGNAL ? 2 : 1;
bool skipInterPass = false;
// 遍历 RDO 轮次
for( int i = 0; i < numRoundRdo; i++ )
{
//change cons modes
// 若当前信号模式值为 LDT_MODE_TYPE_SIGNAL
// 则当前测试模式类型在第一轮设置为 INTER,在第二轮设置为 INTRA
if( signalModeConsVal == LDT_MODE_TYPE_SIGNAL )
{
CHECK( numRoundRdo != 2, "numRoundRdo shall be 2 - [LDT_MODE_TYPE_SIGNAL]" );
tempCS->modeType = partitioner.modeType = (i == 0) ? MODE_TYPE_INTER : MODE_TYPE_INTRA;
}
// 若当前信号模式值为 LDT_MODE_TYPE_INFER
// 则当前测试模式类型为 INTRA
else if( signalModeConsVal == LDT_MODE_TYPE_INFER )
{
CHECK( numRoundRdo != 1, "numRoundRdo shall be 1 - [LDT_MODE_TYPE_INFER]" );
tempCS->modeType = partitioner.modeType = MODE_TYPE_INTRA;
}
// 若当前信号模式值为 LDT_MODE_TYPE_INHERIT
// 则当前测试模式类型为父结点模式类型
else if( signalModeConsVal == LDT_MODE_TYPE_INHERIT )
{
CHECK( numRoundRdo != 1, "numRoundRdo shall be 1 - [LDT_MODE_TYPE_INHERIT]" );
tempCS->modeType = partitioner.modeType = modeTypeParent;
}
//for lite intra encoding fast algorithm, set the status to save inter coding info
//帧间模式
// 若父结点模式类型为 MODE_TYPE_ALL(可以尝试所有类型),且当前测试模式类型为 Inter
// 则 SaveCuCostInSCIPU,且设置 NumCuInSCIPU 为0
if( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType == MODE_TYPE_INTER )
{
m_pcIntraSearch->setSaveCuCostInSCIPU( true );
m_pcIntraSearch->setNumCuInSCIPU( 0 );
}
// 若父结点模式类型为 MODE_TYPE_ALL(可以尝试所有类型),且当前测试模式类型不为 Inter
// 则 SaveCuCostInSCIPU 为 false,且当前测试模式 MODE_TYPE_ALL 时设置 NumCuInSCIPU 为0
else if( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType != MODE_TYPE_INTER )
{
m_pcIntraSearch->setSaveCuCostInSCIPU( false );
if( tempCS->modeType == MODE_TYPE_ALL )
{
m_pcIntraSearch->setNumCuInSCIPU( 0 );
}
}
xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode, modeTypeParent, skipInterPass );
//recover cons modes
tempCS->modeType = partitioner.modeType = modeTypeParent;
tempCS->treeType = partitioner.treeType = treeTypeParent;
partitioner.chType = chTypeParent;
if( modeTypeParent == MODE_TYPE_ALL )
{
m_pcIntraSearch->setSaveCuCostInSCIPU( false );
if( numRoundRdo == 2 && tempCS->modeType == MODE_TYPE_INTRA )
{
m_pcIntraSearch->initCuAreaCostInSCIPU();
}
}
if( skipInterPass )
{
break;
}
}
#if GDR_ENABLED
if (bestCS->cus.size() > 0 && splitmode != bestCS->cus[0]->splitSeries)
#else
if (splitmode != bestCS->cus[0]->splitSeries)
#endif
{
splitmode = bestCS->cus[0]->splitSeries;
const CodingUnit& cu = *bestCS->cus.front();
cu.cs->prevPLT = bestCS->prevPLT;
for (int i = compBegin; i < (compBegin + numComp); i++)
{
ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
bestLastPLTSize[comID] = bestCS->cus[0]->cs->prevPLT.curPLTSize[comID];
memcpy(bestLastPLT[i], bestCS->cus[0]->cs->prevPLT.curPLT[i], bestCS->cus[0]->cs->prevPLT.curPLTSize[comID] * sizeof(Pel));
}
}
}
else
{
THROW( "Don't know how to handle mode: type = " << currTestMode.type << ", options = " << currTestMode.opts );
}
} while( m_modeCtrl->nextMode( *tempCS, partitioner ) );
当前测试模式为划分
SplitSeries 类:对于每个cu尺寸的具体的划分顺序的一个参数
VVC学习之四:VTM中的数据结构——CodingUnit、PredictionUnit、TransformUnit_Aidoneus_y的博客-优快云博客
bestCS->cus.size():若当前CU已划分,则当前BestCs的cu数目就不为零。此时设置splitmode,这个好像与在帧内预测中的一个叫getSplitSeries()函数有关,之后看到帧内时再写。推测表示划分时的顺序
assert:检验当前CS结构modeType是否和划分类中设定的modeType相同,如果不同,则终止程序
getPartSplit():从当前测试模式中得到当前CU的划分方式
https://blog.youkuaiyun.com/dfhg54/article/details/124284888
signalModeConsVal():获得当前信号模式值,规定了子节点是否继承父节点的模式测试方法。0,1,2分别代表三个模式。如0代表LDT_MODE_TYPE_INHERIT
numRoundRdo:当前信号模式值为 LDT_MODE_TYPE_SIGNAL,则Rdo轮次为2
for循环:注释在代码里
xCheckModeSplit():划分模式,这里会递归调用xcompressCu
//
// Finishing CU
if( tempCS->cost == MAX_DOUBLE && bestCS->cost == MAX_DOUBLE )
{
//although some coding modes were planned to be tried in RDO, no coding mode actually finished encoding due to early termination
//thus tempCS->cost and bestCS->cost are both MAX_DOUBLE; in this case, skip the following process for normal case
m_modeCtrl->finishCULevel( partitioner );
return;
}
// set context states
m_CABACEstimator->getCtx() = m_CurrCtx->best;
如果tempCS和bestCS中的cost都等于double最大值,则finishCULevel()中实现
m_ComprCUCtxList.pop_back();
在运行完以后,m_ComprCUCtxList的size减少了1.原本的那层出了intra没有其他模式,所以当前CU已经算测试完了,就把栈中这个CU的信息消去,返回上一层父CU
// QP from last processed CU for further processing
//copy the qp of the last non-chroma CU
int numCUInThisNode = (int)bestCS->cus.size();
if( numCUInThisNode > 1 && bestCS->cus.back()->chType == CHANNEL_TYPE_CHROMA && !CS::isDualITree( *bestCS ) )
{
CHECK( bestCS->cus[numCUInThisNode-2]->chType != CHANNEL_TYPE_LUMA, "wrong chType" );
bestCS->prevQP[partitioner.chType] = bestCS->cus[numCUInThisNode-2]->qp;
}
else
{
bestCS->prevQP[partitioner.chType] = bestCS->cus.back()->qp;
}
if ((!slice.isIntra() || slice.getSPS()->getIBCFlag())
&& partitioner.chType == CHANNEL_TYPE_LUMA
&& bestCS->cus.size() == 1 && (bestCS->cus.back()->predMode == MODE_INTER || bestCS->cus.back()->predMode == MODE_IBC)
&& bestCS->area.Y() == (*bestCS->cus.back()).Y()
)
{
const CodingUnit& cu = *bestCS->cus.front();
bool isIbcSmallBlk = CU::isIBC(cu) && (cu.lwidth() * cu.lheight() <= 16);
CU::saveMotionInHMVP( cu, isIbcSmallBlk );
}
// 将预测像素和重建像素复制到bestCS->picture中
bestCS->picture->getPredBuf(currCsArea).copyFrom(bestCS->getPredBuf(currCsArea));
bestCS->picture->getRecoBuf( currCsArea ).copyFrom( bestCS->getRecoBuf( currCsArea ) );
m_modeCtrl->finishCULevel( partitioner );
if( m_pcIntraSearch->getSaveCuCostInSCIPU() && bestCS->cus.size() == 1 )
{
m_pcIntraSearch->saveCuAreaCostInSCIPU( Area( partitioner.currArea().lumaPos(), partitioner.currArea().lumaSize() ), bestCS->cost );
}
这部分代码有预测和重建像素,要结合后面的预测函数来看
if (bestCS->cus.size() == 1) // no partition
{
CHECK(bestCS->cus[0]->tileIdx != bestCS->pps->getTileIdx(bestCS->area.lumaPos()), "Wrong tile index!");
if (bestCS->cus[0]->predMode == MODE_PLT)
{
for (int i = compBegin; i < (compBegin + numComp); i++)
{
ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
bestCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
memcpy(bestCS->prevPLT.curPLT[i], curLastPLT[i], curLastPLTSize[comID] * sizeof(Pel));
}
bestCS->reorderPrevPLT(bestCS->prevPLT, bestCS->cus[0]->curPLTSize, bestCS->cus[0]->curPLT, bestCS->cus[0]->reuseflag, compBegin, numComp, jointPLT);
}
else
{
for (int i = compBegin; i<(compBegin + numComp); i++)
{
ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
bestCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
memcpy(bestCS->prevPLT.curPLT[i], curLastPLT[i], bestCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
}
}
}
else
{
for (int i = compBegin; i<(compBegin + numComp); i++)
{
ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
bestCS->prevPLT.curPLTSize[comID] = bestLastPLTSize[comID];
memcpy(bestCS->prevPLT.curPLT[i], bestLastPLT[i], bestCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
}
}
const CodingUnit& cu = *bestCS->cus.front();
cu.cs->prevPLT = bestCS->prevPLT;
// Assert if Best prediction mode is NONE
// Selected mode's RD-cost must be not MAX_DOUBLE.
CHECK( bestCS->cus.empty() , "No possible encoding found" );
CHECK( bestCS->cus[0]->predMode == NUMBER_OF_PREDICTION_MODES, "No possible encoding found" );
CHECK( bestCS->cost == MAX_DOUBLE , "No possible encoding found" );
}
与PLT模式有关,之后再看