Outline:
1、 运动估计相关的数据结构和变量
2、 相关重要变量的初始化
3、 运动估计函数(BlockMotionSearch())的流程
4、 运动矢量预测
5、 整象素点运动估计
6、 亚象素点运动估计(即高精度象素点运动估计)
7、 亚象素点的插值预测
8、 遗留问题
1、运动估计相关的数据结构和变量
a、六重指针all_mv的解释
(1)声明的样式如下:
int****** all_mv;
(2)出现之处:
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())的流程
4、运动矢量预测
#define MVPRED_MEDIAN 0 //中值预测方式
#define MVPRED_L 1 //取左相邻块的运动矢量
#define MVPRED_U 2 //取上相邻块的运动矢量
//判断相邻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;
if(input->FMEnable) temp_pred_SAD[hv] = SAD_c;
break;
default:
break;
}
5、 整象素点运动估计
//===== loop over all search positions =====
//对搜索区的每个象素点位置进行循环搜索(全搜索)
6、亚象素点运动估计(即高精度象素点运动估计)
//找到对应的参考帧
ref_picture = listX[list+list_offset][ref];//!!
if (apply_weights)
{
ref_pic = listX[list+list_offset][ref]->imgY_ups_w;
}
else
{//ref_pic指向的是已经上采样到1/4象素点的亮度数据
ref_pic = listX[list+list_offset][ref]->imgY_ups;
}
/*********************************
***** *****
***** HALF-PEL REFINEMENT *****
***** *****
*********************************/
//===== convert search center to quarter-pel units =====
*mv_x <<= 2;
*mv_y <<= 2;
//===== set function for getting pixel values =====
if ((pic4_pix_x + *mv_x > 1) && (pic4_pix_x + *mv_x < max_pos_x4 - 2) &&
(pic4_pix_y + *mv_y > 1) && (pic4_pix_y + *mv_y < max_pos_y4 - 2) )
{
PelY_14 = FastPelY_14;//指向指针的函数
}
else
{
PelY_14 = UMVPelY_14;//无限制运动矢量模式
}
//===== loop over search positions =====
for (best_pos = 0, pos = min_pos2; pos < max_pos2; pos++)
{
/************************************
***** *****
***** QUARTER-PEL REFINEMENT *****
***** *****
(NOTE:箭头表示调用关系)
//开辟缓冲区,IMG_PAD_SIZE是为了满足UMV模式
//所求1/2象素点在水平整象素点的位置上 以及整象素点数据的直接赋值
for (j = -IMG_PAD_SIZE; j < s->size_y + IMG_PAD_SIZE; j++)
{
for (i = -IMG_PAD_SIZE; i < s->size_x + IMG_PAD_SIZE; i++)
{
jj = max (0, min (s->size_y - 1, j));
//6-tap filter
is =
(ONE_FOURTH_TAP[0][0] *
(imgY[jj][max (0, min (s->size_x - 1, i))] +
imgY[jj][max (0, min (s->size_x - 1, i + 1))]) +
ONE_FOURTH_TAP[1][0] *
(imgY[jj][max (0, min (s->size_x - 1, i - 1))] +
imgY[jj][max (0, min (s->size_x - 1, i + 2))]) +
ONE_FOURTH_TAP[2][0] *
(imgY[jj][max (0, min (s->size_x - 1, i - 2))] +
imgY[jj][max (0, min (s->size_x - 1, i + 3))]));
img4Y_tmp[j + IMG_PAD_SIZE][(i + IMG_PAD_SIZE) * 2] = imgY[jj][max (0, min (s->size_x - 1, i))] * 1024; // 1/1 pix pos
img4Y_tmp[j + IMG_PAD_SIZE][(i + IMG_PAD_SIZE) * 2 + 1] = is * 32; // 1/2 pix pos
}
}
//所求1/2象素点在垂直整象素点的位置上以及在1/2象素点的位置上
。。。//略
/* 1/4 pix */
const int ONE_FOURTH_TAP[3][2] =
{
{20,20},
{-5,-4},
{ 1, 0},
7、 遗留问题
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
我是一名学生,对视频处理比较 感兴趣
这段时间在研究264的JM86和V3 H。264/AVC协议
现在对基本的一些技术有一定了解,但还很不够。
目前主要觉得INTER MODE DESISION比较难
我今天上网搜索了一下网页,看到您的BLOG,觉得您写的文章非常不错,让我清楚了一些没有清楚的地方,谢谢您^_^
我现在对ENCODE—ONE——MACROBLOCK函数还不是太清楚,您能不能帮我注释一下这段程序?我不太清楚它怎么选择出来什么样的模式是最好的,谢
谢!
关于INTERMODE DESISION您有什么比较好的文章说明这个问题的吗?我看的都是一些泛泛的东西,实际的过程还不是很了解
对有些关键的变量还没有搞懂什么意思,比如RUNS,RERUN,bestpdir,程序决策的过程是怎么样?等等
* Mode Decision for a macroblock
*************************************************************************************
*/
void encode_one_macroblock ()
{
static const int b8_mode_table[6] = {0, 4, 5, 6, 7}; // DO NOT CHANGE ORDER !!!
static const int mb_mode_table[7] = {0, 1, 2, 3, P8x8, I16MB, I4MB}; // DO NOT CHANGE ORDER !!!
int valid[MAXMODE];
int rerun, block, index, mode, i0, i1, j0, j1, pdir, ref, i, j, k, ctr16x16, dummy;
double qp, lambda_mode, lambda_motion, min_rdcost, rdcost = 0, max_rdcost=1e30;
int lambda_motion_factor;
int fw_mcost, bw_mcost, bid_mcost, mcost, max_mcost=(1<<30);
int curr_cbp_blk, cnt_nonz = 0, best_cnt_nonz = 0, best_fw_ref = 0, best_pdir;//
int cost=0;
int min_cost = max_mcost, min_cost8x8, cost8x8, cost_direct=0, have_direct=0, i16mode;
int intra1 = 0;
int intra = (((img->type==P_SLICE||img->type==SP_SLICE) &&
我是一名学生,对视频处理比较 感兴趣
这段时间在研究264的JM86和V3 H。264/AVC协议
现在对基本的一些技术有一定了解,但还很不够。
目前主要觉得INTER MODE DESISION比较难
我今天上网搜索了一下网页,看到您的BLOG,觉得您写的文章非常不错,让我清楚了一些没有清楚的地方,谢谢您^_^
我现在对ENCODE—ONE——MACROBLOCK函数还不是太清楚,您能不能帮我注释一下这段程序?我不太清楚它怎么选择出来什么样的模式是最好的,谢
谢!
关于INTERMODE DESISION您有什么比较好的文章说明这个问题的吗?我看的都是一些泛泛的东西,实际的过程还不是很了解
对有些关键的变量还没有搞懂什么意思,比如RUNS,RERUN,bestpdir,程序决策的过程是怎么样?等等
encode_one_macroblock()的程序结构大致如下:
{
for(rerun=0;,,,)//rerun和rdo loss有关,不用理会
{
//宏块级的运动估计
for()
{}
//亚宏块级的运动估计和模式选择
if(valid[P8x8])
{}
//宏块级的模式选择,一共有7钟模式,包括帧内预测模式
其中,RDCost_for_macroblocks()是重点!!
}
}
明明知道您已经回了帖子,但是看不到,可能是浏览器有问题,您能发到我的邮箱吗
zsw79923@sina.com
谢谢
if (((*mv)[i][j][k][l] = (int**)calloc(9,sizeof(int*))) == NULL)
这是给blocktype分配空间 怎么来的9呢?
all_mv初始化中9怎么来的?blocktype 不就是只有七种吗? 16*16....4*4
请您回答以下可以吗?
估计是7种宏块模式加上skip mode 和 direct mode
上面看到您提到了skipped mode 和direct mode,我不是很清楚这两种模式的具体区别,尤其是skipped Mode ,您能给我解释一下吗?
谢谢您
skip mode和direct mode的共同特点就是不需要传输运动矢量和残差数据。
skip mode是针对P帧的
direct mode是针对B帧的,又分为空间的和时间的两种。
具体内容这里也讲不清楚,我准备近期写一篇关于这方面的文档,敬请关注。
您好,谢谢您的回复。
我现在还有一个问题不明白,向您请教:
在H.264的mv_search.c中有MotionBlockSearch()和
您在上面讲的SubPelBlockSearch()两个函数,这两者里面都包含sub-pel search,他们2者有什么不同呢?分别有何作用呢?
谢谢
您好,上面这个问题我搞明白了,耽误您时间了,不好意思!
谢谢
应该是BlockMotionSearch吧
两者是调用和被调用的关系。
BlockMotionSearch()分别调用FullPelBlockSearch()和SubPelBlockSearch()来实现整象素点和半象素,1/4象素点的运动估计
看了您写的这篇文章给我很大启发,非常感谢,您说有些内容您在以前的报告中写过,可是我现在看不到了,您能不能发给我一份?非常感谢,我的邮箱是jiepig1983@126.com
我是个本科生 毕业设计的题目是4*4亮度块的贞内预测,不知道您有没有好的文章能帮我理解整个9种模式选择的过程,重要的函数,参数的作用,真是不胜感激
我的邮箱sonicbupt0211113@sogou.com
panfen的一篇文章不错,是JVT WORKSHOP里的文档。
JVT-G013
Fast Mode Decision for Intra Prediction
../../common/frame.h(27) : fatal error C1083: Cannot open include file: 'inttypes.h': No such file or directory
是因为找不到'inttypes.h'这个文件, window c中找不到该文件,谁有这个文件的麻烦发到我邮箱: ytang@fairage.com
谢谢了
===========
你可以把这句代码注释掉,或者换成:
#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
#include <inttypes.h>
#endif
请问结构体InputParameters里面的rdopt是什么,有什么作用?
=0:无rdo
=1: rdo
=2: 允许失真的rdo
今天发现你的专栏,做的真好!对我帮助很大。
请教问题:我已经下载了MPEG4标准测试序列(QCIF格式),用MATLAB做运动估计时,如何读取序列图像的每一帧数据?
我初入视频领域,不懂的问题多多啊,谢谢!
骆驼
您好!
我是一个初学者,264标准里的MbaffFrameFlag 变量,我不是很理解他的意思(我个人觉得MbaffFrameFlag 为1就表示帧场自适应,但是又感觉MbaffFrameFlag 为1代表帧场自适应并且当前为宏块为帧宏块。),还请您指教!谢谢!
当我看到你对张大伟的解释,在P8*8中的RDO又好像是应给在对“亚宏块级的模式选择”,但代码确实是对整个P8*8的宏块作的RDO呀!这到底是怎么回事,是不是我对某部分理解错了,请指点!
如果看到,请一定要回帖呀!谢谢!
因目前正在研究H.264的內插(interpolation),但在看源碼部分有一些不瞭解,您在本篇有提到(详细内容可参考本人8.22的报告“H.264中高精度的运动估计”),但是沒看到存檔,不知可否將这个文件发到我邮箱: changyihs@anet.net.tw,謝謝!!
我下载了一的JM H264解码程序,我想知道文件从什么地方输入,根据decoder.cfg吗,如果是,输入输出的文件的路径需要重新指定吗?还是放在当前工程的 .dsw目录下?
一同打开的工程,除了decode 工程外,还有encode,rtpdump工程,rtpdump工程的功能是什么?
谢谢
以上问题已经基本解决。
但是还有一个问题,我下载了JM12。0 decoder 能在VC6下运行无误,但是encoder也能编译通过,但是总是提示encoder configer 文件某个参数不能识别。不知道是为什么?
谢谢
to mminrong :
decoder.cfg中输入绝对路径即可
to mminrong:
cfg文件的版本不对。
//===============================================
//===== SET MV'S AND RETURN MOTION COST =====
//===============================================
if(input->FMEnable)
{
int h4x4blkno = (img->pix_x>>2)+block_x;
int v4x4blkno = (img->pix_y>>2)+block_y;
for (i=0; i < (bsx>>2); i++)
{
for (j=0; j < (bsy>>2); j++)
{
all_mv[block_x+i][block_y+j][list][ref][blocktype][0] = mv_x;
all_mv[block_x+i][block_y+j][list][ref][blocktype][1] = mv_y;
mv_array[h4x4blkno+i][v4x4blkno+j][0] = mv_x;
mv_array[h4x4blkno+i][v4x4blkno+j][1] = mv_y;
}
}
}
else
{
for (i=0; i < (bsx>>2); i++)
{
for (j=0; j < (bsy>>2); j++)
{
all_mv[block_x+i][block_y+j][list][ref][blocktype][0] = mv_x;
all_mv[block_x+i][block_y+j][list][ref][blocktype][1] = mv_y;
}
}
}
return min_mcost;
请问李老师all_mv和mv_array不都是BlockMotionSearch ()中定义的局部变量吗,这样设置mv变量有什么用?
input->FMEnable的情况,及快速运动估计的情况下,各种块尺寸的运动矢量是有关联性的,可以做个假设,16x16块的运动矢量可以作为16x8等块的初始运动矢量,所以mv_array就是充当暂时存储的作用。