懒人李冰

记录我的生活、学习

X264源码解析之x264_mc_init函数

本文主要记录 X264 中使用到的运动补偿。该函数主要对x264_mc_functions_t结构体中的函数指针进行赋值, 完成了像素内插、拷贝、求平均的函数等等。

运动补偿原理

运动补偿(Motion Compensation)是一种描述相邻帧(相邻指编码关系上的相邻,播放顺序未必相邻)差别的方法,具体来说是描述前一帧的每个小块怎样移动到当前帧中的某个位置去。该方法常被视频压缩/视频编解码器用来减少视频序列中的时域冗余,还可用来进行去交织以及运动插值的操作。

一个视频序列包含一定数量的图片–通常称为帧(frame)。相邻的图片通常很相似,也就是说,包含了很多冗余。使用运动补偿的目的是通过消除这种冗余,来提高压缩比。 最早的运动补偿的设计只是简单的从当前帧中减去参考帧,从而得到通常含有较少能量(或者称为信息)的”残差”,从而可以用较低的码率进行编码。解码器可以通过简单的加法完全恢复编码帧。
一个稍微复杂一点的设计是估计一下整帧场景的移动和场景中物体的移动,并将这些运动通过一定的参数编码到码流中去。这样预测帧上的像素值就是由参考帧上具有一定位移的相应像素值而生成的。这样的方法比简单的相减可以获得能量更小的残差,从而获得更好的压缩比–当然,用来描述运动的参数不能在码流中占据太大的部分,否则就会抵消复杂的运动估计带来的好处。
通常,图像帧是一组一组进行处理的。每组的第一帧(通常是第一帧)在编码的时候不使用运动估计的办法,这种帧称为帧内编码帧(Intra frame)或者I帧。该组中的其它帧使用帧间编码帧(Inter frame),通常是P帧。这种编码方式通常被称为IPPPP,表示编码的时候第一帧是I帧,其它帧是P帧。
在进行预测的时候,不仅仅可以从过去的帧来预测当前帧,还可以使用未来的帧来预测当前帧。当然在编码的时候,未来的帧必须比当前帧更早的编码,也就是说,编码的顺序和播放的顺序是不同的。通常这样的当前帧是使用过去和未来的I帧或者P帧同时进行预测,被称为双向预测帧,即B帧。这种编码方式的编码顺序的一个例子为IBBPBBPBBPBB。

运动补偿的方法包含:全局运动补偿分块运动补偿可变分块运动补偿重叠分块运动个补偿

亮度运动补偿

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const uint8_t x264_hpel_ref0[16] = {0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3, 0, 1, 1, 1};
const uint8_t x264_hpel_ref1[16] = {0, 0, 1, 0, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, 2};

static void mc_luma(pixel *dst, intptr_t i_dst_stride,
                    pixel *src[4], intptr_t i_src_stride,
                    int mvx, int mvy,
                    int i_width, int i_height, const x264_weight_t *weight)
{
    int qpel_idx = ((mvy & 3) << 2 ) + (mvx & 3);
    int offset = (mvy >> 2) * i_src_stride + (mvx >> 2);
    pixel *src1 = src[x264_hpel_ref0[qpel_idx]] + offset + ((mvy & 3) == 3) * i_src_stride;

    if(qpel_idx & 5)
    {
        pixel *src2 = src[x264_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);
        if(weight->weightfn)
            mc_weight(dst, i_dst_stride, dst, i_dst_stride, weight, i_width, i_height);
    }
    else if(weight->weightfn)
            mc_weight(dst, i_dst_stride, src1, i_dst_stride, weight, i_width, i_height);
    else
            mc_copy(src1, i_dst_stride, dst, i_dst_stride, i_width, i_height);
}

运动估计

运动估计(Motion estimation)就是寻找最优或次优的运动向量的过程。某个块的预测误差量常常是指在运动补偿区域所有像素中,预测像素和实际像素数值的均方差或者绝对差别总和。

运动估计的基本思想是将图像序列的每一帧分成许多互不重叠的宏块,并认为宏块内所有象素的位移量都相同,然后对每个宏块到参考帧某一给定特定搜索范围内根据一定的匹配准则找出与当前块最相似的块,即匹配块,匹配块与当前块的相对位移即为运动矢量。视频压缩的时候,只需保存运动矢量和残差数据就可以完全恢复出当前块。得到运动矢量的过程被称为运动估计。

运动矢量和经过运动匹配后得到的预测误差共同发送到解码端,在解码端按照运动矢量指明的位置,从已经解码的邻近参考帧图像中找到相应的块或宏块,和预测误差相加后就得到了块或宏块在当前帧中的位置。

实际应用时,只将运动矢量及最佳匹配块与当前块之间的差值块一起编码传输。在接收端,通过运动矢量在已经恢复的相邻帧中找到当前块的最佳匹配块,并与接收到的差值块相加恢复出当前块,这就是运动补偿基本过程。

运动估计算法是视频压缩编码的核心算法之一。高质量的运动估计算法是高效视频编码的前提和基础。其中块匹配法(BMA, Block Match Algorithm)由于算法简单和易于硬件实现,被广泛应用于各视频编码标准中。块匹配法的基本思想是先将图像划分为许多子块,然后对当前帧中的每一块根据一定的匹配准则在相邻帧中找出当前块的匹配块,由此得到两者的相对位移,即当前块的运动矢量。在H.264标准的搜索算法中,图像序列的当前帧被划分成互不重叠16×16大小的子块,而每个子块又可划分成更小的子块,当前子块按一定的块匹配准则在参考帧中对应位置的一定搜索范围内寻找最佳匹配块,由此得到运动矢量和匹配误差。运动估计的估计精度和运算复杂度取决于搜索策略和块匹配准则。

参考资料

  1. 运动补偿——维基百科
  2. 运动估计