JM8.5中的高精度象素运动估计
Outline:
1、 运动估计相关的数据结构和变量
2、 相关重要变量的初始化
3、 运动估计函数(BlockMotionSearch())的流程
4、 运动矢量预测
5、 整象素点运动估计
6、 亚象素点运动估计(即高精度象素点运动估计)
7、 亚象素点的插值预测
8、 遗留问题
1、运动估计相关的数据结构和变量
a、六重指针all_mv的解释
(1)声明的样式如下:
int****** all_mv;
(2)出现之处:
全局变量img的结构元素all_mv和pred_mv(其数据结构在global.h中定义)
mv_search.c的BlockMotiongSearch()函数(对一块进行运动估计的函数,后面会重点讲述)中声明的局部变量int****** all_mv;
(3)六重指针的含义:
对应的是一个六维数组如下:
all_mv[block_x][block_y][list][ref][blocktype][direction]
其中block_x, block_y分别表示4*4块在整个宏块16*16内的水平和垂直位置,同时也说明所保存的运动矢量都是以4*4为单位的,假如有一个8*4的块,其运动矢量会保存成相同的两份。
List表示的是哪个参考帧列表
Ref表示的是参考帧序号
Blocktype表示的是宏块的类型,有16×16,16×8。。。4×4
Direction表示水平或垂直方向,其值分别是0和1
2、相关重要变量的初始化
a、img->all_mv的初始化
其初始化的整个过程如下:
首先,在lencod.c的main()函数调用了init_img()(在同一文件中)函数
然后init_img()又调用了get_mem_mv(img->all_mv)(在同一文件中)对all_mv进行初始化。Pred_mv的初始化同上。下面重点分析一下get_mem_mv()函数
int get_mem_mv (int******* mv)
{
int i, j, k, l, m;
//4*4块的序号,水平方向:block_x。因为采用的宏块或亚宏块的尺寸不超过16×16,所以最大值为4
if ((*mv = (int******)calloc(4,sizeof(int*****))) == NULL)
no_mem_exit ("get_mem_mv: mv");
for (i=0; i<4; i++)
{
if (((*mv)[i] = (int*****)calloc(4,sizeof(int****))) == NULL)//block_y
no_mem_exit ("get_mem_mv: mv");
for (j=0; j<4; j++)
{
if (((*mv)[i][j] = (int****)calloc(2,sizeof(int***))) == NULL)//list。6?
no_mem_exit ("get_mem_mv: mv");
for (k=0; k<2; k++)
{
if (((*mv)[i][j][k] = (int***)calloc(img->max_num_references,sizeof(int**))) == NULL)//ref
no_mem_exit ("get_mem_mv: mv");
for (l=0; l<img->max_num_references; l++)
{
if (((*mv)[i][j][k][l] = (int**)calloc(9,sizeof(int*))) == NULL)//blocktype:16*16,16*8...
no_mem_exit ("get_mem_mv: mv");
for (m=0; m<9; m++)
if (((*mv)[i][j][k][l][m] = (int*)calloc(2,sizeof(int))) == NULL)//x or y direction:0 or 1
no_mem_exit ("get_mem_mv: mv");
}
}
}
}
return 4*4*img->max_num_references*9*2*sizeof(int);
}
3、 运动估计函数(BlockMotionSearch())的流程
对一个块(各种尺寸)进行运动估计的最核心的函数就是mv_search.c中的BlockMotionSearch(),该函数的具体流程如下:
a、 获取当前块的数据
b、 运动矢量预测
c、 整象素点搜索
d、 亚象素点搜索
e、 保存MV并返回SAD值
下面对b,c,d三块内容进行重点分析
4、运动矢量预测
相关的函数是:SetMotionVectorPredictor(),mv_search.c
a、相应宏定义的解释:
#define MVPRED_MEDIAN 0 //中值预测方式
#define MVPRED_L 1 //取左相邻块的运动矢量
#define MVPRED_U 2 //取上相邻块的运动矢量
#define MVPRED_UR 3 //取右上相邻块的运动矢量
b、该函数的具体流程
(1)先检查各相邻块是否有效
(2)结合参考帧序号等情况来确定预测方式mvPredType
(3)根据mvPredType采取不同的预测方式
c、重要的程序段
。。。
//判断相邻4*4块的有效性
getLuma4x4Neighbour(mb_nr, block_x, block_y, -1, 0, &block_a);//left
getLuma4x4Neighbour(mb_nr, block_x, block_y, 0, -1, &block_b);//up
getLuma4x4Neighbour(mb_nr, block_x, block_y, blockshape_x, -1, &block_c);//right-up
getLuma4x4Neighbour(mb_nr, block_x, block_y, -1, -1, &block_d);//left-up
。。。
/* Prediction if only one of the neighbors uses the reference frame
* we are checking
*///只有一个相邻块的参考帧序号和当前参卡帧序号相同的情况下,采用的预测方式相对较简单
if(rFrameL == ref_frame && rFrameU != ref_frame && rFrameUR != ref_frame) mvPredType = MVPRED_L;
else if(rFrameL != ref_frame && rFrameU == ref_frame && rFrameUR != ref_frame) mvPredType = MVPRED_U;
else if(rFrameL != ref_frame && rFrameU != ref_frame && rFrameUR == ref_frame) mvPredType = MVPRED_UR;
。。。
//根据mvPredType采取不同的预测方式
switch (mvPredType)
{
case MVPRED_MEDIAN:
。。。
{
//取中值
pred_vec = mv_a+mv_b+mv_c-min(mv_a,min(mv_b,mv_c))-max(mv_a,max(mv_b,mv_c));
}
break;
case MVPRED_L:
pred_vec = mv_a;
if(input->FMEnable) temp_pred_SAD[hv] = SAD_a;
break;
case MVPRED_U:
pred_vec = mv_b;
if(input->FMEnable) temp_pred_SAD[hv] = SAD_b;
break;
case MVPRED_UR:
pred_vec = mv_c;