分享

x264代码剖析(十八):核心算法之滤波

 托尼虎 2018-12-25

x264代码剖析(十八):核心算法之滤波

 版权声明:本文为博主原创文章,转载请标注转载网址:http://blog.csdn.net/frd2009041510 https://blog.csdn.net/FRD2009041510/article/details/51076471

x264代码剖析(十八):核心算法之滤波

 

        H.264/MPEG-4 AVC视频编码标准中,在编解码器反变换量化后,图像会出现方块效应,主要原因是:1)基于块的帧内和帧间预测残差的DCT变换,变换系数的量化过程相对粗糙,因而反量化过程恢复的变换系数有误差,会造成在图像块边界上的视觉不连续;2)运动补偿可能是从不是同一帧的不同位置上内插样点数据复制而来,因为运动补偿块的匹配不可能是绝对准确的,所以就会在复制块的边界上产生数据不连续;3)参考帧中的存在的不连续也被复制到需要补偿的图像块内。

 

        尽管H.264采用较小的4×4变换尺寸可以降低这种不连续现象,但仍需要一个去方块滤波器,以最大程度提高编码性能。在x264中,x264_slice_write()函数中调用了x264_fdec_filter_row()的源代码。x264_fdec_filter_row()对应着x264中的滤波模块。滤波模块主要完成了下面3个方面的功能:

1)环路滤波(去块效应滤波);

2)半像素内插;

3)视频质量指标PSNRSSIM的计算。

 

        滤波模块对应的函数关系调用图如下:

 


 

        下面对x264中的滤波模块对应的主要函数分别进行分析。

 

1x264_slice_write()函数

 

        x264_slice_write()函数中调用了x264_fdec_filter_row()函数,对应于滤波模块。具体的代码分析见《x264代码剖析(九):x264_encoder_encode()函数之x264_slice's'_write()函数》。

 

2x264_fdec_filter_row()函数

 

        x264_fdec_filter_row()函数用于对一行宏块进行滤波。该函数的定义位于encoder\encoder.cx264_fdec_filter_row()完成了三步工作:

1)环路滤波(去块效应滤波)。通过调用x264_frame_deblock_row()函数实现。

2)半像素内插。通过调用x264_frame_filter()函数实现。

3)视频质量SSIMPSNR的计算。PSNR通过调用x264_pixel_ssd_wxh()函数实现,在这里只计算了SSDSSIM的计算则是通过x264_pixel_ssim_wxh()函数实现。

 

        对应的代码分析如下:

 

  1. /******************************************************************/
  2. /******************************************************************/
  3. /*
  4. ======Analysed by RuiDong Fang
  5. ======Csdn Blog:http://blog.csdn.net/frd2009041510
  6. ======Date:2016.04.06
  7. */
  8. /******************************************************************/
  9. /******************************************************************/
  10. /************====== x264_fdec_filter_row()函数 ======************/
  11. /*
  12. 功能:对一行宏块进行滤波-去块效应滤波、半像素插值、SSIM/PSNR计算等
  13. */
  14. static void x264_fdec_filter_row( x264_t *h, int mb_y, int pass )
  15. {
  16. /* mb_y is the mb to be encoded next, not the mb to be filtered here */
  17. int b_hpel = h->fdec->b_kept_as_ref;
  18. int b_deblock = h->sh.i_disable_deblocking_filter_idc != 1;
  19. int b_end = mb_y == h->i_threadslice_end;
  20. int b_measure_quality = 1;
  21. int min_y = mb_y - (1 << SLICE_MBAFF);
  22. int b_start = min_y == h->i_threadslice_start;
  23. /* Even in interlaced mode, deblocking never modifies more than 4 pixels
  24. * above each MB, as bS=4 doesn't happen for the top of interlaced mbpairs. */
  25. int minpix_y = min_y*16 - 4 * !b_start;
  26. int maxpix_y = mb_y*16 - 4 * !b_end;
  27. b_deblock &= b_hpel || h->param.b_full_recon || h->param.psz_dump_yuv;
  28. if( h->param.b_sliced_threads )
  29. {
  30. switch( pass )
  31. {
  32. /* During encode: only do deblock if asked for */
  33. default:
  34. case 0:
  35. b_deblock &= h->param.b_full_recon;
  36. b_hpel = 0;
  37. break;
  38. /* During post-encode pass: do deblock if not done yet, do hpel for all
  39. * rows except those between slices. */
  40. case 1:
  41. b_deblock &= !h->param.b_full_recon;
  42. b_hpel &= !(b_start && min_y > 0);
  43. b_measure_quality = 0;
  44. break;
  45. /* Final pass: do the rows between slices in sequence. */
  46. case 2:
  47. b_deblock = 0;
  48. b_measure_quality = 0;
  49. break;
  50. }
  51. }
  52. if( mb_y & SLICE_MBAFF )
  53. return;
  54. if( min_y < h->i_threadslice_start )
  55. return;
  56. if( b_deblock )
  57. for( int y = min_y; y < mb_y; y += (1 << SLICE_MBAFF) )
  58. x264_frame_deblock_row( h, y ); ////////////////////////////去块效应滤波
  59. /* FIXME: Prediction requires different borders for interlaced/progressive mc,
  60. * but the actual image data is equivalent. For now, maintain this
  61. * consistency by copying deblocked pixels between planes. */
  62. if( PARAM_INTERLACED && (!h->param.b_sliced_threads || pass == 1) )
  63. for( int p = 0; p < h->fdec->i_plane; p++ )
  64. for( int i = minpix_y>>(CHROMA_V_SHIFT && p); i < maxpix_y>>(CHROMA_V_SHIFT && p); i++ )
  65. memcpy( h->fdec->plane_fld[p] + i*h->fdec->i_stride[p],
  66. h->fdec->plane[p] + i*h->fdec->i_stride[p],
  67. h->mb.i_mb_width*16*sizeof(pixel) );
  68. if( h->fdec->b_kept_as_ref && (!h->param.b_sliced_threads || pass == 1) )
  69. x264_frame_expand_border( h, h->fdec, min_y );
  70. if( b_hpel )
  71. {
  72. int end = mb_y == h->mb.i_mb_height;
  73. /* Can't do hpel until the previous slice is done encoding. */
  74. if( h->param.analyse.i_subpel_refine )
  75. {
  76. x264_frame_filter( h, h->fdec, min_y, end ); ////////////////////////////半像素内插
  77. x264_frame_expand_border_filtered( h, h->fdec, min_y, end );
  78. }
  79. }
  80. if( SLICE_MBAFF && pass == 0 )
  81. for( int i = 0; i < 3; i++ )
  82. {
  83. XCHG( pixel *, h->intra_border_backup[0][i], h->intra_border_backup[3][i] );
  84. XCHG( pixel *, h->intra_border_backup[1][i], h->intra_border_backup[4][i] );
  85. }
  86. if( h->i_thread_frames > 1 && h->fdec->b_kept_as_ref )
  87. x264_frame_cond_broadcast( h->fdec, mb_y*16 + (b_end ? 10000 : -(X264_THREAD_HEIGHT << SLICE_MBAFF)) );
  88. //计算编码的质量
  89. if( b_measure_quality )
  90. {
  91. maxpix_y = X264_MIN( maxpix_y, h->param.i_height );
  92. //如果需要打印输出PSNR
  93. if( h->param.analyse.b_psnr )
  94. {
  95. //实际上是计算SSD
  96. //输出的时候调用x264_psnr()换算SSD为PSNR
  97. /**
  98. * 计算PSNR的过程
  99. *
  100. * MSE = SSD*1/(w*h)
  101. * PSNR= 10*log10(MAX^2/MSE)
  102. *
  103. * 其中MAX指的是图像的灰度级,对于8bit来说就是2^8-1=255
  104. */
  105. for( int p = 0; p < (CHROMA444 ? 3 : 1); p++ )
  106. h->stat.frame.i_ssd[p] += x264_pixel_ssd_wxh( &h->pixf,
  107. h->fdec->plane[p] + minpix_y * h->fdec->i_stride[p], h->fdec->i_stride[p], //重建帧
  108. h->fenc->plane[p] + minpix_y * h->fenc->i_stride[p], h->fenc->i_stride[p], //编码帧
  109. h->param.i_width, maxpix_y-minpix_y );
  110. if( !CHROMA444 )
  111. {
  112. uint64_t ssd_u, ssd_v;
  113. int v_shift = CHROMA_V_SHIFT;
  114. x264_pixel_ssd_nv12( &h->pixf,
  115. h->fdec->plane[1] + (minpix_y>>v_shift) * h->fdec->i_stride[1], h->fdec->i_stride[1],
  116. h->fenc->plane[1] + (minpix_y>>v_shift) * h->fenc->i_stride[1], h->fenc->i_stride[1],
  117. h->param.i_width>>1, (maxpix_y-minpix_y)>>v_shift, &ssd_u, &ssd_v );
  118. h->stat.frame.i_ssd[1] += ssd_u;
  119. h->stat.frame.i_ssd[2] += ssd_v;
  120. }
  121. }
  122. //如果需要打印输出SSIM
  123. if( h->param.analyse.b_ssim )
  124. {
  125. int ssim_cnt;
  126. x264_emms();
  127. /* offset by 2 pixels to avoid alignment of ssim blocks with dct blocks,
  128. * and overlap by 4 */
  129. minpix_y += b_start ? 2 : -6;
  130. h->stat.frame.f_ssim +=
  131. x264_pixel_ssim_wxh( &h->pixf,
  132. h->fdec->plane[0] + 2+minpix_y*h->fdec->i_stride[0], h->fdec->i_stride[0], //重建帧
  133. h->fenc->plane[0] + 2+minpix_y*h->fenc->i_stride[0], h->fenc->i_stride[0], //编码帧
  134. h->param.i_width-2, maxpix_y-minpix_y, h->scratch_buffer, &ssim_cnt );
  135. h->stat.frame.i_ssim_cnt += ssim_cnt;
  136. }
  137. }
  138. }


3x264_frame_deblock_row()函数

 

        x264_frame_deblock_row()用于进行环路滤波(去块效应滤波)。该函数的定义位于common\deblock.cx264_frame_deblock_row()中有一个很长的宏定义“FILTER()”定义了函数调用的方式。FILTER( intra, dir, edge, qp, chroma_qp )中:

1)“intra”指定了是普通滤波(Bs=123)还是强滤波(Bs=4);

2)“dir”指定了滤波器的方向。0为水平滤波器(垂直边界),1为垂直滤波器(水平边界);

(3)“edge”指定了边界的位置。“0”,“1”,“2”,“3”分别代表了水平(或者垂直)的4条边界

 

        对应的代码分析如下:

 

  1. /************====== x264_frame_deblock_row()函数 ======************/
  2. /*
  3. 功能:去块效应滤波
  4. */
  5. void x264_frame_deblock_row( x264_t *h, int mb_y )
  6. {
  7. int b_interlaced = SLICE_MBAFF;
  8. int a = h->sh.i_alpha_c0_offset - QP_BD_OFFSET;
  9. int b = h->sh.i_beta_offset - QP_BD_OFFSET;
  10. int qp_thresh = 15 - X264_MIN( a, b ) - X264_MAX( 0, h->pps->i_chroma_qp_index_offset );
  11. int stridey = h->fdec->i_stride[0];
  12. int strideuv = h->fdec->i_stride[1];
  13. int chroma444 = CHROMA444;
  14. int chroma_height = 16 >> CHROMA_V_SHIFT;
  15. intptr_t uvdiff = chroma444 ? h->fdec->plane[2] - h->fdec->plane[1] : 1;
  16. for( int mb_x = 0; mb_x < h->mb.i_mb_width; mb_x += (~b_interlaced | mb_y)&1, mb_y ^= b_interlaced )
  17. {
  18. x264_prefetch_fenc( h, h->fdec, mb_x, mb_y );
  19. x264_macroblock_cache_load_neighbours_deblock( h, mb_x, mb_y );
  20. int mb_xy = h->mb.i_mb_xy;
  21. int transform_8x8 = h->mb.mb_transform_size[mb_xy];
  22. int intra_cur = IS_INTRA( h->mb.type[mb_xy] );
  23. uint8_t (*bs)[8][4] = h->deblock_strength[mb_y&1][h->param.b_sliced_threads?mb_xy:mb_x];
  24. //找到像素数据(宏块的大小是16x16)
  25. pixel *pixy = h->fdec->plane[0] + 16*mb_y*stridey + 16*mb_x;
  26. pixel *pixuv = h->fdec->plane[1] + chroma_height*mb_y*strideuv + 16*mb_x;
  27. if( mb_y & MB_INTERLACED )
  28. {
  29. pixy -= 15*stridey;
  30. pixuv -= (chroma_height-1)*strideuv;
  31. }
  32. int stride2y = stridey << MB_INTERLACED;
  33. int stride2uv = strideuv << MB_INTERLACED;
  34. //QP,用于计算环路滤波的门限值alpha和beta
  35. int qp = h->mb.qp[mb_xy];
  36. int qpc = h->chroma_qp_table[qp];
  37. int first_edge_only = (h->mb.partition[mb_xy] == D_16x16 && !h->mb.cbp[mb_xy] && !intra_cur) || qp <= qp_thresh;
  38. /*
  39. * 滤波顺序如下所示(大方框代表16x16块)
  40. *
  41. * +--4-+--4-+--4-+--4-+
  42. * 0 1 2 3 |
  43. * +--5-+--5-+--5-+--5-+
  44. * 0 1 2 3 |
  45. * +--6-+--6-+--6-+--6-+
  46. * 0 1 2 3 |
  47. * +--7-+--7-+--7-+--7-+
  48. * 0 1 2 3 |
  49. * +----+----+----+----+
  50. *
  51. */
  52. //一个比较长的宏,用于进行环路滤波
  53. //根据不同的情况传递不同的参数
  54. //几个参数的含义:
  55. //intra:
  56. //为“_intra”的时候:
  57. //其中的“deblock_edge##intra()”展开为函数deblock_edge_intra()
  58. //其中的“h->loopf.deblock_luma##intra[dir]”展开为强滤波汇编函数h->loopf.deblock_luma_intra[dir]()
  59. //为“”(空),其中的“deblock_edge##intra()”展开为函数deblock_edge()
  60. //其中的“h->loopf.deblock_luma##intra[dir]”展开为普通滤波汇编函数h->loopf.deblock_luma[dir]()
  61. //dir:
  62. //决定了滤波的方向:0为水平滤波器(垂直边界),1为垂直滤波器(水平边界)
  63. #define FILTER( intra, dir, edge, qp, chroma_qp )\
  64. do\
  65. {\
  66. if( !(edge & 1) || !transform_8x8 )\
  67. {\
  68. deblock_edge##intra( h, pixy + 4*edge*(dir?stride2y:1),\
  69. stride2y, bs[dir][edge], qp, a, b, 0,\
  70. h->loopf.deblock_luma##intra[dir] );\
  71. if( CHROMA_FORMAT == CHROMA_444 )\
  72. {\
  73. deblock_edge##intra( h, pixuv + 4*edge*(dir?stride2uv:1),\
  74. stride2uv, bs[dir][edge], chroma_qp, a, b, 0,\
  75. h->loopf.deblock_luma##intra[dir] );\
  76. deblock_edge##intra( h, pixuv + uvdiff + 4*edge*(dir?stride2uv:1),\
  77. stride2uv, bs[dir][edge], chroma_qp, a, b, 0,\
  78. h->loopf.deblock_luma##intra[dir] );\
  79. }\
  80. else if( CHROMA_FORMAT == CHROMA_420 && !(edge & 1) )\
  81. {\
  82. deblock_edge##intra( h, pixuv + edge*(dir?2*stride2uv:4),\
  83. stride2uv, bs[dir][edge], chroma_qp, a, b, 1,\
  84. h->loopf.deblock_chroma##intra[dir] );\
  85. }\
  86. }\
  87. if( CHROMA_FORMAT == CHROMA_422 && (dir || !(edge & 1)) )\
  88. {\
  89. deblock_edge##intra( h, pixuv + edge*(dir?4*stride2uv:4),\
  90. stride2uv, bs[dir][edge], chroma_qp, a, b, 1,\
  91. h->loopf.deblock_chroma##intra[dir] );\
  92. }\
  93. } while(0)
  94. if( h->mb.i_neighbour & MB_LEFT )
  95. {
  96. if( b_interlaced && h->mb.field[h->mb.i_mb_left_xy[0]] != MB_INTERLACED )
  97. {
  98. int luma_qp[2];
  99. int chroma_qp[2];
  100. int left_qp[2];
  101. x264_deblock_inter_t luma_deblock = h->loopf.deblock_luma_mbaff;
  102. x264_deblock_inter_t chroma_deblock = h->loopf.deblock_chroma_mbaff;
  103. x264_deblock_intra_t luma_intra_deblock = h->loopf.deblock_luma_intra_mbaff;
  104. x264_deblock_intra_t chroma_intra_deblock = h->loopf.deblock_chroma_intra_mbaff;
  105. int c = chroma444 ? 0 : 1;
  106. left_qp[0] = h->mb.qp[h->mb.i_mb_left_xy[0]];
  107. luma_qp[0] = (qp + left_qp[0] + 1) >> 1;
  108. chroma_qp[0] = (qpc + h->chroma_qp_table[left_qp[0]] + 1) >> 1;
  109. if( intra_cur || IS_INTRA( h->mb.type[h->mb.i_mb_left_xy[0]] ) )
  110. {
  111. deblock_edge_intra( h, pixy, 2*stridey, bs[0][0], luma_qp[0], a, b, 0, luma_intra_deblock );
  112. deblock_edge_intra( h, pixuv, 2*strideuv, bs[0][0], chroma_qp[0], a, b, c, chroma_intra_deblock );
  113. if( chroma444 )
  114. deblock_edge_intra( h, pixuv + uvdiff, 2*strideuv, bs[0][0], chroma_qp[0], a, b, c, chroma_intra_deblock );
  115. }
  116. else
  117. {
  118. deblock_edge( h, pixy, 2*stridey, bs[0][0], luma_qp[0], a, b, 0, luma_deblock );
  119. deblock_edge( h, pixuv, 2*strideuv, bs[0][0], chroma_qp[0], a, b, c, chroma_deblock );
  120. if( chroma444 )
  121. deblock_edge( h, pixuv + uvdiff, 2*strideuv, bs[0][0], chroma_qp[0], a, b, c, chroma_deblock );
  122. }
  123. int offy = MB_INTERLACED ? 4 : 0;
  124. int offuv = MB_INTERLACED ? 4-CHROMA_V_SHIFT : 0;
  125. left_qp[1] = h->mb.qp[h->mb.i_mb_left_xy[1]];
  126. luma_qp[1] = (qp + left_qp[1] + 1) >> 1;
  127. chroma_qp[1] = (qpc + h->chroma_qp_table[left_qp[1]] + 1) >> 1;
  128. if( intra_cur || IS_INTRA( h->mb.type[h->mb.i_mb_left_xy[1]] ) )
  129. {
  130. deblock_edge_intra( h, pixy + (stridey<<offy), 2*stridey, bs[0][4], luma_qp[1], a, b, 0, luma_intra_deblock );
  131. deblock_edge_intra( h, pixuv + (strideuv<<offuv), 2*strideuv, bs[0][4], chroma_qp[1], a, b, c, chroma_intra_deblock );
  132. if( chroma444 )
  133. deblock_edge_intra( h, pixuv + uvdiff + (strideuv<<offuv), 2*strideuv, bs[0][4], chroma_qp[1], a, b, c, chroma_intra_deblock );
  134. }
  135. else
  136. {
  137. deblock_edge( h, pixy + (stridey<<offy), 2*stridey, bs[0][4], luma_qp[1], a, b, 0, luma_deblock );
  138. deblock_edge( h, pixuv + (strideuv<<offuv), 2*strideuv, bs[0][4], chroma_qp[1], a, b, c, chroma_deblock );
  139. if( chroma444 )
  140. deblock_edge( h, pixuv + uvdiff + (strideuv<<offuv), 2*strideuv, bs[0][4], chroma_qp[1], a, b, c, chroma_deblock );
  141. }
  142. }
  143. else
  144. {
  145. //左边宏块的qp
  146. int qpl = h->mb.qp[h->mb.i_mb_xy-1];
  147. int qp_left = (qp + qpl + 1) >> 1;
  148. int qpc_left = (qpc + h->chroma_qp_table[qpl] + 1) >> 1;
  149. //Intra宏块左边宏块的qp
  150. int intra_left = IS_INTRA( h->mb.type[h->mb.i_mb_xy-1] );
  151. int intra_deblock = intra_cur || intra_left;
  152. /* Any MB that was coded, or that analysis decided to skip, has quality commensurate with its QP.
  153. * But if deblocking affects neighboring MBs that were force-skipped, blur might accumulate there.
  154. * So reset their effective QP to max, to indicate that lack of guarantee. */
  155. if( h->fdec->mb_info && M32( bs[0][0] ) )
  156. {
  157. #define RESET_EFFECTIVE_QP(xy) h->fdec->effective_qp[xy] |= 0xff * !!(h->fdec->mb_info[xy] & X264_MBINFO_CONSTANT);
  158. RESET_EFFECTIVE_QP(mb_xy);
  159. RESET_EFFECTIVE_QP(h->mb.i_mb_left_xy[0]);
  160. }
  161. if( intra_deblock )
  162. //【0】强滤波,水平滤波器(垂直边界)
  163. FILTER( _intra, 0, 0, qp_left, qpc_left );
  164. else
  165. //【0】普通滤波,水平滤波器(垂直边界)
  166. FILTER( , 0, 0, qp_left, qpc_left );
  167. }
  168. }
  169. if( !first_edge_only )
  170. {
  171. //普通滤波,水平滤波器(垂直边界)
  172. FILTER( , 0, 1, qp, qpc );//【1】
  173. FILTER( , 0, 2, qp, qpc );//【2】
  174. FILTER( , 0, 3, qp, qpc );//【3】
  175. }
  176. if( h->mb.i_neighbour & MB_TOP )
  177. {
  178. if( b_interlaced && !(mb_y&1) && !MB_INTERLACED && h->mb.field[h->mb.i_mb_top_xy] )
  179. {
  180. int mbn_xy = mb_xy - 2 * h->mb.i_mb_stride;
  181. for( int j = 0; j < 2; j++, mbn_xy += h->mb.i_mb_stride )
  182. {
  183. int qpt = h->mb.qp[mbn_xy];
  184. int qp_top = (qp + qpt + 1) >> 1;
  185. int qpc_top = (qpc + h->chroma_qp_table[qpt] + 1) >> 1;
  186. int intra_top = IS_INTRA( h->mb.type[mbn_xy] );
  187. if( intra_cur || intra_top )
  188. M32( bs[1][4*j] ) = 0x03030303;
  189. // deblock the first horizontal edge of the even rows, then the first horizontal edge of the odd rows
  190. deblock_edge( h, pixy + j*stridey, 2* stridey, bs[1][4*j], qp_top, a, b, 0, h->loopf.deblock_luma[1] );
  191. if( chroma444 )
  192. {
  193. deblock_edge( h, pixuv + j*strideuv, 2*strideuv, bs[1][4*j], qpc_top, a, b, 0, h->loopf.deblock_luma[1] );
  194. deblock_edge( h, pixuv + uvdiff + j*strideuv, 2*strideuv, bs[1][4*j], qpc_top, a, b, 0, h->loopf.deblock_luma[1] );
  195. }
  196. else
  197. deblock_edge( h, pixuv + j*strideuv, 2*strideuv, bs[1][4*j], qpc_top, a, b, 1, h->loopf.deblock_chroma[1] );
  198. }
  199. }
  200. else
  201. {
  202. int qpt = h->mb.qp[h->mb.i_mb_top_xy];
  203. int qp_top = (qp + qpt + 1) >> 1;
  204. int qpc_top = (qpc + h->chroma_qp_table[qpt] + 1) >> 1;
  205. int intra_top = IS_INTRA( h->mb.type[h->mb.i_mb_top_xy] );
  206. int intra_deblock = intra_cur || intra_top;
  207. /* This edge has been modified, reset effective qp to max. */
  208. if( h->fdec->mb_info && M32( bs[1][0] ) )
  209. {
  210. RESET_EFFECTIVE_QP(mb_xy);
  211. RESET_EFFECTIVE_QP(h->mb.i_mb_top_xy);
  212. }
  213. if( (!b_interlaced || (!MB_INTERLACED && !h->mb.field[h->mb.i_mb_top_xy])) && intra_deblock )
  214. {
  215. FILTER( _intra, 1, 0, qp_top, qpc_top );//【4】普通滤波,垂直滤波器(水平边界)
  216. }
  217. else
  218. {
  219. if( intra_deblock )
  220. M32( bs[1][0] ) = 0x03030303;
  221. FILTER( , 1, 0, qp_top, qpc_top );//【4】普通滤波,垂直滤波器(水平边界)
  222. }
  223. }
  224. }
  225. if( !first_edge_only )
  226. {
  227. //普通滤波,垂直滤波器(水平边界)
  228. FILTER( , 1, 1, qp, qpc );//【5】
  229. FILTER( , 1, 2, qp, qpc );//【6】
  230. FILTER( , 1, 3, qp, qpc );//【7】
  231. }
  232. #undef FILTER
  233. }
  234. }


4x264_frame_filter()函数

 

        x264_frame_filter()用于完成半像素内插的工作。该函数的定义位于common\mc.cx264_frame_filter()调用了汇编函数h->mc.hpel_filter()完成了半像素内插的工作。经过汇编半像素内插函数处理之后,得到的水平半像素内差点存储在x264_frame_tfiltered[][1]中,垂直半像素内差点存储在x264_frame_tfiltered[][2]中,对角线半像素内差点存储在x264_frame_tfiltered[][3]中(整像素点存储在x264_frame_tfiltered[][0]中)。

 

        对应的代码分析如下:

 

  1. /************====== x264_frame_filter()函数 ======************/
  2. /*
  3. 功能:半像素内插
  4. */
  5. void x264_frame_filter( x264_t *h, x264_frame_t *frame, int mb_y, int b_end )
  6. {
  7. const int b_interlaced = PARAM_INTERLACED;
  8. int start = mb_y*16 - 8; // buffer = 4 for deblock + 3 for 6tap, rounded to 8
  9. int height = (b_end ? frame->i_lines[0] + 16*PARAM_INTERLACED : (mb_y+b_interlaced)*16) + 8;
  10. if( mb_y & b_interlaced )
  11. return;
  12. for( int p = 0; p < (CHROMA444 ? 3 : 1); p++ )
  13. {
  14. int stride = frame->i_stride[p];
  15. const int width = frame->i_width[p];
  16. int offs = start*stride - 8; // buffer = 3 for 6tap, aligned to 8 for simd
  17. //半像素内插
  18. if( !b_interlaced || h->mb.b_adaptive_mbaff )
  19. h->mc.hpel_filter(
  20. frame->filtered[p][1] + offs,//水平半像素内插
  21. frame->filtered[p][2] + offs,//垂直半像素内插
  22. frame->filtered[p][3] + offs,//中间半像素内插
  23. frame->plane[p] + offs,
  24. stride, width + 16, height - start,
  25. h->scratch_buffer );
  26. if( b_interlaced )
  27. {
  28. /* MC must happen between pixels in the same field. */
  29. stride = frame->i_stride[p] << 1;
  30. start = (mb_y*16 >> 1) - 8;
  31. int height_fld = ((b_end ? frame->i_lines[p] : mb_y*16) >> 1) + 8;
  32. offs = start*stride - 8;
  33. for( int i = 0; i < 2; i++, offs += frame->i_stride[p] )
  34. {
  35. h->mc.hpel_filter(
  36. frame->filtered_fld[p][1] + offs,
  37. frame->filtered_fld[p][2] + offs,
  38. frame->filtered_fld[p][3] + offs,
  39. frame->plane_fld[p] + offs,
  40. stride, width + 16, height_fld - start,
  41. h->scratch_buffer );
  42. }
  43. }
  44. }
  45. /* generate integral image:
  46. * frame->integral contains 2 planes. in the upper plane, each element is
  47. * the sum of an 8x8 pixel region with top-left corner on that point.
  48. * in the lower plane, 4x4 sums (needed only with --partitions p4x4). */
  49. if( frame->integral )
  50. {
  51. int stride = frame->i_stride[0];
  52. if( start < 0 )
  53. {
  54. memset( frame->integral - PADV * stride - PADH, 0, stride * sizeof(uint16_t) );
  55. start = -PADV;
  56. }
  57. if( b_end )
  58. height += PADV-9;
  59. for( int y = start; y < height; y++ )
  60. {
  61. pixel *pix = frame->plane[0] + y * stride - PADH;
  62. uint16_t *sum8 = frame->integral + (y+1) * stride - PADH;
  63. uint16_t *sum4;
  64. if( h->frames.b_have_sub8x8_esa )
  65. {
  66. h->mc.integral_init4h( sum8, pix, stride );
  67. sum8 -= 8*stride;
  68. sum4 = sum8 + stride * (frame->i_lines[0] + PADV*2);
  69. if( y >= 8-PADV )
  70. h->mc.integral_init4v( sum8, sum4, stride );
  71. }
  72. else
  73. {
  74. h->mc.integral_init8h( sum8, pix, stride );
  75. if( y >= 8-PADV )
  76. h->mc.integral_init8v( sum8-8*stride, stride );
  77. }
  78. }
  79. }
  80. }


5x264_pixel_ssd_wxh()函数

 

        x264_pixel_ssd_wxh()用于计算SSD(用于以后计算PSNR)。该函数的定义位于common\pixel.cx264_pixel_ssd_wxh()在计算大部分块的SSD的时候是以16x16的块为单位;当宽度不是16的整数倍的时候,在左侧边缘处不足16像素的地方使用了8x16的块进行计算;当高度不是16的整数倍的时候,在下方不足16像素的地方使用了8x8的块进行计算;当宽高不是8的整数倍的时候,则再单独计算。

 

        对应的代码分析如下:

 

  1. /************====== x264_pixel_ssd_wxh()函数 ======************/
  2. /*
  3. * 功能:计算SSD(可用于计算PSNR)
  4. * pix1: 受损数据
  5. * pix2: 原始数据
  6. * i_width: 图像宽
  7. * i_height: 图像高
  8. */
  9. uint64_t x264_pixel_ssd_wxh( x264_pixel_function_t *pf, pixel *pix1, intptr_t i_pix1,
  10. pixel *pix2, intptr_t i_pix2, int i_width, int i_height )
  11. {
  12. uint64_t i_ssd = 0;//计算结果都累加到i_ssd变量上
  13. int y;
  14. int align = !(((intptr_t)pix1 | (intptr_t)pix2 | i_pix1 | i_pix2) & 15);
  15. #define SSD(size) i_ssd += pf->ssd[size]( pix1 + y*i_pix1 + x, i_pix1, \
  16. pix2 + y*i_pix2 + x, i_pix2 );
  17. /*
  18. * SSD计算过程:
  19. * 从左上角开始,绝大部分块使用16x16的SSD计算
  20. * 右边边界部分可能用16x8的SSD计算
  21. * 下边边界可能用8x8的SSD计算
  22. * 注意:这么做主要是出于汇编优化的考虑
  23. *
  24. * +----+----+----+----+----+----+----+----+----+----+-+
  25. * | | | |
  26. * + + + +
  27. * | | | |
  28. * + 16x16 + 16x16 + 8x16 +
  29. * | | | |
  30. * + + + +
  31. * | | | |
  32. * +----+----+----+----+----+----+----+----+----+----+-+
  33. * | |
  34. * + 8x8 +
  35. * | |
  36. * +----+----+
  37. * + +
  38. */
  39. for( y = 0; y < i_height-15; y += 16 )
  40. {
  41. int x = 0;
  42. if( align )//大部分使用16x16的SSD
  43. for( ; x < i_width-15; x += 16 )
  44. SSD(PIXEL_16x16);
  45. for( ; x < i_width-7; x += 8 )//右边边缘部分可能用8x16的SSD
  46. SSD(PIXEL_8x16);
  47. }
  48. if( y < i_height-7 )//下边边缘部分可能用到8x8的SSD
  49. for( int x = 0; x < i_width-7; x += 8 )
  50. SSD(PIXEL_8x8);
  51. #undef SSD
  52. #define SSD1 { int d = pix1[y*i_pix1+x] - pix2[y*i_pix2+x]; i_ssd += d*d; }
  53. if( i_width & 7 )//如果像素不是16/8的整数倍,边界上的点需要单独算
  54. {
  55. for( y = 0; y < (i_height & ~7); y++ )
  56. for( int x = i_width & ~7; x < i_width; x++ )
  57. SSD1;
  58. }
  59. if( i_height & 7 )
  60. {
  61. for( y = i_height & ~7; y < i_height; y++ )
  62. for( int x = 0; x < i_width; x++ )
  63. SSD1;
  64. }
  65. #undef SSD1
  66. return i_ssd;
  67. }


6x264_pixel_ssim_wxh()函数

 

        x264_pixel_ssim_wxh()用于计算SSIM。该函数的定义位于common\pixel.cx264_pixel_ssim_wxh()中是按照4x4的块对像素进行处理的。使用sum1[]保存上一行块的信息sum0[]保存当前一行块的信息信息包含4个元素:

s1:原始像素之和;

s2:受损像素之和;

ss:原始像素平方之和+受损像素平方之和;

s12:原始像素*受损像素的值的和。

 

        对应的代码分析如下:

 

  1. /************====== x264_pixel_ssd_wxh()函数 ======************/
  2. /*
  3. * 功能:计算SSIM
  4. * pix1: 受损数据
  5. * pix2: 原始数据
  6. * i_width: 图像宽
  7. * i_height: 图像高
  8. */
  9. float x264_pixel_ssim_wxh( x264_pixel_function_t *pf,
  10. pixel *pix1, intptr_t stride1,
  11. pixel *pix2, intptr_t stride2,
  12. int width, int height, void *buf, int *cnt )
  13. {
  14. /*
  15. * SSIM公式
  16. * SSIM = ((2*ux*uy+C1)(2*σxy+C2))/((ux^2+uy^2+C1)(σx^2+σy^2+C2))
  17. *
  18. * 其中
  19. * ux=E(x)
  20. * uy=E(y)
  21. * σxy=cov(x,y)=E(XY)-ux*uy
  22. * σx^2=E(x^2)-E(x)^2
  23. *
  24. */
  25. int z = 0;
  26. float ssim = 0.0;
  27. //这是数组指针,注意和指针数组的区别
  28. //数组指针就是指向数组的指针
  29. int (*sum0)[4] = buf;
  30. /*
  31. * sum0是一个数组指针,其中存储了一个4元素数组的地址
  32. * 换句话说,sum0[]中每一个元素对应一个4x4块的信息(该信息包含4个元素)。
  33. *
  34. * 4个元素中:
  35. * [0]原始像素之和
  36. * [1]受损像素之和
  37. * [2]原始像素平方之和+受损像素平方之和
  38. * [3]原始像素*受损像素的值的和
  39. *
  40. */
  41. int (*sum1)[4] = sum0 + (width >> 2) + 3;
  42. //除以4,编程以“4x4块”为单位
  43. width >>= 2;
  44. height >>= 2;
  45. //以8*8的块为单位计算SSIM值。然后以4个像素为step滑动窗口
  46. for( int y = 1; y < height; y++ )
  47. {
  48. //下面这个循环,只有在第一次执行的时候执行2次,处理第1行和第2行的块
  49. //后面的都只会执行一次
  50. for( ; z <= y; z++ )
  51. {
  52. //执行完XCHG()之后,sum1[]存储上1行块的值(在上面),而sum0[]等待ssim_4x4x2_core()计算当前行的值(在下面)
  53. XCHG( void*, sum0, sum1 );
  54. //获取4x4块的信息(这里并没有代入公式计算SSIM结果)
  55. //结果存储在sum0[]中。从左到右每个4x4的块依次存储在sum0[0],sum0[1],sum0[2]...
  56. //每次x前进2个块
  57. /*
  58. * ssim_4x4x2_core():计算2个4x4块
  59. * +----+----+
  60. * | | |
  61. * +----+----+
  62. */
  63. for( int x = 0; x < width; x+=2 )
  64. pf->ssim_4x4x2_core( &pix1[4*(x+z*stride1)], stride1, &pix2[4*(x+z*stride2)], stride2, &sum0[x] );
  65. }
  66. //x每次增加4,前进4个块
  67. //以8*8的块为单位计算
  68. /*
  69. * sum1[]为上一行4x4块信息,sum0[]为当前行4x4块信息
  70. * 示例(line以4x4块为单位)
  71. * 第1次运行
  72. * +----+----+----+----+
  73. * 1line | sum1[]
  74. * +----+----+----+----+
  75. * 2line | sum0[]
  76. * +----+----+----+----+
  77. *
  78. * 第2次运行
  79. * +
  80. * 1line |
  81. * +----+----+----+----+
  82. * 2line | sum1[]
  83. * +----+----+----+----+
  84. * 3line | sum0[]
  85. * +----+----+----+----+
  86. */
  87. for( int x = 0; x < width-1; x += 4 )
  88. ssim += pf->ssim_end4( sum0+x, sum1+x, X264_MIN(4,width-x-1) );
  89. }
  90. *cnt = (height-1) * (width-1);
  91. return ssim;
  92. }

 大笑滤波模块的主要代码分析就到这儿,其实中间有很多实用且有效的函数块,待后面用到时更新。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多