靈活的塊劃分技術給H.265帶來了很高的性能提升,相比於H.264而言,在參考軟件中利用遞歸的方式實現了塊的四叉樹劃分,
H.265標準中對於編碼單元有四個概念CTU, CU,PU,TU
概念定義如下:
(1) 編碼樹單元(CTU)和編碼樹塊(CTB)結構:
在之前的標準中,編碼層的核心是宏塊,一個宏塊包含一個16×16的亮度塊,以及對於常用的4:2:0採樣格式來說還包含兩個8×8的色度塊;而在HEVC中類似的結構爲編碼樹單元(CTU),其尺寸由編碼器進行指定且可以比傳統的宏塊大。一個CTU包含一個亮度CTB和兩個對應的色度CTB及句法元素。一個L×L的亮度CTB的L可以設置爲16,32或者64。一般來說,L越大,可以獲得越好的壓縮性能。HEVC支持使用樹結構和類四叉樹的標誌來將CTB劃分成更小的塊。
(2) 編碼單元(CU)和編碼塊(CB):
CTU的四叉樹句法指定了它所屬的亮度和色度CB的尺寸和位置。四叉樹的根與CTU相關聯。因此,亮度CB的最大尺寸爲其所屬的亮度CTB的尺寸。對於一個CTU來說,其亮度CB和色度CB的劃分標誌都是使用的同一個。一個亮度CB通常和兩個色度CB及它們相關的句法共同組成一個編碼單元(CU)。一個CTB可能只包含一個CU,也可能被劃分成多個CU,每個CU包含着與之相關聯的預測單元(PU)和變換單元(TU)。
(3) 預測單元(PU)和預測塊(PB):
決定一個圖像區域是以幀間還是幀內方式進行預測是在CU層進行的。一個PU劃分結構的根在CU層。根據基本的預測類型,亮度CB和色度CB可以繼續進行劃分並利用其它的亮度PB和色度PB進行預測。HEVC支持多種PB尺寸,最大爲64×64到最小4×4。
(4) 變換單元(TU)和變換塊(TB):
預測殘差以塊變換的方式進行編碼。一個變換單元樹結構的根在CU層。亮度CB殘差的尺寸可能與亮度變換塊TB的尺寸相等,也可能會被劃分成更小的亮度TB。色度CB的情況也是一樣的。定義了與離散餘弦變換(DCT)類似的整數變換的基本函數提供給尺寸爲4×4,8×8,16×16,32×32的TB。對於尺寸爲4×4且殘差由幀內預測得到的TB來說,一種以離散正弦變換(DST)爲基礎的整數變換可供採用。
劃分關係爲:CTU可以四叉樹劃分爲四個CU也可以不劃分,這是根據率失真代價決定的,
一個CU可以劃分成多個PU,265有8中劃分模式(如下圖),具體選哪一種,在劃分中是根據率失真代價決定的。
變換單元TU是在CU的基礎上劃分的,跟PU沒有任何關係,採用四叉樹劃分方式,具體劃分有率失真代價決定,下圖給出了某個CU劃分成TU的結構。
注:率失真代價表達式爲:D+lambda*R D爲量化失真,R爲編碼的比特數,lambda爲乘子,由配置文件決定,這部分屬於率失真優化技術範疇,率失真優化問題是編碼界公認的難題,至今還沒有很好的解決,lambda的選取直接影響着編碼性能,這方面我的導師(朱策老師)在這方面有較大的成就,有不少相關的論文,想了解的可以去我們實驗室(視頻通信與智能計算實驗室)網站下載:http://www.avc2-lab.net/,願意從事視頻編解碼研究的,可以考慮來我們實驗室,朱策老師因在視頻通信和視頻壓縮領域的貢獻獲得IEEE
FELLOW和IET FELLOW 的榮譽。
在HM代碼中實現塊劃分的函數爲:compressCtu,具體調用xcompressCU完成劃分模式的決策,具體函數過程解析轉載一遍比較詳細的博客。
現將內容轉載如下:
轉載地址:http://blog.sina.com.cn/s/blog_9f945ced0101p3ut.html
1.xCompressCU 函數的調用
在編碼一個片的函數CompressSlice 函數中有這個幾行代碼
// run CU encoder
m_pcCuEncoder->compressCU( pcCU ); 就是一個LCU的編碼,包括CU的劃分,PU模式的決定,TU的劃分
m_pcCuEncoder->encodeCU( pcCU ); 這裏可以看出來pcCU是存儲着需要編碼的信息。
2.進入這個函數
Void TEncCu::xcompressCU( TComDataCU*& rpcCU )
{
// initialize CU data
m_ppcBestCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );
//這裏的 m_ppcBestCU[0]和m_ppcTempCU[0]都是記錄模式信息的,至於如何記錄我們一點點的來看。由//於是遞歸的劃分CU,這裏容易繞暈啊。
m_ppcTempCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );
// analysis of CU
xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 );
//這裏進入xCompressCU函數,這裏的第三個參數是 CU的分割深度。64x64的深度爲0,以此類推,
//最小的CU爲8x8深度爲3。至於如何將最優的模式信息賦值到pcCU裏,我們後面跟蹤代碼可以看到。
這裏首先要弄清一個概念,就是CU的劃分是遞歸的
第一步 CU的大小爲64x64, 搜索最優的PU的劃分得到最優的預測模式,進行TU的劃分
第二步 CU的大小爲32x32, 第一個CU(按之子掃描順序) 同上
第三步 CU的大小爲16x16, 第一個CU 同上
第四步 CU的大小爲8x8, 以此進行第一個CU,第二個CU,第三個CU和第四個CU的PU和TU的劃分和最優 模式的選擇。這裏面完成每個CU後將這個的RD與前面進行累加。
第五步 返回到CU爲 16x16的CU,將其RD-COST 與第四部記錄的四個8X8的CU的RD-cost進行比較。決定 了這個16X16的最優的CU劃分及最優的CU下的PU和CU的劃分。
第六步 CU的大小爲 16X16,第二個CU。重複 第四步第五步,可以得到第二個最優的16x16的CU的劃分和 PU TU 的模式。 同時將改第二個CU的最優的RD-COST與5步得到的第一個16x16的CURD-COST
進行累加。
第七步 :同理完成第三個和第四個的16X16的CU的最優的劃分和模式的選擇,將其RD-COST累加。這樣 我們就得到了分割爲16X16最佳的RD-cost。
第八步 :返回到第二步,比較第一個32X32CU的RD-cost 和 分割爲4個16X16的CU的RD-cost,得了第一 個32X32 CU的分割信息和最優的模式。
第九步:同理完成第二個32x32,第三個32X32和第四個32X32的最優的劃分和模式選擇。通過記錄和累加 每一個32x32的RD-cost,與64x64的CU的RD-Cost進行比較。我們得到了最終的CU 的劃分和每個 CU的最優的PU的劃分及PU的預測模式以及TU的劃分。
}
3.xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 )函數
現在就按照2中的過程來看一看這函數的流程:
首先進入該函數,CU的大小已經確定了爲64x64,進行PU的劃分和TU的劃分
PU的劃分將按下列順序進行嘗試:
幀間
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N, bFMD ) skip 2NX2N
xCheckRDCostMerge2Nx2N( rpcBestCU, rpcTempCU, &earlyDetectionSkipMode ); Merge 2NX2N
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2Nx2N, bFMD ); 2NX2N
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_NxN, bFMD ); NXN 劃分爲4個PU
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_Nx2N, bFMD ); Nx2N 劃分爲2個PU
xCheckRDCostInter ( rpcBestCU, rpcTempCU, SIZE_2NxN, bFMD );
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU, bFMD );
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD, bFMD ); 4種非對稱的劃分
xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N, bFMD );
這裏在調用這個幀內RDcost函數時,rpcBestCU中始終存放的是當前CU下最優的PU的模式和劃分信息的CU結構體。
幀內:
xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_2Nx2N ); 2NX2N的劃分。 PU爲CU的大小。
xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_NxN ); 當CU爲最小的CU的時候,將嘗試 分割爲4個
PU。
在RD-cost的函數的最後有這樣一個函數
xCheckBestMode(rpcBestCU, rpcTempCU, uiDepth);
接下來就該遞歸的分割LCU,下面看看分割的判斷
if( bSubBranch && bTrySplitDQP && uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth ) 條件爲真進行分割
{
UChar uhNextDepth = uiDepth+1;
TComDataCU* pcSubBestPartCU = m_ppcBestCU[uhNextDepth];
TComDataCU* pcSubTempPartCU = m_ppcTempCU[uhNextDepth];
for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ )
{
pcSubBestPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP );
pcSubTempPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP );
// 注意這裏是用rpcTempCU對pcSubBestPartCU和pcSubTemPartC進行初始化,函數到此 rpcBestCU裏面//還是當前CU64x64大小最優的PU信息。
//接下來是遞歸的調用自己
if ( rpcBestCU->isIntra(0) )
{
xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth, SIZE_NONE );
//進入這個函數後,CU的大小爲32X32 iPartUnitIdx 這裏pcSubBestPartCU就和rpcBestCU一樣始終放//着當前最優的預測的信息。
}
else
{
xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth, rpcBestCU->getPartitionSize(0) );
}
顯然這裏要做的是將4個最優的32X32CU的RD-cost累加。注意這裏有一個語句是
rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth );
跟進去一看這個函數的幾個代碼就知道這個語句就是完成4個劃分的最優的信息的累加,以便和爲分割前的CU的最優的預測模式的RD-cost進行比較也就是m_ppcBestCU進行比較。
}
}
分割的循環
完成了四個分割,即2中的過程2中的第九步。這裏就 rpcTempCU存儲的是CU的4個劃分的信息。
可以看見 xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth);函數將未分割的CU和分割之後的4個CU進行比
較來決定是否進行CU的劃分。
}
但是奇怪的是如何和最開始CompressCU的參數pcCU的參數聯繫上。這裏有個函數。在最後面
rpcBestCU->copyToPic(uiDepth);
這個函數就是將得到的最優的PU的模式和預測信息,及CU的劃分的信息賦值到pcCU中。
進去看這個函數,
TComDataCU*& rpcCU = m_pcPic->getCU( m_uiCUAddr );第一個語句中的rpcCU正好是CompressCU的參數。
下面給出某一次劃分的示意圖: