CU递归的算法,那么就是xDecodeCU这个函数里面
首先对其中一些比较难以理解的部分进行相应的整理
UInt uiCurNumParts = pcPic->getNumPartitionsInCtu() >> (uiDepth<<1);
pcPic->getNumPartitionsInCtu() 获得的数据就是LCU中也就是根节点中CTU对应的按照最小TU进行划分得到的数量,后面进行>> (uiDepth<<1)
这样一个移位操作表示递归操作过后,当前CU当中按照最小TU进行划分的数量
因为才开始整理,为什么要以最小TU来进行数据存储,想可能是因为最后都要落实到变换操作,所以按照这个划分,数据结构都是可以的
UInt uiQNumParts = uiCurNumParts>>2; 这个变量主要是用于递归操作的时候一个递增数据的操作,假设当前CU需要划分,那么索引值需要增加多少进行返回
if( ( ( uiDepth < pcCU->getDepth( uiAbsPartIdx ) ) && ( uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth ) ) || bBoundary ) 这一个判断表示读取划分split的时候是否需要进行划分
g_uiMaxCUDepth 这个变量表示,LCU 到 最小TU 之间的级别,比如 LCU 为64*64, 最小TU 为4*4, 那么这个变量表示的级别就是 log2(64/4) = 4;
g_uiAddCUDepth 这个变量表示,SCU到最小TU之间的级别,SCU为8*8, 那么级别就是3
两者相减获得的就是LCU 到 SCU 之间的级别
那么这个判断就很好理解了,如果当前uiDepth < 两者差值,同时满足条件了,就应该进行劈分
递归过程还是比较简单,同时在存储相应的数据的时候:比如存储CU的深度信息
UInt uiCurrPartNumb = m_pcPic->getNumPartitionsInCtu() >> (uiDepth << 1);
memset( m_puhDepth + uiAbsPartIdx, uiDepth, sizeof(UChar)*uiCurrPartNumb );
m_puhDepth 对于每一个LCU(根节点CTU) 都有一个存储变量,最后就是进行memset进行赋值操作
下面是部分有注释的代码,帮助大家分析代码
Void TDecCu::xDecodeCU( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth, Bool &isLastCtuOfSliceSegment) //uiAbsPartIdx 表示当前CU在CTU中X扫描位置对应的索引位置
{
TComPic* pcPic = pcCU->getPic();
UInt uiCurNumParts = pcPic->getNumPartitionsInCtu() >> (uiDepth<<1); //<当前CU中按照最小TU为单元划分,有多少个宏块
UInt uiQNumParts = uiCurNumParts>>2; //<用于递归过程中,偏移量递增,如果当前CU需要劈分,那么劈分后每一个CU中包含的STU数量
Bool bBoundary = false;//<是否为边界位置
UInt uiLPelX = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ];//getCUPelX()获取当前CTU在图片中的绝对位置,后者为当前CU在CTU中位置的偏移量
UInt uiRPelX = uiLPelX + (g_uiMaxCUWidth>>uiDepth) - 1;//>uiDepth)表示当前CU的宽度
UInt uiTPelY = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsPartIdx] ];
UInt uiBPelY = uiTPelY + (g_uiMaxCUHeight>>uiDepth) - 1;
TComSlice * pcSlice = pcCU->getPic()->getSlice(pcCU->getPic()->getCurrSliceIdx());
if( ( uiRPelX < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( uiBPelY < pcSlice->getSPS()->getPicHeightInLumaSamples() ) )//<根据图片的像素位置进行判断
{
m_pcEntropyDecoder->decodeSplitFlag( pcCU, uiAbsPartIdx, uiDepth );//<将当前的深度存储在m_puhDepth
}
else
{
bBoundary = true;
}
//getDepth( uiAbsPartIdx ) ) && ( uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth ) ) || bBoundary )//<需要劈分或者是达到边界?
{
UInt uiIdx = uiAbsPartIdx; //>uiDepth) == pcCU->getSlice()->getPPS()->getMinCuDQPSize() && pcCU->getSlice()->getPPS()->getUseDQP())//XT?getMinCuDQPSize: LCU宽度>>diff_cu_qp_delta_depth获得一个最小的什么的宽度
{
setdQPFlag(true);
pcCU->setQPSubParts( pcCU->getRefQP(uiAbsPartIdx), uiAbsPartIdx, uiDepth ); // set QP to default QP
}
if( (g_uiMaxCUWidth>>uiDepth) == pcCU->getSlice()->getPPS()->getMinCuChromaQpAdjSize() && pcCU->getSlice()->getUseChromaQpAdj() )
{
setIsChromaQpAdjCoded(true);
}
for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ ) //<如果劈分,对应分成4份
{
uiLPelX = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiIdx] ];
uiTPelY = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiIdx] ];//<仍然是获得当前CU在图片中位置
if ( !isLastCtuOfSliceSegment && ( uiLPelX < pcCU->getSlice()->getSPS()->getPicWidthInLumaSamples() ) && ( uiTPelY < pcCU->getSlice()->getSPS()->getPicHeightInLumaSamples() ) )
{
xDecodeCU( pcCU, uiIdx, uiDepth+1, isLastCtuOfSliceSegment ); //<递归操作,uiIdx表示当前CU在CTU中的Z扫描索引,深度+1
}
else
{
pcCU->setOutsideCUPart( uiIdx, uiDepth+1 ); //<将深度,高度,宽度信息进行存储m_puhDepth,m_puhWidth,m_puhHeight
}
uiIdx += uiQNumParts; //<位置索引进行递增的操作
}
if( (g_uiMaxCUWidth>>uiDepth) == pcCU->getSlice()->getPPS()->getMinCuDQPSize() && pcCU->getSlice()->getPPS()->getUseDQP()) //<这一部分应该是当前CU和DQP直接有关系的部分
{
if ( getdQPFlag() )
{
UInt uiQPSrcPartIdx = uiAbsPartIdx;
pcCU->setQPSubParts( pcCU->getRefQP( uiQPSrcPartIdx ), uiAbsPartIdx, uiDepth ); // set QP to default QP
}
}
return;
}