在做B帧的运动预测时,有两组参考图像列表(refList0, refList1),需要进行分别前向预测、后向预测。传统的预测方式是:

  1. 对refList0进行前向预测,得到最佳前向ref与mv。
  2. 对refList1进行后向预测,得到最佳后向ref与mv。
  3. 对比三组ref与mv(前向、后向、前向+后向),从中选择最佳的ref与mv。

如果最终得到的是前向+后向的方式,那这就是典型的B帧预测方式。不过这种方式会存在以下的问题:

  1. refList0,refList1的最佳ref与mv是分开进行预测的,即在计算cost时,预测块取值采用的是单独的前向预测块与单独的后向预测块。
  2. 对比三组ref与mv时,预测块取值为前向预测块与后向预测块之和的一半。
  3. 在进行残差编码时,预测块取值为前向预测块与后向预测块之和的一半。

可以看到在运动预测时,预测块的取值方式与最后的残差编码的取值方式不同,那么这种运动预测所得到的mv能否在计算残差编码时使得残差最小?答案不能肯定,因为运动预测时的预测块值与残差编码的预测块值不是一样的。不过对于这种B帧预测的情况,肯定有更好的运动预测方式:令运动预测时的预测块的预测方式与残差编码的预测块的预测方式一致。

Bi-predictive motion search

双向预测运动搜索(Bi-predictive motion search)相对于上述传统类型的运动预测方式,主要的改进是在计算cost时,预测块的取值为前向预测块与后向预测块的一半。

这种运动估计方式,既保持了运动估计与残差编码的一致性,又充分考虑到了前后参考图像的耦合性。

在传统的运动估计中,由于前、后向估计是分开的,但是在残差编码时需要对前、后预测块进行组合,因此如果前、后参考帧关联性很强的话,这种单独的预测方式所得到的组合预测块(combination predicted block)并可能非最佳的预测块,如下图:

(注:左:前后预测块的像素直方图;右:当前块的像素直方图)

采用双向预测运动搜索后,即使原本在单向预测时被判定为不是最佳的运动向量,如果前、后参考帧的关联性很强的话,也可能由于前后向结合搜索从而得到与当前块差距最小的组合预测块(combination predicted block)

(注:左:前后预测块的像素直方图;右:当前块的像素直方图)

JM18.6实现

首先,是在B slice下才会进行双向预测运动搜索。

既然需要对两个参考图像同时进行预测,那么这两个参考图像应该如何选择呢?在JM18.6中,只对两个参考图像列表的ref0执行这种双向预测运动搜索,并且在进行搜索时,两个参考图像都是ref0,即ref0List[0],ref1List[0]。

/*!
***********************************************************************
* \brief
* Block motion search
***********************************************************************
*/
distblk //!< minimum motion cost after search
BlockMotionSearch (Macroblock *currMB, //!< Current Macroblock
MEBlock *mv_block, //!< Motion estimation information block
int mb_x, //!< x-coordinate inside macroblock
int mb_y, //!< y-coordinate inside macroblock
int* lambda_factor) //!< lagrangian parameter for determining motion cost
{
... // Bipred ME consideration: returns minimum bipred cost
if (is_bipred_enabled(p_Vid, blocktype) && (ref == 0))
{
BiPredBlockMotionSearch(currMB, mv_block, &pred, mb_x, mb_y, lambda_factor);
} return min_mcost;
} /*!
************************************************************************
* \brief
* UMHEXBipredIntegerPelBlockMotionSearch: fast pixel block motion search for bipred mode
* this algrithm is called UMHexagonS(see JVT-D016),which includes
* four steps with different kinds of search patterns
* \author
* Main contributors: (see contributors.h for copyright, address and affiliation details)
* - Zhibo Chen <chenzhibo@tsinghua.org.cn>
* - JianFeng Xu <fenax@video.mdc.tsinghua.edu.cn>
* - Xiaozhong Xu <xxz@video.mdc.tsinghua.edu.cn>
* \date :
* 2006.1
************************************************************************
*/
distblk // ==> minimum motion cost after search
UMHEXBipredIntegerPelBlockMotionSearch (Macroblock *currMB, // <-- current Macroblock
int list, // <-- current reference list
MotionVector *pred_mv1, // <-- motion vector predictor (x|y) in sub-pel units
MotionVector *pred_mv2, // <-- motion vector predictor (x|y) in sub-pel units
MotionVector *mv1, // <--> in: search center (x|y) / out: motion vector (x|y) - in sub-pel units
MotionVector *mv2, // <--> in: search center (x|y)
MEBlock *mv_block, // <-- motion vector information
int search_range, // <-- 1-d search range in sub-pel units
distblk min_mcost, // <-- minimum motion cost (cost for center or huge value)
int lambda_factor // <-- lagrangian parameter for determining motion cost
)
{
...
//maybe wrong,should swap ref1 and ref2 ,not listX[list][ref] and listX[list][0]
//ok, only ref == 0 comes into this function
StorablePicture *ref_picture1 = currSlice->listX[list + currMB->list_offset][ref];
StorablePicture *ref_picture2 = currSlice->listX[list == 0 ? 1 + currMB->list_offset: currMB->list_offset][ 0 ]; ...
}

由于有两个参考图像,但是在进行一次运动搜索时,不可能对两个参考图像同时进行搜索,因此轮询对两张参考图像进行搜索。流程如下:

  1. 假设当前参考图像为ref0List[0],那么令ref0List[0](前向)由单向运动搜索已经得到的mv作为该参考图像的候选mv,并且在此次搜索过程中保持不变。
  2. Ref1List[0]为本次搜索过程的搜索对象。先求出后向mvp,用于运动搜索。
  3. 采用某种算法(如:UMH)进行运动搜索,与普通的运动搜索的不同只有在计算cost时同时用了ref0List[0]的候选mv与ref1List[0]的候选mv。
  4. 得到此次运动搜索的ref1List[0]的最佳mv后,保存起来,交换ref0List[0]与ref1List[1],刚刚得到的最佳mv为下一次运动搜索的固定mv。
  5. 3与4轮询几次后即可得到最终结果。

/*!
***********************************************************************
* \brief
* Bi-predictive motion search
***********************************************************************
*/
static distblk BiPredBlockMotionSearch(Macroblock *currMB, //!< Current Macroblock
MEBlock *mv_block,
MotionVector *pred_mv, //!< current list motion vector predictor
int mb_x, //!< x-coordinate inside macroblock
int mb_y, //!< y-coordinate inside macroblock
int* lambda_factor) //!< lagrangian parameter for determining motion cost
{
VideoParameters *p_Vid = currMB->p_Vid;
InputParameters *p_Inp = currMB->p_Inp;
Slice *currSlice = currMB->p_Slice;
int list = mv_block->list;
int i, j;
short bipred_type = list ? 0 : 1;
MotionVector ***** bipred_mv = currSlice->bipred_mv[bipred_type];
distblk min_mcostbi = DISTBLK_MAX;
MotionVector *mv = &mv_block->mv[list]; //1.用从单向运动搜索得到的mv作为候选mv
MotionVector bimv, tempmv;
MotionVector pred_mv1, pred_mv2, pred_bi;
MotionVector *bi_mv1 = NULL, *bi_mv2 = NULL;
short iterlist = (short) list;
int block_x = (mb_x>>2);
int block_y = (mb_y>>2);
int blocktype = mv_block->blocktype;
int bsx = mv_block->blocksize_x;
int bsy = mv_block->blocksize_y;
//PixelPos block[4]; // neighbor blocks //get_neighbors(currMB, mv_block->block, mb_x, mb_y, bsx); if (p_Inp->SearchMode[p_Vid->view_id] == UM_HEX)
{
p_Vid->p_UMHex->bipred_flag = 1;
//2.求出后(前)向mvp
UMHEXSetMotionVectorPredictor(currMB, &pred_bi, p_Vid->enc_picture->mv_info, 0, list ^ 1, mb_x, mb_y, bsx, bsy, mv_block);
}
else
currMB->GetMVPredictor (currMB, mv_block->block, &pred_bi, 0, p_Vid->enc_picture->mv_info, list ^ 1, mb_x, mb_y, bsx, bsy); if ((p_Inp->SearchMode[p_Vid->view_id] != EPZS) || (p_Inp->EPZSSubPelGrid == 0))
{
mv->mv_x = ((mv->mv_x + 2) >> 2) * 4;
mv->mv_y = ((mv->mv_y + 2) >> 2) * 4;
bimv.mv_x = ((pred_bi.mv_x + 2) >> 2) * 4;
bimv.mv_y = ((pred_bi.mv_y + 2) >> 2) * 4;
}
else
{
bimv = pred_bi;
} //Bi-predictive motion Refinements
for (mv_block->iteration_no = 0; mv_block->iteration_no <= p_Inp->BiPredMERefinements; mv_block->iteration_no++)
{
if (mv_block->iteration_no & 0x01) //4.参考图像交换
{
pred_mv1 = *pred_mv;
pred_mv2 = pred_bi;
bi_mv1 = mv;
bi_mv2 = &bimv;
iterlist = (short) list;
}
else
{
pred_mv1 = pred_bi;
pred_mv2 = *pred_mv;
bi_mv1 = &bimv;
bi_mv2 = mv;
iterlist = (short) (list ^ 1);
} tempmv = *bi_mv1; PrepareBiPredMEParams(currSlice, mv_block, mv_block->ChromaMEEnable, iterlist, currMB->list_offset, mv_block->ref_idx);
// Get bipred mvs for list iterlist given previously computed mvs from other list
min_mcostbi = currMB->BiPredME (currMB, iterlist,
&pred_mv1, &pred_mv2, bi_mv1, bi_mv2, mv_block,
(p_Inp->BiPredMESearchRange[p_Vid->view_id] <<2)>>mv_block->iteration_no, min_mcostbi, lambda_factor[F_PEL]); if (mv_block->iteration_no > 0 && (tempmv.mv_x == bi_mv1->mv_x) && (tempmv.mv_y == bi_mv1->mv_y))
{
break;
}
} if (!p_Inp->DisableSubpelME[p_Vid->view_id])
{
if (p_Inp->BiPredMESubPel)
{
if ( !p_Vid->start_me_refinement_hp )
min_mcostbi = DISTBLK_MAX;
PrepareBiPredMEParams(currSlice, mv_block, mv_block->ChromaMEEnable, iterlist, currMB->list_offset, mv_block->ref_idx); min_mcostbi = currMB->SubPelBiPredME (currMB, mv_block, iterlist, &pred_mv1, &pred_mv2, bi_mv1, bi_mv2, min_mcostbi, lambda_factor);
} if (p_Inp->BiPredMESubPel==2)
{
if ( !p_Vid->start_me_refinement_qp )
min_mcostbi = DISTBLK_MAX;
PrepareBiPredMEParams(currSlice, mv_block, mv_block->ChromaMEEnable, iterlist ^ 1, currMB->list_offset, mv_block->ref_idx); min_mcostbi = currMB->SubPelBiPredME (currMB, mv_block, iterlist ^ 1, &pred_mv2, &pred_mv1, bi_mv2, bi_mv1, min_mcostbi, lambda_factor);
}
} clip_mv_range(p_Vid, 0, bi_mv1, Q_PEL);
clip_mv_range(p_Vid, 0, bi_mv2, Q_PEL); for (j=block_y; j < block_y + (bsy>>2); j++)
{
for (i=block_x ; i < block_x + (bsx>>2); i++)
{
bipred_mv[iterlist ][(short) mv_block->ref_idx][blocktype][j][i] = *bi_mv1;
bipred_mv[iterlist ^ 1][(short) mv_block->ref_idx][blocktype][j][i] = *bi_mv2;
}
} return min_mcostbi;
} #define SEARCH_ONE_PIXEL_BIPRED \
if((iabs(cand.mv_x - center2.mv_x) >> 2) < search_range && (iabs(cand.mv_y - center2.mv_y) >> 2) < search_range) \
{ \
if(!p_UMHex->McostState[((cand.mv_y - center2.mv_y) >> 2) + search_range][((cand.mv_x-center2.mv_x) >> 2)+search_range]) \
{ \
mcost = mv_cost (p_Vid, lambda_factor, &center1, &pred1); \
mcost += mv_cost (p_Vid, lambda_factor, &cand, &pred2); \
if(mcost<min_mcost) \
{ \
mcost += mv_block->computeBiPredFPel(ref_picture1, ref_picture2, \ //3.组合两个ref来进行cost计算
mv_block, min_mcost - mcost, &center1, &cand); \
p_UMHex->McostState[((cand.mv_y - center2.mv_y) >> 2) + search_range][((cand.mv_x - center2.mv_x) >> 2) + search_range] = 1; \
if (mcost < min_mcost) \
{ \
best = cand; \
min_mcost = mcost; \
} \
} \
} \
}

在JM18.6中,采用这种运动搜索方式需要满足以下几种条件:1. Bslice, 2. 输入参数指定了BiPredMotionEstimation, 3. 分割模式被指定了bipred_enabled, 4. Ref = 0。

h.264 Bi-Predictive Motion Search的更多相关文章

  1. (转载)H.264码流的RTP封包说明

    H.264的NALU,RTP封包说明(转自牛人) 2010-06-30 16:28 H.264 RTP payload 格式 H.264 视频 RTP 负载格式 1. 网络抽象层单元类型 (NALU) ...

  2. x264 - 高品质 H.264 编码器

    转自:http://www.5i01.cn/topicdetail.php?f=510&t=3735840&r=18&last=48592660 H.264 / MPEG-4 ...

  3. H.264 / MPEG-4 Part 10 White Paper-翻译

    1. Introduction Broadcast(广播) television and home entertainment(娱乐) have been revolutionised(彻底改变) b ...

  4. 转:MediaCoder H.264格式编码参数设置及详解

    转: http://mediacoder.com.cn/node/81 由于现在大部分视频转码都选择H.264格式进行编码,同时CUDA编码的画质还达不到x264软编码的质量(如果你对画质无要求,可以 ...

  5. h.264宏块与子宏块类型

    宏块类型mb_type 宏块类型表示的是宏块不同的分割和编码方式,在h.264的语法结构中,宏块类型在宏块层(macroblock_layer)中用mb_type表示(请参考h.264语法结构分析中的 ...

  6. h.264语法结构分析

    NAL Unit Stream Network Abstraction Layer,简称NAL. h.264把原始的yuv文件编码成码流文件,生成的码流文件就是NAL单元流(NAL unit Stre ...

  7. h.264直接预测

    直接预测是B帧上一种独有的预测方式,其中直接预测又分为两种模式: 时域直接模式(temporal direct).空域直接模式(spatial direct). 在分析这两种模式之前,有一个前提概念需 ...

  8. h.264 率失真优化

    Rate Distortion Optimization 搜索时,一个不可避免的问题就是如何对mv进行比较,从而得到最优 对于同一压缩算法来说,码率越高表示图像质量越好.失真越小,但是码率越高要求更大 ...

  9. Chapter 6. H.264/MPEG4 Part10

    本章节主要介绍有关H.264的内容 H.264有三种profile,分别是: Baseline Profile Main Profile Extended Profile 三者之间的关系和主要内容可以 ...

随机推荐

  1. WinForm(C#)CheckedlistBox绑定数据,并获得选中的值(ValueMember)和显示文本(DisplayMember)

    本文中我将和大家讨论关于在WinForm开发中给CheckedlistBox空间绑定数据源,并获取控件中选中的所有元素的显示文本(DisplayMember)和对应的实际值(ValueMember)的 ...

  2. 统计学习导论:基于R应用——第二章习题

    目前在看统计学习导论:基于R应用,觉得这本书非常适合入门,打算把课后习题全部做一遍,记录在此博客中. 第二章习题 1. (a) 当样本量n非常大,预测变量数p很小时,这样容易欠拟合,所以一个光滑度更高 ...

  3. Etcd学习(一)安装和.NETclient測试

    Etcd是一个比較新的分布式协调框架,由CoreOS的开发团队开发,如今才仅仅到0.4.6版本号,还没公布1.0版本号 我看了一下GitHub上作者们的提交记录,如今应该还在如火如荼的开发以及改动Bu ...

  4. CSS控制LI行字符溢出用省略号取代

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. Java基础知识强化之IO流笔记11:递归之递归概述和注意事项

    1. 递归: 方法定义中调用方法本身的现象. e.g: public void show(int n ) { if(n <= 0) { System.exit(0); } System.out. ...

  6. 9.2noip模拟试题

      题目名称 改造二叉树 数字对 交换 英文名称 binary pair swap 输入文件名 binary.in pair.in swap.in 输出文件名 binary.out pair.out ...

  7. VB编程技巧推荐

    VB编程技巧推荐   1.zyl910的专栏——理论水平高 用VB写高效的图像处理程序 V2.0 优化分支代码——避免跳转指令堵塞流水线 2.Laviewpbt的专栏 —— 有很多算法的代码,实用性高 ...

  8. js 实现关键词球状旋转效果

    效果图 html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...

  9. querystring,parse和stringify相互转换

    var querystring = require('querystring');var str = 'name==zfpx@age==8';//手工指定字段分隔符和 keyvalue分隔符var q ...

  10. 状态开关按钮(ToggleButton)及按钮(Swich)的使用

    状态开关按钮(ToggleButton)和开关(Switch)也是由Button派生出来的,因此它们本质上都是按钮,Button支持的各种属性.方法也适用于ToggleButton和Switch.从功 ...