HM编码器代码阅读(24)——变换系数的编码

熵编码的入口

入口函数TEncSlice::encodeSlice。
在处理完VPS,SPS,PPS和片头信息之后就开始对片的数据(不仅是数据还有一些其他的参数和控制信息)进行编码了。
encodeSlice函数就是熵编码的重头戏了。处理过程如下:
(1)先初始化熵编码器,然后设置CABAC熵编码器为当前的熵编码器。
(2)然后就是加载各种熵编码器,各种初始化,各种加载上下文信息。
(3)遍历片中的每一个LCU,进行下面的处理:
     1)设置比特流(即熵编码之后要把数据写到哪里去)
     2)重置熵编码器,调用updateContextTables更新上下文信息
     3)如果使用了SAO,那么调用encodeSAOBlkParam对SAO的参数进行编码
     4)调用encodeCU对LCU进行熵编码(我们注意到前面compressSlice中也会调用该函数,在compressSlice中的调用只是为了选择最优的模式)。

Void TEncSlice::encodeSlice   ( TComPic*& rpcPic, TComOutputBitstream* pcSubstreams )
{
	UInt       uiCUAddr;
	UInt       uiStartCUAddr;
	UInt       uiBoundingCUAddr;

	// 获取当前处理的条带
	TComSlice* pcSlice = rpcPic->getSlice(getSliceIdx());

	uiStartCUAddr=pcSlice->getSliceSegmentCurStartCUAddr();
	uiBoundingCUAddr=pcSlice->getSliceSegmentCurEndCUAddr();
	// choose entropy coder

	// 设置熵编码器CABAC
	{
		m_pcSbacCoder->init( (TEncBinIf*)m_pcBinCABAC );
		m_pcEntropyCoder->setEntropyCoder ( m_pcSbacCoder, pcSlice );
	}

	m_pcCuEncoder->setBitCounter( NULL );
	m_pcBitCounter = NULL;
	// Appropriate substream bitstream is switched later.
	// for every CU
#if ENC_DEC_TRACE
	g_bJustDoIt = g_bEncDecTraceEnable;
#endif
	DTRACE_CABAC_VL( g_nSymbolCounter++ );
	DTRACE_CABAC_T( "\tPOC: " );
	DTRACE_CABAC_V( rpcPic->getPOC() );
	DTRACE_CABAC_T( "\n" );
#if ENC_DEC_TRACE
	g_bJustDoIt = g_bEncDecTraceDisable;
#endif

	TEncTop* pcEncTop = (TEncTop*) m_pcCfg;
	TEncSbac* pcSbacCoders = pcEncTop->getSbacCoders(); //coder for each substream
	Int iNumSubstreams = pcSlice->getPPS()->getNumSubstreams();
	UInt uiBitsOriginallyInSubstreams = 0;
	{
		UInt uiTilesAcross = rpcPic->getPicSym()->getNumColumnsMinus1()+1;
		for (UInt ui = 0; ui < uiTilesAcross; ui++)
		{
			m_pcBufferSbacCoders[ui].load(m_pcSbacCoder); //init. state
		}

		for (Int iSubstrmIdx=0; iSubstrmIdx < iNumSubstreams; iSubstrmIdx++)
		{
			uiBitsOriginallyInSubstreams += pcSubstreams[iSubstrmIdx].getNumberOfWrittenBits();
		}

		for (UInt ui = 0; ui < uiTilesAcross; ui++)
		{
			m_pcBufferLowLatSbacCoders[ui].load(m_pcSbacCoder);  //init. state
		}
	}

	UInt uiWidthInLCUs  = rpcPic->getPicSym()->getFrameWidthInCU();
	UInt uiCol=0, uiLin=0, uiSubStrm=0;
	UInt uiTileCol      = 0;
	UInt uiTileStartLCU = 0;
	UInt uiTileLCUX     = 0;

	// 是否使用了条带片段(按照默认的配置,没有使用)
	Bool depSliceSegmentsEnabled = pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag();
	uiCUAddr = rpcPic->getPicSym()->getCUOrderMap( uiStartCUAddr /rpcPic->getNumPartInCU());  /* for tiles, uiStartCUAddr is NOT the real raster scan address, it is actually
																							  an encoding order index, so we need to convert the index (uiStartCUAddr)
																							  into the real raster scan address (uiCUAddr) via the CUOrderMap */
	uiTileStartLCU = rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr();
	
	// 按照默认的设置,没有使用
	if( depSliceSegmentsEnabled )
	{
		if( pcSlice->isNextSlice()||
			uiCUAddr == rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr())
		{
			if(m_pcCfg->getWaveFrontsynchro())
			{
				CTXMem[1]->loadContexts(m_pcSbacCoder);
			}
			CTXMem[0]->loadContexts(m_pcSbacCoder);
		}
		else
		{
			if(m_pcCfg->getWaveFrontsynchro())
			{
				uiTileCol = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr) % (rpcPic->getPicSym()->getNumColumnsMinus1()+1);
				m_pcBufferSbacCoders[uiTileCol].loadContexts( CTXMem[1] );
				Int iNumSubstreamsPerTile = iNumSubstreams/rpcPic->getPicSym()->getNumTiles();
				uiLin     = uiCUAddr / uiWidthInLCUs;
				uiSubStrm = rpcPic->getPicSym()->getTileIdxMap(rpcPic->getPicSym()->getCUOrderMap( uiCUAddr))*iNumSubstreamsPerTile
					+ uiLin%iNumSubstreamsPerTile;
				if ( (uiCUAddr%uiWidthInLCUs+1) >= uiWidthInLCUs  )
				{
					uiCol     = uiCUAddr % uiWidthInLCUs;
					uiTileLCUX = uiTileStartLCU % uiWidthInLCUs;
					if(uiCol==uiTileLCUX)
					{
						CTXMem[0]->loadContexts(m_pcSbacCoder);
					}
				}
			}
			pcSbacCoders[uiSubStrm].loadContexts( CTXMem[0] );
		}
	}

	UInt uiEncCUOrder;

	// 遍历条带中的每一个CU
	for( uiEncCUOrder = uiStartCUAddr /rpcPic->getNumPartInCU();
		uiEncCUOrder < (uiBoundingCUAddr+rpcPic->getNumPartInCU()-1)/rpcPic->getNumPartInCU();
		uiCUAddr = rpcPic->getPicSym()->getCUOrderMap(++uiEncCUOrder) )
	{
		uiTileCol = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr) % (rpcPic->getPicSym()->getNumColumnsMinus1()+1); // what column of tiles are we in?
		uiTileStartLCU = rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr();
		uiTileLCUX = uiTileStartLCU % uiWidthInLCUs;
		//UInt uiSliceStartLCU = pcSlice->getSliceCurStartCUAddr();
		uiCol     = uiCUAddr % uiWidthInLCUs;
		uiLin     = uiCUAddr / uiWidthInLCUs;
		if (pcSlice->getPPS()->getNumSubstreams() > 1)
		{
			// independent tiles => substreams are "per tile".  iNumSubstreams has already been multiplied.
			Int iNumSubstreamsPerTile = iNumSubstreams/rpcPic->getPicSym()->getNumTiles();
			uiSubStrm = rpcPic->getPicSym()->getTileIdxMap(uiCUAddr)*iNumSubstreamsPerTile
				+ uiLin%iNumSubstreamsPerTile;
		}
		else
		{
			// dependent tiles => substreams are "per frame".
			uiSubStrm = uiLin % iNumSubstreams;
		}

		m_pcEntropyCoder->setBitstream( &pcSubstreams[uiSubStrm] );
		// Synchronize cabac probabilities with upper-right LCU if it's available and we're at the start of a line.
		if (((pcSlice->getPPS()->getNumSubstreams() > 1) || depSliceSegmentsEnabled) && (uiCol == uiTileLCUX) && m_pcCfg->getWaveFrontsynchro())
		{
			// We'll sync if the TR is available.
			TComDataCU *pcCUUp = rpcPic->getCU( uiCUAddr )->getCUAbove();
			UInt uiWidthInCU = rpcPic->getFrameWidthInCU();
			UInt uiMaxParts = 1<<(pcSlice->getSPS()->getMaxCUDepth()<<1);
			TComDataCU *pcCUTR = NULL;
			if ( pcCUUp && ((uiCUAddr%uiWidthInCU+1) < uiWidthInCU)  )
			{
				pcCUTR = rpcPic->getCU( uiCUAddr - uiWidthInCU + 1 );
			}
			if ( (true/*bEnforceSliceRestriction*/ &&
				((pcCUTR==NULL) || (pcCUTR->getSlice()==NULL) ||
				(pcCUTR->getSCUAddr()+uiMaxParts-1 < pcSlice->getSliceCurStartCUAddr()) ||
				((rpcPic->getPicSym()->getTileIdxMap( pcCUTR->getAddr() ) != rpcPic->getPicSym()->getTileIdxMap(uiCUAddr)))
				))
				)
			{
				// TR not available.
			}
			else
			{
				// TR is available, we use it.
				pcSbacCoders[uiSubStrm].loadContexts( &m_pcBufferSbacCoders[uiTileCol] );
			}
		}
		m_pcSbacCoder->load(&pcSbacCoders[uiSubStrm]);  //this load is used to simplify the code (avoid to change all the call to m_pcSbacCoder)

		// reset the entropy coder
		if( uiCUAddr == rpcPic->getPicSym()->getTComTile(rpcPic->getPicSym()->getTileIdxMap(uiCUAddr))->getFirstCUAddr() &&                                   // must be first CU of tile
			uiCUAddr!=0 &&                                                                                                                                    // cannot be first CU of picture
			uiCUAddr!=rpcPic->getPicSym()->getPicSCUAddr(rpcPic->getSlice(rpcPic->getCurrSliceIdx())->getSliceSegmentCurStartCUAddr())/rpcPic->getNumPartInCU() &&
			uiCUAddr!=rpcPic->getPicSym()->getPicSCUAddr(rpcPic->getSlice(rpcPic->getCurrSliceIdx())->getSliceCurStartCUAddr())/rpcPic->getNumPartInCU())     // cannot be first CU of slice
		{
			{
				// We're crossing into another tile, tiles are independent.
				// When tiles are independent, we have "substreams per tile".  Each substream has already been terminated, and we no longer
				// have to perform it here.
				if (pcSlice->getPPS()->getNumSubstreams() > 1)
				{
					; // do nothing.
				}
				else
				{
					SliceType sliceType  = pcSlice->getSliceType();
					if (!pcSlice->isIntra() && pcSlice->getPPS()->getCabacInitPresentFlag() && pcSlice->getPPS()->getEncCABACTableIdx()!=I_SLICE)
					{
						sliceType = (SliceType) pcSlice->getPPS()->getEncCABACTableIdx();
					}
					// 上下文表的更新
					m_pcEntropyCoder->updateContextTables( sliceType, pcSlice->getSliceQp() );
					// Byte-alignment in slice_data() when new tile
					pcSubstreams[uiSubStrm].writeByteAlignment();
				}
			}
			{
				UInt numStartCodeEmulations = pcSubstreams[uiSubStrm].countStartCodeEmulations();
				UInt uiAccumulatedSubstreamLength = 0;
				for (Int iSubstrmIdx=0; iSubstrmIdx < iNumSubstreams; iSubstrmIdx++)
				{
					uiAccumulatedSubstreamLength += pcSubstreams[iSubstrmIdx].getNumberOfWrittenBits();
				}
				// add bits coded in previous dependent slices + bits coded so far
				// add number of emulation prevention byte count in the tile
				pcSlice->addTileLocation( ((pcSlice->getTileOffstForMultES() + uiAccumulatedSubstreamLength - uiBitsOriginallyInSubstreams) >> 3) + numStartCodeEmulations );
			}
		}

		TComDataCU*& pcCU = rpcPic->getCU( uiCUAddr );    
		if ( pcSlice->getSPS()->getUseSAO() )
		{
			if (pcSlice->getSaoEnabledFlag()||pcSlice->getSaoEnabledFlagChroma())
			{
				SAOBlkParam& saoblkParam = (rpcPic->getPicSym()->getSAOBlkParam())[uiCUAddr];
				Bool sliceEnabled[NUM_SAO_COMPONENTS];
				sliceEnabled[SAO_Y] = pcSlice->getSaoEnabledFlag();
				sliceEnabled[SAO_Cb]= sliceEnabled[SAO_Cr]= pcSlice->getSaoEnabledFlagChroma();

				Bool leftMergeAvail = false;
				Bool aboveMergeAvail= false;
				//merge left condition
				Int rx = (uiCUAddr % uiWidthInLCUs);
				if(rx > 0)
				{
					leftMergeAvail = rpcPic->getSAOMergeAvailability(uiCUAddr, uiCUAddr-1);
				}

				//merge up condition
				Int ry = (uiCUAddr / uiWidthInLCUs);
				if(ry > 0)
				{
					aboveMergeAvail = rpcPic->getSAOMergeAvailability(uiCUAddr, uiCUAddr-uiWidthInLCUs);
				}

				m_pcEntropyCoder->encodeSAOBlkParam(saoblkParam,sliceEnabled, leftMergeAvail, aboveMergeAvail);
			}
		}

#if ENC_DEC_TRACE
		g_bJustDoIt = g_bEncDecTraceEnable;
#endif
		if ( (m_pcCfg->getSliceMode()!=0 || m_pcCfg->getSliceSegmentMode()!=0) &&
			uiCUAddr == rpcPic->getPicSym()->getCUOrderMap((uiBoundingCUAddr+rpcPic->getNumPartInCU()-1)/rpcPic->getNumPartInCU()-1) )
		{
			// 和compressSlice一样调用了encodeCU函数,但是实现的功能已经不一样了
			// 因为compressSlice主要是进行帧内(帧间)最优模式的选择,还有变换、量化等功能
			// 而在这里,由于前面的几个步骤都已经进行完毕,所以,这里是单纯的进行熵编码
			m_pcCuEncoder->encodeCU( pcCU );
		}
		else
		{
			m_pcCuEncoder->encodeCU( pcCU );
		}
#if ENC_DEC_TRACE
		g_bJustDoIt = g_bEncDecTraceDisable;
#endif    
		pcSbacCoders[uiSubStrm].load(m_pcSbacCoder);   //load back status of the entropy coder after encoding the LCU into relevant bitstream entropy coder
		//Store probabilties of second LCU in line into buffer
		if ( (depSliceSegmentsEnabled || (pcSlice->getPPS()->getNumSubstreams() > 1)) && (uiCol == uiTileLCUX+1) && m_pcCfg->getWaveFrontsynchro())
		{
			m_pcBufferSbacCoders[uiTileCol].loadContexts( &pcSbacCoders[uiSubStrm] );
		}
	}
	if( depSliceSegmentsEnabled )
	{
		if (m_pcCfg->getWaveFrontsynchro())
		{
			CTXMem[1]->loadContexts( &m_pcBufferSbacCoders[uiTileCol] );//ctx 2.LCU
		}
		CTXMem[0]->loadContexts( m_pcSbacCoder );//ctx end of dep.slice
	}
#if ADAPTIVE_QP_SELECTION
	if( m_pcCfg->getUseAdaptQpSelect() )
	{
		m_pcTrQuant->storeSliceQpNext(pcSlice);
	}
#endif
	if (pcSlice->getPPS()->getCabacInitPresentFlag())
	{
		if  (pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag())
		{
			pcSlice->getPPS()->setEncCABACTableIdx( pcSlice->getSliceType() );
		}
		else
		{
			// 决定CABAC的初始化索引
			m_pcEntropyCoder->determineCabacInitIdx();
		}
	}
}

TEncCu::encodeCU(或TEncCu::xEncodeCU)函数的执行步骤(用的仍然是CABAC熵编码器):
(1)调用encodeSplitFlag对分割标志进行编码(最终调用TEncSbac::codeSplitFlag)
(2)如果当前CU会继续向下进行分割,那么递归调用xEncodeCU,直到到达最底层
(3)调用encodeCUTransquantBypassFlag,对变换量化跳过标志进行编码(最终调用的是TEncSbac::codeCUTransquantBypassFlag)
(4)如果不是帧内预测,那么编码skip标志(最后调用的是TEncSbac::codeSkipFlag)
(5)如果当前的CU是帧间skip模式的,那么调用encodeMergeIndex(最后调用TEncSbac::codeMergeIndex),对merge模式选出的索引进行编码,然后结束编码,返回(因为它的MV等信息是预测出来的,而不是计算出来的,因此到此结束处理)。
(6)调用encodePredMode,对预测的模式进行编码(最后调用TEncSbac::codePredMode)
(7)调用encodePartSize,对分割的尺寸进行编码(最后调用TEncSbac::codePartSize)
(8)如果是帧内预测,并且划分的方式是SIZE_2Nx2N,那么调用encodeIPCMInfo对IPCM的信息进行编码(最后调用TEncSbac::codeIPCMInfo),如果确实使用了IPCM,那么结束编码,返回!
(9)调用encodePredInfo,对预测的信息进行编码
(10)调用encodeCoeff,对系数进行编码(最后调用TEncSbac::codeCoeffNxN)

(11)调用finishCU,结束熵编码

Void TEncCu::xEncodeCU( TComDataCU* pcCU, UInt uiAbsPartIdx, UInt uiDepth )
{
	TComPic* pcPic = pcCU->getPic();

	Bool bBoundary = false;
	UInt uiLPelX   = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ];
	UInt uiRPelX   = uiLPelX + (g_uiMaxCUWidth>>uiDepth)  - 1;
	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 slice start is within this cu...
	Bool bSliceStart = pcSlice->getSliceSegmentCurStartCUAddr() > pcPic->getPicSym()->getInverseCUOrderMap(pcCU->getAddr())*pcCU->getPic()->getNumPartInCU()+uiAbsPartIdx && 
		pcSlice->getSliceSegmentCurStartCUAddr() < pcPic->getPicSym()->getInverseCUOrderMap(pcCU->getAddr())*pcCU->getPic()->getNumPartInCU()+uiAbsPartIdx+( pcPic->getNumPartInCU() >> (uiDepth<<1) );
	// We need to split, so don't try these modes.
	if(!bSliceStart&&( uiRPelX < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( uiBPelY < pcSlice->getSPS()->getPicHeightInLumaSamples() ) )
	{
		// 对分割标志进行编码
		m_pcEntropyCoder->encodeSplitFlag( pcCU, uiAbsPartIdx, uiDepth );
	}
	else
	{
		bBoundary = true;
	}

	if( ( ( uiDepth < pcCU->getDepth( uiAbsPartIdx ) ) && ( uiDepth < (g_uiMaxCUDepth-g_uiAddCUDepth) ) ) || bBoundary )
	{
		UInt uiQNumParts = ( pcPic->getNumPartInCU() >> (uiDepth<<1) )>>2;
		if( (g_uiMaxCUWidth>>uiDepth) == pcCU->getSlice()->getPPS()->getMinCuDQPSize() && pcCU->getSlice()->getPPS()->getUseDQP())
		{
			setdQPFlag(true);
		}
		for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++, uiAbsPartIdx+=uiQNumParts )
		{
			uiLPelX   = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ];
			uiTPelY   = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsPartIdx] ];
			Bool bInSlice = pcCU->getSCUAddr()+uiAbsPartIdx+uiQNumParts>pcSlice->getSliceSegmentCurStartCUAddr()&&pcCU->getSCUAddr()+uiAbsPartIdx<pcSlice->getSliceSegmentCurEndCUAddr();
			if(bInSlice&&( uiLPelX < pcSlice->getSPS()->getPicWidthInLumaSamples() ) && ( uiTPelY < pcSlice->getSPS()->getPicHeightInLumaSamples() ) )
			{
				// 递归调用,直到每一个slice中的每一个cu都处理完毕
				xEncodeCU( pcCU, uiAbsPartIdx, uiDepth+1 );
			}
		}
		return;
	}

	if( (g_uiMaxCUWidth>>uiDepth) >= pcCU->getSlice()->getPPS()->getMinCuDQPSize() && pcCU->getSlice()->getPPS()->getUseDQP())
	{
		setdQPFlag(true);
	}
	if (pcCU->getSlice()->getPPS()->getTransquantBypassEnableFlag())
	{
		// 对跳过变换过程的编制进行编码
		m_pcEntropyCoder->encodeCUTransquantBypassFlag( pcCU, uiAbsPartIdx );
	}
	if( !pcCU->getSlice()->isIntra() )
	{
		// 编码跳过标志
		m_pcEntropyCoder->encodeSkipFlag( pcCU, uiAbsPartIdx );
	}

	if( pcCU->isSkipped( uiAbsPartIdx ) )
	{
		// 编码合并索引
		m_pcEntropyCoder->encodeMergeIndex( pcCU, uiAbsPartIdx );
		// CU结束
		finishCU(pcCU,uiAbsPartIdx,uiDepth);
		return;
	}

	// 编码预测模式
	m_pcEntropyCoder->encodePredMode( pcCU, uiAbsPartIdx );

	// 对编码块的尺寸进行编码
	m_pcEntropyCoder->encodePartSize( pcCU, uiAbsPartIdx, uiDepth );

	if (pcCU->isIntra( uiAbsPartIdx ) && pcCU->getPartitionSize( uiAbsPartIdx ) == SIZE_2Nx2N )
	{
		// 编码IPCM的信息
		m_pcEntropyCoder->encodeIPCMInfo( pcCU, uiAbsPartIdx );

		if(pcCU->getIPCMFlag(uiAbsPartIdx))
		{
			// Encode slice finish
			finishCU(pcCU,uiAbsPartIdx,uiDepth);
			return;
		}
	}

	// 编码预测信息,很重要!!!!!!!!!!!!!!
	// prediction Info ( Intra : direction mode, Inter : Mv, reference idx )
	m_pcEntropyCoder->encodePredInfo( pcCU, uiAbsPartIdx );
	// 很重要!!!!!!!!!!!!!!!!!!!!!

	// Encode Coefficients
	Bool bCodeDQP = getdQPFlag();

	// 对系数进行编码,很重要!!!!!!!!!!!!!!!!!!!!!!!!!!
	m_pcEntropyCoder->encodeCoeff( pcCU, uiAbsPartIdx, uiDepth, pcCU->getWidth (uiAbsPartIdx), pcCU->getHeight(uiAbsPartIdx), bCodeDQP );
	// 很重要!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

	setdQPFlag( bCodeDQP );

	// --- write terminating bit ---
	finishCU(pcCU,uiAbsPartIdx,uiDepth);
}

变换系数的熵编码

上面简要的分析了一下HEVC编码器的熵编码的主要流程,下面我们看看对于变换系数,HEVC是如何对它进行编码的。
变换系数的熵编码:
    熵编码中最重要的一步就是变换系数的熵编码。
    在HEVC中,变换系数的熵编码是以TU为单位进行的,通过编码非0系数的位置信息和非零系数的幅值信息来表示其变换系数。
    变换系数的熵编码有三个步骤:
    1、变换系数的扫描
        变换系数的扫描是基于4x4子块进行的,因此要把TU分割成4x4的子块,然后按照一定的规则进行扫描,4x4子块内部的系数的扫描规则和4x4子块的扫描顺序是一样的。一个4x4子块内部系数扫描之后的16个连续的系数被称为系数组CG。有三种扫描顺序:
        (1)水平扫描。从TU的右下角开始,从右到左,从下到上。每扫描到一个4x4子块,那么对这个4x4子块内部的系数也使用相同的方式扫描。适用于4x4和8x8的TU的垂直预测模式。
        (2)垂直扫描。从TU的右下角开始,从下到上,从右到左。适用于4x4和8x8的TU的水平预测模式。
        (3)对角扫描。从TU的右下角开始扫描,按照对角的方式进行。适用于其他各种模式。
    2、非零系数位置信息编码。
        一个TU经过扫描之后就可以得到一组一维的变换系数,该组系数可以根据非零系数位置信息和幅值信息完全表示。
        (1)最后一个非零系数的位置。TU最后一个非零系数其实就是扫描之后的第一个非零系数(所有的扫描方式都是从右下角开始的!)
        (2)其余非零系数的位置。它们涉及到两个语法元素:csbf和sig_coeff_flag。csbf表示当前系数组CG中是否含有非零系数,其值等于1表示CG内至少含有一个非零系数;其值为0表示CG内没有非零系数(即该CG内都是零系数)。sig_coeff_flag表示当前位置上的系数是否为0,值是1表示该位置的系数值是非0的,值是0表示当前位置的系数值是0。对于一个CG,先编码一个CG的csbf,如果csbf等于0,那就编码下一个CG的csbf;如果csbf等于1,那么按照扫描顺序逐一编码CG内每一个位置上非零系数标识sig_coeff_flag,然后再编码所有非零系数的幅值信息。按照这种方式编码下一个CG,直到TU被编码完成。
    3、非零系数幅值信息编码
        非零系数幅值信息涉及下面几个语法元素:coeff_sign_flag表示非零系数是正值还是负值,coeff_abs_level_greater1_flag表示编码的非零系数幅值绝对值是否大于1,coeff_abs_level_greater2_flag表示非零系数幅值绝对值是否大于2,coeff_abs_level_remaining表示非零系数幅值绝对值的剩余部分。
        编码过程如下:
        (1)按照扫描顺序逐一编码CG内前8个非零系数各自的coeff_abs_level_greater1_flag,后续的非零系数不再编码coeff_abs_level_greater1_flag,因为默认它们各自的coeff_abs_level_greater1_flag是0。
        (2)编码CG内第一个absCoeffLevel(非零系数幅值绝对值)>1的非零系数的coeff_abs_level_greater2_flag,后续非零系数不再编码coeff_abs_level_greater2_flag,
因为默认它们的coeff_abs_level_greater2_flag都是0。
        (3)编码CG内所有非零系数的符号coeff_sign_flag
        (4)计算CG内所有非零系数的coeff_abs_level_remaining,按照扫描顺序依次编码。
        (5)其中coeff_abs_level_remaining和coeff_sign_flag实用旁路编码;coeff_abs_level_greater1_flag和coeff_abs_level_greater2_flag使用常规编码。
// 对NxN的已量化的矩阵系数进行熵编码
Void TEncSbac::codeCoeffNxN( TComDataCU* pcCU, TCoeff* pcCoef, UInt uiAbsPartIdx, UInt uiWidth, UInt uiHeight, UInt uiDepth, TextType eTType )
{
	DTRACE_CABAC_VL( g_nSymbolCounter++ )
		DTRACE_CABAC_T( "\tparseCoeffNxN()\teType=" )
		DTRACE_CABAC_V( eTType )
		DTRACE_CABAC_T( "\twidth=" )
		DTRACE_CABAC_V( uiWidth )
		DTRACE_CABAC_T( "\theight=" )
		DTRACE_CABAC_V( uiHeight )
		DTRACE_CABAC_T( "\tdepth=" )
		DTRACE_CABAC_V( uiDepth )
		DTRACE_CABAC_T( "\tabspartidx=" )
		DTRACE_CABAC_V( uiAbsPartIdx )
		DTRACE_CABAC_T( "\ttoCU-X=" )
		DTRACE_CABAC_V( pcCU->getCUPelX() )
		DTRACE_CABAC_T( "\ttoCU-Y=" )
		DTRACE_CABAC_V( pcCU->getCUPelY() )
		DTRACE_CABAC_T( "\tCU-addr=" )
		DTRACE_CABAC_V(  pcCU->getAddr() )
		DTRACE_CABAC_T( "\tinCU-X=" )
		DTRACE_CABAC_V( g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsPartIdx] ] )
		DTRACE_CABAC_T( "\tinCU-Y=" )
		DTRACE_CABAC_V( g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsPartIdx] ] )
		DTRACE_CABAC_T( "\tpredmode=" )
		DTRACE_CABAC_V(  pcCU->getPredictionMode( uiAbsPartIdx ) )
		DTRACE_CABAC_T( "\n" )

		// 获取矩阵的宽和高
		if( uiWidth > m_pcSlice->getSPS()->getMaxTrSize() )
		{
			uiWidth  = m_pcSlice->getSPS()->getMaxTrSize();
			uiHeight = m_pcSlice->getSPS()->getMaxTrSize();
		}

		UInt uiNumSig = 0;

		// compute number of significant coefficients
		// 计算非0系数的个数
		uiNumSig = TEncEntropy::countNonZeroCoeffs(pcCoef, uiWidth * uiHeight);

		// 如果矩阵中都是0,那么直接返回
		if ( uiNumSig == 0 )
			return;

		// 判断是否使用了变换跳过的模式
		if(pcCU->getSlice()->getPPS()->getUseTransformSkip())
		{
			// 如果使用了变换跳过模式,就对跳过模式进行编码
			codeTransformSkipFlags( pcCU,uiAbsPartIdx, uiWidth, uiHeight, eTType );
		}
		eTType = eTType == TEXT_LUMA ? TEXT_LUMA : ( eTType == TEXT_NONE ? TEXT_NONE : TEXT_CHROMA );

		//----- encode significance map -----
		const UInt   uiLog2BlockSize = g_aucConvertToBit[ uiWidth ] + 2;
		UInt uiScanIdx = pcCU->getCoefScanIdx(uiAbsPartIdx, uiWidth, eTType==TEXT_LUMA, pcCU->isIntra(uiAbsPartIdx));

		// 扫描的顺序
		const UInt *scan = g_auiSigLastScan[ uiScanIdx ][ uiLog2BlockSize - 1 ];

		Bool beValid;
		if (pcCU->getCUTransquantBypass(uiAbsPartIdx))
		{
			beValid = false;
		}
		else 
		{
			beValid = pcCU->getSlice()->getPPS()->getSignHideFlag() > 0;
		}

		// Find position of last coefficient
		Int scanPosLast = -1;
		Int posLast;

		// 系数组的扫描顺序
		const UInt * scanCG;
		{
			scanCG = g_auiSigLastScan[ uiScanIdx ][ uiLog2BlockSize > 3 ? uiLog2BlockSize-2-1 : 0 ];

			// 对于尺寸为8的情况
			if( uiLog2BlockSize == 3 )
			{
				scanCG = g_sigLastScan8x8[ uiScanIdx ];
			}
			// 对于尺寸为32的情况
			else if( uiLog2BlockSize == 5 )
			{
				scanCG = g_sigLastScanCG32x32;
			}
		}
		UInt uiSigCoeffGroupFlag[ MLS_GRP_NUM ];
		static const UInt uiShift = MLS_CG_SIZE >> 1;
		const UInt uiNumBlkSide = uiWidth >> uiShift;

		::memset( uiSigCoeffGroupFlag, 0, sizeof(UInt) * MLS_GRP_NUM );

		// 找到最后一个非0系数的位置
		do
		{
			posLast = scan[ ++scanPosLast ];

			// get L1 sig map
			UInt uiPosY    = posLast >> uiLog2BlockSize;
			UInt uiPosX    = posLast - ( uiPosY << uiLog2BlockSize );
			UInt uiBlkIdx  = uiNumBlkSide * (uiPosY >> uiShift) + (uiPosX >> uiShift);
			if( pcCoef[ posLast ] )
			{
				uiSigCoeffGroupFlag[ uiBlkIdx ] = 1;
			}

			uiNumSig -= ( pcCoef[ posLast ] != 0 );
		}
		while ( uiNumSig > 0 );

		// Code position of last coefficient
		Int posLastY = posLast >> uiLog2BlockSize;
		Int posLastX = posLast - ( posLastY << uiLog2BlockSize );

		// 对最后一个非零系数的位置进行编码
		codeLastSignificantXY(posLastX, posLastY, uiWidth, uiHeight, eTType, uiScanIdx);

		//===== code significance flag =====
		ContextModel * const baseCoeffGroupCtx = m_cCUSigCoeffGroupSCModel.get( 0, eTType );
		ContextModel * const baseCtx = (eTType==TEXT_LUMA) ? m_cCUSigSCModel.get( 0, 0 ) : m_cCUSigSCModel.get( 0, 0 ) + NUM_SIG_FLAG_CTX_LUMA;


		const Int  iLastScanSet      = scanPosLast >> LOG2_SCAN_SET_SIZE;
		UInt c1 = 1;
		UInt uiGoRiceParam           = 0;
		Int  iScanPosSig             = scanPosLast;

		// 循环,对其他非零系数的位置以及幅值信息进行编码
		for( Int iSubSet = iLastScanSet; iSubSet >= 0; iSubSet-- )
		{
			Int numNonZero = 0;
			Int  iSubPos     = iSubSet << LOG2_SCAN_SET_SIZE;
			uiGoRiceParam    = 0;
			Int absCoeff[16];
			UInt coeffSigns = 0;

			Int lastNZPosInCG = -1, firstNZPosInCG = SCAN_SET_SIZE;

			if( iScanPosSig == scanPosLast )
			{
				absCoeff[ 0 ] = abs( pcCoef[ posLast ] );
				coeffSigns    = ( pcCoef[ posLast ] < 0 );
				numNonZero    = 1;
				lastNZPosInCG  = iScanPosSig;
				firstNZPosInCG = iScanPosSig;
				iScanPosSig--;
			}

			// encode significant_coeffgroup_flag
			// 对csbf进行编码
			Int iCGBlkPos = scanCG[ iSubSet ];
			Int iCGPosY   = iCGBlkPos / uiNumBlkSide;
			Int iCGPosX   = iCGBlkPos - (iCGPosY * uiNumBlkSide);
			if( iSubSet == iLastScanSet || iSubSet == 0)
			{
				uiSigCoeffGroupFlag[ iCGBlkPos ] = 1;
			}
			else
			{
				UInt uiSigCoeffGroup   = (uiSigCoeffGroupFlag[ iCGBlkPos ] != 0);
				UInt uiCtxSig  = TComTrQuant::getSigCoeffGroupCtxInc( uiSigCoeffGroupFlag, iCGPosX, iCGPosY, uiWidth, uiHeight );

				m_pcBinIf->encodeBin( uiSigCoeffGroup, baseCoeffGroupCtx[ uiCtxSig ] );
			}

			// encode significant_coeff_flag
			// 对significant_coeff_flag标志进行编码
			if( uiSigCoeffGroupFlag[ iCGBlkPos ] )
			{
				Int patternSigCtx = TComTrQuant::calcPatternSigCtx( uiSigCoeffGroupFlag, iCGPosX, iCGPosY, uiWidth, uiHeight );
				UInt uiBlkPos, uiPosY, uiPosX, uiSig, uiCtxSig;
				for( ; iScanPosSig >= iSubPos; iScanPosSig-- )
				{
					uiBlkPos  = scan[ iScanPosSig ]; 
					uiPosY    = uiBlkPos >> uiLog2BlockSize;
					uiPosX    = uiBlkPos - ( uiPosY << uiLog2BlockSize );
					uiSig     = (pcCoef[ uiBlkPos ] != 0);
					if( iScanPosSig > iSubPos || iSubSet == 0 || numNonZero )
					{
						uiCtxSig  = TComTrQuant::getSigCtxInc( patternSigCtx, uiScanIdx, uiPosX, uiPosY, uiLog2BlockSize, eTType );

						m_pcBinIf->encodeBin( uiSig, baseCtx[ uiCtxSig ] );
					}
					if( uiSig )
					{
						absCoeff[ numNonZero ] = abs( pcCoef[ uiBlkPos ] );
						coeffSigns = 2 * coeffSigns + ( pcCoef[ uiBlkPos ] < 0 );
						numNonZero++;
						if( lastNZPosInCG == -1 )
						{
							lastNZPosInCG = iScanPosSig;
						}
						firstNZPosInCG = iScanPosSig;
					}
				}
			}
			else
			{
				iScanPosSig = iSubPos - 1;
			}

			if( numNonZero > 0 )
			{
				Bool signHidden = ( lastNZPosInCG - firstNZPosInCG >= SBH_THRESHOLD );
				UInt uiCtxSet = (iSubSet > 0 && eTType==TEXT_LUMA) ? 2 : 0;

				if( c1 == 0 )
				{
					uiCtxSet++;
				}
				c1 = 1;
				ContextModel *baseCtxMod = ( eTType==TEXT_LUMA ) ? m_cCUOneSCModel.get( 0, 0 ) + 4 * uiCtxSet : m_cCUOneSCModel.get( 0, 0 ) + NUM_ONE_FLAG_CTX_LUMA + 4 * uiCtxSet;

				Int numC1Flag = min(numNonZero, C1FLAG_NUMBER);
				Int firstC2FlagIdx = -1;

				// 对一个CG内前八个非零系数的coeff_abs_level_greater1_flag进行编码
				for( Int idx = 0; idx < numC1Flag; idx++ )
				{
					UInt uiSymbol = absCoeff[ idx ] > 1;
					m_pcBinIf->encodeBin( uiSymbol, baseCtxMod[c1] );
					if( uiSymbol )
					{
						c1 = 0;

						if (firstC2FlagIdx == -1)
						{
							firstC2FlagIdx = idx;
						}
					}
					else if( (c1 < 3) && (c1 > 0) )
					{
						c1++;
					}
				}

				// 编码CG内第一个absCoeffLevel(非零系数幅值绝对值)>1的非零系数的coeff_abs_level_greater2_flag
				if (c1 == 0)
				{

					baseCtxMod = ( eTType==TEXT_LUMA ) ? m_cCUAbsSCModel.get( 0, 0 ) + uiCtxSet : m_cCUAbsSCModel.get( 0, 0 ) + NUM_ABS_FLAG_CTX_LUMA + uiCtxSet;
					if ( firstC2FlagIdx != -1)
					{
						UInt symbol = absCoeff[ firstC2FlagIdx ] > 2;
						m_pcBinIf->encodeBin( symbol, baseCtxMod[0] );
					}
				}

				// 对coeff_sign_flag进行编码
				if( beValid && signHidden )
				{
					m_pcBinIf->encodeBinsEP( (coeffSigns >> 1), numNonZero-1 );
				}
				else
				{
					m_pcBinIf->encodeBinsEP( coeffSigns, numNonZero );
				}

				Int iFirstCoeff2 = 1;    
				if (c1 == 0 || numNonZero > C1FLAG_NUMBER)
				{
					for ( Int idx = 0; idx < numNonZero; idx++ )
					{
						UInt baseLevel  = (idx < C1FLAG_NUMBER)? (2 + iFirstCoeff2 ) : 1;

						if( absCoeff[ idx ] >= baseLevel)
						{
							// 对coeff_abs_level_remaining进行编码
							xWriteCoefRemainExGolomb( absCoeff[ idx ] - baseLevel, uiGoRiceParam );
							if(absCoeff[idx] > 3*(1<<uiGoRiceParam))
							{
								uiGoRiceParam = min<UInt>(uiGoRiceParam+ 1, 4);
							}
						}
						if(absCoeff[ idx ] >= 2)  
						{
							iFirstCoeff2 = 0;
						}
					}        
				}
			}
		}

		return;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值