CodingStructure& cs = *pcPic->cs;
Slice* pcSlice = cs.slice;
const PreCalcValues& pcv = *cs.pcv;//提前计算好的数据,所有ctu通用
const uint32_t widthInCtus = pcv.widthInCtus;//可知一帧中一行有的ctu个数
#if ENABLE_QPA
const int iQPIndex = pcSlice->getSliceQpBase();
#endif
CABACWriter* pCABACWriter = pEncLib->getCABACEncoder()->getCABACEstimator( pcSlice->getSPS() );
TrQuant* pTrQuant = pEncLib->getTrQuant();//transform 和 quantization
RdCost* pRdCost = pEncLib->getRdCost();//率失真优化
EncCfg* pCfg = pEncLib;
RateCtrl* pRateCtrl = pEncLib->getRateCtrl();
pRdCost->setLosslessRDCost(pcSlice->isLossless());
#if RDOQ_CHROMA_LAMBDA //色度的率失真优化量化的lambda
pTrQuant ->setLambdas( pcSlice->getLambdas() );
#else
pTrQuant ->setLambda ( pcSlice->getLambdas()[0] );
#endif
pRdCost ->setLambda ( pcSlice->getLambdas()[0], pcSlice->getSPS()->getBitDepths() );
#if WCG_EXT && ER_CHROMA_QP_WCG_PPS && ENABLE_QPA
if (!pCfg->getWCGChromaQPControl().isEnabled() && pCfg->getUsePerceptQPA() && !pCfg->getUseRateCtrl())
{
pRdCost->saveUnadjustedLambda();
}
#endif
CodingStructure& cs = *pcPic->cs //存储一帧中所有编码数据的cs。 *pcPic是传输过来的图片指针,指向coding strcture,然后赋值给以引用方式新建立的一个cs对象,可以在后面直接修改
H.266/VVC-VTM代码学习25-VTM中RDcost的计算与λ的设定(一)_liaojq2020的博客-优快云博客
下面是一些初始化,有些是关于变换和量化的,之后看
int prevQP[2];
int currQP[2];
prevQP[0] = prevQP[1] = pcSlice->getSliceQp();
currQP[0] = currQP[1] = pcSlice->getSliceQp();
prevQP[0] = prevQP[1] = pcSlice->getSliceQp();
if ( pcSlice->getSPS()->getFpelMmvdEnabledFlag() ||
(pcSlice->getSPS()->getIBCFlag() && m_pcCuEncoder->getEncCfg()->getIBCHashSearch()))
{
m_pcCuEncoder->getIbcHashMap().rebuildPicHashMap(cs.picture->getTrueOrigBuf());
if (m_pcCfg->getIntraPeriod() != -1)
{
int hashBlkHitPerc = m_pcCuEncoder->getIbcHashMap().calHashBlkMatchPerc(cs.area.Y());
cs.slice->setDisableSATDForRD(hashBlkHitPerc > 59);
}
if ((pcSlice->getSPS()->getSpsRangeExtension().getTSRCRicePresentFlag()) && (m_pcGOPEncoder->getPreQP() != pcSlice->getSliceQp()) && (pcPic->cs->pps->getNumSlicesInPic() == 1) && (pcSlice->get_tsrc_index() > 0) && (pcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) <= 12))
{
uint32_t totalCtu = 0;
uint32_t hashRatio = 0;
for (uint32_t ctuIdx = 0; ctuIdx < pcSlice->getNumCtuInSlice(); ctuIdx++)
{
const uint32_t ctuRsAddr = pcSlice->getCtuAddrInSlice(ctuIdx);
const uint32_t ctuXPosInCtus = ctuRsAddr % widthInCtus;
const uint32_t ctuYPosInCtus = ctuRsAddr / widthInCtus;
const Position pos(ctuXPosInCtus * pcv.maxCUWidth, ctuYPosInCtus * pcv.maxCUHeight);
const UnitArea ctuArea(cs.area.chromaFormat, Area(pos.x, pos.y, pcv.maxCUWidth, pcv.maxCUHeight));
hashRatio += m_pcCuEncoder->getIbcHashMap().calHashBlkMatchPerc(cs.area.Y());
totalCtu++;
}
if (totalCtu > 0)
{
if ((hashRatio < 4200) || (hashRatio < (41 * totalCtu)))
{
pcSlice->set_tsrc_index(0);
}
}
}
}
prevQP[], currQP[]在compressSlice()中也设置过,当时是设置图片的先前和现在的QP值。0和1分别代表亮度和色度,2代表MAX_NUM_CHANNEL_TYPE
现在是设置这两个参数,然后读取之前设置的QP值,然后赋值
10分钟拿下 HashMap_一小页的博客-优快云博客
getIBCFlag():IntraBlockCopy(帧内块复制)模式标志,IBC默认false
IntraPeriod:I帧间隔。当为-1时代表没有间隔
第一个if语句:求hashBlkHitPerc值,perc推测是perceptually的缩写,如果大于59,则不适用SATD
SAD和SATD的区别_yanbdsky的博客-优快云博客_satd
第二个if语句:同时满足五个条件,(1)TSRCRicePresent 开启(2)亮度的encodeGOP的preQP值 不等于现在slice的QP值 (3)一幅图中只有一个Slice (4)TSRC(Transform Skip Residual Coding),变换跳跃残差编码的目录大于0 (5)亮度通道的比特深度小于12
TSRC默认为false,所以这个if语句跳过。
// for every CTU in the slice
for( uint32_t ctuIdx = 0; ctuIdx < pcSlice->getNumCtuInSlice(); ctuIdx++ )
{
const int32_t ctuRsAddr = pcSlice->getCtuAddrInSlice( ctuIdx );
// update CABAC state
const uint32_t ctuXPosInCtus = ctuRsAddr % widthInCtus;
const uint32_t ctuYPosInCtus = ctuRsAddr / widthInCtus;
const Position pos (ctuXPosInCtus * pcv.maxCUWidth, ctuYPosInCtus * pcv.maxCUHeight);
const UnitArea ctuArea( cs.area.chromaFormat, Area( pos.x, pos.y, pcv.maxCUWidth, pcv.maxCUHeight ) );
DTRACE_UPDATE( g_trace_ctx, std::make_pair( "ctu", ctuRsAddr ) );
if( pCfg->getSwitchPOC() != pcPic->poc || -1 == pCfg->getDebugCTU() )
if ((cs.slice->getSliceType() != I_SLICE || cs.sps->getIBCFlag()) && cs.pps->ctuIsTileColBd( ctuXPosInCtus ))
{
cs.motionLut.lut.resize(0);
cs.motionLut.lutIbc.resize(0);
}
对Slice中所有Ctu进行处理
注意这里
ctuRsAddr:这里的RS是raster-scan的缩写,表示光栅扫描模式下slice中的每一个ctu的地址。
值跟ctuIdx一样
ctuXPosInCtus,ctuYPosInCtus:算出当前Ctu的x,y位置坐标。比如ctuRsAddr = 0时,x,y分别等于0,0,表明这个CTU的位置为(0,0).ctuRsAddr = 13时,x,y分别等于6,1,表明这个CTU的位置为(6,1)。这里面每一个块都是一个CTU,这个地址是CTU的相对地址
注意:这个图为832X480,CTU大小默认设置为128 X 128,而如果除下来,CTU应该只有24.3个,这与代码中28个的结果不同。原因是在边界不足以划分一个完整的CTU时,图像做了填充,加大了一部分面积,使一个CTU被完整划分出来。填充前图像宽度叫width,填充后的图像宽度叫stride。之后再具体了解
Image Stride(内存图像行跨度)_jsn_ze的博客-优快云博客
pcv.maxCUWidth:PreCalcValue下的maxCUWidth,好像锁定为128,我改cfg文件中的maxCUWidth也没有变化。之后再看。推测可认定这个就为CTU的宽度,之后再看是否正确
pos():因为cu的大小可以像CTU一样大,这里又设置为128.所以ctuXPosInCtus * pcv.maxCUWidth即可表示当前CTU的位置,像素为基本单位。
ctuArea():表示要处理的CTU的位置和大小还有这个位置的色度采样格式
ctuIsTileColBd():设置m_tileColBd,为ture时代表CTU在Tile的左边界线
第一个if语句:当前帧是B或P帧,或者允许开启IBC且CTU在Tile的左边界线,需要清空motionLut,里面存储着HMVP候选列表
const SubPic &curSubPic = pcSlice->getPPS()->getSubPicFromPos(pos);
// padding/restore at slice level
if (pcSlice->getPPS()->getNumSubPics() >= 2 && curSubPic.getTreatedAsPicFlag() && ctuIdx == 0)
{
int subPicX = (int)curSubPic.getSubPicLeft();
int subPicY = (int)curSubPic.getSubPicTop();
int subPicWidth = (int)curSubPic.getSubPicWidthInLumaSample();
int subPicHeight = (int)curSubPic.getSubPicHeightInLumaSample();
for (int rlist = REF_PIC_LIST_0; rlist < NUM_REF_PIC_LIST_01; rlist++)
{
int n = pcSlice->getNumRefIdx((RefPicList)rlist);//n应该是参考帧的总数量
for (int idx = 0; idx < n; idx++)
{
Picture *refPic = pcSlice->getRefPic((RefPicList)rlist, idx);//从参考帧列表中得到参考帧及其序号
if( !refPic->getSubPicSaved() && refPic->subPictures.size() > 1 )//如果参考帧的subpicture的边界没有被保存,且参考帧的有subpicure划分
{
refPic->saveSubPicBorder(refPic->getPOC(), subPicX, subPicY, subPicWidth, subPicHeight);
refPic->extendSubPicBorder(refPic->getPOC(), subPicX, subPicY, subPicWidth, subPicHeight);
refPic->setSubPicSaved(true);
}
}
}
}
VTM10.0代码学习3:DecSlice_decompressSlice()_柴门风雪夜的博客-优快云博客
curSubPic:包含当前CTU的subpicture
curSubPic.getTreatedAsPicFlag():将子图片看做图片,为true时则子图片的边界也被当成图片的边界,环路滤波时不执行这个。在当前设置下,默认为true。
第一个if语句:在子图片数量大于2,getTreatedAsPicFlag为TRUE且CTU序号为0时。将子图片的左和上边界设为子图片的X,Y,将子图片在亮度分量中的宽度和高度设为子图片的宽度和高度。
for循环:猜测NUM_REF_PIC_LIST_01为参考帧列表0和1都放在一起,全包括。此处遍历所有参考帧列表的每一个参考帧,如果参考帧也有subpicture划分,且没有保存的边界,那么就要进行以下操作(这个没有验证,之后再看):
- saveSubPicBorder():保存未被扩展的边界
- extendSubPicBorder():扩展边界
- setSubPicSaved():设置有边界被保存
if (cs.pps->ctuIsTileColBd( ctuXPosInCtus ) && cs.pps->ctuIsTileRowBd( ctuYPosInCtus ))
{
pCABACWriter->initCtxModels( *pcSlice );
cs.resetPrevPLT(cs.prevPLT);
prevQP[0] = prevQP[1] = pcSlice->getSliceQp();
}
else if (cs.pps->ctuIsTileColBd( ctuXPosInCtus ) && pEncLib->getEntropyCodingSyncEnabledFlag())
{
// reset and then update contexts to the state at the end of the top CTU (if within current slice and tile).
pCABACWriter->initCtxModels( *pcSlice );
cs.resetPrevPLT(cs.prevPLT);
if( cs.getCURestricted( pos.offset(0, -1), pos, pcSlice->getIndependentSliceIdx(), cs.pps->getTileIdx( pos ), CH_L ) )
{
// Top is available, we use it.
pCABACWriter->getCtx() = pEncLib->m_entropyCodingSyncContextState;
cs.setPrevPLT(pEncLib->m_palettePredictorSyncState);
}
prevQP[0] = prevQP[1] = pcSlice->getSliceQp();
}
第一个if语句:如果CTU同时在Tile的上和左边界,则
- initCtxModels():初始化上下文模型。看CABAC时注意这里,H.264将一个片内可能出现的数据划分为 399 个上下文模型,每个模型以 ctxIdx 标识
- 重置prePLT,palette mode coding
- 重置色度和亮度的preQP
else if语句:如果CTU在Tile的左边界且开启了EntropyCodingSync
- initCtxModels():初始化上下文模型。看CABAC时注意这里
- 重置prePLT,palette mode coding
- 重置色度和亮度的preQP
- 其中如果getCURestricted()为true,重新设置CABAC上下文模型的ctx和prePLT模式
const double oldLambda = pRdCost->getLambda();
if ( pCfg->getUseRateCtrl() )
{
int estQP = pcSlice->getSliceQp();
double estLambda = -1.0;
double bpp = -1.0;
if( ( pcPic->slices[0]->isIRAP() && pCfg->getForceIntraQP() ) || !pCfg->getLCULevelRC() )
{
estQP = pcSlice->getSliceQp();
}
else
{
bpp = pRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->isIRAP());
if ( pcPic->slices[0]->isIntra())
{
estLambda = pRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);
}
else
{
estLambda = pRateCtrl->getRCPic()->getLCUEstLambda( bpp );
estQP = pRateCtrl->getRCPic()->getLCUEstQP ( estLambda, pcSlice->getSliceQp() );
}
estQP = Clip3( -pcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, estQP );
pRdCost->setLambda(estLambda, pcSlice->getSPS()->getBitDepths());
#if WCG_EXT
pRdCost->saveUnadjustedLambda();
#endif
for (uint32_t compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++)
{
const ComponentID compID = ComponentID(compIdx);
int chromaQPOffset = pcSlice->getPPS()->getQpOffset(compID) + pcSlice->getSliceChromaQpDelta(compID);
int qpc = pcSlice->getSPS()->getMappedChromaQpValue(compID, estQP) + chromaQPOffset;
double tmpWeight = pow(2.0, (estQP - qpc) / 3.0); // takes into account of the chroma qp mapping and chroma qp Offset
if (m_pcCfg->getDepQuantEnabledFlag())
{
tmpWeight *= (m_pcCfg->getGOPSize() >= 8 ? pow(2.0, 0.1 / 3.0) : pow(2.0, 0.2 / 3.0)); // increase chroma weight for dependent quantization (in order to reduce bit rate shift from chroma to luma)
}
m_pcRdCost->setDistortionWeight(compID, tmpWeight);
}
#if RDOQ_CHROMA_LAMBDA
const double lambdaArray[MAX_NUM_COMPONENT] = {estLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Y),
estLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Cb),
estLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Cr)};
pTrQuant->setLambdas( lambdaArray );
#else
pTrQuant->setLambda( estLambda );
#endif
}
pRateCtrl->setRCQP( estQP );
}
#if ENABLE_QPA
else if (pCfg->getUsePerceptQPA() && pcSlice->getPPS()->getUseDQP())
{
#if ENABLE_QPA_SUB_CTU
const int adaptedQP = applyQPAdaptationSubCtu (cs, ctuArea, ctuRsAddr, m_pcCfg->getLumaLevelToDeltaQPMapping().mode == LUMALVL_TO_DQP_NUM_MODES);
#else
const int adaptedQP = pcPic->m_iOffsetCtu[ctuRsAddr];
#endif
const double newLambda = pcSlice->getLambdas()[0] * pow (2.0, double (adaptedQP - iQPIndex) / 3.0);
pcPic->m_uEnerHpCtu[ctuRsAddr] = newLambda; // for ALF and SAO
#if !ENABLE_QPA_SUB_CTU
#if RDOQ_CHROMA_LAMBDA
pTrQuant->getLambdas (oldLambdaArray); // save the old lambdas
const double lambdaArray[MAX_NUM_COMPONENT] = {newLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Y),
newLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Cb),
newLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Cr)};
pTrQuant->setLambdas (lambdaArray);
#else
pTrQuant->setLambda (newLambda);
#endif
pRdCost->setLambda (newLambda, pcSlice->getSPS()->getBitDepths());
#endif
currQP[0] = currQP[1] = adaptedQP;
}
#endif
第一个if语句:ratecontrol 默认是false,先跳过
else if语句:关于QPA,也是默认false,跳过
bool updateBcwCodingOrder = cs.slice->getSliceType() == B_SLICE && ctuIdx == 0;
if( updateBcwCodingOrder )
{
resetBcwCodingOrder(false, cs);
m_pcInterSearch->initWeightIdxBits();
}
if (pcSlice->getSPS()->getUseLmcs())
{
m_pcCuEncoder->setDecCuReshaperInEncCU(m_pcLib->getReshaper(), pcSlice->getSPS()->getChromaFormatIdc());
}
if( !cs.slice->isIntra() && pCfg->getMCTSEncConstraint() )
{
pcPic->mctsInfo.init( &cs, ctuRsAddr );
}
if (pCfg->getSwitchPOC() != pcPic->poc || ctuRsAddr >= pCfg->getDebugCTU())
{
m_pcCuEncoder->compressCtu(cs, ctuArea, ctuRsAddr, prevQP, currQP);
}
updateBcwCodingOrder:如果当前为B 帧且是slice里第一个CTU,则开启BCW
if语句(BCW为true):没仔细看,应该是重置BCW且根据序号初始化加权系数
setDecCuReshaperInEncCU():因为与lmcs有关,推测与对亮度色度分量的形状改变有关,之后再仔细看
最后一个if语句:如果poc和CTU未出现错误,则进入compressCtu()函数