在之前的 HEVC代码学习39:decodeCtu和xDecodeCU函数 中提到,xDecodeCU
通过迭代完成每个CU的解码,其中skip模式会直接解码merge列表和索引,取出对应MV;PCM模式也会直接进行解码;其他模式会调用decodePredInfo
解码预测模式信息(帧内预测、帧间预测)。下面就来学习decodePredInfo
及其调用的重要函数decodePUWise
。
decodePredInfo
实际是一个入口函数,其功能是输入当前待解码CU及其对应的绝对偏移索引、深度和子CU,进行非skip模式和非PCM模式解码,根据预测模式调用对应函数来进行解码。如果是帧内模式,调用decodeIntraDirModeLuma
解码帧内预测模式;否则会调用decodePUWise
解码帧间预测模式。
decodeIntraDirModeLuma
解码帧内预测模式时,先构建MPM,取出选中的帧内预测模式序号。decodePUWise
解码帧间预测模式,要分merge和AMVP处理解码运动信息。下一章将具体来看decodePUWise
函数。
decodeIntraDirModeLuma
代码比较简单,分析如下:
//解码预测模式
Void TDecEntropy::decodePredInfo ( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth, TComDataCU* pcSubCU )
{
//帧内预测
if( pcCU->isIntra( uiAbsPartIdx ) ) // If it is Intra mode, encode intra prediction mode.
{
//解码亮度帧内角度预测模式
decodeIntraDirModeLuma ( pcCU, uiAbsPartIdx, uiDepth );
if (pcCU->getPic()->getChromaFormat()!=CHROMA_400) //非YUV400,解码色度角度预测模式
{
decodeIntraDirModeChroma( pcCU, uiAbsPartIdx, uiDepth );
if (enable4ChromaPUsInIntraNxNCU(pcCU->getPic()->getChromaFormat()) && pcCU->getPartitionSize( uiAbsPartIdx )==SIZE_NxN)
{
UInt uiPartOffset = ( pcCU->getPic()->getNumPartitionsInCtu() >> ( pcCU->getDepth(uiAbsPartIdx) << 1 ) ) >> 2;
decodeIntraDirModeChroma( pcCU, uiAbsPartIdx + uiPartOffset, uiDepth+1 );
decodeIntraDirModeChroma( pcCU, uiAbsPartIdx + uiPartOffset*2, uiDepth+1 );
decodeIntraDirModeChroma( pcCU, uiAbsPartIdx + uiPartOffset*3, uiDepth+1 );
}
}
}
else //帧间预测 // if it is Inter mode, encode motion vector and reference index
{
//解码PU运动信息
decodePUWise( pcCU, uiAbsPartIdx, uiDepth, pcSubCU );
}
}
从名字就可以看出,解码PU级的信息,实际功能是解码PU的运动信息。
主要流程如下:
一.初始化分块信息、候选列表。
二.遍历待解码CU,对其中所有PU分块进行帧间预测解码。
1.如果是merge模式,解码Merge Index,并调用getInterMergeCandidates
构造merge候选列表,根据m索引取出选中的MV候选。
2.如果是AMVP模式,调用decodeMvdPU
解码MVD,调用decodeMVPIdxPU
解码MVP索引,计算MV=MVP+MVD。
注意这里只是在解码运动信息,没有进行预测图像的重构。
代码分析:
/** decode motion information for every PU block.
* \param pcCU
* \param uiAbsPartIdx
* \param uiDepth
* \param pcSubCU
* \returns Void
*/
//解码PU运动信息
Void TDecEntropy::decodePUWise( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth, TComDataCU* pcSubCU )
{
PartSize ePartSize = pcCU->getPartitionSize( uiAbsPartIdx ); //PU划分情况
UInt uiNumPU = ( ePartSize == SIZE_2Nx2N ? 1 : ( ePartSize == SIZE_NxN ? 4 : 2 ) ); //分块数量
UInt uiPUOffset = ( g_auiPUOffset[UInt( ePartSize )] << ( ( pcCU->getSlice()->getSPS()->getMaxTotalCUDepth() - uiDepth ) << 1 ) ) >> 4;
TComMvField cMvFieldNeighbours[MRG_MAX_NUM_CANDS << 1]; // double length for mv of both lists //候选MV列表
UChar uhInterDirNeighbours[MRG_MAX_NUM_CANDS]; //候选索引列表
//初始化
for ( UInt ui = 0; ui < pcCU->getSlice()->getMaxNumMergeCand(); ui++ )
{
uhInterDirNeighbours[ui] = 0;
}
Int numValidMergeCand = 0;
Bool hasMergedCandList = false;
//从CTU复制当前PU的帧间信息
pcSubCU->copyInterPredInfoFrom( pcCU, uiAbsPartIdx, REF_PIC_LIST_0 );
pcSubCU->copyInterPredInfoFrom( pcCU, uiAbsPartIdx, REF_PIC_LIST_1 );
//遍历PU的每一个分块
for ( UInt uiPartIdx = 0, uiSubPartIdx = uiAbsPartIdx; uiPartIdx < uiNumPU; uiPartIdx++, uiSubPartIdx += uiPUOffset )
{
//解码merge flag
decodeMergeFlag( pcCU, uiSubPartIdx, uiDepth, uiPartIdx );
//merge flag为真
if ( pcCU->getMergeFlag( uiSubPartIdx ) )
{
//解码merge index
decodeMergeIndex( pcCU, uiPartIdx, uiSubPartIdx, uiDepth );
#if ENVIRONMENT_VARIABLE_DEBUG_AND_TEST
if (bDebugPredEnabled)
{
std::cout << "Coded merge flag, CU absPartIdx: " << uiAbsPartIdx << " PU(" << uiPartIdx << ") absPartIdx: " << uiSubPartIdx;
std::cout << " merge index: " << (UInt)pcCU->getMergeIndex(uiSubPartIdx) << std::endl;
}
#endif
//获取merge index
UInt uiMergeIndex = pcCU->getMergeIndex(uiSubPartIdx);
if ( pcCU->getSlice()->getPPS()->getLog2ParallelMergeLevelMinus2() && ePartSize != SIZE_2Nx2N && pcSubCU->getWidth( 0 ) <= 8 )
{
//第一个分块时执行,初始化分块
if ( !hasMergedCandList )
{
pcSubCU->setPartSizeSubParts( SIZE_2Nx2N, 0, uiDepth ); // temporarily set.
pcSubCU->getInterMergeCandidates( 0, 0, cMvFieldNeighbours, uhInterDirNeighbours, numValidMergeCand );
pcSubCU->setPartSizeSubParts( ePartSize, 0, uiDepth ); // restore.
hasMergedCandList = true;
}
}
else //其他分块获取merge索引和候选
{
uiMergeIndex = pcCU->getMergeIndex(uiSubPartIdx);
pcSubCU->getInterMergeCandidates( uiSubPartIdx-uiAbsPartIdx, uiPartIdx, cMvFieldNeighbours, uhInterDirNeighbours, numValidMergeCand, uiMergeIndex );
}
pcCU->setInterDirSubParts( uhInterDirNeighbours[uiMergeIndex], uiSubPartIdx, uiPartIdx, uiDepth );
TComMv cTmpMv( 0, 0 );
//遍历两个list0和list1
for ( UInt uiRefListIdx = 0; uiRefListIdx < 2; uiRefListIdx++ )
{
//当参考索引数大于0时,取出merge参考列表
if ( pcCU->getSlice()->getNumRefIdx( RefPicList( uiRefListIdx ) ) > 0 )
{
pcCU->setMVPIdxSubParts( 0, RefPicList( uiRefListIdx ), uiSubPartIdx, uiPartIdx, uiDepth);
pcCU->setMVPNumSubParts( 0, RefPicList( uiRefListIdx ), uiSubPartIdx, uiPartIdx, uiDepth);
pcCU->getCUMvField( RefPicList( uiRefListIdx ) )->setAllMvd( cTmpMv, ePartSize, uiSubPartIdx, uiDepth, uiPartIdx );
pcCU->getCUMvField( RefPicList( uiRefListIdx ) )->setAllMvField( cMvFieldNeighbours[ 2*uiMergeIndex + uiRefListIdx ], ePartSize, uiSubPartIdx, uiDepth, uiPartIdx );
}
}
}
else //非merge
{
//解码帧间预测方向
decodeInterDirPU( pcCU, uiSubPartIdx, uiDepth, uiPartIdx );
//遍历list0和list1
for ( UInt uiRefListIdx = 0; uiRefListIdx < 2; uiRefListIdx++ )
{
//当参考索引数大于0时,取出AMVP参考列表
if ( pcCU->getSlice()->getNumRefIdx( RefPicList( uiRefListIdx ) ) > 0 )
{
decodeRefFrmIdxPU( pcCU, uiSubPartIdx, uiDepth, uiPartIdx, RefPicList( uiRefListIdx ) );
decodeMvdPU ( pcCU, uiSubPartIdx, uiDepth, uiPartIdx, RefPicList( uiRefListIdx ) );
decodeMVPIdxPU ( pcSubCU, uiSubPartIdx-uiAbsPartIdx, uiDepth, uiPartIdx, RefPicList( uiRefListIdx ) );
#if ENVIRONMENT_VARIABLE_DEBUG_AND_TEST
if (bDebugPredEnabled)
{
std::cout << "refListIdx: " << uiRefListIdx << std::endl;
std::cout << "MVD horizontal: " << pcCU->getCUMvField(RefPicList(uiRefListIdx))->getMvd( uiAbsPartIdx ).getHor() << std::endl;
std::cout << "MVD vertical: " << pcCU->getCUMvField(RefPicList(uiRefListIdx))->getMvd( uiAbsPartIdx ).getVer() << std::endl;
std::cout << "MVPIdxPU: " << pcCU->getMVPIdx(RefPicList( uiRefListIdx ), uiSubPartIdx) << std::endl;
std::cout << "InterDir: " << (UInt)pcCU->getInterDir(uiSubPartIdx) << std::endl;
}
#endif
}
}
}
//不确认这一块是做的什么,好像是list1不可用
if ( (pcCU->getInterDir(uiSubPartIdx) == 3) && pcSubCU->isBipredRestriction(uiPartIdx) )
{
pcCU->getCUMvField( REF_PIC_LIST_1 )->setAllMv( TComMv(0,0), ePartSize, uiSubPartIdx, uiDepth, uiPartIdx);
pcCU->getCUMvField( REF_PIC_LIST_1 )->setAllRefIdx( -1, ePartSize, uiSubPartIdx, uiDepth, uiPartIdx);
pcCU->setInterDirSubParts( 1, uiSubPartIdx, uiPartIdx, uiDepth);
}
}
return;
}