HEVC多次進行熵編碼的原因

HEVC多次進行熵編碼的原因



    在HM中有個讓人很疑惑的地方,就是熵編碼會被多次調用
    1、compressSlice中有兩次
        (1)第一次是compressCU中,使用熵編碼來進行RDO優化,用來選擇最優的編碼參數
        (2)第二次是緊接着compressCU後面調用了encodeCU
    2、encodeSlice中有一次
        (1)調用encodeCU


原因解釋


    後來有大神解釋了一下:
    1、在compressCU中調用熵編碼,是爲了進行RDO優化,這個比較好理解
    2、compressCU後面調用encodeCU,是爲了保持CABAC的狀態,因爲熵編碼是以slice爲單位進行的,在一個slice中要保證這種狀態的連續性,RDO的時候,在開始編碼第二個CTU的時候的CABAC的狀態得保證和第一個CTU熵編碼完了以後的CABAC狀態是一樣的,因此要調用encodeCU
    3、在encodeSlice中調用encodeCU的目的是真正的熵編碼
    4、另外需要注意的是,第一、二次調用熵編碼操作不會寫入比特流中,只有第三次調用纔會寫入比特流中。





在代碼中的實現


Void TEncSlice::compressSlice( TComPic*& rpcPic )
{
	//***省略

	TEncTop* pcEncTop = (TEncTop*) m_pcCfg;


	TEncSbac**** ppppcRDSbacCoders    = pcEncTop->getRDSbacCoders();

	// 比特計數器
	TComBitCounter* pcBitCounters     = pcEncTop->getBitCounters();

	
	// ***省略

	for( uiEncCUOrder = uiStartCUAddr/rpcPic->getNumPartInCU();
		uiEncCUOrder < (uiBoundingCUAddr+(rpcPic->getNumPartInCU()-1))/rpcPic->getNumPartInCU();
		uiCUAddr = rpcPic->getPicSym()->getCUOrderMap(++uiEncCUOrder) )
	{
		// ***省略

		// set go-on entropy coder
		m_pcEntropyCoder->setEntropyCoder ( m_pcRDGoOnSbacCoder, pcSlice );
		// 注意這裏,pcBitCounters是比特計數器,它和比特流類有相同的接口,但是隻是用於比特計數,不會寫數據到比特流中
		m_pcEntropyCoder->setBitstream( &pcBitCounters[uiSubStrm] );

		// 啓用比特計數的功能
		((TEncBinCABAC*)m_pcRDGoOnSbacCoder->getEncBinIf())->setBinCountingEnableFlag(true);

		// ***
		
		// 選擇最優的編碼模式
		m_pcCuEncoder->compressCU( pcCU );

		// restore entropy coder to an initial stage
		m_pcEntropyCoder->setEntropyCoder ( m_pppcRDSbacCoder[0][CI_CURR_BEST], pcSlice );

		// 同樣還是比特計數器
		m_pcEntropyCoder->setBitstream( &pcBitCounters[uiSubStrm] );
		m_pcCuEncoder->setBitCounter( &pcBitCounters[uiSubStrm] );

		m_pcBitCounter = &pcBitCounters[uiSubStrm];
		pppcRDSbacCoder->setBinCountingEnableFlag( true );
		m_pcBitCounter->resetBits();
		pppcRDSbacCoder->setBinsCoded( 0 );

		// 爲了在一個slice保證CABAC狀態的連續性,因爲CABAC是以slice爲單位的
		m_pcCuEncoder->encodeCU( pcCU );

		
		// *** 省略
	} // for end


	// ***省略
}


Void TEncSlice::encodeSlice   ( TComPic*& rpcPic, TComOutputBitstream* pcSubstreams )
{
	//*** 省略

	// 遍歷條帶中的每一個CU
	for( uiEncCUOrder = uiStartCUAddr /rpcPic->getNumPartInCU();
		uiEncCUOrder < (uiBoundingCUAddr+rpcPic->getNumPartInCU()-1)/rpcPic->getNumPartInCU();
		uiCUAddr = rpcPic->getPicSym()->getCUOrderMap(++uiEncCUOrder) )
	{
		// ***省略

		// 注意這裏,pcSubstreams是比特流,不是比特計數器,因此,熵編碼後會將數據寫入比特流中
		m_pcEntropyCoder->setBitstream( &pcSubstreams[uiSubStrm] );
		
		// *** 省略
		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 );
		}

		// ***省略
	}
	// ***省略
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章