x264_macroblock_analyse(x264_t *h ) 分析

x264_macroblock_analyse(x264_t *h )

功能

初始化;

對於I Slice,遍歷所有16x16和4x4預測模式,得到最小Cost所對應的分塊方式和預測模式;

對於P Slice,如果宏塊周圍有Skip模式的宏塊,檢測當前塊是否是Skip塊,如果不是Skip塊,遍歷所有可能的塊劃分模式,對每種模式進行運動估計並計算Cost,計算intra對應的16x16和4x4的Cost(包括色度),比較所有的Cost,選最小的Cost對應的模式爲最終模式。

輸入項目

x264_t *h

程序邏輯

如附圖3所示:

 


 

//×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

 本文對x264_macroblock_analyse()及其子函數的流程進行詳盡的分析。希望對大家有所幫助。
      參考x264_050530版本。
      1、void x264_macroblock_analyse( x264_t *h )
      首先初始化函數,然後進入一個選擇語句
      if( h->sh.i_type == SLICE_TYPE_I )
      {
             ……..
      }
      else if( h->sh.i_type == SLICE_TYPE_P )
      {
             ……..
      }
      else if( h->sh.i_type == SLICE_TYPE_B )
      {
             ………
      }
      很明顯,這是對不同類型的塊採用不同的處理步驟,我們就進入SLICE_TYPE_P深入分析一下吧,因爲P類型包含有I模式的檢測,SLICE_TYPE_B只是SLICE_TYPE_P的簡單延伸而已。
      if( h->param.analyse.b_fast_pskip )
              {
                  if( h->param.analyse.i_subpel_refine >= 3 )
                      analysis.b_try_pskip = 1;
                  else if( h->mb.i_mb_type_left == P_SKIP ||
                           h->mb.i_mb_type_top == P_SKIP ||
                           h->mb.i_mb_type_topleft == P_SKIP ||
                           h->mb.i_mb_type_topright == P_SKIP )
                      b_skip = x264_macroblock_probe_pskip( h );
              }
      條件進入後首先判斷skip模式,如果滿足skip模式則判斷結束,否則繼續下面的判斷。

      x264_mb_analyse_inter_p16x16( h, &analysis );
                  if( flags & X264_ANALYSE_PSUB16x16 )
                  {
                      if( h->param.analyse.b_mixed_references )
                          x264_mb_analyse_inter_p8x8_mixed_ref( h, &analysis ); 
                    else
                          x264_mb_analyse_inter_p8x8( h, &analysis );
                  }
      分析16×16和8×8模式。模式代價值分別保存在和analysis.l0.me16x16.cost 、analysis.l0.i_cost8x8中。

      if( ( flags & X264_ANALYSE_PSUB16x16 ) && analysis.l0.i_cost8x8 <
      analysis.l0.me16x16.cost )
                           {
                                  …….
      }
      如果8×8代價值小於16×16,則進行子宏塊分割的判斷。

      for( i = 0; i < 4; i++ )
                          {
                              x264_mb_analyse_inter_p4x4( h, &analysis, i );
                              if( analysis.l0.i_cost4x4[i] <
      analysis.l0.me8x8[i].cost )
                              {
                                                              ………
      }
      依次對4個子宏塊(8×8)進行處理,x264_mb_analyse_inter_p4x4()函數實際上是得到4個4×4塊的代價和analysis.l0.i_cost4x4[i]。如果4×4模式優於8×8模式,才進行8×8塊的細分割。細分割代碼分析略。

      if( ( flags & X264_ANALYSE_PSUB16x16 ) && analysis.l0.i_cost8x8 <
      analysis.l0.me16x16.cost + i_thresh16x8 )
                  {
                      x264_mb_analyse_inter_p16x8( h, &analysis );
                      ……..
                      x264_mb_analyse_inter_p8x16( h, &analysis );
                      ……...
                  }
      緊接着檢測16×8和8×16模式

      x264_me_refine_qpel( h, &analysis.l0.me16x16 );
      ……..
      幀間模式選擇後,對該模式進行亞象素精細搜索。以進一步減少誤差。值得注意的是,在前面每個模式的檢測時,也要進行亞象素搜索,見x264_me_search_ref()函數的最後幾行。這裏的亞象素搜索是在前面基礎上再進行精細搜索的。二者亞象素搜索(包括半象素和1/4象素)的次數由subpel_iterations[i][4]確定,而i由編譯參數subme確定,看運行幫助:
      -m, --subme <integer>       Subpixel motion estimation quality: 1=fast,
      5=best
      實際上subme就是決定模式選擇前後亞象素估計的點數。Subme越大,壓縮效率越好,計算量越大。

      x264_mb_analyse_intra( h, &analysis, i_cost );
                  if( h->mb.b_chroma_me && !analysis.b_mbrd &&
                      ( analysis.i_sad_i16x16 < i_cost
                     || analysis.i_sad_i8x8 < i_cost
                     || analysis.i_sad_i4x4 < i_cost ))
                  {
                      x264_mb_analyse_intra_chroma( h, &analysis );
                      analysis.i_sad_i16x16 += analysis.i_sad_i8x8chroma;
                      analysis.i_sad_i8x8 += analysis.i_sad_i8x8chroma;
                      analysis.i_sad_i4x4 += analysis.i_sad_i8x8chroma;
                  }
      分析宏塊的幀內編碼,包括亮度和色度。亮度的16×16、8×8、4×4代價分別存在analysis.i_sad_i16x16、analysis.i_sad_i8x8、analysis.i_sad_i4x4中,色度代價存在analysis.i_sad_i8x8chroma中。

      i_intra_type = I_16x16;
                  i_intra_cost = analysis.i_sad_i16x16;
                  if( analysis.i_sad_i8x8 < i_intra_cost )
                  {
                      i_intra_type = I_8x8;
                      i_intra_cost = analysis.i_sad_i8x8;
                  }
                  if( analysis.i_sad_i4x4 < i_intra_cost )
                  {
                      i_intra_type = I_4x4;
                      i_intra_cost = analysis.i_sad_i4x4;
                  }
                  if( i_intra_cost < i_cost )
                  {
                      i_type = i_intra_type;
                      i_cost = i_intra_cost;
                  }
      比較得到最佳的幀內預測模式。

      if( i_intra_cost < i_cost )
                  {
                      i_type = i_intra_type;
                      i_cost = i_intra_cost;
                  }
      幀內代價與幀間代價比較,得到最佳的預測模式。

      整個P類型就分析完了,然後看看條件跳出以後執行什麼
      if( !analysis.b_mbrd )
              x264_mb_analyse_transform( h );
      判斷變換的時候是採用8×8變換還是4×4變換。

      這樣,怎麼函數就分析完了^_^,下面看看調用的重要子函數。
      2、static inline int x264_macroblock_probe_pskip( x264_t *h )
      該函數直接調用了x264_macroblock_probe_skip(h, 0);看看裏邊有什麼東東。
      if( !b_bidir )
          {
              x264_mb_predict_mv_pskip( h, mvp );
              mvp[0] = x264_clip3( mvp[0], h->mb.mv_min[0], h->mb.mv_max[0] );
              mvp[1] = x264_clip3( mvp[1], h->mb.mv_min[1], h->mb.mv_max[1] );
              h->mc.mc_luma( h->mb.pic.p_fref[0][0], h->mb.pic.i_stride[0],
                             h->mb.pic.p_fdec[0],    FDEC_STRIDE,
                             mvp[0], mvp[1], 16, 16 );
          }
      先得到預測矢量MVp,然後對MVp進行飽和處理,再進行相應的運動補償。

      h->dctf.sub16x16_dct( dct4x4, h->mb.pic.p_fenc[0], FENC_STRIDE,
                                        h->mb.pic.p_fdec[0], FDEC_STRIDE );
      for( i8x8 = 0, i_decimate_mb = 0; i8x8 < 4; i8x8++ )
          {
              /* encode one 4x4 block */
              for( i4x4 = 0; i4x4 < 4; i4x4++ )
              {
                  const int idx = i8x8 * 4 + i4x4;

                  quant_4x4( h, dct4x4[idx], (int(*)[4][4])def_quant4_mf, i_qp,
      0 );
                  scan_zigzag_4x4full( dctscan, dct4x4[idx] );

                  i_decimate_mb += x264_mb_decimate_score( dctscan, 16 );

                  if( i_decimate_mb >= 6 )
                  {
                      /* not as P_SKIP */
                      return 0;
                  }
              }
          }
      進行dct變換(注意是4×4變換,不是8×8!!!),然後對每個8×8塊中的4×4對進行量化,zigzag掃描,得到8×8塊的i_decimate_mb值。如果量化後係數中只有零星的非零係數,且都是1或-1,i_decimate_mb就比較小。if(i_decimate_mb<6),可以將係數全變爲0。注意,其他模式下的殘差編碼也用到了該處理過程。
      程序後面是對色度進行處理,與亮度類似,不進行討論。

      3、static void x264_mb_analyse_inter_p16x16 ( x264_t *h, x264_mb_analysis_t
      *a )
      直接看核心吧。
      for( i_ref = 0; i_ref < h->i_ref0; i_ref++ )
          {
      循環搜索搜索每個參考幀

      x264_mb_predict_mv_16x16( h, 0, i_ref, m.mvp );
      得到MVp,

      x264_mb_predict_mv_ref16x16( h, 0, i_ref, mvc, &i_mvc );
      得到鄰塊的MV、前一幀對應位置的MV,可用來預測搜索起點,加速運動估計。

      x264_me_search_ref( h, &m, mvc, i_mvc, p_halfpel_thresh );
      運動估計函數。下面將詳細討論
      再接着就是一個多參考幀的中止判斷,略。

      4、void x264_me_search_ref( x264_t *h, x264_me_t *m, int (*mvc)[2], int
      i_mvc, int *p_halfpel_thresh )

      bmx = pmx = x264_clip3( ( m->mvp[0] + 2 ) >> 2, mv_x_min, mv_x_max );
          bmy = pmy = x264_clip3( ( m->mvp[1] + 2 ) >> 2, mv_y_min, mv_y_max );
          bcost = COST_MAX;
          COST_MV( pmx, pmy );
          bcost -= p_cost_mvx[ bmx<<2 ] + p_cost_mvy[ bmy<<2 ];
          for( i = 0; i < i_mvc; i++ )
          {
              const int mx = x264_clip3( ( mvc[i][0] + 2 ) >> 2, mv_x_min,
      mv_x_max );
              const int my = x264_clip3( ( mvc[i][1] + 2 ) >> 2, mv_y_min,
      mv_y_max );
              if( mx != bmx || my != bmy )
                  COST_MV( mx, my );
          }
          COST_MV( 0, 0 );
      先檢測MVp點,再檢測其他預測矢量,最後檢測原點(0,0)。注意mvp[0
      ]保留的是1/4精度,所以除以4就變成了整象素精度。

      然後就是具體的搜索算法。代碼不貼,直接解釋吧。^_^
      case 菱形搜索:用小菱形模板反覆搜索。菱形算法還有大模板搜索,這裏沒用到。
      case 六邊形:先用六邊形模板反覆搜索,粗匹配。
                  然後用小菱形模板搜索一次,得到最終的整象素運動矢量
      case UMHexagonS:看我主頁中的註釋,呵呵,是基於JM9.5的。
      case 連續消除法(SEA) 全搜索法的快速運算。這裏不介紹了。

      if( h->mb.i_subpel_refine >= 2 )
          {
              int hpel = subpel_iterations[h->mb.i_subpel_refine][2];
              int qpel = subpel_iterations[h->mb.i_subpel_refine][3];
              refine_subpel( h, m, hpel, qpel, p_halfpel_thresh, 0 );
          }
      亞象素搜索,在x264_macroblock_analyse()函數我已經介紹過了

      5、void x264_me_refine_qpel( x264_t *h, x264_me_t *m )
      該函數一開始得到半象素、1/4象素搜索的次數(菱形小模板),分別爲hpel、q hpel,然後調用refine_subpel(),去看看!
      if( hpel_iters )
          {
              int mx = x264_clip3( m->mvp[0], h->mb.mv_min_spel[0],
      h->mb.mv_max_spel[0] );
              int my = x264_clip3( m->mvp[1], h->mb.mv_min_spel[1],
      h->mb.mv_max_spel[1] );
              if( mx != bmx || my != bmy )
                  COST_MV_SAD( mx, my, -1 );
          }
      檢測MVp的小數精度。

      for( i = hpel_iters; i > 0; i-- )
          {
              odir = bdir;
              omx = bmx;
              omy = bmy;
              COST_MV_SAD( omx, omy - 2, 0 );
              COST_MV_SAD( omx, omy + 2, 1 );
              COST_MV_SAD( omx - 2, omy, 2 );
              COST_MV_SAD( omx + 2, omy, 3 );
              if( bmx == omx && bmy == omy )
                  break;
          }
      對半象素精度進行hpel_iters次小菱形搜索。後面有1/4象素精度的qpel_iters次小模板搜索,略。

      6、static uint8_t *get_ref( uint8_t *src[4], int i_src_stride, uint8_t
      *dst,    int * i_dst_stride, int mvx,int mvy, int i_width, int i_height )
      該函數得到亞象素搜索時參考塊的指針。
      src1、src2分別指向半象素精度塊。1/4搜索時需要臨時插值,就是pixel_avg()函數的只能功能。
      值得注意的是變量correction的作用,當作是1/4插值時的偏移量吧。N個人問過我,其實結合1/4象素插值,仔細推導一下就出來了。

      7、static void x264_mb_analyse_intra( x264_t *h, x264_mb_analysis_t *a, int
      i_cost_inter )

      依次檢測Intra_16x16、Intra4x4、Intra8x8的最佳模式。
      值得注意,if(subme<=1)
      h->pixf.mbcmp是求sad值
      else
      h->pixf.mbcmp是求satd值
      另外,對與Intra4x4、Intra8x8,此時就要進行真正的變換量化、反變換反量化、重建,因爲要爲後續的塊做參考。而且,就算其係數值很小,也不能改變cbp,切記切記。
      具體分析略。

      8、static inline void x264_mb_analyse_transform( x264_t *h )
      就是對殘差進行4x4、8x8的satd變換,比較絕對和值,值較小對應的尺寸用於變換的尺寸。

發佈了11 篇原創文章 · 獲贊 8 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章