H.265的塊劃分技術

靈活的塊劃分技術給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的參數。
下面給出某一次劃分的示意圖:




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