关于帧内亮度预测的过程在之前H.266/VVC代码学习1:帧内预测框架中已简要讲述。重要的操作如下
0.初始化;H.266/VVC代码学习22:帧内预测的初始化(initIntraPatternChType)
1.获取色度预测列表getIntraChromaCandModes,并对pu和tu初始化;
2.其中把5种模式使用SATD运算进行色度预测列表的第一轮精简,去掉2个;(DC、Ver、Hor、LM_L、LM_T)
3.将剩下的6种模式进行遍历,都进行后续变换编码等操作,得到代价;见H.266/VVC代码学习8:xRecurIntraChromaCodingQT函数
4.比较代价,选出最优模式,更新代价
关于色度预测的模式,在VTM4.0中有角度预测、LM和DM三类共71种,如需查阅代码请点击:
H.266/VVC代码学习20:角度预测入口——predIntraAng
H.266/VVC代码学习15:LM模式——predIntraChromaLM
H.266/VVC代码学习14:DM模式——getFinalIntraMode
下面vtm4.0中代码部分的是详细内容,可结合注释理解:
#if JVET_M0102_INTRA_SUBPARTITIONS
void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner, const double maxCostAllowed )
#else
void IntraSearch::estIntraPredChromaQT(CodingUnit &cu, Partitioner &partitioner)
#endif
{
/******************************************** 初始化 ***************************************************/
const ChromaFormat format = cu.chromaFormat;
const uint32_t numberValidComponents = getNumberValidComponents(format);
CodingStructure &cs = *cu.cs;
const TempCtx ctxStart ( m_CtxCache, m_CABACEstimator->getCtx() );
cs.setDecomp( cs.area.Cb(), false );
#if JVET_M0102_INTRA_SUBPARTITIONS
double bestCostSoFar = maxCostAllowed;
bool lumaUsesISP = !CS::isDualITree( *cu.cs ) && cu.ispMode;
PartSplit ispType = lumaUsesISP ? CU::getISPType( cu, COMPONENT_Y ) : TU_NO_ISP;
CHECK( cu.ispMode && bestCostSoFar < 0, "bestCostSoFar must be positive!" );
#endif
/******************************************* 对pu的处理 ***************************************************/
auto &pu = *cu.firstPU;
{
/************************* 处理列表 ********************/
uint32_t uiBestMode = 0;
Distortion uiBestDist = 0;
double dBestCost = MAX_DOUBLE;
//----- init mode list 初始化模式列表(列出空表) ----
{
uint32_t uiMinMode = 0;
uint32_t uiMaxMode = NUM_CHROMA_MODE;
//----- check chroma modes 获取列表的值(填满表)-----
uint32_t chromaCandModes[ NUM_CHROMA_MODE ];
PU::getIntraChromaCandModes( pu, chromaCandModes );
// create a temporary CS 创建一个临时CS
CodingStructure &saveCS = *m_pSaveCS[0];
saveCS.pcv = cs.pcv;
saveCS.picture = cs.picture;
saveCS.area.repositionTo( cs.area );
saveCS.clearTUs();
#if JVET_M0102_INTRA_SUBPARTITIONS
if( !CS::isDualITree( cs ) && cu.ispMode )
{
saveCS.clearCUs();
saveCS.clearPUs();
}
#endif
/*********************************** 将TU保存 **********************************/
//如果是色度单独划分的话,将色度进行划分,并把TU存好
if( CS::isDualITree( cs ) )
{
if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
{
partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
do
{
cs.addTU( CS::getArea( cs, partitioner.currArea(), partitioner.chType ), partitioner.chType ).depth = partitioner.currTrDepth;
} while( partitioner.nextPart( cs ) );
partitioner.exitCurrSplit();
}
else
cs.addTU( CS::getArea( cs, partitioner.currArea(), partitioner.chType ), partitioner.chType );
}
std::vector<TransformUnit*> orgTUs;
#if JVET_M0102_INTRA_SUBPARTITIONS
if( lumaUsesISP )
{
CodingUnit& auxCU = saveCS.addCU( cu, partitioner.chType );
auxCU.ispMode = cu.ispMode;
saveCS.sps = cu.cs->sps;
saveCS.addPU( *cu.firstPU, partitioner.chType );
}
#endif
// create a store for the TUs 保存tu进orgTUs中以备后续处理
for( const auto &ptu : cs.tus )
{
// for split TUs in HEVC, add the TUs without Chroma parts for correct setting of Cbfs
#if JVET_M0102_INTRA_SUBPARTITIONS
if( lumaUsesISP || pu.contains( *ptu, CHANNEL_TYPE_CHROMA ) )
#else
if( pu.contains( *ptu, CHANNEL_TYPE_CHROMA ) )
#endif
{
saveCS.addTU( *ptu, partitioner.chType );
orgTUs.push_back( ptu );
}
}
#if JVET_M0102_INTRA_SUBPARTITIONS
if( lumaUsesISP )
{
saveCS.clearCUs();
}
#endif
/*************进行第一次选择,按DC、Ver、Hor、LM_L、LM_T进行预测,按SATD排序,去掉两个SATD最大的模式**************/
// SATD pre-selecting.
int satdModeList[NUM_CHROMA_MODE];
int64_t satdSortedCost[NUM_CHROMA_MODE];
for (int i = 0; i < NUM_CHROMA_MODE; i++)
{//对于未通过SATD预先选择的模式,默认情况下执行RDO,因此将初始值设置为0。
satdSortedCost[i] = 0; // for the mode not pre-select by SATD, do RDO by default, so set the initial value 0.
satdModeList[i] = 0;
}
bool modeIsEnable[NUM_INTRA_MODE + 1]; // use intra mode idx to check whether enable
for (int i = 0; i < NUM_INTRA_MODE + 1; i++)
{
modeIsEnable[i] = 1;
}
DistParam distParam;
const bool useHadamard = true;
pu.intraDir[1] = MDLM_L_IDX; // temporary assigned, just to indicate this is a MDLM mode. for luma down-sampling operation.
initIntraPatternChType(cu, pu.Cb());//参考像素滤波
initIntraPatternChType(cu, pu.Cr());
xGetLumaRecPixels(pu, pu.Cb());//获取亮度重建值
for (int idx = uiMinMode; idx <= uiMaxMode - 1; idx++)
{
int mode = chromaCandModes[idx];
satdModeList[idx] = mode;
if (PU::isLMCMode(mode) && !PU::isLMCModeEnabled(pu, mode))
{
continue;
}
if ((mode == LM_CHROMA_IDX) || (mode == PLANAR_IDX) || (mode == DM_CHROMA_IDX)) // only pre-check regular modes and MDLM modes, not including DM ,Planar, and LM
{
continue;
}
pu.intraDir[1] = mode; // temporary assigned, for SATD checking.
int64_t sad = 0;
CodingStructure& cs = *(pu.cs);
CompArea areaCb = pu.Cb();
PelBuf orgCb = cs.getOrgBuf(areaCb);
PelBuf predCb = cs.getPredBuf(areaCb);
m_pcRdCost->setDistParam(distParam, orgCb, predCb, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, useHadamard);
distParam.applyWeight = false;
if (PU::isLMCMode(mode))
{
predIntraChromaLM(COMPONENT_Cb, predCb, pu, areaCb, mode);
}
else
{
predIntraAng(COMPONENT_Cb, predCb, pu, false);
}
sad += distParam.distFunc(distParam);
CompArea areaCr = pu.Cr();
PelBuf orgCr = cs.getOrgBuf(areaCr);
PelBuf predCr = cs.getPredBuf(areaCr);
m_pcRdCost->setDistParam(distParam, orgCr, predCr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, useHadamard);
distParam.applyWeight = false;
if (PU::isLMCMode(mode))
{
predIntraChromaLM(COMPONENT_Cr, predCr, pu, areaCr, mode);
}
else
{
predIntraAng(COMPONENT_Cr, predCr, pu, false);
}
sad += distParam.distFunc(distParam);
satdSortedCost[idx] = sad;
}
// sort the mode based on the cost from small to large. 根据SATD从小到大排序模式。
int tempIdx = 0;
int64_t tempCost = 0;
for (int i = uiMinMode; i <= uiMaxMode - 1; i++)
{
for (int j = i + 1; j <= uiMaxMode - 1; j++)
{
if (satdSortedCost[j] < satdSortedCost[i])
{
tempIdx = satdModeList[i];
satdModeList[i] = satdModeList[j];
satdModeList[j] = tempIdx;
tempCost = satdSortedCost[i];
satdSortedCost[i] = satdSortedCost[j];
satdSortedCost[j] = tempCost;
}
}
}
int reducedModeNumber = 2; // reduce the number of chroma modes 把SATD最大的两个排除出去
for (int i = 0; i < reducedModeNumber; i++)
{
modeIsEnable[satdModeList[uiMaxMode - 1 - i]] = 0; // disable the last reducedModeNumber modes
}
// save the dist
Distortion baseDist = cs.dist;
/***********进行第二次选择,把SATD小的三种和未比较的三种共六种模式,编码后计算RDcost *******************/
for (uint32_t uiMode = uiMinMode; uiMode < uiMaxMode; uiMode++)
{
const int chromaIntraMode = chromaCandModes[uiMode];
if( PU::isLMCMode( chromaIntraMode ) && ! PU::isLMCModeEnabled( pu, chromaIntraMode ) )
{
continue;
}
if (!modeIsEnable[chromaIntraMode] && PU::isLMCModeEnabled(pu, chromaIntraMode)) // when CCLM is disable, then MDLM is disable. not use satd checking
{
continue;
}
cs.setDecomp( pu.Cb(), false );
cs.dist = baseDist;
//----- restore context models -----
m_CABACEstimator->getCtx() = ctxStart;
//----- chroma coding -----
pu.intraDir[1] = chromaIntraMode;
#if JVET_M0102_INTRA_SUBPARTITIONS
xRecurIntraChromaCodingQT( cs, partitioner, bestCostSoFar, ispType );
if( lumaUsesISP && cs.dist == MAX_UINT )
{
continue;
}
#else
xRecurIntraChromaCodingQT( cs, partitioner );
#endif
if (cs.pps->getUseTransformSkip())
{
m_CABACEstimator->getCtx() = ctxStart;
}
#if JVET_M0102_INTRA_SUBPARTITIONS
uint64_t fracBits = xGetIntraFracBitsQT( cs, partitioner, false, true, -1, ispType );
#else
uint64_t fracBits = xGetIntraFracBitsQT( cs, partitioner, false, true );
#endif
Distortion uiDist = cs.dist;
double dCost = m_pcRdCost->calcRdCost( fracBits, uiDist - baseDist );
/********************************* 获取最佳色度模式 ***********************************/
//----- compare -----
if( dCost < dBestCost )
{
#if JVET_M0102_INTRA_SUBPARTITIONS
if( lumaUsesISP && dCost < bestCostSoFar )
{
bestCostSoFar = dCost;
}
#endif
for( uint32_t i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ )
{
const CompArea &area = pu.blocks[i];
saveCS.getRecoBuf ( area ).copyFrom( cs.getRecoBuf ( area ) );
#if KEEP_PRED_AND_RESI_SIGNALS
saveCS.getPredBuf ( area ).copyFrom( cs.getPredBuf ( area ) );
saveCS.getResiBuf ( area ).copyFrom( cs.getResiBuf ( area ) );
#endif
#if JVET_M0427_INLOOP_RESHAPER
saveCS.getPredBuf ( area ).copyFrom( cs.getPredBuf (area ) );
cs.picture->getPredBuf( area ).copyFrom( cs.getPredBuf (area ) );
#endif
cs.picture->getRecoBuf( area ).copyFrom( cs.getRecoBuf( area ) );
for( uint32_t j = 0; j < saveCS.tus.size(); j++ )
{
saveCS.tus[j]->copyComponentFrom( *orgTUs[j], area.compID );
}
}
dBestCost = dCost;
uiBestDist = uiDist;
uiBestMode = chromaIntraMode;
}
}
for( uint32_t i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ )
{
const CompArea &area = pu.blocks[i];
cs.getRecoBuf ( area ).copyFrom( saveCS.getRecoBuf( area ) );
#if KEEP_PRED_AND_RESI_SIGNALS
cs.getPredBuf ( area ).copyFrom( saveCS.getPredBuf( area ) );
cs.getResiBuf ( area ).copyFrom( saveCS.getResiBuf( area ) );
#endif
#if JVET_M0427_INLOOP_RESHAPER
cs.getPredBuf ( area ).copyFrom( saveCS.getPredBuf( area ) );
cs.picture->getPredBuf( area ).copyFrom( cs.getPredBuf ( area ) );
#endif
cs.picture->getRecoBuf( area ).copyFrom( cs. getRecoBuf( area ) );
for( uint32_t j = 0; j < saveCS.tus.size(); j++ )
{
orgTUs[ j ]->copyComponentFrom( *saveCS.tus[ j ], area.compID );
}
}
}
pu.intraDir[1] = uiBestMode;
cs.dist = uiBestDist;
#if PRINT
//printf("xGetIntraFracBitsQT (%d,%d) %d %d : %lld\n", cs.area.blocks[0].x, cs.area.blocks[0].y, cs.area.blocks[0].width, cs.area.blocks[0].height, cs.fracBits);
#endif
}
/******************************** 重建上下文模型 ************************************/
//----- restore context models -----
m_CABACEstimator->getCtx() = ctxStart;
#if JVET_M0102_INTRA_SUBPARTITIONS
if( lumaUsesISP && bestCostSoFar >= maxCostAllowed )
{
cu.ispMode = 0;
}
#endif
}