HM编码器代码阅读(32)——帧间预测之AMVP/Merge模式(七)encodeResAndCalcRdInterCU函数:残差计算、变换量化

encodeResAndCalcRdInterCU


原理和细节

    经过运动估计、运动补偿,我们得到了MV以及参考块,那么接下来是计算残差、计算MVD,然后对系数进行变换、量化。


    encodeResAndCalcRdInterCU函数就是帧间预测之后,专门用来计算残差、进行变换量化的函数。但是,除了变换量化之外,encodeResAndCalcRdInterCU函数中还有熵编码方面的处理,我们都知道熵编码是在encodeSlice(xEncodeCU)中进行的,为什么会这样呢,这样做不就导致熵编码进行了多次吗?为了搞清楚这个问题,我们需要了解下面的一些概念:
    1、compressSlice/compressCU,这些函数的作用是模式选取,目的是选取最优的编码模式,使得率失真代价/比特代价最优。具体的操作就是利用率失真优化的原理,选取LCU到CU的最优划分模式,CU到PU的最优划分模式,PU的最优预测模式,CU到TU的最优划分模式,最优的QP参数,使得率失真代价/比特代价最优,但是为了算比特代价(bitCost),必须要经过熵编码这一步骤才能得到,而熵编码之前又有变化量化等步骤,因此compressSlice实际上是把各种各样的模式进行组合,然后每一个组合进行一个完整的编码操作(预测、变换、量化、熵编码),根据率失真代价/比特代价来选取最优的模式组合!
    2、encodeSlice/encodeCU,经过了compressSlice之后,我们已经得到了最优的划分模式、预测模式等参数,同时也得到了变换量化之后的系数既然已经得到了最优模式和变换系数,那么最后的一步就是对这些模式和系数进行熵编码,得到最终的比特率,因此encodeCU的功能就是熵编码。


encodeResAndCalcRdInterCU的流程

    encodeResAndCalcRdInterCU实际就是计算PU的残差,然后进行变换量化,同时通过熵编码来计算率失真代价/比特代价来得到最优QP参数,这就是为什么encodeResAndCalcRdInterCU中还会有熵编码处理的原因。该函数执行完毕之后,变换量化、反变换反量化就已经处理好了,同时还选取了最优的QP参数。所以,这个函数的核心实际是变换量化!

/*
** 估计残差的编码代价
** 主要是进行变换量化以及熵编码,然后选出最优的模式
*/
Void TEncSearch::xEstimateResidualQT( TComDataCU* pcCU, UInt uiQuadrant, UInt uiAbsPartIdx, UInt absTUPartIdx, TComYuv* pcResi, const UInt uiDepth, Double &rdCost, UInt &ruiBits, UInt &ruiDist, UInt *puiZeroDist )
{
	// 删除无关紧要代码***************

	// 完整检查的标志
	Bool bCheckFull;
	if ( SplitFlag && uiDepth == pcCU->getDepth(uiAbsPartIdx) && ( uiLog2TrSize >  pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) ) )
	{
		bCheckFull = false;
	}
	else
	{
		bCheckFull =  ( uiLog2TrSize <= pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() );
	}

	// 是否检查split标志
	const Bool bCheckSplit  = ( uiLog2TrSize >  pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) );
	
	// 完整检查模式
	if( bCheckFull )
	{
		// 删除无关紧要代码***************

		// 是否使用了率失真优化的量化
		if (m_pcEncCfg->getUseRDOQ())
		{
			// 估算比特数
			m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidth, trHeight, TEXT_LUMA );        
		}

		// 删除无关紧要代码***************
		
		/*【==============变换量化开始===================】*/
        // Y分量变换和量化
		m_pcTrQuant->transformNxN( pcCU, pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride (), pcCoeffCurrY, 
#if ADAPTIVE_QP_SELECTION
			pcArlCoeffCurrY, 
#endif      
			trWidth,   trHeight,    uiAbsSumY, TEXT_LUMA,     uiAbsPartIdx );

		// 删除无关紧要代码***************

		if( bCodeChroma )
		{
			if (m_pcEncCfg->getUseRDOQ())
			{
				m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidthC, trHeightC, TEXT_CHROMA );          
			}

			// 删除无关紧要代码***************
			
			// 变换和量化(U分量)
			m_pcTrQuant->transformNxN( pcCU, pcResi->getCbAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrU, 
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrU, 
#endif        
				trWidthC, trHeightC, uiAbsSumU, TEXT_CHROMA_U, uiAbsPartIdx );

			// 删除无关紧要代码***************

			// 变换和量化(V分量)
			m_pcTrQuant->transformNxN( pcCU, pcResi->getCrAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrV,
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrV, 
#endif        
				trWidthC, trHeightC, uiAbsSumV, TEXT_CHROMA_V, uiAbsPartIdx );

			// 删除无关紧要代码***************
		}

		/*【==============变换量化结束===================】*/
		
		/*【==============变换系数以及其他信息熵编码开始===================】*/
		m_pcEntropyCoder->resetBits();

        // 对CBF进行编码(Y分量)
		m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA,     uiTrMode );

        // 对残差系数进行编码(Y分量)
		m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx,  trWidth,  trHeight,    uiDepth, TEXT_LUMA );
		
		// 删除无关紧要代码***************

		// 对色度部分进行编码
		if( bCodeChroma )
		{
			// 删除无关紧要代码***************

			// 对CBF进行编码(U分量)
			m_pcEntropyCoder->encodeQtCbf   ( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode );
			// 对系数进行编码(U分量)
			m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U );
			
			// 删除无关紧要代码***************

			// 对CBF进行编码(V分量)
			m_pcEntropyCoder->encodeQtCbf   ( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode );
			// 对系数进行编码(V分量)
			m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V );
			
			// 删除无关紧要代码***************
		}

		/*【==============变换系数以及其他信息熵编码结束===================】*/
		
		
		// 删除无关紧要代码***************

		/*【==============反量化、反变换开始===================】*/
		
		// 如果Y分量绝对系数和大于0
		// 开始执行反量化、反变换
		if( uiAbsSumY )
		{
			// 删除无关紧要代码***************
            // 反变换、反量化(Y分量)
			m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_LUMA,REG_DCT, pcResiCurrY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(),  pcCoeffCurrY, trWidth, trHeight, scalingListType );//this is for inter mode only

			const UInt uiNonzeroDistY = m_pcRdCost->getDistPart(g_bitDepthY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getLumaAddr( absTUPartIdx ), m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(),
				pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride(), trWidth,trHeight );
				
			if (pcCU->isLosslessCoded(0)) 
			{
				uiDistY = uiNonzeroDistY;
			}
			else
			{
				// 删除无关紧要代码***************
				m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_LUMA,     uiTrMode );
				// 删除无关紧要代码***************
			}
		}
		// 否则,如果是跳过Y分量
		else if( checkTransformSkipY )
		{
			// 删除无关紧要代码***************
			
			// 编码Y分量的CBF的0系数
			m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_LUMA, uiTrMode );
			
			// 删除无关紧要代码***************
		}

		// 删除无关紧要代码***************
		
		// 如果对色度部分进行了变换量化,那么需要对色度部分进行反变换反量化
		if( bCodeChroma )
		{
			// 删除无关紧要代码***************

			// 如果对U分量进行了变换量化
			if( uiAbsSumU )
			{
				// 删除无关紧要代码***************

				// 反变换、反量化(U分量)
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrU, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrU, trWidthC, trHeightC, scalingListType  );

				const UInt uiNonzeroDistU = m_pcRdCost->getDistPart(g_bitDepthC, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCbAddr( absTUPartIdxC), m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(),
					pcResi->getCbAddr( absTUPartIdxC), pcResi->getCStride(), trWidthC, trHeightC
					, TEXT_CHROMA_U
					);

				if(pcCU->isLosslessCoded(0))  
				{
					uiDistU = uiNonzeroDistU;
				}
				else
				{
					// 删除无关紧要代码***************
					
					m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_U,     uiTrMode );
					
					// 删除无关紧要代码***************
				}
			}
			// 否则(即变换量化没有效果或者不够优?)
			else if( checkTransformSkipUV )
			{
				// 删除无关紧要代码***************
				
				// 编码CBF的0系数
				m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_U, uiTrMode );
				
				// 删除无关紧要代码***************
			}

			// 删除无关紧要代码***************

			// 对V分量进行处理(过程同U分量)
			if( uiAbsSumV )
			{
				// 删除无关紧要代码***************

				// 反变换反量化(V分量)
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrV, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrV, trWidthC, trHeightC, scalingListType );

				const UInt uiNonzeroDistV = m_pcRdCost->getDistPart(g_bitDepthC, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCrAddr( absTUPartIdxC ), m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(),
					pcResi->getCrAddr( absTUPartIdxC ), pcResi->getCStride(), trWidthC, trHeightC
					, TEXT_CHROMA_V
					);
				if (pcCU->isLosslessCoded(0)) 
				{
					uiDistV = uiNonzeroDistV;
				}
				else
				{
					// 删除无关紧要代码***************
					
					// 对CBF的0系数进行编码
					m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_V,     uiTrMode );
					
					// 删除无关紧要代码***************
				}
			}
			// 如果跳过了UV分量
			else if( checkTransformSkipUV )
			{
				// 删除无关紧要代码***************
				
				// 对CBF的0系数进行编码
				m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_V, uiTrMode );
				
				// 删除无关紧要代码***************
			}
			
			if( !uiAbsSumV )
			{
				// 删除无关紧要代码***************
			}
		}
		
		/*【==============反量化、反变换结束===================】*/
		// 删除无关紧要代码***************

		/*【==============TransformSkip模式的变换量化、反变换反量化、熵编码 开始===================】*/
		if( checkTransformSkipY )
		{
			// 删除无关紧要代码***************
			
			if (m_pcEncCfg->getUseRDOQTS())
			{
				// 估算比特数
				m_pcEntropyCoder->estimateBit( m_pcTrQuant->m_pcEstBitsSbac, trWidth, trHeight, TEXT_LUMA );        
			}

			// 删除无关紧要代码***************
			
			// 变换和量化
			m_pcTrQuant->transformNxN( pcCU, pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride (), pcCoeffCurrY, 
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrY, 
#endif      
				trWidth,   trHeight,    uiAbsSumTransformSkipY, TEXT_LUMA, uiAbsPartIdx, true );
			pcCU->setCbfSubParts( uiAbsSumTransformSkipY ? uiSetCbf : 0, TEXT_LUMA, uiAbsPartIdx, uiDepth );

			if( uiAbsSumTransformSkipY != 0 )
			{
				m_pcEntropyCoder->resetBits();
				// 对CBF的0系数进行编码
				m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA, uiTrMode );
				// 对残差系数进行编码
				m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_LUMA );
				
				// 删除无关紧要代码***************
				
				// 反变换、反量化
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_LUMA,REG_DCT, pcResiCurrY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(),  pcCoeffCurrY, trWidth, trHeight, scalingListType, true );

				// 删除无关紧要代码***************
			}

			// 删除无关紧要代码***************
		}

		// 如果对色度部分进行编码,但是跳过了UV分量(这是什么情况?)
		if( bCodeChroma && checkTransformSkipUV  )
		{
			// 删除无关紧要代码***************
			
			if (m_pcEncCfg->getUseRDOQTS())
			{
				// 比特数估算
				m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidthC, trHeightC, TEXT_CHROMA );          
			}

			// 删除无关紧要代码***************
			
			// 变换量化(U分量)
			m_pcTrQuant->transformNxN( pcCU, pcResi->getCbAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrU, 
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrU, 
#endif        
				trWidthC, trHeightC, uiAbsSumTransformSkipU, TEXT_CHROMA_U, uiAbsPartIdx, true );
			
			// 删除无关紧要代码***************
			
			// 变换量化(V分量)
			m_pcTrQuant->transformNxN( pcCU, pcResi->getCrAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrV,
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrV, 
#endif        
				trWidthC, trHeightC, uiAbsSumTransformSkipV, TEXT_CHROMA_V, uiAbsPartIdx, true );


			// 删除无关紧要代码***************

			// 如果跳过了U分量
			if( uiAbsSumTransformSkipU )
			{
				// 删除无关紧要代码***************
				
				// 对系数进行编码
				m_pcEntropyCoder->encodeQtCbf   ( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode );
				m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U );
				
				// 删除无关紧要代码***************
				
				// 反变换反量化
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrU, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrU, trWidthC, trHeightC, scalingListType, true  );

				// 删除无关紧要代码***************
			}

			// 删除无关紧要代码***************

			// 如果跳过了V分量
			if( uiAbsSumTransformSkipV )
			{
				// 删除无关紧要代码***************
				
				// 对系数进行编码
				m_pcEntropyCoder->encodeQtCbf   ( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode );
				m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V );
				
				// 删除无关紧要代码***************

				// 反变换反量化
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrV, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrV, trWidthC, trHeightC, scalingListType, true );

				// 删除无关紧要代码***************
			}

			// 删除无关紧要代码***************
		}
		/*【==============TransformSkip模式的变换量化、反变换反量化、熵编码 结束===================】*/
		// 删除无关紧要代码***************

		if( uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) )
		{
			// 编码分割标志?
			m_pcEntropyCoder->encodeTransformSubdivFlag( 0, 5 - uiLog2TrSize );
		}

		if( bCodeChroma )
		{
			m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode );
			m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode );
		}

		m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA,     uiTrMode );

		m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight,    uiDepth, TEXT_LUMA );

		if( bCodeChroma )
		{
			m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U );
			m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V );
		}
		// 删除无关紧要代码***************
	}  

	// code sub-blocks
	// 对TU的四个子TU进行计算(使用递归的方式)
	if( bCheckSplit )
	{
		// 删除无关紧要代码***************
		for( UInt ui = 0; ui < 4; ++ui )
		{
			UInt nsAddr = uiAbsPartIdx + ui * uiQPartNumSubdiv;
			// 递归调用
			xEstimateResidualQT( pcCU, ui, uiAbsPartIdx + ui * uiQPartNumSubdiv, nsAddr, pcResi, uiDepth + 1, dSubdivCost, uiSubdivBits, uiSubdivDist, bCheckFull ? NULL : puiZeroDist );
		}

		// 删除无关紧要代码***************

		// 对残差四叉树进行编码
		xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, true,  TEXT_LUMA );
		xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_LUMA );
		xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_CHROMA_U );
		xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_CHROMA_V );

		// 删除无关紧要代码***************
	}
	// 删除无关紧要代码***************
}


估算残差的率失真代价和比特代价

    入口函数是:xEstimateResidualQT

    流程如下:

    一、首先看到CheckFull标志,如果为真,就对各种模式进行测试,估计他们的率失真代价和比特代价等,主要步骤如下:
        1、分别对Y、U、V三个分量进行变换和量化,transformNxN最后的参数是false,表示不是TransformSkip模式
        2、对Y、U、V三个分量的变换系数以及其他的信息进行熵编码,计算出率失真代价以及比特代价
        3、对Y、U、V三个分量进行反变换反量化
        4、如果是TransformSkip模式,那么在TransformSkip模式下,重新执行步骤1、2、3,但是transformNxN最后的参数是true,表示是TransformSkip模式
        5、判断比较得出最优的模式
    二、检测CheckSplit标识,如果为真,那么进行子TU的递归处理,并对当前TU进行熵编码
        1、对四个子TU递归调用xEstimateResidualQT
        2、对Y分量调用xEncodeResidualQT,进行熵编码,bSubdivAndCbf参数是true,表示计算当前TU划分成4个子TU时的代价,另外如果bSubdivAndCbf参数是true,那么会在xEncodeResidualQT里面计算U、V分量的率失真代价/比特代价
        3、对Y、U、V三个分量调用xEncodeResidualQT进行熵编码,bSubdivAndCbf参数是false,表示计算当前TU不划分成子TU时的代价

/*
** 估计残差的编码代价
** 主要是进行变换量化以及熵编码,然后选出最优的模式
*/
Void TEncSearch::xEstimateResidualQT( TComDataCU* pcCU, UInt uiQuadrant, UInt uiAbsPartIdx, UInt absTUPartIdx, TComYuv* pcResi, const UInt uiDepth, Double &rdCost, UInt &ruiBits, UInt &ruiDist, UInt *puiZeroDist )
{
	// 删除无关紧要代码***************

	// 完整检查的标志
	Bool bCheckFull;
	if ( SplitFlag && uiDepth == pcCU->getDepth(uiAbsPartIdx) && ( uiLog2TrSize >  pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) ) )
	{
		bCheckFull = false;
	}
	else
	{
		bCheckFull =  ( uiLog2TrSize <= pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() );
	}

	// 是否检查split标志
	const Bool bCheckSplit  = ( uiLog2TrSize >  pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) );
	
	// 完整检查模式
	if( bCheckFull )
	{
		// 删除无关紧要代码***************

		// 是否使用了率失真优化的量化
		if (m_pcEncCfg->getUseRDOQ())
		{
			// 估算比特数
			m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidth, trHeight, TEXT_LUMA );        
		}

		// 删除无关紧要代码***************
		
		/*【==============变换量化开始===================】*/
        // Y分量变换和量化
		m_pcTrQuant->transformNxN( pcCU, pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride (), pcCoeffCurrY, 
#if ADAPTIVE_QP_SELECTION
			pcArlCoeffCurrY, 
#endif      
			trWidth,   trHeight,    uiAbsSumY, TEXT_LUMA,     uiAbsPartIdx );

		// 删除无关紧要代码***************

		if( bCodeChroma )
		{
			if (m_pcEncCfg->getUseRDOQ())
			{
				m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidthC, trHeightC, TEXT_CHROMA );          
			}

			// 删除无关紧要代码***************
			
			// 变换和量化(U分量)
			m_pcTrQuant->transformNxN( pcCU, pcResi->getCbAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrU, 
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrU, 
#endif        
				trWidthC, trHeightC, uiAbsSumU, TEXT_CHROMA_U, uiAbsPartIdx );

			// 删除无关紧要代码***************

			// 变换和量化(V分量)
			m_pcTrQuant->transformNxN( pcCU, pcResi->getCrAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrV,
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrV, 
#endif        
				trWidthC, trHeightC, uiAbsSumV, TEXT_CHROMA_V, uiAbsPartIdx );

			// 删除无关紧要代码***************
		}

		/*【==============变换量化结束===================】*/
		
		/*【==============变换系数以及其他信息熵编码开始===================】*/
		m_pcEntropyCoder->resetBits();

        // 对CBF进行编码(Y分量)
		m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA,     uiTrMode );

        // 对残差系数进行编码(Y分量)
		m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx,  trWidth,  trHeight,    uiDepth, TEXT_LUMA );
		
		// 删除无关紧要代码***************

		// 对色度部分进行编码
		if( bCodeChroma )
		{
			// 删除无关紧要代码***************

			// 对CBF进行编码(U分量)
			m_pcEntropyCoder->encodeQtCbf   ( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode );
			// 对系数进行编码(U分量)
			m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U );
			
			// 删除无关紧要代码***************

			// 对CBF进行编码(V分量)
			m_pcEntropyCoder->encodeQtCbf   ( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode );
			// 对系数进行编码(V分量)
			m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V );
			
			// 删除无关紧要代码***************
		}

		/*【==============变换系数以及其他信息熵编码结束===================】*/
		
		
		// 删除无关紧要代码***************

		/*【==============反量化、反变换开始===================】*/
		
		// 如果Y分量绝对系数和大于0
		// 开始执行反量化、反变换
		if( uiAbsSumY )
		{
			// 删除无关紧要代码***************
            // 反变换、反量化(Y分量)
			m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_LUMA,REG_DCT, pcResiCurrY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(),  pcCoeffCurrY, trWidth, trHeight, scalingListType );//this is for inter mode only

			const UInt uiNonzeroDistY = m_pcRdCost->getDistPart(g_bitDepthY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getLumaAddr( absTUPartIdx ), m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(),
				pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride(), trWidth,trHeight );
				
			if (pcCU->isLosslessCoded(0)) 
			{
				uiDistY = uiNonzeroDistY;
			}
			else
			{
				// 删除无关紧要代码***************
				m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_LUMA,     uiTrMode );
				// 删除无关紧要代码***************
			}
		}
		// 否则,如果是跳过Y分量
		else if( checkTransformSkipY )
		{
			// 删除无关紧要代码***************
			
			// 编码Y分量的CBF的0系数
			m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_LUMA, uiTrMode );
			
			// 删除无关紧要代码***************
		}

		// 删除无关紧要代码***************
		
		// 如果对色度部分进行了变换量化,那么需要对色度部分进行反变换反量化
		if( bCodeChroma )
		{
			// 删除无关紧要代码***************

			// 如果对U分量进行了变换量化
			if( uiAbsSumU )
			{
				// 删除无关紧要代码***************

				// 反变换、反量化(U分量)
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrU, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrU, trWidthC, trHeightC, scalingListType  );

				const UInt uiNonzeroDistU = m_pcRdCost->getDistPart(g_bitDepthC, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCbAddr( absTUPartIdxC), m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(),
					pcResi->getCbAddr( absTUPartIdxC), pcResi->getCStride(), trWidthC, trHeightC
					, TEXT_CHROMA_U
					);

				if(pcCU->isLosslessCoded(0))  
				{
					uiDistU = uiNonzeroDistU;
				}
				else
				{
					// 删除无关紧要代码***************
					
					m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_U,     uiTrMode );
					
					// 删除无关紧要代码***************
				}
			}
			// 否则(即变换量化没有效果或者不够优?)
			else if( checkTransformSkipUV )
			{
				// 删除无关紧要代码***************
				
				// 编码CBF的0系数
				m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_U, uiTrMode );
				
				// 删除无关紧要代码***************
			}

			// 删除无关紧要代码***************

			// 对V分量进行处理(过程同U分量)
			if( uiAbsSumV )
			{
				// 删除无关紧要代码***************

				// 反变换反量化(V分量)
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrV, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrV, trWidthC, trHeightC, scalingListType );

				const UInt uiNonzeroDistV = m_pcRdCost->getDistPart(g_bitDepthC, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCrAddr( absTUPartIdxC ), m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(),
					pcResi->getCrAddr( absTUPartIdxC ), pcResi->getCStride(), trWidthC, trHeightC
					, TEXT_CHROMA_V
					);
				if (pcCU->isLosslessCoded(0)) 
				{
					uiDistV = uiNonzeroDistV;
				}
				else
				{
					// 删除无关紧要代码***************
					
					// 对CBF的0系数进行编码
					m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_V,     uiTrMode );
					
					// 删除无关紧要代码***************
				}
			}
			// 如果跳过了UV分量
			else if( checkTransformSkipUV )
			{
				// 删除无关紧要代码***************
				
				// 对CBF的0系数进行编码
				m_pcEntropyCoder->encodeQtCbfZero( pcCU, TEXT_CHROMA_V, uiTrMode );
				
				// 删除无关紧要代码***************
			}
			
			if( !uiAbsSumV )
			{
				// 删除无关紧要代码***************
			}
		}
		
		/*【==============反量化、反变换结束===================】*/
		// 删除无关紧要代码***************

		/*【==============TransformSkip模式的变换、量化、熵编码 开始===================】*/
		if( checkTransformSkipY )
		{
			// 删除无关紧要代码***************
			
			if (m_pcEncCfg->getUseRDOQTS())
			{
				// 估算比特数
				m_pcEntropyCoder->estimateBit( m_pcTrQuant->m_pcEstBitsSbac, trWidth, trHeight, TEXT_LUMA );        
			}

			// 删除无关紧要代码***************
			
			// 变换和量化
			m_pcTrQuant->transformNxN( pcCU, pcResi->getLumaAddr( absTUPartIdx ), pcResi->getStride (), pcCoeffCurrY, 
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrY, 
#endif      
				trWidth,   trHeight,    uiAbsSumTransformSkipY, TEXT_LUMA, uiAbsPartIdx, true );
			pcCU->setCbfSubParts( uiAbsSumTransformSkipY ? uiSetCbf : 0, TEXT_LUMA, uiAbsPartIdx, uiDepth );

			if( uiAbsSumTransformSkipY != 0 )
			{
				m_pcEntropyCoder->resetBits();
				// 对CBF的0系数进行编码
				m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA, uiTrMode );
				// 对残差系数进行编码
				m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_LUMA );
				
				// 删除无关紧要代码***************
				
				// 反变换、反量化
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_LUMA,REG_DCT, pcResiCurrY, m_pcQTTempTComYuv[uiQTTempAccessLayer].getStride(),  pcCoeffCurrY, trWidth, trHeight, scalingListType, true );

				// 删除无关紧要代码***************
			}

			// 删除无关紧要代码***************
		}

		// 如果对色度部分进行编码,但是跳过了UV分量(这是什么情况?)
		if( bCodeChroma && checkTransformSkipUV  )
		{
			// 删除无关紧要代码***************
			
			if (m_pcEncCfg->getUseRDOQTS())
			{
				// 比特数估算
				m_pcEntropyCoder->estimateBit(m_pcTrQuant->m_pcEstBitsSbac, trWidthC, trHeightC, TEXT_CHROMA );          
			}

			// 删除无关紧要代码***************
			
			// 变换量化(U分量)
			m_pcTrQuant->transformNxN( pcCU, pcResi->getCbAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrU, 
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrU, 
#endif        
				trWidthC, trHeightC, uiAbsSumTransformSkipU, TEXT_CHROMA_U, uiAbsPartIdx, true );
			
			// 删除无关紧要代码***************
			
			// 变换量化(V分量)
			m_pcTrQuant->transformNxN( pcCU, pcResi->getCrAddr(absTUPartIdxC), pcResi->getCStride(), pcCoeffCurrV,
#if ADAPTIVE_QP_SELECTION
				pcArlCoeffCurrV, 
#endif        
				trWidthC, trHeightC, uiAbsSumTransformSkipV, TEXT_CHROMA_V, uiAbsPartIdx, true );


			// 删除无关紧要代码***************

			// 如果跳过了U分量
			if( uiAbsSumTransformSkipU )
			{
				// 删除无关紧要代码***************
				
				// 对系数进行编码
				m_pcEntropyCoder->encodeQtCbf   ( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode );
				m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U );
				
				// 删除无关紧要代码***************
				
				// 反变换反量化
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrU, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrU, trWidthC, trHeightC, scalingListType, true  );

				// 删除无关紧要代码***************
			}

			// 删除无关紧要代码***************

			// 如果跳过了V分量
			if( uiAbsSumTransformSkipV )
			{
				// 删除无关紧要代码***************
				
				// 对系数进行编码
				m_pcEntropyCoder->encodeQtCbf   ( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode );
				m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V );
				
				// 删除无关紧要代码***************

				// 反变换反量化
				m_pcTrQuant->invtransformNxN( pcCU->getCUTransquantBypass(uiAbsPartIdx), TEXT_CHROMA,REG_DCT, pcResiCurrV, m_pcQTTempTComYuv[uiQTTempAccessLayer].getCStride(), pcCoeffCurrV, trWidthC, trHeightC, scalingListType, true );

				// 删除无关紧要代码***************
			}

			// 删除无关紧要代码***************
		}
		/*【==============TransformSkip模式的变换、量化、熵编码 结束===================】*/
		// 删除无关紧要代码***************

		if( uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) )
		{
			// 编码分割标志?
			m_pcEntropyCoder->encodeTransformSubdivFlag( 0, 5 - uiLog2TrSize );
		}

		// 前面的是为了选出最优模式,下面对系数进行编码

		/*【==============前面的好几次变换量化熵编码,只是为了选取最优的编码模式,现在根据最优的编码模式进行编码 开始===================】*/
		// 对色度部分CBF进行编码
		if( bCodeChroma )
		{
			m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode );
			m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode );
		}

		// 对亮度的CBF进行编码
		m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA,     uiTrMode );

		// 编码亮度部分的系数
		m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight,    uiDepth, TEXT_LUMA );

		// 对色度部分的系数进行编码
		if( bCodeChroma )
		{
			m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_U );
			m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidthC, trHeightC, uiDepth, TEXT_CHROMA_V );
		}
		/*【==============前面的好几次变换量化熵编码,只是为了选取最优的编码模式,现在根据最优的编码模式进行编码 结束===================】*/
		// 删除无关紧要代码***************
	}  

	// code sub-blocks
	// 对TU的四个子TU进行计算(使用递归的方式)
	if( bCheckSplit )
	{
		// 删除无关紧要代码***************
		for( UInt ui = 0; ui < 4; ++ui )
		{
			UInt nsAddr = uiAbsPartIdx + ui * uiQPartNumSubdiv;
			// 递归调用
			xEstimateResidualQT( pcCU, ui, uiAbsPartIdx + ui * uiQPartNumSubdiv, nsAddr, pcResi, uiDepth + 1, dSubdivCost, uiSubdivBits, uiSubdivDist, bCheckFull ? NULL : puiZeroDist );
		}

		// 删除无关紧要代码***************

		// 对残差四叉树进行编码
		xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, true,  TEXT_LUMA );
		xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_LUMA );
		xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_CHROMA_U );
		xEncodeResidualQT( pcCU, uiAbsPartIdx, uiDepth, false, TEXT_CHROMA_V );

		// 删除无关紧要代码***************
	}
	// 删除无关紧要代码***************
}


对Cbf、残差系数进行熵编码


/*
** xEncodeResidualQT的主要功能是对Cbf、残差系数进行熵编码
*/
Void TEncSearch::xEncodeResidualQT( TComDataCU* pcCU, UInt uiAbsPartIdx, const UInt uiDepth, Bool bSubdivAndCbf, TextType eType )
{
	assert( pcCU->getDepth( 0 ) == pcCU->getDepth( uiAbsPartIdx ) );

	// 模式
	const UInt uiCurrTrMode = uiDepth - pcCU->getDepth( 0 );
	const UInt uiTrMode = pcCU->getTransformIdx( uiAbsPartIdx );

	// 对当前TU是否会继续向下分割
	const Bool bSubdiv = uiCurrTrMode != uiTrMode;

	const UInt uiLog2TrSize = g_aucConvertToBit[pcCU->getSlice()->getSPS()->getMaxCUWidth() >> uiDepth]+2;

	if( bSubdivAndCbf && uiLog2TrSize <= pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() && uiLog2TrSize > pcCU->getQuadtreeTULog2MinSizeInCU(uiAbsPartIdx) )
	{
		// 编码向下分割的标志
		m_pcEntropyCoder->encodeTransformSubdivFlag( bSubdiv, 5 - uiLog2TrSize );
	}

	assert( pcCU->getPredictionMode(uiAbsPartIdx) != MODE_INTRA );

	// 如果bSubdivAndCbf是true,那么对U、V分量进行处理,Y分量在前面已经处理了
	if( bSubdivAndCbf )
	{
		const Bool bFirstCbfOfCU = uiCurrTrMode == 0;
		// 编码UV分量的CBF(Y分量已经在调用这个函数之前处理完了)
		if( bFirstCbfOfCU || uiLog2TrSize > 2 )
		{
			if( bFirstCbfOfCU || pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_U, uiCurrTrMode - 1 ) )
			{
				m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_U, uiCurrTrMode );
			}
			if( bFirstCbfOfCU || pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_V, uiCurrTrMode - 1 ) )
			{
				m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_CHROMA_V, uiCurrTrMode );
			}
		}
		else if( uiLog2TrSize == 2 )
		{
			assert( pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_U, uiCurrTrMode ) == pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_U, uiCurrTrMode - 1 ) );
			assert( pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_V, uiCurrTrMode ) == pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_V, uiCurrTrMode - 1 ) );
		}
	}

	// 如果不在继续向下分割,那么直接编码YUV三个分量的系数,否则需要对子TU递归调用
	if( !bSubdiv )
	{
		const UInt uiNumCoeffPerAbsPartIdxIncrement = pcCU->getSlice()->getSPS()->getMaxCUWidth() * pcCU->getSlice()->getSPS()->getMaxCUHeight() >> ( pcCU->getSlice()->getSPS()->getMaxCUDepth() << 1 );
		//assert( 16 == uiNumCoeffPerAbsPartIdxIncrement ); // check
		const UInt uiQTTempAccessLayer = pcCU->getSlice()->getSPS()->getQuadtreeTULog2MaxSize() - uiLog2TrSize;
		TCoeff *pcCoeffCurrY = m_ppcQTTempCoeffY [uiQTTempAccessLayer] +  uiNumCoeffPerAbsPartIdxIncrement * uiAbsPartIdx;
		TCoeff *pcCoeffCurrU = m_ppcQTTempCoeffCb[uiQTTempAccessLayer] + (uiNumCoeffPerAbsPartIdxIncrement * uiAbsPartIdx>>2);
		TCoeff *pcCoeffCurrV = m_ppcQTTempCoeffCr[uiQTTempAccessLayer] + (uiNumCoeffPerAbsPartIdxIncrement * uiAbsPartIdx>>2);

		Bool  bCodeChroma   = true;
		UInt  uiTrModeC     = uiTrMode;
		UInt  uiLog2TrSizeC = uiLog2TrSize-1;
		if( uiLog2TrSize == 2 )
		{
			uiLog2TrSizeC++;
			uiTrModeC    --;
			UInt  uiQPDiv = pcCU->getPic()->getNumPartInCU() >> ( ( pcCU->getDepth( 0 ) + uiTrModeC ) << 1 );
			bCodeChroma   = ( ( uiAbsPartIdx % uiQPDiv ) == 0 );
		}

		if( bSubdivAndCbf )
		{
			m_pcEntropyCoder->encodeQtCbf( pcCU, uiAbsPartIdx, TEXT_LUMA,     uiTrMode );
		}
		else
		{
			if( eType == TEXT_LUMA     && pcCU->getCbf( uiAbsPartIdx, TEXT_LUMA,     uiTrMode ) )
			{
				Int trWidth  = 1 << uiLog2TrSize;
				Int trHeight = 1 << uiLog2TrSize;
				// 对Y分量的系数进行编码
				m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrY, uiAbsPartIdx, trWidth, trHeight,    uiDepth, TEXT_LUMA );
			}
			if( bCodeChroma )
			{
				Int trWidth  = 1 << uiLog2TrSizeC;
				Int trHeight = 1 << uiLog2TrSizeC;
				// 对U分量的系数进行编码
				if( eType == TEXT_CHROMA_U && pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_U, uiTrMode ) )
				{
					m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrU, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_CHROMA_U );
				}
				// 对V分量的系数进行编码
				if( eType == TEXT_CHROMA_V && pcCU->getCbf( uiAbsPartIdx, TEXT_CHROMA_V, uiTrMode ) )
				{
					m_pcEntropyCoder->encodeCoeffNxN( pcCU, pcCoeffCurrV, uiAbsPartIdx, trWidth, trHeight, uiDepth, TEXT_CHROMA_V );
				}
			}
		}
	}
	else
	{
		if( bSubdivAndCbf || pcCU->getCbf( uiAbsPartIdx, eType, uiCurrTrMode ) )
		{
			const UInt uiQPartNumSubdiv = pcCU->getPic()->getNumPartInCU() >> ((uiDepth + 1 ) << 1);
			// 对子TU递归调用该函数
			for( UInt ui = 0; ui < 4; ++ui )
			{
				xEncodeResidualQT( pcCU, uiAbsPartIdx + ui * uiQPartNumSubdiv, uiDepth + 1, bSubdivAndCbf, eType );
			}
		}
	}
}

    至此,帧间预测的AMVP模式全部讲解完毕!


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值