三角预测模式,首先会获取一个三角预测单向候选模式列表,表长为5,就像普通merge模式的函数流程那样;
然后MC计算5个候选的预测像素pred,方便之后的40种三角组合模式的pred像素加权计算;
对40种三角组合模式进行SATD,计算出最优的3种组合模式;
对SATD得到的最优的三角组合预测模式,RDO得到最优的一种三角组合预测模式。
void EncCu::xCheckRDCostMergeTriangle2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
{
const Slice &slice = *tempCS->slice;
const SPS &sps = *tempCS->sps;
CHECK( slice.getSliceType() != B_SLICE, "Triangle mode is only applied to B-slices" ); //M0329扩展到了P
tempCS->initStructData( encTestMode.qp, encTestMode.lossless ); //tempCS清空
bool trianglecandHasNoResidual[TRIANGLE_MAX_NUM_CANDS];
for( int mergeCand = 0; mergeCand < TRIANGLE_MAX_NUM_CANDS; mergeCand++ )
{
trianglecandHasNoResidual[mergeCand] = false;
}
#if JVET_L0293_CPR
bool bestIsSkip;
CodingUnit* cuTemp = bestCS->getCU(partitioner.chType);
if (cuTemp)
bestIsSkip = m_pcEncCfg->getUseFastDecisionForMerge() ? bestCS->getCU(partitioner.chType)->rootCbf == 0 : false;
else
bestIsSkip = false;
#else
bool bestIsSkip = m_pcEncCfg->getUseFastDecisionForMerge() ? bestCS->getCU( partitioner.chType )->rootCbf == 0 : false;
#endif
uint8_t numTriangleCandidate = TRIANGLE_MAX_NUM_CANDS; //40
uint8_t triangleNumMrgSATDCand = TRIANGLE_MAX_NUM_SATD_CANDS; //SATD选择3个mvp
PelUnitBuf triangleBuffer[TRIANGLE_MAX_NUM_UNI_CANDS]; //存储构建的三角预测单向列表中5个mvp的预测像素
PelUnitBuf triangleWeightedBuffer[TRIANGLE_MAX_NUM_CANDS];//存储40种三角组合模式的加权像素值
static_vector<uint8_t, TRIANGLE_MAX_NUM_CANDS> triangleRdModeList;
static_vector<double, TRIANGLE_MAX_NUM_CANDS> tianglecandCostList; //modeList和costList
if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
{
bestIsSkip |= blkCache->isSkip( tempCS->area );
}
DistParam distParam;
const bool useHadamard = !encTestMode.lossless;
m_pcRdCost->setDistParam( distParam, tempCS->getOrgBuf().Y(), m_acMergeBuffer[0].Y(), sps.getBitDepth( CHANNEL_TYPE_LUMA ), COMPONENT_Y, useHadamard );
const UnitArea localUnitArea( tempCS->area.chromaFormat, Area( 0, 0, tempCS->area.Y().width, tempCS->area.Y().height) );
const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda(encTestMode.lossless);
MergeCtx triangleMrgCtx; //三角预测候选列表
{
CodingUnit cu( tempCS->area );
cu.cs = tempCS;
cu.partSize = SIZE_2Nx2N;
cu.predMode = MODE_INTER;
cu.slice = tempCS->slice;
cu.triangle = true;
#if JVET_L0054_MMVD
cu.mmvdSkip = false;
#endif
#if JVET_L0646_GBI
cu.GBiIdx = GBI_DEFAULT;
#endif
PredictionUnit pu( tempCS->area );
pu.cu = &cu;
pu.cs = tempCS;
PU::getTriangleMergeCandidates( pu, triangleMrgCtx ); // 获取三角预测单向MV列表
for( uint8_t mergeCand = 0; mergeCand < TRIANGLE_MAX_NUM_UNI_CANDS; mergeCand++ ) //对5个三角预测候选分别MC
{
triangleBuffer[mergeCand] = m_acMergeBuffer[mergeCand].getBuf(localUnitArea);
triangleMrgCtx.setMergeInfo( pu, mergeCand ); //给pu赋值单向三角候选的motioninfo
PU::spanMotionInfo( pu, triangleMrgCtx );
//直接取单向mv,通过MC得到pred像素,存储于triangleBuffer
m_pcInterSearch->motionCompensation( pu, triangleBuffer[mergeCand] ); //MC 得到三角单向候选的pred像素,方便之后组合三角模式的计算
}
}
bool tempBufSet = bestIsSkip ? false : true;
triangleNumMrgSATDCand = bestIsSkip ? TRIANGLE_MAX_NUM_CANDS : TRIANGLE_MAX_NUM_SATD_CANDS;
if( bestIsSkip )
{
for( uint8_t i = 0; i < TRIANGLE_MAX_NUM_CANDS; i++ ) //skip时需要RDO选择40个三角模式
{
triangleRdModeList.push_back(i);
}
}
else //SATD
{ //SATD从40个三角模式中选出最优的3个模式
CodingUnit &cu = tempCS->addCU( tempCS->area, partitioner.chType );
partitioner.setCUData( cu );
cu.slice = tempCS->slice;
cu.skip = false;
cu.partSize = SIZE_2Nx2N;
cu.predMode = MODE_INTER;
cu.transQuantBypass = encTestMode.lossless;
cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
cu.triangle = true;
#if JVET_L0054_MMVD
cu.mmvdSkip = false;
#endif
#if JVET_L0646_GBI
cu.GBiIdx = GBI_DEFAULT;
#endif
PredictionUnit &pu = tempCS->addPU( cu, partitioner.chType ); //tempCS添加cu pu
if( abs(g_aucLog2[cu.lwidth()] - g_aucLog2[cu.lheight()]) >= 2 )
{
numTriangleCandidate = 30; //当前块的长宽比,大于等于4时
}
else
{
numTriangleCandidate = TRIANGLE_MAX_NUM_CANDS; //40
}
for( uint8_t mergeCand = 0; mergeCand < numTriangleCandidate; mergeCand++ ) //loop 40种三角组合模式
{ //g_triangleCombination即为三角预测的那张表格
bool splitDir = g_triangleCombination[mergeCand][0]; //三角划分方向,对角反对角
uint8_t candIdx0 = g_triangleCombination[mergeCand][1]; //第一个三角的候选idx
uint8_t candIdx1 = g_triangleCombination[mergeCand][2]; //第二个三角的候选idx
pu.mergeIdx = mergeCand;
pu.mergeFlag = true;
triangleWeightedBuffer[mergeCand] = m_acTriangleWeightedBuffer[mergeCand].getBuf( localUnitArea );
triangleBuffer[candIdx0] = m_acMergeBuffer[candIdx0].getBuf( localUnitArea );
triangleBuffer[candIdx1] = m_acMergeBuffer[candIdx1].getBuf( localUnitArea );
//三角预测的两个三角预测像素加权
m_pcInterSearch->weightedTriangleBlk( pu, PU::getTriangleWeights(pu, triangleMrgCtx, candIdx0, candIdx1), splitDir, CHANNEL_TYPE_LUMA, triangleWeightedBuffer[mergeCand], triangleBuffer[candIdx0], triangleBuffer[candIdx1] );
distParam.cur = triangleWeightedBuffer[mergeCand].Y();
Distortion uiSad = distParam.distFunc( distParam ); //计算失真
uint32_t uiBitsCand = g_triangleIdxBins[mergeCand];
double cost = (double)uiSad + (double)uiBitsCand * sqrtLambdaForFirstPass; //一种三角组合模式的cost
#if JVET_L0283_MULTI_REF_LINE
static_vector<int, TRIANGLE_MAX_NUM_CANDS> * nullList = nullptr;
#endif
updateCandList( mergeCand, cost, triangleRdModeList, tianglecandCostList //根据cost,排序各种三角组合模式
#if JVET_L0283_MULTI_REF_LINE
, *nullList, -1
#endif
, triangleNumMrgSATDCand );
} //40种三角组合模式loop结束
// limit number of candidates using SATD-costs
for( uint8_t i = 0; i < triangleNumMrgSATDCand; i++ )
{
if( tianglecandCostList[i] > MRG_FAST_RATIO * tianglecandCostList[0] || tianglecandCostList[i] > getMergeBestSATDCost() )
{
triangleNumMrgSATDCand = i;
break;
}
}
// perform chroma weighting process
for( uint8_t i = 0; i < triangleNumMrgSATDCand; i++ ) //SATD选取3个最优的三角组合模式
{
uint8_t mergeCand = triangleRdModeList[i];
bool splitDir = g_triangleCombination[mergeCand][0];
uint8_t candIdx0 = g_triangleCombination[mergeCand][1];
uint8_t candIdx1 = g_triangleCombination[mergeCand][2];
pu.mergeIdx = mergeCand;
pu.mergeFlag = true;
//计算三个最优的三角组合模式的加权预测像素
m_pcInterSearch->weightedTriangleBlk( pu, PU::getTriangleWeights(pu, triangleMrgCtx, candIdx0, candIdx1), splitDir, CHANNEL_TYPE_CHROMA, triangleWeightedBuffer[mergeCand], triangleBuffer[candIdx0], triangleBuffer[candIdx1] );
}
tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
}
{
const uint8_t iteration = encTestMode.lossless ? 1 : 2;
for( uint8_t noResidualPass = 0; noResidualPass < iteration; noResidualPass++ ) //两次循环
{
for( uint8_t mrgHADIdx = 0; mrgHADIdx < triangleNumMrgSATDCand; mrgHADIdx++ ) //对SATD选择的三角组合模式进行RDO
{
uint8_t mergeCand = triangleRdModeList[mrgHADIdx];
if ( ( (noResidualPass != 0) && trianglecandHasNoResidual[mergeCand] )
|| ( (noResidualPass == 0) && bestIsSkip ) )
{
continue;
}
bool splitDir = g_triangleCombination[mergeCand][0]; //划分方向
uint8_t candIdx0 = g_triangleCombination[mergeCand][1]; //第一个三角的候选idx
uint8_t candIdx1 = g_triangleCombination[mergeCand][2]; //第二个三角的候选idx
CodingUnit &cu = tempCS->addCU(tempCS->area, partitioner.chType);
partitioner.setCUData(cu);
cu.slice = tempCS->slice;
cu.skip = false;
cu.partSize = SIZE_2Nx2N;
cu.predMode = MODE_INTER;
cu.transQuantBypass = encTestMode.lossless;
cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
cu.triangle = true;
#if JVET_L0054_MMVD
cu.mmvdSkip = false;
#endif
#if JVET_L0646_GBI
cu.GBiIdx = GBI_DEFAULT;
#endif
PredictionUnit &pu = tempCS->addPU(cu, partitioner.chType); //tempCS添加cu pu
pu.mergeIdx = mergeCand;
pu.mergeFlag = true;
//将当前test的三角组合候选的MotionInfo赋给pu
PU::spanTriangleMotionInfo(pu, triangleMrgCtx, mergeCand, splitDir, candIdx0, candIdx1 );
if( tempBufSet ) //非skip模式时为true,因为此时的pred像素已在上面计算过,无需再计算
{
tempCS->getPredBuf().copyFrom( triangleWeightedBuffer[mergeCand] );
}
else
{ // skip模式时
triangleBuffer[candIdx0] = m_acMergeBuffer[candIdx0].getBuf( localUnitArea );
triangleBuffer[candIdx1] = m_acMergeBuffer[candIdx1].getBuf( localUnitArea );
PelUnitBuf predBuf = tempCS->getPredBuf();
m_pcInterSearch->weightedTriangleBlk( pu, PU::getTriangleWeights(pu, triangleMrgCtx, candIdx0, candIdx1), splitDir, MAX_NUM_CHANNEL_TYPE, predBuf, triangleBuffer[candIdx0], triangleBuffer[candIdx1] );
}
//计算cost,并将最后的模式信息给bestCS // xEncodeInterResidual
xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, noResidualPass, NULL, true, ( (noResidualPass == 0 ) ? &trianglecandHasNoResidual[mergeCand] : NULL ) );
if (m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip)
{
bestIsSkip = bestCS->getCU(partitioner.chType)->rootCbf == 0;
}
tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
}// end loop mrgHADIdx
}
}
}