【转】X264的帧间预测
来源:互联网 发布:java图片上传原理 编辑:程序博客网 时间:2024/05/16 18:47
【转】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_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};
也按像素的平面图画出来的话
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预测过程详解(附图)
===========第一步:确定相邻块===========
MV 预测以宏块分割(或亚宏块分割,如果宏块存在亚分割)为单位,同一个宏块分割(或亚宏块分割)内所有 4*4 块 MV 预测值相同。以每个宏块分割(或亚宏块分割)的左上角像素 pixel1 和右上角像素 pixel2 为参考点来确定相邻块则:
pixel1 左侧相邻像素所在 4*4 块为当前宏块分割(或亚宏块分割)的相邻块 A
pixel1 上方相邻像素所在 4*4 块为当前宏块分割(或亚宏块分割)的相邻块 B
pixel2 右上对角线像素所在 4*4 块为当前宏块分割(或亚宏块分割)的相邻块 C
pixel1 左上对角线像素所在 4*4 块为当前宏块分割(或亚宏块分割)的相邻块 D
图片附件: MV预测示意图.JPG (2006-9-29 11:14 AM, 85.25 K)
以最复杂的 8*8 宏块分割类型为例(此时只存在亚宏块分割),分析如下:
假设图中黑色框表示宏块、每个绿色框表示一个 4*4 块、每个红色框表示一个 8*8 块。当前宏块的宏块分割模式为 8*8(如图中红色线),其亚宏块分割模式分别为:第一个 8*8 块为 8*8,第二个 8*8 块为 4*4(如图中蓝色线),第三个 8*8 块为 4*8(如图中蓝色线),第四个 8*8 块为 8*4(如图中蓝色线)。则按照上述方法来确定相邻块的方法如下:
第一个预测对象为第一个 8*8 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 7 号 4*4 块,B 为 2 号 4*4 块,C 为 4 号 4*4 块,D 为 1 号 4*4 块。9、14、15 与 8 具有相同 MV 预测值
第二个预测对象为第二个 8*8 块的第一个 4*4 块,即 10 号块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 9 号4*4块,B 为 4 号4*4块,C 为 5 号 4*4 块, D 为 3 号 4*4 块
第三个预测对象为第二个 8*8 块的第二个 4*4 块,即 11 号块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 10 号4*4块,B 为 5 号4*4块,C 为 6 号 4*4 块,D 为4 号 4*4 块
第四个预测对象为第二个 8*8 块的第三个 4*4 块,即 16 号块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 15 号4*4块,B 为 10 号4*4块,C 为 11 号 4*4 块,D 为 9 号 4*4 块
第五个预测对象为第二个 8*8 块的第四个 4*4 块,即 17 号块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 16 号4*4块,B 为 11 号4*4块,C 为 12 号 4*4 块,D 为 10 号 4*4 块
第六个预测对象为第三个 8*8 块的第一个 4*8 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 19 号 4*4 块,B 为 14 号 4*4 块,C 为 15 号 4*4 块,D 为 13 号 4*4 块。26 与 20 具有相同 MV 预测值
第七个预测对象为第三个 8*8 块的第二个 4*8 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 20 号 4*4 块,B 为 15 号 4*4 块,C 为 16 号 4*4 块,D 为 14 号 4*4 块。27 与 21 具有相同 MV 预测值
第八个预测对象为第四个 8*8 块的第一个 8*4 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 21 号 4*4 块,B 为 16 号 4*4 块,C 为 18 号 4*4 块,D 为 15 号 4*4 块。23 与 22 具有相同 MV 预测值
第九个预测对象为第四个 8*8 块的第二个 8*4 块,以其左上角像素 pixel1 和右上角像素 pixel2 为参考点,则:A 为 27 号 4*4 块,B 为 22 号 4*4 块,C 为 24 号 4*4 块,D 为 21 号 4*4 块。29 与 28 具有相同 MV 预测值
===========第二步:确定 A、B、C 的可用性===========
根据 A、B、C 所在宏块是否存在或者是否允许参与预测来判断。如果 C 不可用,采用 D 代替 C
===========第三步:预测 MV ===========
1、如果 A、B、C 三个参考块中只有一个与当前预测对象为同一参考帧,则选取该参考块的 MV 作为最终 MV 预测值
2、当前宏块是否为 8*16 或者 16*8 分割:
(1)、如果当前宏块为 8*16 分割类型:
对于左边 8*16 分割,如果 A 与当前分割为同一参考帧,则采用 A 的 MV 为该分割的最终 MV 预测值
对于右边 8*16 分割,如果 C 与当前分割为同一参考帧,则采用 C 的 MV 为该分割的最终 MV 预测值
(2)、如果当前宏块为 16*8 分割类型:
对于上边 16*8 分割,如果 B 与当前分割为同一参考帧,则采用 B 的 MV 为该分割的最终 MV 预测值
对于下边 16*8 分割,如果 A 与当前分割为同一参考帧,则采用 A 的 MV 为该分割的最终 MV 预测值
3、其余情况并且 B、C 中有一个可用或者两者都可用,则采用中值预测(取 A、B、C 三者中MV的中值为最终 MV 预测值)
4、其余情况并且 B、C 皆不可用,则采用 A 的 MV 为最终 MV 预测值
【注】:1、宏块分割时的相邻块确定方法与第一步所述过程雷同:16*16 相当于 8*8,8*16、16*8 分别相当于 4*8、8*4
2、对于不可用的相邻块,其 MV 仍然可能参与 MV 预测,但其值为 0。例如:A 不可用,B、C 可用,则最终可能仍然是在 A、B、C 中取中值,但此时 A 的 MV 为 0;
3、对于不可用的相邻块,其参考帧索引被设置为 -1,即必然与当前预测对象非同一参考帧;
4、可以验证:同时满足第三步的第一、第二两种情况时,按第一种情况计算 MV 预测值与按第二种情况计算 MV 预测值等效;
5、该预测过程即为标准 8.4.1.3 小节的内容,在 JM86 中对应的代码为 SetMotionVectorPredictor 函数;
6、MBAFF 情况下的相邻块均指对应位置(co-locate)块。
- 【转】X264的帧间预测
- x264帧内预测
- x264 帧内预测学习笔记1
- X264帧内预测编码模式
- x264帧内预测 (转载)
- x264代码剖析(十三):核心算法之帧间预测函数x264_mb_analyse_inter_*()
- x264视频编码中预测模式的实现方法
- x264中16x16帧内预测模式函数分析
- x264 4x4帧内预测代码解析
- 帧内预测和帧间预测的比较
- 帧内预测和帧间预测的比较
- 帧内预测和帧间预测的关系
- X264的参考帧管理机制
- X264的参考帧设置
- x264代码剖析(十二):核心算法之帧内预测函数x264_mb_analyse_intra()
- H.265的帧间预测
- 帧内预测与帧间预测
- 帧内预测和帧间预测
- ubuntu 9.04下安装skyeye通过------转
- ubuntu+skyeye+arm-elf-tools+uClinux-------转
- 99乘法表
- SkyEye的总结文章-----------转
- 每天找一个人来赞美
- 【转】X264的帧间预测
- java利用outlook express发送邮件
- 考试了。。。。。
- matlab 回车问题
- 计算器
- 发现了一个好的学习网站
- word技巧
- 我看来csdn学生大本营的两种人。
- asp,没兴趣,但还是去上了!