X264的帧间预测

  • 格式:doc
  • 大小:66.50 KB
  • 文档页数:6

【转】X264的帧间预测 已有 162 次阅读 2009-06-15 10:06

【转】X264的帧间预测

1、x264学习笔记(9)--x264中16x16运动搜索过程 函数实现是函数 static void x264_mb_analyse_inter_p16x16( x264_t *h, x264_mb_analysis_t *a ) 1、大循环是参考帧的循环,从最近的一个参考帧开始搜索,一直到最远的一个参考帧;

2、调用x264_mb_predict_mv_16x16函数,以上、右上、左块运动矢量的中值m.mvp作为候 选运动矢量。

3、调用x264_mb_predict_mv_ref16x16函数,寻找其它候选运动矢量。这些候选者包括: 空间相邻的左、左上、上、右上块的MV;第0个参考帧中的当前块、右边块、下边快运动矢 量乘以时间差权重。

4、调用x264_me_search_ref进行运动搜索。搜索时先从所有候选运动矢量中选出最佳的起 点,然后使用小钻石法、六边形法、UMH或者全搜索搜索出最佳的整像素位置。

5、x264_me_search_ref调用refine_subpel进行1/2和1/4运动搜索。两者都使用小钻石法 。

6、搜索出最佳运动矢量后,如果当前是最近一个参考帧,而且最佳SA(T)D小与检测门限, 则尝试对其进行P_SKIP编码。

7、保存搜索结果。

2、x264学习笔记(10)---分像素的运动估计总结

得到分像素的值函数是下面两个函数,对照着 (1) 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 ) { int qpel_idx = ((mvy&3)<<2) + (mvx&3); //取出运动矢量的分像素部分。 int offset = (mvy>>2)*i_src_stride + (mvx>>2); //偏移到所选的整像素点 uint8_t *src1 = src[hpel_ref0[qpel_idx]] + offset + ((mvy&3) == 3) * i_src_stride; /*src1和src2都分别指向的是1/2像素块,关键是这个hpel_ref0[qpel_idx]和hpel_ref1[qpel_idx],下面将详细介绍。 注意一点就是参考帧定义了uint8_t *p_fref[2][32][4+2]; /* last: lN, lH, lV, lHV, cU, cV */ 这里面的 4+2 的这个2代表色度,而这个4分别代表整像素,在整像素水平右边的1/2像素,在整像素垂直下面的1/2像素和整像素右下角的1/2像素。1/2像素的值已经在前面函数里面插值存好了,只要调用就可以了,而如果要进行1/4像素估计,要临时插值。现在这个函数 get_ref 中,src[0]、src[1]、src[2]、src[3]这传进来的就是分别是 lN, lH, lV, lHV */ if( qpel_idx & 5 ) /* qpel interpolation needed */ { uint8_t *src2 = src[hpel_ref1[qpel_idx]] + offset + ((mvx&3) == 3); pixel_avg( dst, *i_dst_stride, src1, i_src_stride, src2, i_src_stride, i_width, i_height );//1/4搜索时需要临时插值函数 return dst; } else { *i_dst_stride = i_src_stride; return src1; } } 按照 毕厚杰 的《新一代视频压缩编码标准——H.264/AVC》关于运动矢量那一节的介绍。看图6.22 那四个像素点,G为整像素点 b、h、i分别是lH, lV, lHV,也就是水平,垂直和对角线的值。 G b h i 对应为 src[0] src[1] src[2] src[3] 现在看这两个数组 static const int hpel_ref0[16] = {0,1,1,1,0,1,1,1,2,3,3,3,0,1,1,1}; static const int hpel_ref1[16] = {0,0,0,0,2,2,3,2,2,2,3,2,2,2,3,2}; hpel_ref0,hpel_ref1这两个参数的解释详见浅析x264中的hpel_ref0与hpel_ref1数组的用法.doc 也按像素的平面图画出来的话 src[hpel_ref0[qpel_idx]]为 0 1 1 1 0 1 1 1 2 3 3 3 0 1 1 1 src[hpel_ref1[qpel_idx]]为 0 0 0 0 2 2 3 2 2 2 3 2 2 2 3 2 这上面的数字 0、1、2、3分别代表 整像素、水平1/2像素值、垂直1/2像素值 和对角线1/2像素值,也就是毕厚杰书中的 G、b、h、I 。这里要注意src[hpel_ref0[qpel_idx]]最后一行的 0 1 1 1 和src[hpel_ref1[qpel_idx]]最右边一列0 2 2 2不是当前的整像素0的1/2像素,而分别是其下面和右边一个整像素的对应的1/2像素值,因为 ((mvy&3) == 3) * i_src_stride 和((mvx&3) == 3)。 为什么要这么来排,是因为要根据1/4像素是通过1/2像素线性插值的公式来的,具体看下面这个函数。 (2) static inline void pixel_avg( uint8_t *dst, int i_dst_stride, uint8_t *src1, int i_src1_stride, uint8_t *src2, int i_src2_stride, int i_width, int i_height ) { //1/4搜索时需要临时插值函数 int x, y; for( y = 0; y < i_height; y++ ) { for( x = 0; x < i_width; x++ ) { dst[x] = ( src1[x] + src2[x] + 1 ) >> 1; //利用相邻半像素和两个像素取平均插值 } dst += i_dst_stride; src1 += i_src1_stride; src2 += i_src2_stride; } } 不过最后我有个疑问,那就是1/4插值后,应该原来的1/2 值保持不变的.但是分析发现,这个 b 、h、 i 这三个1/2像素中,h和i是不变的,不过 b会发生变化. 个人觉得 static const int hpel_ref1[16] = {0,0,0,0,2,2,3,2,2,2,3,2,2,2,3,2};如果改为 static const int hpel_ref1[16] = {0,0,1,0,2,2,3,2,2,2,3,2,2,2,3,2};则 b也不会发生变化. 所以这里打个问号?

3、x264学习笔记(11)---关于运动矢量MV不传输的问题

昨天看到H.264乐园群里面有人在讨论运动矢量MV不用传输的问题,就去看了下x264源代码,作个总结

编码端: 运动估计搜索得到的运动矢量MV是不需要传送的,需要传送的是MVD,MVD即运动矢量MV(运动估计得到)和运动矢量的预测矢量MVP(预测得到)的差值。 MVD = MV - MVP

解码端:通过预测得到MVP,将传输过来的MVD和MVP相加得到 MV = MVD + MVP,然后用这个MV去参考帧中获取预测象素值,最后把这个预测值和残差加一起, 作为重构像素值

x264中把这个过程放在了熵编码阶段,在这个函数里 x264_macroblock_write_cabac

MVD并保存下来以备传输的函数如下: static inline void x264_cabac_mb_mvd( x264_t *h, x264_cabac_t *cb, int i_list, int idx, int width, int height ) { int mvp[2]; int mdx, mdy;

/* Calculate mvd */ x264_mb_predict_mv( h, i_list, idx, width, mvp ); //预测MVP mdx = h->mb.cache.mv[i_list][x264_scan8[idx]][0] - mvp[0]; //计算MVD mdy = h->mb.cache.mv[i_list][x264_scan8[idx]][1] - mvp[1];

/* encode */ x264_cabac_mb_mvd_cpn( h, cb, i_list, idx, 0, mdx ); //编码 x264_cabac_mb_mvd_cpn( h, cb, i_list, idx, 1, mdy );

/* save value */ x264_macroblock_cache_mvd( h, block_idx_x[idx], block_idx_y[idx], width, height, i_list, mdx, mdy ); // 保存MVD }

4、firstime

MV预测过程详解(附图)

===========第一步:确定相邻块===========