,VTM代码学习(1)CU划分_jwgroot的博客-CSDN博客
H.266/VVC-VTM代码学习20-CU层进行RDO函数xCompressCU_liaojq2020的博客-CSDN博客
VVC代码学习1:xCompressCU函数_Everglow_zbz的博客-CSDN博客
H.266/VVC代码学习:xCompressCU函数_涵小呆的博客-CSDN博客
VTM3.0代码阅读:xCompressCU函数_矛盾统一的博客-CSDN博客
VTM1.0代码阅读:xCompressCU函数_矛盾统一的博客-CSDN博客
H.266/VVC代码学习13:VTM4.0中的CU层操作(compressCtu 、 xCompressCU)_海洋之心。的博客-CSDN博客
一个CTU分割细节过程—— VTM (13.0)源码阅读_chvfily的博客-CSDN博客
VTM10.0代码学习11:from_EncSlice_compressSlice()_to_EncCu_xCompressCU()_柴门风雪夜的博客-CSDN博客
注意:看xCompressCU函数时不要在这个函数开头打断点,因为后面函数调用时会有干扰
void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Partitioner& partitioner, double maxCostAllowed )
{
这里传输过来的参数tempCS,bestCS应该是待处理的CTU级别的CS结构。这里推测tempCS存储的根据CTU宽高度得到的默认最优cs,bestCS存储的也是默认最优cs。后面tempCS随时会变化,然后做比较后,在看是否传给beseCs
CHECK(maxCostAllowed < 0, "Wrong value of maxCostAllowed!");
uint32_t compBegin; //亮色度成员分量属性
uint32_t numComp; //亮色度分量数目
bool jointPLT = false;
if (partitioner.isSepTree( *tempCS ))
{
if( !CS::isDualITree(*tempCS) && partitioner.treeType != TREE_D )// TREE_L或者TREE_C情况下
{
compBegin = COMPONENT_Y;
numComp = (tempCS->area.chromaFormat != CHROMA_400)?3: 1;
jointPLT = true;
}
else //dual tree,当前为tree-D情况下
{
if (isLuma(partitioner.chType)) //亮度分量tree _L
{
compBegin = COMPONENT_Y;
numComp = 1;
}
else //色度分量tree _C
{
compBegin = COMPONENT_Cb;
numComp = 2;///起始分量为 Cb,总分量数为2
}
}
}
else //若当前不是dualtree,且为tree-D时,则当前CS为joint tree,亮度和色度共同划分
{
compBegin = COMPONENT_Y;
numComp = (tempCS->area.chromaFormat != CHROMA_400) ? 3 : 1;
jointPLT = true;
}
isSepTree():如果当前CS属于Dual tree或不是tree_D(对single tree,tree_D = joint tree;对dual tree,tree_D = tree _C),则返回值为true
isDualITree():如果当前帧为I 帧且当前待处理的CTU不是single tree,则返回值为true
第一个if语句:在CS结构为TREE_L或者TREE_C情况下,起始分量为Y分量,若亮色度采样格式不为4:0:0,则亮色度总分量数为3,否则为1。使用jointPLT
SplitSeries splitmode = -1;
// 以下是用于存储 PLT mode 的相关变量
uint8_t bestLastPLTSize[MAX_NUM_CHANNEL_TYPE];
Pel bestLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT for
uint8_t curLastPLTSize[MAX_NUM_CHANNEL_TYPE];
Pel curLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT if no partition
// 遍历所有分量,为 PLT mode 的存储变量赋值
for (int i = compBegin; i < (compBegin + numComp); i++)
{
ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
bestLastPLTSize[comID] = 0;
curLastPLTSize[comID] = tempCS->prevPLT.curPLTSize[comID];
memcpy(curLastPLT[i], tempCS->prevPLT.curPLT[i], tempCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
}
Slice& slice = *tempCS->slice;// 当前 slice
const PPS &pps = *tempCS->pps;
const SPS &sps = *tempCS->sps;
const uint32_t uiLPelX = tempCS->area.Y().lumaPos().x;// 当前块的左上角x
const uint32_t uiTPelY = tempCS->area.Y().lumaPos().y;// 当前块的左上角y
// 父结点的模式类型(MODE_TYPE_ALL / MODE_TYPE_INTER / MODE_TYPE_INTRA)
const ModeType modeTypeParent = partitioner.modeType;
// 父结点的划分树类型(TREE_D / TREE_L / TREE_C)
const TreeType treeTypeParent = partitioner.treeType;
// 父结点的通道类型(CHANNEL_TYPE_LUMA / CHANNEL_TYPE_CHROMA)
const ChannelType chTypeParent = partitioner.chType;
const UnitArea currCsArea = clipArea( CS::getArea( *bestCS, bestCS->area, partitioner.chType ), *tempCS->picture );
m_modeCtrl->initCULevel( partitioner, *tempCS );
注意:PLT的变量之后再看
currCsArea参数具体意思_青椒鸡汤的博客-CSDN博客
currCsArea:当前待划分CTU的区域信息,YCbCr的区域信息和限制信息
initCULevel():获取当前CU可能的预测和编码模式,并且按照顺序推入栈中。
这里传输了tempCS和partitioner,所以函数中对CU的操作,其中的CU是现在系统算出来的临时CS结构中的CU
很重要这个之后再看
H.266/VVC-VTM代码学习19-CU层确定测试模式函数initCULevel_liaojq2020的博客-CSDN博客
#if GDR_ENABLED
if (m_pcEncCfg->getGdrEnabled())
{
bool isInGdrInterval = slice.getPicHeader()->getInGdrInterval();
// 1.0 applicable to inter picture only
if (isInGdrInterval)
{
int gdrPocStart = m_pcEncCfg->getGdrPocStart();
int gdrInterval = m_pcEncCfg->getGdrInterval();
int picWidth = slice.getPPS()->getPicWidthInLumaSamples();
int m1, m2, n1;
int curPoc = slice.getPOC();
int gdrPoc = (curPoc - gdrPocStart) % gdrInterval;
int begGdrX = 0;
int endGdrX = 0;
double dd = (picWidth / (double)gdrInterval);
int mm = (int)((picWidth / (double)gdrInterval) + 0.49999);
m1 = ((mm + 7) >> 3) << 3;
m2 = ((mm + 0) >> 3) << 3;
if (dd > mm && m1 == m2)
{
m1 = m1 + 8;
}
n1 = (picWidth - m2 * gdrInterval) / 8;
if (gdrPoc < n1)
{
begGdrX = m1 * gdrPoc;
endGdrX = begGdrX + m1;
}
else
{
begGdrX = m1 * n1 + m2 * (gdrPoc - n1);
endGdrX = begGdrX + m2;
if (picWidth <= endGdrX)
{
begGdrX = picWidth;
endGdrX = picWidth;
}
}
bool isInRefreshArea = tempCS->withinRefresh(begGdrX, endGdrX);
if (isInRefreshArea)
{
m_modeCtrl->forceIntraMode();
}
else if (tempCS->containRefresh(begGdrX, endGdrX) || tempCS->overlapRefresh(begGdrX, endGdrX))
{
// 1.3.1 enable only vertical splits (QT, BT_V, TT_V)
m_modeCtrl->forceVerSplitOnly();
// 1.3.2 remove TT_V if it does not satisfy the condition
if (tempCS->refreshCrossTTV(begGdrX, endGdrX))
{
m_modeCtrl->forceRemoveTTV();
}
}
if (tempCS->area.lwidth() != tempCS->area.lheight())
{
m_modeCtrl->forceRemoveQT();
}
if (!m_modeCtrl->anyPredModeLeft())
{
m_modeCtrl->forceRemoveDontSplit();
}
if (isInRefreshArea && !m_modeCtrl->anyIntraIBCMode() && (tempCS->area.lwidth() == 4 || tempCS->area.lheight() == 4))
{
m_modeCtrl->finishCULevel(partitioner);
return;
}
}
}
#endif
与GDR的设置有关,跳过
if( partitioner.currQtDepth == 0 && partitioner.currMtDepth == 0 && !tempCS->slice->isIntra() && ( sps.getUseSBT() || sps.getUseInterMTS() ) )
{
auto slsSbt = dynamic_cast<SaveLoadEncInfoSbt*>( m_modeCtrl );
int maxSLSize = sps.getUseSBT() ? tempCS->slice->getSPS()->getMaxTbSize() : MTS_INTER_MAX_CU_SIZE;
slsSbt->resetSaveloadSbt( maxSLSize );
}
m_sbtCostSave[0] = m_sbtCostSave[1] = MAX_DOUBLE;// 用于存储子块变换(sub-block transform,SBT)模式的 cost
if语句:如果当前QT深度和Mt深度为0,不为I帧且SBT或MTS开启,则运行。因为SBT默认false,所以跳过
SBT:子块变换(sub-block transform,SBT)
m_CurrCtx->start = m_CABACEstimator->getCtx();
if( slice.getUseChromaQpAdj() )
{
// TODO M0133 : double check encoder decisions with respect to chroma QG detection and actual encode
int lgMinCuSize = sps.getLog2MinCodingBlockSize() +
std::max<int>(0, floorLog2(sps.getCTUSize()) - sps.getLog2MinCodingBlockSize() - int((slice.getCuChromaQpOffsetSubdiv()+1) / 2));
if( partitioner.currQgChromaEnable() )
{
m_cuChromaQpOffsetIdxPlus1 = ( ( uiLPelX >> lgMinCuSize ) + ( uiTPelY >> lgMinCuSize ) ) % ( pps.getChromaQpOffsetListLen() + 1 );
}
}
else
{
m_cuChromaQpOffsetIdxPlus1 = 0;
}
if( !m_modeCtrl->anyMode() )
{
m_modeCtrl->finishCULevel( partitioner );
return;
}
// 以下是 RDO 前的初始化
// 记录当前的位置、尺寸信息
DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cux", uiLPelX ) );
DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuy", uiTPelY ) );
DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuw", tempCS->area.lwidth() ) );
DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuh", tempCS->area.lheight() ) );
DTRACE( g_trace_ctx, D_COMMON, "@(%4d,%4d) [%2dx%2d]\n", tempCS->area.lx(), tempCS->area.ly(), tempCS->area.lwidth(), tempCS->area.lheight() );
// 初始化 Affine(仿射运动补偿预测)模式的相关变量
m_pcInterSearch->resetSavedAffineMotion();
double bestIntPelCost = MAX_DOUBLE;//最佳的整像素帧间预测的cost
m_CurrCtx->start:上下文模型起点
getUseChromaQpAdj():是否使用色度QP调整。默认为false
第二个if分支:如果没有要测试的模式则结束此函数
// 若使用色彩变换
// 当前CS与最佳CS的 cost 均初始化为最大值,且选中第一色彩空间
if (tempCS->slice->getSPS()->getUseColorTrans())
{
tempCS->tmpColorSpaceCost = MAX_DOUBLE;
bestCS->tmpColorSpaceCost = MAX_DOUBLE;
tempCS->firstColorSpaceSelected = true;
bestCS->firstColorSpaceSelected = true;
}
// 若使用色彩变换,且不是双重intra树时
// 当前CS与最佳CS均设置不止测试第一色彩空间,且两色彩空间的 Intra cost 初始化为最大值
if (tempCS->slice->getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS))
{
tempCS->firstColorSpaceTestOnly = false;
bestCS->firstColorSpaceTestOnly = false;
tempCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE;
tempCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE;
bestCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE;
bestCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE;
// 若当前 CS 的最佳父结点存在,且最佳父结点只测试第一色彩空间、
// 则当前 CS 与最佳 CS 均只测试第一色彩空间
if (tempCS->bestParent && tempCS->bestParent->firstColorSpaceTestOnly)
{
tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;
}
}
色彩转换ACT(adaptive color transform ) :默认为false
bestParent:代表当前CS结构的最佳父节点
if (tempCS->slice->getCheckLDC())
{
m_bestBcwCost[0] = m_bestBcwCost[1] = std::numeric_limits<double>::max();
m_bestBcwIdx[0] = m_bestBcwIdx[1] = -1;
}