预测编码理论
利用已编码的样本值,根据某种模型,对当前样本值进行预测,并对差值编码
算出相邻像素的差值,能够去除像素间的冗余,得到的差值信号相关性很小
主要思想:通过预测来消除像素间的相关性
根据参考像素的不同,分为
- 帧内预测
- 帧间预测
1.帧内预测编码
使用基于块的空域帧内预测方法:
当前块的预测像素由其预测方向上相邻块的边界重建像素生成,由此能够根据视频内容特征自适应的选择预测模式,如较为平坦时,选择DC模式(参考像素取平均),纹理呈水平状时,采用水平预测模式。
通过拉格朗日率失真优化RDO,进行模式选择
J = D + lam*R
其中D表示当前预测模式下的失真,R表示当前预测模式下的比特信息(变换系数,宏块划分、模式信息等)
1.1 H.264/AVC帧内预测
-
3种大小亮度帧内预测块:4x4,8x8,16x16(参考的相邻像素值包含上、左像素区域)
- 4x4,8x8有9种预测模式
- 垂直模式、水平模式、DC模式、左下对角线模式、左下对角线模式、垂直向右、水平向下、垂直向左、水平向上
- 16x16有4种预测模式
- 垂直模式、水平模式、DC模式、Plane模式(1种角度)
- 4x4,8x8有9种预测模式
-
1种大小色度帧内预测块:8x8
- 8x8由4种预测模式
- 垂直模式、水平模式、DC模式、Plane模式(1种角度)
- 8x8由4种预测模式
1.2 H.265/HEVC帧内预测
1.2.1 亮度帧内编码
- 5种大小亮度帧内预测块PU:4x4,8x8,16x16,32x32,64x64
- 每一种PU都对应35种预测模式(相比H.264,H.265由于使用了四叉树形编码结构,参考的相邻像素值更多,包含了左、左下、上、右上等像素区域)
- planar模式、DC模式、33种角度模式
帧内预测候选表:当前块PU参考相邻块(左A、上B)的预测模式
- 通过建立帧内预测模式候选表candModeList,存储相邻PU的预测模式
- 根据参考相邻块(左A、上B)的预测模式,填充候选表,可填充3种模式
1.2.2 色度帧内编码
- 色度分量有5种帧内预测模式:planar模式、垂直、水平、DC及对应亮度分量的预测模式
1.2.3 帧内预测过程:
- 1、判断当前TU相邻参考像素是否可用,不可用则进行填充
- 2、对参考像素进行滤波(更好的利用邻近像素的相关性,提高预测精度)
(1)常规滤波:使用抽头系数为[0.25,0.5,0.25]的滤波器
(2)强滤波(只对32x32进行):需满足- abs(A+C-2B) <Threshold
- abs(C+E-2D) <Threshold (A~E为邻近像素区域)
- 3、根据滤波后的参考像素计算当前TU的预测像素值
- planar模式
- DC模式
- 角度模式
2. 帧间预测编码
利用视频时域相关性,使用邻近已编码图像像素预测当前图像像素
帧间预测部分都采用基于块的运动补偿技术
运动估计:当前图像的每个像素块在之前已编码图像中寻找一个最佳匹配块
用于预测的图像为参考图像,参考块到当前像素块的位移为运动向量MV,当前像素与参考块差值为预测残差PR,
实际运动场景中物体运动的距离不一定是像素尺寸的整数倍,因此为了提高**运动估计(ME)**精度,使用半像素精度的运动估计
MPEG2将一个宏块分成更小的块进行预测,分别进行运动补偿,使运动估计和补偿更加精细
H.264/AVC规定了7种大小的运动补偿块,分别为16x16,16x8,8x16,8x8,8x4,4x8,4x4
2.1 帧间预测编码关键技术:
1、运动估计,运动估计准则:判定两个块的匹配程度
- 最小均方误差 MSE:两个块对应位置像素差值平方求和
- 最小平均绝对误差 MAD:两个块对应位置像素差值的绝对值求和
- 最大匹配像素数 MPC:两个块对应位置像素差小于等于阈值t的个数
2、搜索算法
-
全搜索、二维对数搜索、三步搜索算法等
-
亚像素精度运动估计:相邻两图像之间物体的运动可能以半像素、1/4、1/8像素为单位,整像素匹配会出现匹配不准确的问题,导致运动补偿残差幅度较大,影响编码效率。
- H.264/AVC半像素精度插值:使用了6抽头滤波器,即对当前像素左右(或者上下,效果相同)各三个像素进行滤波器计算,抽头系数为(1/32,-5/32,20/32,20/32,-5/32,1/32)
- H.265/HEVC半像素精度插值
- 亮度分量半像素位置值由离散余弦变换的8抽头滤波器生成,1/4,3/4像素的位置由离散余弦变换的7抽头滤波器生成
- 色度需要达到1/8精度像素,由离散余弦变换的4抽头滤波器生成
3、MV预测:时域相邻图像同一位置像素块运动向量和空间域相邻块运动向量具有较强的相关性
H.264/AVC:
- MV空域预测:当前预测块MV,参考左上块MV,上左块MV,右上块MV,并取三者的中值
- MV时域预测:主要用于B slice
- 当B图像的两个MV都来自同一个参考方向时,其中一个MV可以参考另一个MV进行预测,两参考图像与当前图像的距离为L0,L1时,
- 预测向量MVP1 = L1 / L0 * MV0
- MVD1 = MV1 - MVP1
- 直接模式MV预测(B图像的两个MV不是来自同一个参考方向):设ref0,ref1分别为前向和后向参考图像,且与当前图像距离为L0,L1,在ref1中与当前块对应位置块有一个执行ref0的MV,则当前图像的两个前后向MV为:
- MV0 = MV * L0 / (L1 + L0)
- MV1 = MV0 * MV
- 当B图像的两个MV都来自同一个参考方向时,其中一个MV可以参考另一个MV进行预测,两参考图像与当前图像的距离为L0,L1时,
H.265/HEVC:
- Merge模式:为当前PU建立MV候选列表,存在5个候选MV,通过遍历候选表,计算cost后选择代价最小的作为该merge模式的最优MV
- 空域(最多提供4个):5个邻近块mv ,左下:A0,A1,左上:B2,右上:B1,B0
- 时域(最多提供1个):利用当前PU在邻近已编码图像中对应位置PU的运动信息,并需要根据与参考图像的位置关系做相应的比例伸缩,如果同位PU的(H位置-当前PU右下角)不可用,则用C3位置(当前块中心)的同位PU进行替换,若候选mv个数达不到5个,则用(0,0)进行填充
- 组合列表的建立:对应B slice,由于存在两个MV,其候选表也要提供两个预测MV,即将前4个候选MV两两组合,生成用于B lise的组合列表
- AMVP:高级运动向量预测,为当前PU建立MV候选列表,存在2个候选MV,编码器从中选出最优的预测MV
- 空域:从左侧(A0,A1)和上方(B1,B0)各产生一个候选预测MV,左侧选择顺序为A0-A1-scaled A0-scaled A1,上方选择顺序为B0-B1-B2(-scaled B0-scaled B1-scaled B2),scaled A0表示将A0的mv进行比例伸缩
- 时域:与merge模式时域相同
4、多参考图像及加权预测
2.2 帧内预测编码:
xCheckRDCostIntra
主要调用estIntraPredLumaQT和estIntraPredChromaQT,对一个CU块亮度色度进行预测模式的决策和TU的划分,根据最优模式得到CU块重建值
Void TEncCu::xCheckRDCostIntra( TComDataCU *&rpcBestCU,
TComDataCU *&rpcTempCU,
PartSize eSize
DEBUG_STRING_FN_DECLARE(sDebug) )
{
DEBUG_STRING_NEW(sTest)
if(getFastDeltaQp())
{
const TComSPS &sps=*(rpcTempCU->getSlice()->getSPS());
const UInt fastDeltaQPCuMaxSize = Clip3(sps.getMaxCUHeight()>>(sps.getLog2DiffMaxMinCodingBlockSize()), sps.getMaxCUHeight(), 32u);
if(rpcTempCU->getWidth( 0 ) > fastDeltaQPCuMaxSize)
{
return; // only check necessary 2Nx2N Intra in fast deltaqp mode
}
}
UInt uiDepth = rpcTempCU->getDepth( 0 );
rpcTempCU->setSkipFlagSubParts( false, 0, uiDepth );
rpcTempCU->setPartSizeSubParts( eSize, 0, uiDepth );
rpcTempCU->setPredModeSubParts( MODE_INTRA, 0, uiDepth );
rpcTempCU->setChromaQpAdjSubParts( rpcTempCU->getCUTransquantBypass(0) ? 0 : m_cuChromaQpOffsetIdxPlus1, 0, uiDepth );
Pel resiLuma[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE];
m_pcPredSearch->estIntraPredLumaQT( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth], m_ppcResiYuvTemp[uiDepth], m_ppcRecoYuvTemp[uiDepth], resiLuma DEBUG_STRING_PASS_INTO(sTest) );
m_ppcRecoYuvTemp[uiDepth]->copyToPicComponent(COMPONENT_Y, rpcTempCU->getPic()->getPicYuvRec(), rpcTempCU->getCtuRsAddr(), rpcTempCU->getZorderIdxInCtu() );
if (rpcBestCU->getPic()->getChromaFormat()!=CHROMA_400)
{
m_pcPredSearch->estIntraPredChromaQT( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth], m_ppcResiYuvTemp[uiDepth], m_ppcRecoYuvTemp[uiDepth], resiLuma DEBUG_STRING_PASS_INTO(sTest) );
}
m_pcEntropyCoder->resetBits();
if ( rpcTempCU->getSlice()->getPPS()->getTransquantBypassEnableFlag())
{
m_pcEntropyCoder->encodeCUTransquantBypassFlag( rpcTempCU, 0, true );
}
m_pcEntropyCoder->encodeSkipFlag ( rpcTempCU, 0, true );
m_pcEntropyCoder->encodePredMode( rpcTempCU, 0, true );
m_pcEntropyCoder->encodePartSize( rpcTempCU, 0, uiDepth, true );
m_pcEntropyCoder->encodePredInfo( rpcTempCU, 0 );
m_pcEntropyCoder->encodeIPCMInfo(rpcTempCU, 0, true );
// Encode Coefficients
Bool bCodeDQP = getdQPFlag();
Bool codeChromaQpAdjFlag = getCodeChromaQpAdjFlag();
m_pcEntropyCoder->encodeCoeff( rpcTempCU, 0, uiDepth, bCodeDQP, codeChromaQpAdjFlag );
setCodeChromaQpAdjFlag( codeChromaQpAdjFlag );
setdQPFlag( bCodeDQP );
m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]);
rpcTempCU->getTotalBits() = m_pcEntropyCoder->getNumberOfWrittenBits();
rpcTempCU->getTotalBins() = ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );
xCheckDQP( rpcTempCU );
xCheckBestMode(rpcBestCU, rpcTempCU, uiDepth DEBUG_STRING_PASS_INTO(sDebug) DEBUG_STRING_PASS_INTO(sTest));
}
estIntraPredLumaQT
1、计算率失真优化的Lambda系数
2、遍历CU的所有PU块
3、initIntraPatternChType中fillReferenceSamples获取邻近宏块参考像素,若没有则进行填充,同时对参考像素滤波,有常规滤波和强滤波(useStrongIntraSmoothing)两种
4、遍历35种预测模式
5、filteringIntraReferenceSamples 判断该模式是否需要滤波,得到bUseFilter
6、predIntraAng 计算各个预测模式的Hadamard失真
7、先从35种模式种粗选出n种模式,再进行细选
-
粗选:使用计算复杂度较低的Hadamard失真
-
细选:个数根据块宽度决定,细选需要经过编码重建计算cost,复杂度较高
8、xUpdateCandList更新细选预测模式候选表
细选部分:
1、getFastUDIUseMPMEnabled判断是否使能MPM,即帧内预测候选列表
2、根据相邻PU建立模式MPM候选表,相邻PU的预测模式不同,建立的模式候选表也不同
- 如果候选表中存在当前PU的预测模式,则只需传输候选表对应 该预测模式的索引
- 否则将候选表中的预测模式放入细选列表中
5、遍历细选候选表中的预测模式
6、xRecurIntraCodingLumaQT (只递归到最大限度TU大小32x32的子块)递归将CU划分成四叉树,计算不同模式下子块经过预测-变换-量化-反量化-反变换-重建的cost
7、选择cost最小的预测模式作为最终预测模式
8、第二次调用xRecurIntraCodingLumaQT (递归到根据最小限度TU大小和深度决定的子块),递归基于最优预测模式,重建并计算得到各个子块的cost
9、比较6、7cost,得到最小的Cost
Void
TEncSearch::estIntraPredLumaQT(TComDataCU* pcCU,
TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
TComYuv* pcRecoYuv,
Pel resiLuma[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE]
DEBUG_STRING_FN_DECLARE(sDebug))
{
const UInt uiDepth = pcCU->getDepth(0);
const UInt uiInitTrDepth = pcCU->getPartitionSize(0) == SIZE_2Nx2N ? 0 : 1;
const UInt uiNumPU = 1<<(2*uiInitTrDepth);
const UInt uiQNumParts = pcCU->getTotalNumPart() >> 2;
const UInt uiWidthBit = pcCU->getIntraSizeIdx(0);
const ChromaFormat chFmt = pcCU->getPic()->getChromaFormat();
const UInt numberValidComponents = getNumberValidComponents(chFmt);
const TComSPS &sps = *(pcCU->getSlice()->getSPS());
const TComPPS &pps = *(pcCU->getSlice()->getPPS());
Distortion uiOverallDistY = 0;
UInt CandNum;
Double CandCostList[ FAST_UDI_MAX_RDMODE_NUM ];
Pel resiLumaPU[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE];
Bool bMaintainResidual[NUMBER_OF_STORED_RESIDUAL_TYPES];
for (UInt residualTypeIndex = 0; residualTypeIndex < NUMBER_OF_STORED_RESIDUAL_TYPES; residualTypeIndex++)
{
bMaintainResidual[residualTypeIndex] = true; //assume true unless specified otherwise
}
bMaintainResidual[RESIDUAL_ENCODER_SIDE] = !(m_pcEncCfg->getUseReconBasedCrossCPredictionEstimate());
// Lambda calculation at equivalent Qp of 4 is recommended because at that Qp, the quantisation divisor is 1.
/*计算率失真优化的Lambda系数*/
#if FULL_NBIT
const Double sqrtLambdaForFirstPass= (m_pcEncCfg->getCostMode()==COST_MIXED_LOSSLESS_LOSSY_CODING && pcCU->getCUTransquantBypass(0)) ?
sqrt(0.57 * pow(2.0, ((LOSSLESS_AND_MIXED_LOSSLESS_RD_COST_TEST_QP_PRIME - 12) / 3.0)))
: m_pcRdCost->getSqrtLambda();
#else
const Double sqrtLambdaForFirstPass= (m_pcEncCfg->getCostMode()==COST_MIXED_LOSSLESS_LOSSY_CODING && pcCU->getCUTransquantBypass(0)) ?
sqrt(0.57 * pow(2.0, ((LOSSLESS_AND_MIXED_LOSSLESS_RD_COST_TEST_QP_PRIME - 12 - 6 * (sps.getBitDepth(CHANNEL_TYPE_LUMA) - 8)) / 3.0)))
: m_pcRdCost->getSqrtLambda();
#endif
//===== set QP and clear Cbf =====
if ( pps.getUseDQP() == true)
{
pcCU->setQPSubParts( pcCU->getQP(0), 0, uiDepth );
}
else
{
pcCU->setQPSubParts( pcCU->getSlice()->getSliceQp(), 0, uiDepth );
}
//===== loop over partitions =====
TComTURecurse tuRecurseCU(pcCU, 0);
TComTURecurse tuRecurseWithPU(tuRecurseCU, false, (uiInitTrDepth==0)?TComTU::DONT_SPLIT : TComTU::QUAD_SPLIT);
do /*遍历CU的所有PU*/
{
const UInt uiPartOffset=tuRecurseWithPU.GetAbsPartIdxTU();
// for( UInt uiPU = 0, uiPartOffset=0; uiPU < uiNumPU; uiPU++, uiPartOffset += uiQNumParts )
//{
//===== init pattern for luma prediction =====
DEBUG_STRING_NEW(sTemp2)
//===== determine set of modes to be tested (using prediction signal only) =====
/*35种预测模式*/
Int numModesAvailable = 35; //total number of Intra modes
UInt uiRdModeList[FAST_UDI_MAX_RDMODE_NUM];
/*根据块宽度设定失真优化的模式数,该数量决定了有多少种模式需要进行量化编码重建,再与原像素计算cost,即精细筛选的个数,而粗选只是计算复杂度较低的Hadamard失真*/
Int numModesForFullRD = m_pcEncCfg->getFastUDIUseMPMEnabled()?g_aucIntraModeNumFast_UseMPM[ uiWidthBit ] : g_aucIntraModeNumFast_NotUseMPM[ uiWidthBit ];
// this should always be true
assert (tuRecurseWithPU.ProcessComponentSection(COMPONENT_Y));
/*获取邻近宏块参考像素,若没有则进行填充,同时对参考像素滤波,有常规滤波和强滤波两种*/
initIntraPatternChType( tuRecurseWithPU, COMPONENT_Y, true DEBUG_STRING_PASS_INTO(sTemp2) );
Bool doFastSearch = (numModesForFullRD != numModesAvailable);
/*快速搜索粗选,计算各个预测模式的Hadamard失真*/
if (doFastSearch)
{
assert(numModesForFullRD < numModesAvailable);
for( Int i=0; i < numModesForFullRD; i++ )
{
CandCostList[ i ] = MAX_DOUBLE;
}
CandNum = 0;
/**/
const TComRectangle &puRect=tuRecurseWithPU.getRect(COMPONENT_Y);
const UInt uiAbsPartIdx=tuRecurseWithPU.GetAbsPartIdxTU();
Pel* piOrg = pcOrgYuv ->getAddr( COMPONENT_Y, uiAbsPartIdx );
Pel* piPred = pcPredYuv->getAddr( COMPONENT_Y, uiAbsPartIdx );
UInt uiStride = pcPredYuv->getStride( COMPONENT_Y );
DistParam distParam;
const Bool bUseHadamard=pcCU->getCUTransquantBypass(0) == 0;
/*设置计算cost的参数如src数据、dst数据、宽高,其中计算cost计算方式由函数指针指向Hadamard cost计算函数,全部的cost计算函数都在TcomRdCost.cpp中定义*/
m_pcRdCost->setDistParam(distParam, sps.getBitDepth(CHANNEL_TYPE_LUMA), piOrg, uiStride, piPred, uiStride, puRect.width, puRect.height, bUseHadamard);
distParam.bApplyWeight = false;
/*遍历35种预测模式*/
for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ )
{
UInt uiMode = modeIdx;
Distortion uiSad = 0;
/*filteringIntraReferenceSamples 判断该模式是否需要滤波,得到bUseFilter*/
/*1、DC模式以及4x4大小的TU不需要滤波*/
/*2、32x32 TU:除水平、垂直之外的所有角度及planar模式*/
/*3、16x16 TU:在32x32基础上除去接近水平和垂直的4个模式,9、11、25、27模式*/
/*4、8x8 TU:只对3个45度倾斜方向的模式滤波*/
const Bool bUseFilter=TComPrediction::filteringIntraReferenceSamples(COMPONENT_Y, uiMode, puRect.width, puRect.height, chFmt, sps.getSpsRangeExtension().getIntraSmoothingDisabledFlag());
/*计算相应模式的预测值*/
predIntraAng( COMPONENT_Y, uiMode, piOrg, uiStride, piPred, uiStride, tuRecurseWithPU, bUseFilter, TComPrediction::UseDPCMForFirstPassIntraEstimation(tuRecurseWithPU, uiMode) );
/*通过cost函数指针计算cost*/
// use hadamard transform here
/*计算Hadamard失真*/
uiSad+=distParam.DistFunc(&distParam);
UInt iModeBits = 0;
/*计算该预测模式所需要的比特*/
// NB xModeBitsIntra will not affect the mode for chroma that may have already been pre-estimated.
iModeBits+=xModeBitsIntra( pcCU, uiMode, uiPartOffset, uiDepth, CHANNEL_TYPE_LUMA );
/*通过拉格朗日率失真代价 J = D + lam*R*/
Double cost = (Double)uiSad + (Double)iModeBits * sqrtLambdaForFirstPass;
#if DEBUG_INTRA_SEARCH_COSTS
std::cout << "1st pass mode " << uiMode << " SAD = " << uiSad << ", mode bits = " << iModeBits << ", cost = " << cost << "\n";
#endif
/*更新预测模式候选表*/
CandNum += xUpdateCandList( uiMode, cost, numModesForFullRD, uiRdModeList, CandCostList );
}
/*是否使能MPM,即帧内预测候选列表*/
if (m_pcEncCfg->getFastUDIUseMPMEnabled())
{
/*初始化MPM列表,长度为3*/
Int uiPreds[NUM_MOST_PROBABLE_MODES] = {-1, -1, -1};
Int iMode = -1;
/*根据相邻PU建立模式候选表,参考书籍P115*/
/*相邻PU的预测模式不同,建立的模式候选表也不同*/
pcCU->getIntraDirPredictor( uiPartOffset, uiPreds, COMPONENT_Y, &iMode );
const Int numCand = ( iMode >= 0 ) ? iMode : Int(NUM_MOST_PROBABLE_MODES);
for( Int j=0; j < numCand; j++)
{
Bool mostProbableModeIncluded = false;
Int mostProbableMode = uiPreds[j];
/*参考数据P116*/
/*若当前PU最优模式moduleC,在候选表uiPreds中也存在,则只需要编码modecC在候选表中的位置即可*/
/*如果候选表中存在当前PU的预测模式,则只需传输候选表对应 该预测模式的索引即可*/
/*否则将候选表中的预测模式放入细选列表中*/
for( Int i=0; i < numModesForFullRD; i++)
{
mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);
}
if (!mostProbableModeIncluded)
{
uiRdModeList[numModesForFullRD++] = mostProbableMode;
}
}
}
}
else
{
/*不使用快速模式,则将所有模式都加入细选的率失真列表中*/
for( Int i=0; i < numModesForFullRD; i++)
{
uiRdModeList[i] = i;
}
}
//===== check modes (using r-d costs) =====
#if HHI_RQT_INTRA_SPEEDUP_MOD
UInt uiSecondBestMode = MAX_UINT;
Double dSecondBestPUCost = MAX_DOUBLE;
#endif
DEBUG_STRING_NEW(sPU)
UInt uiBestPUMode = 0;
Distortion uiBestPUDistY = 0;
Double dBestPUCost = MAX_DOUBLE;
#if ENVIRONMENT_VARIABLE_DEBUG_AND_TEST
UInt max=numModesForFullRD;
if (DebugOptionList::ForceLumaMode.isSet())
{
max=0; // we are forcing a direction, so don't bother with mode check
}
/*遍历细选候选表中的预测模式*/
for ( UInt uiMode = 0; uiMode < max; uiMode++)
#else
for( UInt uiMode = 0; uiMode < numModesForFullRD; uiMode++ )
#endif
{
// set luma prediction mode
UInt uiOrgMode = uiRdModeList[uiMode];
pcCU->setIntraDirSubParts ( CHANNEL_TYPE_LUMA, uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth );
DEBUG_STRING_NEW(sMode)
// set context models
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] );
/*递归将CU划分成四叉树,计算不同模式下子块经过预测-变换-量化-反量化-反变换-重建的cost*/
// determine residual for partition
Distortion uiPUDistY = 0;
Double dPUCost = 0.0;
#if HHI_RQT_INTRA_SPEEDUP
xRecurIntraCodingLumaQT( pcOrgYuv, pcPredYuv, pcResiYuv, resiLumaPU, uiPUDistY, true, dPUCost, tuRecurseWithPU DEBUG_STRING_PASS_INTO(sMode) );
#else
xRecurIntraCodingLumaQT( pcOrgYuv, pcPredYuv, pcResiYuv, resiLumaPU, uiPUDistY, dPUCost, tuRecurseWithPU DEBUG_STRING_PASS_INTO(sMode) );
#endif
#if DEBUG_INTRA_SEARCH_COSTS
std::cout << "2nd pass [luma,chroma] mode [" << Int(pcCU->getIntraDir(CHANNEL_TYPE_LUMA, uiPartOffset)) << "," << Int(pcCU->getIntraDir(CHANNEL_TYPE_CHROMA, uiPartOffset)) << "] cost = " << dPUCost << "\n";
#endif
/*选择cost最小的预测模式作为最终预测模式*/
// check r-d cost
if( dPUCost < dBestPUCost )
{
DEBUG_STRING_SWAP(sPU, sMode)
#if HHI_RQT_INTRA_SPEEDUP_MOD
uiSecondBestMode = uiBestPUMode;
dSecondBestPUCost = dBestPUCost;
#endif
uiBestPUMode = uiOrgMode;
uiBestPUDistY = uiPUDistY;
dBestPUCost = dPUCost;
xSetIntraResultLumaQT( pcRecoYuv, tuRecurseWithPU );
if (pps.getPpsRangeExtension().getCrossComponentPredictionEnabledFlag())
{
const Int xOffset = tuRecurseWithPU.getRect( COMPONENT_Y ).x0;
const Int yOffset = tuRecurseWithPU.getRect( COMPONENT_Y ).y0;
for (UInt storedResidualIndex = 0; storedResidualIndex < NUMBER_OF_STORED_RESIDUAL_TYPES; storedResidualIndex++)
{
if (bMaintainResidual[storedResidualIndex])
{
xStoreCrossComponentPredictionResult(resiLuma[storedResidualIndex], resiLumaPU[storedResidualIndex], tuRecurseWithPU, xOffset, yOffset, MAX_CU_SIZE, MAX_CU_SIZE );
}
}
}
UInt uiQPartNum = tuRecurseWithPU.GetAbsPartIdxNumParts();
::memcpy( m_puhQTTempTrIdx, pcCU->getTransformIdx() + uiPartOffset, uiQPartNum * sizeof( UChar ) );
for (UInt component = 0; component < numberValidComponents; component++)
{
const ComponentID compID = ComponentID(component);
::memcpy( m_puhQTTempCbf[compID], pcCU->getCbf( compID ) + uiPartOffset, uiQPartNum * sizeof( UChar ) );
::memcpy( m_puhQTTempTransformSkipFlag[compID], pcCU->getTransformSkip(compID) + uiPartOffset, uiQPartNum * sizeof( UChar ) );
}
}
#if HHI_RQT_INTRA_SPEEDUP_MOD
else if( dPUCost < dSecondBestPUCost )
{
uiSecondBestMode = uiOrgMode;
dSecondBestPUCost = dPUCost;
}
#endif
} // Mode loop
/*********在此已经细选出了最优的预测模式***********/
#if HHI_RQT_INTRA_SPEEDUP
#if HHI_RQT_INTRA_SPEEDUP_MOD
for( UInt ui =0; ui < 2; ++ui )
#endif
{
#if HHI_RQT_INTRA_SPEEDUP_MOD
UInt uiOrgMode = ui ? uiSecondBestMode : uiBestPUMode;
if( uiOrgMode == MAX_UINT )
{
break;
}
#else
UInt uiOrgMode = uiBestPUMode;
#endif
#if ENVIRONMENT_VARIABLE_DEBUG_AND_TEST
if (DebugOptionList::ForceLumaMode.isSet())
{
uiOrgMode = DebugOptionList::ForceLumaMode.getInt();
}
#endif
pcCU->setIntraDirSubParts ( CHANNEL_TYPE_LUMA, uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth );
DEBUG_STRING_NEW(sModeTree)
// set context models
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] );
// determine residual for partition
Distortion uiPUDistY = 0;
Double dPUCost = 0.0;
/*在此已经得到了最优的预测模式,这里为第二次调用xRecurIntraCodingLumaQT ,*/
/*递归基于最优预测模式,重建并计算得到各个子块的cost,最小子块为16x16*/
xRecurIntraCodingLumaQT( pcOrgYuv, pcPredYuv, pcResiYuv, resiLumaPU, uiPUDistY, false, dPUCost, tuRecurseWithPU DEBUG_STRING_PASS_INTO(sModeTree));
/*dBestPUCost为细选时最小的cost,与划分到16x16的dPUCost比较*/
// check r-d cost
if( dPUCost < dBestPUCost )
{
DEBUG_STRING_SWAP(sPU, sModeTree)
uiBestPUMode = uiOrgMode;
uiBestPUDistY = uiPUDistY;
/*得到最小的Cost*/
dBestPUCost = dPUCost;
xSetIntraResultLumaQT( pcRecoYuv, tuRecurseWithPU );
if (pps.getPpsRangeExtension().getCrossComponentPredictionEnabledFlag())
{
const Int xOffset = tuRecurseWithPU.getRect( COMPONENT_Y ).x0;
const Int yOffset = tuRecurseWithPU.getRect( COMPONENT_Y ).y0;
for (UInt storedResidualIndex = 0; storedResidualIndex < NUMBER_OF_STORED_RESIDUAL_TYPES; storedResidualIndex++)
{
if (bMaintainResidual[storedResidualIndex])
{
xStoreCrossComponentPredictionResult(resiLuma[storedResidualIndex], resiLumaPU[storedResidualIndex], tuRecurseWithPU, xOffset, yOffset, MAX_CU_SIZE, MAX_CU_SIZE );
}
}
}
const UInt uiQPartNum = tuRecurseWithPU.GetAbsPartIdxNumParts();
::memcpy( m_puhQTTempTrIdx, pcCU->getTransformIdx() + uiPartOffset, uiQPartNum * sizeof( UChar ) );
for (UInt component = 0; component < numberValidComponents; component++)
{
const ComponentID compID = ComponentID(component);
::memcpy( m_puhQTTempCbf[compID], pcCU->getCbf( compID ) + uiPartOffset, uiQPartNum * sizeof( UChar ) );
::memcpy( m_puhQTTempTransformSkipFlag[compID], pcCU->getTransformSkip(compID) + uiPartOffset, uiQPartNum * sizeof( UChar ) );
}
}
} // Mode loop
#endif
DEBUG_STRING_APPEND(sDebug, sPU)
//--- update overall distortion ---
/*得到最小的Cost*/
uiOverallDistY += uiBestPUDistY;
//--- update transform index and cbf ---
const UInt uiQPartNum = tuRecurseWithPU.GetAbsPartIdxNumParts();
::memcpy( pcCU->getTransformIdx() + uiPartOffset, m_puhQTTempTrIdx, uiQPartNum * sizeof( UChar ) );
for (UInt component = 0; component < numberValidComponents; component++)
{
const ComponentID compID = ComponentID(component);
::memcpy( pcCU->getCbf( compID ) + uiPartOffset, m_puhQTTempCbf[compID], uiQPartNum * sizeof( UChar ) );
::memcpy( pcCU->getTransformSkip( compID ) + uiPartOffset, m_puhQTTempTransformSkipFlag[compID ], uiQPartNum * sizeof( UChar ) );
}
//--- set reconstruction for next intra prediction blocks ---
if( !tuRecurseWithPU.IsLastSection() )
{
const TComRectangle &puRect=tuRecurseWithPU.getRect(COMPONENT_Y);
const UInt uiCompWidth = puRect.width;
const UInt uiCompHeight = puRect.height;
const UInt uiZOrder = pcCU->getZorderIdxInCtu() + uiPartOffset;
Pel* piDes = pcCU->getPic()->getPicYuvRec()->getAddr( COMPONENT_Y, pcCU->getCtuRsAddr(), uiZOrder );
const UInt uiDesStride = pcCU->getPic()->getPicYuvRec()->getStride( COMPONENT_Y);
const Pel* piSrc = pcRecoYuv->getAddr( COMPONENT_Y, uiPartOffset );
const UInt uiSrcStride = pcRecoYuv->getStride( COMPONENT_Y);
for( UInt uiY = 0; uiY < uiCompHeight; uiY++, piSrc += uiSrcStride, piDes += uiDesStride )
{
for( UInt uiX = 0; uiX < uiCompWidth; uiX++ )
{
piDes[ uiX ] = piSrc[ uiX ];
}
}
}
//=== update PU data ====
pcCU->setIntraDirSubParts ( CHANNEL_TYPE_LUMA, uiBestPUMode, uiPartOffset, uiDepth + uiInitTrDepth );
} while (tuRecurseWithPU.nextSection(tuRecurseCU));/*遍历PU块*/
if( uiNumPU > 1 )
{ // set Cbf for all blocks
UInt uiCombCbfY = 0;
UInt uiCombCbfU = 0;
UInt uiCombCbfV = 0;
UInt uiPartIdx = 0;
for( UInt uiPart = 0; uiPart < 4; uiPart++, uiPartIdx += uiQNumParts )
{
uiCombCbfY |= pcCU->getCbf( uiPartIdx, COMPONENT_Y, 1 );
uiCombCbfU |= pcCU->getCbf( uiPartIdx, COMPONENT_Cb, 1 );
uiCombCbfV |= pcCU->getCbf( uiPartIdx, COMPONENT_Cr, 1 );
}
for( UInt uiOffs = 0; uiOffs < 4 * uiQNumParts; uiOffs++ )
{
pcCU->getCbf( COMPONENT_Y )[ uiOffs ] |= uiCombCbfY;
pcCU->getCbf( COMPONENT_Cb )[ uiOffs ] |= uiCombCbfU;
pcCU->getCbf( COMPONENT_Cr )[ uiOffs ] |= uiCombCbfV;
}
}
//===== reset context models =====
m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]);
//===== set distortion (rate and r-d costs are determined later) =====
pcCU->getTotalDistortion() = uiOverallDistY;
}
getIntraDirPredictor:亮度模式的编码,参考书籍P115
输出:uiPreds,参考左上,上左相邻块PU预测模式的候选表
相邻块之间具有较强的相关性,265建立了帧内预测模式候选列表
帧内预测亮度模式的编码:当前块PU参考相邻块(左A、上B)的预测模式
- 通过建立帧内预测模式候选表candModeList,存储相邻PU的预测模式
- 根据参考相邻块(左A、上B)的预测模式,填充候选表,可填充3种模式
Void TComDataCU::getIntraDirPredictor( UInt uiAbsPartIdx, Int uiIntraDirPred[NUM_MOST_PROBABLE_MODES], const ComponentID compID, Int* piMode ) const
{
UInt LeftPartIdx = MAX_UINT;
UInt AbovePartIdx = MAX_UINT;
Int iLeftIntraDir, iAboveIntraDir;
const TComSPS *sps=getSlice()->getSPS();
const UInt partsPerMinCU = 1<<(2*(sps->getMaxTotalCUDepth() - sps->getLog2DiffMaxMinCodingBlockSize()));
const ChannelType chType = toChannelType(compID);
const ChromaFormat chForm = getPic()->getChromaFormat();
// Get intra direction of left PU
const TComDataCU *pcCULeft = getPULeft( LeftPartIdx, m_absZIdxInCtu + uiAbsPartIdx );
if (isChroma(compID))
{
LeftPartIdx = getChromasCorrespondingPULumaIdx(LeftPartIdx, chForm, partsPerMinCU);
}
/*或者左上-A块的预测模式*/
iLeftIntraDir = pcCULeft ? ( pcCULeft->isIntra( LeftPartIdx ) ? pcCULeft->getIntraDir( chType, LeftPartIdx ) : DC_IDX ) : DC_IDX;
// Get intra direction of above PU
const TComDataCU *pcCUAbove = getPUAbove( AbovePartIdx, m_absZIdxInCtu + uiAbsPartIdx, true, true );
if (isChroma(compID))
{
AbovePartIdx = getChromasCorrespondingPULumaIdx(AbovePartIdx, chForm, partsPerMinCU);
}
/*或者上左-B块的预测模式*/
iAboveIntraDir = pcCUAbove ? ( pcCUAbove->isIntra( AbovePartIdx ) ? pcCUAbove->getIntraDir( chType, AbovePartIdx ) : DC_IDX ) : DC_IDX;
if (isChroma(chType))
{
if (iLeftIntraDir == DM_CHROMA_IDX)
{
iLeftIntraDir = pcCULeft-> getIntraDir( CHANNEL_TYPE_LUMA, LeftPartIdx );
}
if (iAboveIntraDir == DM_CHROMA_IDX)
{
iAboveIntraDir = pcCUAbove->getIntraDir( CHANNEL_TYPE_LUMA, AbovePartIdx );
}
}
assert (2<NUM_MOST_PROBABLE_MODES);
/*moduleA 等于moduleB都为plannar模式或者DC模式,
/*则candModeList[0] = plannar\candModeList[1] = DC\candModeList[0] = 26垂直模式*/
if(iLeftIntraDir == iAboveIntraDir)
{
if( piMode )
{
*piMode = 1;
}
if (iLeftIntraDir > 1) // angular modes
{
uiIntraDirPred[0] = iLeftIntraDir;
uiIntraDirPred[1] = ((iLeftIntraDir + 29) % 32) + 2;
uiIntraDirPred[2] = ((iLeftIntraDir - 1 ) % 32) + 2;
}
else //non-angular
{
uiIntraDirPred[0] = PLANAR_IDX;
uiIntraDirPred[1] = DC_IDX;
uiIntraDirPred[2] = VER_IDX;
}
}
else
{
if( piMode )
{
*piMode = 2;
}
uiIntraDirPred[0] = iLeftIntraDir;
uiIntraDirPred[1] = iAboveIntraDir;
if (iLeftIntraDir && iAboveIntraDir ) //both modes are non-planar
{
uiIntraDirPred[2] = PLANAR_IDX;
}
else
{
uiIntraDirPred[2] = (iLeftIntraDir+iAboveIntraDir)<2? VER_IDX : DC_IDX;
}
}
for (UInt i=0; i<NUM_MOST_PROBABLE_MODES; i++)
{
assert(uiIntraDirPred[i] < 35);
}
}
xRecurIntraCodingLumaQT - 递归划分CU块
输出:CU进行对TU进行划分后的cost
1、estIntraPredLumaQT在进行预测模式的选择时, 调用了两次xRecurIntraCodingLumaQT,第一次调用时bCheckFirst参数为true,即只会将CU划分到限定的最大TU大小(如TU最大限定为32x32,则64x64的CU只递归进行一次四叉树划分),如果继续划分,算法复杂度会呈指数上升。
2、遍历候选列表的预测模式,再次计算四个子块预测值,进行变换-量化-反量化-反变换-重建(xIntraCodingTUBlock函数),与原像素比较得到最终的全率失真代价,以此作为候选列表的预测模式的选择
3、在此已经得到了最优的预测模式,第二次调用xRecurIntraCodingLumaQT ,此时bCheckFirst参数为false,即将CU块划分到最小限定大小(能够保留更多图像细节),如16x16,此时64x64的CU会进行2次递归调用:
- 第一次划分成4个32x32子块,在遍历第一个32x32子块时,首先进入bCheckFull 判断语句,调用xIntraCodingTUBlock,通过最优预测模式,进行变换-量化-反量化-反变换-重建,得到该子块的cost,
- 继续划分成4个16x16子块,并计算4个16x16子块的cost总和
- 计算第二个32x32子块的cost,将第二个32x32子块的cost划分成4个16x16子块计算cost
- …
4、最终得到各个子块的cost
简化的xRecurIntraCodingLumaQT:
xRecurIntraCodingLumaQT()
{
if( bCheckFull )
{
if(checkTransformSkip == true)
skip情况处理...
else
//变换-量化-反量化-反变换-重建
xRecurIntraCodingLumaQT:得到不划分的cost uiSingleDistLuma
}
if( bCheckSplit )
{
do //递归遍历四个子块
{
xRecurIntraCodingLumaQT:得到划分的cost dSplitCost
} while (tuRecurseChild.nextSection(rTu) );
if( dSplitCost < dSingleCost )
xStoreCrossComponentPredictionResult
return;//递归终止
}
dRDCost += dSingleCost;
//递归终止
}
完整的xRecurIntraCodingLumaQT:
Void
TEncSearch::xRecurIntraCodingLumaQT(TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
Pel resiLuma[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE],
Distortion& ruiDistY,
#if HHI_RQT_INTRA_SPEEDUP
Bool bCheckFirst,
#endif
Double& dRDCost,
TComTU& rTu
DEBUG_STRING_FN_DECLARE(sDebug))
{
TComDataCU *pcCU = rTu.getCU();
const UInt uiAbsPartIdx = rTu.GetAbsPartIdxTU();
const UInt uiFullDepth = rTu.GetTransformDepthTotal();
const UInt uiTrDepth = rTu.GetTransformDepthRel();
const UInt uiLog2TrSize = rTu.GetLog2LumaTrSize();/*当前Y分量TU大小*/
/*如果当前CU小于限定的最大TU大小,则bCheckFull标志位1*/
Bool bCheckFull = ( uiLog2TrSize <= pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() );
/*如果当前CU大于限定的最小TU大小,则对CU进行划分*/
Bool bCheckSplit = ( uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) );
Pel resiLumaSplit [NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE];
Pel resiLumaSingle[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE];
Bool bMaintainResidual[NUMBER_OF_STORED_RESIDUAL_TYPES];
for (UInt residualTypeIndex = 0; residualTypeIndex < NUMBER_OF_STORED_RESIDUAL_TYPES; residualTypeIndex++)
{
bMaintainResidual[residualTypeIndex] = true; //assume true unless specified otherwise
}
bMaintainResidual[RESIDUAL_ENCODER_SIDE] = !(m_pcEncCfg->getUseReconBasedCrossCPredictionEstimate());
#if HHI_RQT_INTRA_SPEEDUP
/*限定的最大TU大小*/
Int maxTuSize = pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize();
Int isIntraSlice = (pcCU->getSlice()->getSliceType() == I_SLICE);
// don't check split if TU size is less or equal to max TU size
Bool noSplitIntraMaxTuSize = bCheckFull;
if(m_pcEncCfg->getRDpenalty() && ! isIntraSlice)
{
// in addition don't check split if TU size is less or equal to 16x16 TU size for non-intra slice
noSplitIntraMaxTuSize = ( uiLog2TrSize <= min(maxTuSize,4) );
// if maximum RD-penalty don't check TU size 32x32
if(m_pcEncCfg->getRDpenalty()==2)
{
bCheckFull = ( uiLog2TrSize <= min(maxTuSize,4));
}
}
/*如果CU块大于最大TU szie,则一直进行划分*/
/*如果该TU为第一次划分,且小于等于最大TU szie,则不再进行划分*/
if( bCheckFirst && noSplitIntraMaxTuSize )
{
bCheckSplit = false;
}
#else
Int maxTuSize = pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize();
Int isIntraSlice = (pcCU->getSlice()->getSliceType() == I_SLICE);
// if maximum RD-penalty don't check TU size 32x32
if((m_pcEncCfg->getRDpenalty()==2) && !isIntraSlice)
{
bCheckFull = ( uiLog2TrSize <= min(maxTuSize,4));
}
#endif
Double dSingleCost = MAX_DOUBLE;
Distortion uiSingleDistLuma = 0;
UInt uiSingleCbfLuma = 0;
/*指定当前CU是否使用变换、量化和环路滤波,skip*/
Bool checkTransformSkip = pcCU->getSlice()->getPPS()->getUseTransformSkip();
Int bestModeId[MAX_NUM_COMPONENT] = { 0, 0, 0};
checkTransformSkip &= TUCompRectHasAssociatedTransformSkipFlag(rTu.getRect(COMPONENT_Y), pcCU->getSlice()->getPPS()->getPpsRangeExtension().getLog2MaxTransformSkipBlockSize());
checkTransformSkip &= (!pcCU->getCUTransquantBypass(0));
assert (rTu.ProcessComponentSection(COMPONENT_Y));
const UInt totalAdjustedDepthChan = rTu.GetTransformDepthTotalAdj(COMPONENT_Y);
if ( m_pcEncCfg->getUseTransformSkipFast() )
{
checkTransformSkip &= (pcCU->getPartitionSize(uiAbsPartIdx)==SIZE_NxN);
}
if( bCheckFull )
{
if(checkTransformSkip == true)
{
//----- store original entropy coding status -----
m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[ uiFullDepth ][ CI_QT_TRAFO_ROOT ] );
Distortion singleDistTmpLuma = 0;
UInt singleCbfTmpLuma = 0;
Double singleCostTmp = 0;
Int firstCheckId = 0;
/*遍历skip和no skip情况*/
for(Int modeId = firstCheckId; modeId < 2; modeId ++)
{
DEBUG_STRING_NEW(sModeString)
Int default0Save1Load2 = 0;
singleDistTmpLuma=0;
if(modeId == firstCheckId)
{
default0Save1Load2 = 1;
}
else
{
default0Save1Load2 = 2;
}
pcCU->setTransformSkipSubParts ( modeId, COMPONENT_Y, uiAbsPartIdx, totalAdjustedDepthChan );
xIntraCodingTUBlock( pcOrgYuv, pcPredYuv, pcResiYuv, resiLumaSingle, false, singleDistTmpLuma, COMPONENT_Y, rTu DEBUG_STRING_PASS_INTO(sModeString), default0Save1Load2 );
singleCbfTmpLuma = pcCU->getCbf( uiAbsPartIdx, COMPONENT_Y, uiTrDepth );
//----- determine rate and r-d cost -----
if(modeId == 1 && singleCbfTmpLuma == 0)
{
//In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
singleCostTmp = MAX_DOUBLE;
}
else
{
UInt uiSingleBits = xGetIntraBitsQT( rTu, true, false, false );
singleCostTmp = m_pcRdCost->calcRdCost( uiSingleBits, singleDistTmpLuma );
}
if(singleCostTmp < dSingleCost)
{
DEBUG_STRING_SWAP(sDebug, sModeString)
dSingleCost = singleCostTmp;
uiSingleDistLuma = singleDistTmpLuma;
uiSingleCbfLuma = singleCbfTmpLuma;
bestModeId[COMPONENT_Y] = modeId;
if(bestModeId[COMPONENT_Y] == firstCheckId)
{
xStoreIntraResultQT(COMPONENT_Y, rTu );
m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[ uiFullDepth ][ CI_TEMP_BEST ] );
}
if (pcCU->getSlice()->getPPS()->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag())
{
const Int xOffset = rTu.getRect( COMPONENT_Y ).x0;
const Int yOffset = rTu.getRect( COMPONENT_Y ).y0;
for (UInt storedResidualIndex = 0; storedResidualIndex < NUMBER_OF_STORED_RESIDUAL_TYPES; storedResidualIndex++)
{
if (bMaintainResidual[storedResidualIndex])
{
xStoreCrossComponentPredictionResult(resiLuma[storedResidualIndex], resiLumaSingle[storedResidualIndex], rTu, xOffset, yOffset, MAX_CU_SIZE, MAX_CU_SIZE);
}
}
}
}
if (modeId == firstCheckId)
{
m_pcRDGoOnSbacCoder->load ( m_pppcRDSbacCoder[ uiFullDepth ][ CI_QT_TRAFO_ROOT ] );
}
}
pcCU ->setTransformSkipSubParts ( bestModeId[COMPONENT_Y], COMPONENT_Y, uiAbsPartIdx, totalAdjustedDepthChan );
if(bestModeId[COMPONENT_Y] == firstCheckId)
{
xLoadIntraResultQT(COMPONENT_Y, rTu );
pcCU->setCbfSubParts ( uiSingleCbfLuma << uiTrDepth, COMPONENT_Y, uiAbsPartIdx, rTu.GetTransformDepthTotalAdj(COMPONENT_Y) );
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[ uiFullDepth ][ CI_TEMP_BEST ] );
}
}
else /*不是skip块*/
{
//----- store original entropy coding status -----
if( bCheckSplit )
{
m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[ uiFullDepth ][ CI_QT_TRAFO_ROOT ] );
}
//----- code luma/chroma block with given intra prediction mode and store Cbf-----
dSingleCost = 0.0;
/*设置为no skip块*/
pcCU ->setTransformSkipSubParts ( 0, COMPONENT_Y, uiAbsPartIdx, totalAdjustedDepthChan );
/*当前TU块根据候选预测模式进行预测后,经变换-量化-反量化-反变换后的重建值计算cost*/
xIntraCodingTUBlock( pcOrgYuv, pcPredYuv, pcResiYuv, resiLumaSingle, false, uiSingleDistLuma, COMPONENT_Y, rTu DEBUG_STRING_PASS_INTO(sDebug));
if( bCheckSplit )
{
/*getCbf 当前CU是否存在TU树形结构*/
uiSingleCbfLuma = pcCU->getCbf( uiAbsPartIdx, COMPONENT_Y, uiTrDepth );
}
/*获取该TU编码比特字节*/
//----- determine rate and r-d cost -----
UInt uiSingleBits = xGetIntraBitsQT( rTu, true, false, false );
if(m_pcEncCfg->getRDpenalty() && (uiLog2TrSize==5) && !isIntraSlice)
{
uiSingleBits=uiSingleBits*4;
}
/*计算率失真cost*/
dSingleCost = m_pcRdCost->calcRdCost( uiSingleBits, uiSingleDistLuma );
if (pcCU->getSlice()->getPPS()->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag())
{
const Int xOffset = rTu.getRect( COMPONENT_Y ).x0;
const Int yOffset = rTu.getRect( COMPONENT_Y ).y0;
for (UInt storedResidualIndex = 0; storedResidualIndex < NUMBER_OF_STORED_RESIDUAL_TYPES; storedResidualIndex++)
{
if (bMaintainResidual[storedResidualIndex])
{
xStoreCrossComponentPredictionResult(resiLuma[storedResidualIndex], resiLumaSingle[storedResidualIndex], rTu, xOffset, yOffset, MAX_CU_SIZE, MAX_CU_SIZE);
}
}
}
}
}
/*当前层级需要继续划分*/
if( bCheckSplit )
{
//----- store full entropy coding status, load original entropy coding status -----
if( bCheckFull )
{
m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[ uiFullDepth ][ CI_QT_TRAFO_TEST ] );
m_pcRDGoOnSbacCoder->load ( m_pppcRDSbacCoder[ uiFullDepth ][ CI_QT_TRAFO_ROOT ] );
}
else
{
m_pcRDGoOnSbacCoder->store( m_pppcRDSbacCoder[ uiFullDepth ][ CI_QT_TRAFO_ROOT ] );
}
//----- code splitted block -----
Double dSplitCost = 0.0;
Distortion uiSplitDistLuma = 0;
UInt uiSplitCbfLuma = 0;
/*创建四叉树划分TU子块,并对四个子块进行遍历,*/
TComTURecurse tuRecurseChild(rTu, false);
DEBUG_STRING_NEW(sSplit)
do
{
DEBUG_STRING_NEW(sChild)
/*递归调用xRecurIntraCodingLumaQT,将当前子块作为根节点的TU块,划分为四个子块*/
#if HHI_RQT_INTRA_SPEEDUP
xRecurIntraCodingLumaQT( pcOrgYuv, pcPredYuv, pcResiYuv, resiLumaSplit, uiSplitDistLuma, bCheckFirst, dSplitCost, tuRecurseChild DEBUG_STRING_PASS_INTO(sChild) );
#else
xRecurIntraCodingLumaQT( pcOrgYuv, pcPredYuv, pcResiYuv, resiLumaSplit, uiSplitDistLuma, dSplitCost, tuRecurseChild DEBUG_STRING_PASS_INTO(sChild) );
#endif
DEBUG_STRING_APPEND(sSplit, sChild)
uiSplitCbfLuma |= pcCU->getCbf( tuRecurseChild.GetAbsPartIdxTU(), COMPONENT_Y, tuRecurseChild.GetTransformDepthRel() );
} while (tuRecurseChild.nextSection(rTu) );/*遍历四个子块累加cost uiSplitDistLuma*/
UInt uiPartsDiv = rTu.GetAbsPartIdxNumParts();
{
if (uiSplitCbfLuma)
{
const UInt flag=1<<uiTrDepth;
UChar *pBase=pcCU->getCbf( COMPONENT_Y );
for( UInt uiOffs = 0; uiOffs < uiPartsDiv; uiOffs++ )
{
pBase[ uiAbsPartIdx + uiOffs ] |= flag;
}
}
}
//----- restore context states -----
m_pcRDGoOnSbacCoder->load ( m_pppcRDSbacCoder[ uiFullDepth ][ CI_QT_TRAFO_ROOT ] );
//----- determine rate and r-d cost -----
UInt uiSplitBits = xGetIntraBitsQT( rTu, true, false, false );
dSplitCost = m_pcRdCost->calcRdCost( uiSplitBits, uiSplitDistLuma );
/*比较当前CU重建全率失真dSingleCost与当前CU的四个子TU重建全率失真dSplitCost,选取最小cost的划分方式*/
//===== compare and set best =====
if( dSplitCost < dSingleCost )
{
//--- update cost ---
DEBUG_STRING_SWAP(sSplit, sDebug)
ruiDistY += uiSplitDistLuma;
dRDCost += dSplitCost;
if (pcCU->getSlice()->getPPS()->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag())
{
const Int xOffset = rTu.getRect( COMPONENT_Y ).x0;
const Int yOffset = rTu.getRect( COMPONENT_Y ).y0;
for (UInt storedResidualIndex = 0; storedResidualIndex < NUMBER_OF_STORED_RESIDUAL_TYPES; storedResidualIndex++)
{
if (bMaintainResidual[storedResidualIndex])
{
xStoreCrossComponentPredictionResult(resiLuma[storedResidualIndex], resiLumaSplit[storedResidualIndex], rTu, xOffset, yOffset, MAX_CU_SIZE, MAX_CU_SIZE);
}
}
}
return;
}
//----- set entropy coding status -----
m_pcRDGoOnSbacCoder->load ( m_pppcRDSbacCoder[ uiFullDepth ][ CI_QT_TRAFO_TEST ] );
//--- set transform index and Cbf values ---
pcCU->setTrIdxSubParts( uiTrDepth, uiAbsPartIdx, uiFullDepth );
const TComRectangle &tuRect=rTu.getRect(COMPONENT_Y);
pcCU->setCbfSubParts ( uiSingleCbfLuma << uiTrDepth, COMPONENT_Y, uiAbsPartIdx, totalAdjustedDepthChan );
pcCU ->setTransformSkipSubParts ( bestModeId[COMPONENT_Y], COMPONENT_Y, uiAbsPartIdx, totalAdjustedDepthChan );
//--- set reconstruction for next intra prediction blocks ---
const UInt uiQTLayer = pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() - uiLog2TrSize;
const UInt uiZOrder = pcCU->getZorderIdxInCtu() + uiAbsPartIdx;
const UInt uiWidth = tuRect.width;
const UInt uiHeight = tuRect.height;
Pel* piSrc = m_pcQTTempTComYuv[ uiQTLayer ].getAddr( COMPONENT_Y, uiAbsPartIdx );
UInt uiSrcStride = m_pcQTTempTComYuv[ uiQTLayer ].getStride ( COMPONENT_Y );
Pel* piDes = pcCU->getPic()->getPicYuvRec()->getAddr( COMPONENT_Y, pcCU->getCtuRsAddr(), uiZOrder );
UInt uiDesStride = pcCU->getPic()->getPicYuvRec()->getStride ( COMPONENT_Y );
for( UInt uiY = 0; uiY < uiHeight; uiY++, piSrc += uiSrcStride, piDes += uiDesStride )
{
for( UInt uiX = 0; uiX < uiWidth; uiX++ )
{
piDes[ uiX ] = piSrc[ uiX ];
}
}
}
/*当前层级需要继续划分,则直接返回cost*/
ruiDistY += uiSingleDistLuma;
dRDCost += dSingleCost;
}
色度分量的预测模式选择,与亮度分量相似,但是没有粗选部分,直接对5种预测模式进行细选
Void
TEncSearch::estIntraPredChromaQT(TComDataCU* pcCU,
TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
TComYuv* pcRecoYuv,
Pel resiLuma[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE]
DEBUG_STRING_FN_DECLARE(sDebug))
{
const UInt uiInitTrDepth = pcCU->getPartitionSize(0) != SIZE_2Nx2N && enable4ChromaPUsInIntraNxNCU(pcOrgYuv->getChromaFormat()) ? 1 : 0;
TComTURecurse tuRecurseCU(pcCU, 0);
TComTURecurse tuRecurseWithPU(tuRecurseCU, false, (uiInitTrDepth==0)?TComTU::DONT_SPLIT : TComTU::QUAD_SPLIT);
const UInt uiQNumParts = tuRecurseWithPU.GetAbsPartIdxNumParts();
const UInt uiDepthCU=tuRecurseWithPU.getCUDepth();
const UInt numberValidComponents = pcCU->getPic()->getNumberValidComponents();
do
{
UInt uiBestMode = 0;
Distortion uiBestDist = 0;
Double dBestCost = MAX_DOUBLE;
//----- init mode list -----
if (tuRecurseWithPU.ProcessChannelSection(CHANNEL_TYPE_CHROMA))
{
UInt uiModeList[FAST_UDI_MAX_RDMODE_NUM];
const UInt uiQPartNum = uiQNumParts;
const UInt uiPartOffset = tuRecurseWithPU.GetAbsPartIdxTU();
{
UInt uiMinMode = 0;
UInt uiMaxMode = NUM_CHROMA_MODE;
//----- check chroma modes -----
pcCU->getAllowedChromaDir( uiPartOffset, uiModeList );
#if ENVIRONMENT_VARIABLE_DEBUG_AND_TEST
if (DebugOptionList::ForceChromaMode.isSet())
{
uiMinMode=DebugOptionList::ForceChromaMode.getInt();
if (uiModeList[uiMinMode]==34)
{
uiMinMode=4; // if the fixed mode has been renumbered because DM_CHROMA covers it, use DM_CHROMA.
}
uiMaxMode=uiMinMode+1;
}
#endif
DEBUG_STRING_NEW(sPU)
/*遍历色度分量5种预测模式*/
for( UInt uiMode = uiMinMode; uiMode < uiMaxMode; uiMode++ )
{
//----- restore context models -----
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepthCU][CI_CURR_BEST] );
DEBUG_STRING_NEW(sMode)
//----- chroma coding -----
Distortion uiDist = 0;
pcCU->setIntraDirSubParts ( CHANNEL_TYPE_CHROMA, uiModeList[uiMode], uiPartOffset, uiDepthCU+uiInitTrDepth );
xRecurIntraChromaCodingQT ( pcOrgYuv, pcPredYuv, pcResiYuv, resiLuma, uiDist, tuRecurseWithPU DEBUG_STRING_PASS_INTO(sMode) );
if( pcCU->getSlice()->getPPS()->getUseTransformSkip() )
{
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepthCU][CI_CURR_BEST] );
}
UInt uiBits = xGetIntraBitsQT( tuRecurseWithPU, false, true, false );
Double dCost = m_pcRdCost->calcRdCost( uiBits, uiDist );
//----- compare -----
if( dCost < dBestCost )
{
DEBUG_STRING_SWAP(sPU, sMode);
dBestCost = dCost;
uiBestDist = uiDist;
uiBestMode = uiModeList[uiMode];
xSetIntraResultChromaQT( pcRecoYuv, tuRecurseWithPU );
for (UInt componentIndex = COMPONENT_Cb; componentIndex < numberValidComponents; componentIndex++)
{
const ComponentID compID = ComponentID(componentIndex);
::memcpy( m_puhQTTempCbf[compID], pcCU->getCbf( compID )+uiPartOffset, uiQPartNum * sizeof( UChar ) );
::memcpy( m_puhQTTempTransformSkipFlag[compID], pcCU->getTransformSkip( compID )+uiPartOffset, uiQPartNum * sizeof( UChar ) );
::memcpy( m_phQTTempCrossComponentPredictionAlpha[compID], pcCU->getCrossComponentPredictionAlpha(compID)+uiPartOffset, uiQPartNum * sizeof( SChar ) );
}
}
}
DEBUG_STRING_APPEND(sDebug, sPU)
//----- set data -----
for (UInt componentIndex = COMPONENT_Cb; componentIndex < numberValidComponents; componentIndex++)
{
const ComponentID compID = ComponentID(componentIndex);
::memcpy( pcCU->getCbf( compID )+uiPartOffset, m_puhQTTempCbf[compID], uiQPartNum * sizeof( UChar ) );
::memcpy( pcCU->getTransformSkip( compID )+uiPartOffset, m_puhQTTempTransformSkipFlag[compID], uiQPartNum * sizeof( UChar ) );
::memcpy( pcCU->getCrossComponentPredictionAlpha(compID)+uiPartOffset, m_phQTTempCrossComponentPredictionAlpha[compID], uiQPartNum * sizeof( SChar ) );
}
}
if( ! tuRecurseWithPU.IsLastSection() )
{
for (UInt ch=COMPONENT_Cb; ch<numberValidComponents; ch++)
{
const ComponentID compID = ComponentID(ch);
const TComRectangle &tuRect = tuRecurseWithPU.getRect(compID);
const UInt uiCompWidth = tuRect.width;
const UInt uiCompHeight = tuRect.height;
const UInt uiZOrder = pcCU->getZorderIdxInCtu() + tuRecurseWithPU.GetAbsPartIdxTU();
Pel* piDes = pcCU->getPic()->getPicYuvRec()->getAddr( compID, pcCU->getCtuRsAddr(), uiZOrder );
const UInt uiDesStride = pcCU->getPic()->getPicYuvRec()->getStride( compID);
const Pel* piSrc = pcRecoYuv->getAddr( compID, uiPartOffset );
const UInt uiSrcStride = pcRecoYuv->getStride( compID);
for( UInt uiY = 0; uiY < uiCompHeight; uiY++, piSrc += uiSrcStride, piDes += uiDesStride )
{
for( UInt uiX = 0; uiX < uiCompWidth; uiX++ )
{
piDes[ uiX ] = piSrc[ uiX ];
}
}
}
}
pcCU->setIntraDirSubParts( CHANNEL_TYPE_CHROMA, uiBestMode, uiPartOffset, uiDepthCU+uiInitTrDepth );
pcCU->getTotalDistortion () += uiBestDist;
}
} while (tuRecurseWithPU.nextSection(tuRecurseCU));
//----- restore context models -----
if( uiInitTrDepth != 0 )
{ // set Cbf for all blocks
UInt uiCombCbfU = 0;
UInt uiCombCbfV = 0;
UInt uiPartIdx = 0;
for( UInt uiPart = 0; uiPart < 4; uiPart++, uiPartIdx += uiQNumParts )
{
uiCombCbfU |= pcCU->getCbf( uiPartIdx, COMPONENT_Cb, 1 );
uiCombCbfV |= pcCU->getCbf( uiPartIdx, COMPONENT_Cr, 1 );
}
for( UInt uiOffs = 0; uiOffs < 4 * uiQNumParts; uiOffs++ )
{
pcCU->getCbf( COMPONENT_Cb )[ uiOffs ] |= uiCombCbfU;
pcCU->getCbf( COMPONENT_Cr )[ uiOffs ] |= uiCombCbfV;
}
}
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepthCU][CI_CURR_BEST] );
}