JM-对整象素搜索的一些理解

整象素搜索主要包括3个函数SetupFastFullPelSearch,SetMotionVectorPredictor,SetupLargerBlocks,都位于文件mv_search.c中间。

1)该函数对参考帧ref,实现list0(前向),list1(后向)全局整象素搜索,并统计各种分块模式下的SAD值(整象素只计算SAD值,见蓝风车FAQ语录),因此一个宏块只需要计算一次,是整个宏块计算复杂度最大的一个函数,通过这个函数,可以明白,所谓搜索半径只是对整象素搜索而言,下面我对该函数步逐作详细注解。

void SetupFastFullPelSearch (short ref, int list) // <-- reference frame parameter, list0 or 1
{
short   pmv[2];//预测向量X,Y两个方向
pel_t   orig_blocks[256], *orgptr=orig_blocks, *refptr;//对宏块分配地址,static值系统当然只分配一次.
int   offset_x, offset_y, x, y, range_partly_outside, ref_x, ref_y, pos, abs_x, abs_y, bindex, blky;
int   LineSadBlk0, LineSadBlk1, LineSadBlk2, LineSadBlk3;
//为4X4块计算SAD分配空间,注意该4个4X4块为一个宏块内同一行内4个子块
int   max_width, max_height;
int   img_width, img_height;
 
StorablePicture *ref_picture;//参考帧
pel_t   *ref_pic;//参考象素
 
distpel**   block_sad = BlockSAD[ list ][ref][7];
//[7]为4X4子块模式SAD值下标
int   search_range = max_search_range[ list ][ref];
//获得当前帧对ref参考帧list方向预测搜索半径,有点疑问,难道可以有不同的搜索半径
int   max_pos     = (2*search_range+1) * (2*search_range+1);
//计算最大搜索点
int   list_offset   = img->mb_data[img->current_mb_nr].list_offset;
//list方向偏移,个人认为该值和field编码有关,当帧编码时为0  
int   apply_weights = ( (active_pps->weighted_pred_flag && (img->type == P_SLICE || img->type == SP_SLICE)) ||
  (active_pps->weighted_bipred_idc && (img->type == B_SLICE)));
//是否权重预测  
 
ref_picture   = listX[list+list_offset][ref];
//获得参考帧,为计算SAD准备.
//===== Use weighted Reference for ME ====
if (apply_weights && input->UseWeightedReferenceME)
  ref_pic     = ref_picture->imgY_11_w;
else
  ref_pic     = ref_picture->imgY_11;
//是否使用权重预测,使用不同的参考象素值
max_width   = ref_picture->size_x - 17;
max_height   = ref_picture->size_y - 17;
 
img_width   = ref_picture->size_x;
img_height   = ref_picture->size_y;
 
//===== get search center: predictor of 16x16 block =====
SetMotionVectorPredictor (pmv, enc_picture->ref_idx[ list ], enc_picture->mv[ list ], ref, list, 0, 0, 16, 16);
//以上预测MV,该MV为1/4象素为单位向量值,下次再讲这个函数。
search_center_x[ list ][ref] = pmv[0] / 4;
search_center_y[ list ][ref] = pmv[1] / 4;
//ref帧对当前帧预测偏移(整象素值)

if (!input->rdopt)
{
  //--- correct center so that (0,0) vector is inside ---
  search_center_x[ list ][ref] = max(-search_range, min(search_range, search_center_x[ list ][ref]));
  search_center_y[ list ][ref] = max(-search_range, min(search_range, search_center_y[ list ][ref]));
}
search_center_x[ list ][ref] = Clip3(-2047 + search_range, 2047 - search_range, search_center_x[ list ][ref]);
search_center_y[ list ][ref] = Clip3(LEVELMVLIMIT[img->LevelIndex][0] + search_range, LEVELMVLIMIT[img->LevelIndex][1] - search_range, search_center_y[ list ][ref]);
//对上下方向饱和
search_center_x[ list ][ref] += img->opix_x;
search_center_y[ list ][ref] += img->opix_y;

offset_x = search_center_x[ list ][ref];
offset_y = search_center_y[ list ][ref];
//计算绝对偏移(对整幅图像左上角(0,0)位置)
 
//===== copy original block for fast access =====
for   (y = img->opix_y; y < img->opix_y+16; y++)
  for (x = img->opix_x; x < img->opix_x+16; x++)
    *orgptr++ = imgY_org [y][x];
//拷贝原始象素放入数组,一维数组比较快,所以叫做fast access
//===== check if whole search range is inside image =====
if (offset_x >= search_range && offset_x <= max_width - search_range &&
    offset_y >= search_range && offset_y <= max_height - search_range   )
{
  range_partly_outside = 0; PelYline_11 = FastLine16Y_11;
}
else
{
  range_partly_outside = 1;
}
//检查是否越界,第1个宏块16搜索半径当然越界


//===== determine position of (0,0)-vector =====
if (!input->rdopt)
{
  ref_x = img->opix_x - offset_x;
  ref_y = img->opix_y - offset_y;
  for (pos = 0; pos < max_pos; pos++)
  {
    if (ref_x == spiral_search_x[pos] &&
      ref_y == spiral_search_y[pos])
    {
    pos_00[ list ][ref] = pos;
    break;
    }
  }
}
//===== loop over search range (spiral search): get blockwise SAD =====
for (pos = 0; pos < max_pos; pos++)
{
  abs_y = offset_y + spiral_search_y[pos];
  abs_x = offset_x + spiral_search_x[pos];
//开始搜索,abs_y,abs_x为参考宏块绝对坐标,对为何要加offset,有些疑问,
//这样子会不会因为MV的预测误差,导致累计误差,导致最优点落入搜索半径之外.
  if (range_partly_outside)
  {
    if (abs_y >= 0 && abs_y <= max_height &&
      abs_x >= 0 && abs_x <= max_width   )
    {
    PelYline_11 = FastLine16Y_11;
    }
    else
    {
    PelYline_11 = UMVLine16Y_11;
    }
  }
//对是否越界,使用不同的参考象素值,越界后,要插值饱和。
  orgptr = orig_blocks;
  bindex = 0;
  for (blky = 0; blky < 4; blky++)
  {
    LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0;
    for (y = 0; y < 4; y++)
    {
    refptr = PelYline_11 (ref_pic, abs_y++, abs_x, img_height, img_width);
    LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
    LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
    }
    block_sad[bindex++][pos] = LineSadBlk0;
    block_sad[bindex++][pos] = LineSadBlk1;
    block_sad[bindex++][pos] = LineSadBlk2;
    block_sad[bindex++][pos] = LineSadBlk3;
//参考位置为pos,4X4块SAD值,一共有max_pos*16个SAD值.
  }
}


//===== combine SAD's for larger block types =====
SetupLargerBlocks (list, ref, max_pos);
//混合成更大的块包括16X16,16X8,8X16,8X8,8X4,4X8等,这样子各种模式的整象素的SAD值都得出来了,该函数下次再讲。
//===== set flag marking that search setup have been done =====
search_setup_done[ list ][ref] = 1;
//置标志为1,下次遇到该宏块别的模式时候,就不要再计算了.
}


2)该函数比较简单,但是用引入了宏,可读性比较差
//为何能用4X4块的SAD值混合,可以得到各种分块模式的SAD值,因为道理很简单,4X4是最基本分块单元
//比如16X16,是16个4X4,16X8是8个4X4
void
SetupLargerBlocks (int list, int refindex, int max_pos)
{
#define ADD_UP_BLOCKS()   _o=*_bo; _i=*_bi; _j=*_bj; for(pos=0;pos<max_pos;pos++) _o[pos] = _i[pos] + _j[pos];
#define INCREMENT(inc)   _bo+=inc; _bi+=inc; _bj+=inc;
 
distpel   pos, **_bo, **_bi, **_bj;
register distpel *_o,   *_i,   *_j;
 
//--- blocktype 6 ---
_bo = BlockSAD[ list ][refindex][6];
_bi = BlockSAD[ list ][refindex][7];
_bj = _bi + 4;
ADD_UP_BLOCKS(); INCREMENT(1);
ADD_UP_BLOCKS(); INCREMENT(1);
ADD_UP_BLOCKS(); INCREMENT(1);
ADD_UP_BLOCKS(); INCREMENT(5);
ADD_UP_BLOCKS(); INCREMENT(1);
ADD_UP_BLOCKS(); INCREMENT(1);
ADD_UP_BLOCKS(); INCREMENT(1);
ADD_UP_BLOCKS();
//以上为4x8,一共有max_pos*8个SAD
//--- blocktype 5 ---
_bo = BlockSAD[ list ][refindex][5];
_bi = BlockSAD[ list ][refindex][7];
_bj = _bi + 1;
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS();
//以上为8X4,一共有max_pos*8个SAD
//--- blocktype 4 ---
_bo = BlockSAD[ list ][refindex][4];
_bi = BlockSAD[ list ][refindex][6];
_bj = _bi + 1;
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS(); INCREMENT(6);
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS();
//以上为8X8,一共有max_pos*4个SAD
//--- blocktype 3 ---
_bo = BlockSAD[ list ][refindex][3];
_bi = BlockSAD[ list ][refindex][4];
_bj = _bi + 8;
ADD_UP_BLOCKS(); INCREMENT(2);
ADD_UP_BLOCKS();
//以上为8X16,一共有max_pos*2个SAD
//--- blocktype 2 ---
_bo = BlockSAD[ list ][refindex][2];
_bi = BlockSAD[ list ][refindex][4];
_bj = _bi + 2;
ADD_UP_BLOCKS(); INCREMENT(8);
ADD_UP_BLOCKS();
//以上为16X8,一共有max_pos*2个SAD  
//--- blocktype 1 ---
_bo = BlockSAD[ list ][refindex][1];
_bi = BlockSAD[ list ][refindex][3];
_bj = _bi + 2;
ADD_UP_BLOCKS();
//以上为16X16,一共有max_pos个SAD
}
//还有一个函数SetMotionVectorPredictor,这个函数稍微复杂一点,
//需要根据子块周围的4X4临块A,B,C,D的状况,进行MV预测模式选择.


下面继续上次未完成的任务
分析函数SetMotionVectorPredictor
该函数传入参数.
pmv:将要获得的预测向量.
refPic:当前编码帧每个4X4子块的参考帧索引.
tmp_mv:当前编码帧每个4X4子块的运动MV包括X,Y两个方向.
(已经运动估计过的的子块,当前编码块相邻的A,B,C,D子块都是已经运动估计了,所以refPic,tmp_mv可以用来参考)
ref_frame:参考帧索引号,不是frame_num,也不是POC号,是位于list中的序号.
block_x:X方向的坐标
block_y:Y方向的坐标
blockshape_x:该子块X方向的尺寸
blockshape_y:该子块Y方向的尺寸
void SetMotionVectorPredictor (short pmv[2],
                    char   **refPic,
                    short ***tmp_mv,
                    short ref_frame,
                    int   list,
                    int   block_x,
                    int   block_y,
                    int   blockshape_x,
                    int   blockshape_y)
{
int mb_x           = 4*block_x;
int mb_y           = 4*block_y;
int mb_nr           = img->current_mb_nr;
//以上获得实际坐标(整象素)
int mv_a, mv_b, mv_c, pred_vec=0;
int mvPredType, rFrameL, rFrameU, rFrameUR;
int hv;
 
PixelPos block_a, block_b, block_c, block_d;
//A,B,C,D子块的坐标,尺寸,以4X4为单位.
int SAD_a=0, SAD_b=0, SAD_c=0, SAD_d=0;
int temp_pred_SAD[2];
//SAD预测,个人估计是为快速运动估计准备的
int fastme_sp_enable=(input->FMEnable && (ref_frame<=0) && (FME_blocktype==1));
if (fastme_sp_enable)
  pred_SAD_space=0;
//是否启用快速运动估计
getLuma4x4Neighbour(mb_nr, block_x, block_y,       -1, 0, &block_a);
getLuma4x4Neighbour(mb_nr, block_x, block_y,         0, -1, &block_b);
getLuma4x4Neighbour(mb_nr, block_x, block_y, blockshape_x, -1, &block_c);
getLuma4x4Neighbour(mb_nr, block_x, block_y,       -1, -1, &block_d);
//获得a,b,c,d四个相邻子块的位置,尺寸。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值