📄 motion_est.c
字号:
/**************************************************************************
*
* Modifications:
*
* 02.04.2002 add EPZS(^2) as ME algorithm, use PMV_USESQUARES to choose between
* EPZS and EPZS^2
* 08.02.2002 split up PMVfast into three routines: PMVFast, PMVFast_MainLoop
* PMVFast_Refine to support multiple searches with different start points
* 07.01.2002 uv-block-based interpolation
* 06.01.2002 INTER/INTRA-decision is now done before any SEARCH8 (speedup)
* changed INTER_BIAS to 150 (as suggested by suxen_drol)
* removed halfpel refinement step in PMVfastSearch8 + quality=5
* added new quality mode = 6 which performs halfpel refinement
* filesize difference between quality 5 and 6 is smaller than 1%
* (Isibaar)
* 31.12.2001 PMVfastSearch16 and PMVfastSearch8 (gruel)
* 30.12.2001 get_range/MotionSearchX simplified; blue/green bug fix
* 22.12.2001 commented best_point==99 check
* 19.12.2001 modified get_range (purple bug fix)
* 15.12.2001 moved pmv displacement from mbprediction
* 02.12.2001 motion estimation/compensation split (Isibaar)
* 16.11.2001 rewrote/tweaked search algorithms; pross@cs.rmit.edu.au
* 10.11.2001 support for sad16/sad8 functions
* 28.08.2001 reactivated MODE_INTER4V for EXT_MODE
* 24.08.2001 removed MODE_INTER4V_Q, disabled MODE_INTER4V for EXT_MODE
* 22.08.2001 added MODE_INTER4V_Q
* 20.08.2001 added pragma to get rid of internal compiler error with VC6
* idea by Cyril. Thanks.
*
* Michael Militzer <isibaar@videocoding.de>
*
**************************************************************************/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "../encoder.h"
#include "../utils/mbfunctions.h"
#include "../prediction/mbprediction.h"
#include "../global.h"
#include "../utils/timer.h"
#include "sad.h"
// very large value
#define MV_MAX_ERROR (4096 * 256)
// stop search if sdelta < THRESHOLD
#define MV16_THRESHOLD 192
#define MV8_THRESHOLD 56
/* sad16(0,0) bias; mpeg4 spec suggests nb/2+1 */
/* nb = vop pixels * 2^(bpp-8) */
#define MV16_00_BIAS (128+1)
/* INTER bias for INTER/INTRA decision; mpeg4 spec suggests 2*nb */
#define INTER_BIAS 512
/* Parameters which control inter/inter4v decision */
#define IMV16X16 5
/* vector map (vlc delta size) smoother parameters */
#define NEIGH_TEND_16X16 2
#define NEIGH_TEND_8X8 2
// fast ((A)/2)*2
#define EVEN(A) (((A)<0?(A)+1:(A)) & ~1)
#ifndef MIN
#define MIN(X, Y) ((X)<(Y)?(X):(Y))
#endif
#ifndef MAX
#define MAX(X, Y) ((X)>(Y)?(X):(Y))
#endif
#define ABS(X) (((X)>0)?(X):-(X))
#define SIGN(X) (((X)>0)?1:-1)
int32_t PMVfastSearch16(
const uint8_t * const pRef,
const uint8_t * const pRefH,
const uint8_t * const pRefV,
const uint8_t * const pRefHV,
const IMAGE * const pCur,
const int x, const int y,
const uint32_t MotionFlags,
const MBParam * const pParam,
MACROBLOCK * const pMBs,
VECTOR * const currMV,
VECTOR * const currPMV);
int32_t EPZSSearch16(
const uint8_t * const pRef,
const uint8_t * const pRefH,
const uint8_t * const pRefV,
const uint8_t * const pRefHV,
const IMAGE * const pCur,
const int x, const int y,
const uint32_t MotionFlags,
const MBParam * const pParam,
MACROBLOCK * const pMBs,
VECTOR * const currMV,
VECTOR * const currPMV);
int32_t PMVfastSearch8(
const uint8_t * const pRef,
const uint8_t * const pRefH,
const uint8_t * const pRefV,
const uint8_t * const pRefHV,
const IMAGE * const pCur,
const int x, const int y,
const int start_x, int start_y,
const uint32_t MotionFlags,
const MBParam * const pParam,
MACROBLOCK * const pMBs,
VECTOR * const currMV,
VECTOR * const currPMV);
int32_t EPZSSearch8(
const uint8_t * const pRef,
const uint8_t * const pRefH,
const uint8_t * const pRefV,
const uint8_t * const pRefHV,
const IMAGE * const pCur,
const int x, const int y,
const int start_x, int start_y,
const uint32_t MotionFlags,
const MBParam * const pParam,
MACROBLOCK * const pMBs,
VECTOR * const currMV,
VECTOR * const currPMV);
typedef int32_t (MainSearch16Func)(
const uint8_t * const pRef,
const uint8_t * const pRefH,
const uint8_t * const pRefV,
const uint8_t * const pRefHV,
const uint8_t * const cur,
const int x, const int y,
int32_t startx, int32_t starty,
int32_t iMinSAD,
VECTOR * const currMV,
const VECTOR * const pmv,
const int32_t min_dx, const int32_t max_dx,
const int32_t min_dy, const int32_t max_dy,
const int32_t iEdgedWidth,
const int32_t iDiamondSize,
const int32_t iFcode,
const int32_t iQuant,
int iFound);
typedef MainSearch16Func* MainSearch16FuncPtr;
typedef int32_t (MainSearch8Func)(
const uint8_t * const pRef,
const uint8_t * const pRefH,
const uint8_t * const pRefV,
const uint8_t * const pRefHV,
const uint8_t * const cur,
const int x, const int y,
int32_t startx, int32_t starty,
int32_t iMinSAD,
VECTOR * const currMV,
const VECTOR * const pmv,
const int32_t min_dx, const int32_t max_dx,
const int32_t min_dy, const int32_t max_dy,
const int32_t iEdgedWidth,
const int32_t iDiamondSize,
const int32_t iFcode,
const int32_t iQuant,
int iFound);
typedef MainSearch8Func* MainSearch8FuncPtr;
// mv.length table
static const uint32_t mvtab[33] = {
1, 2, 3, 4, 6, 7, 7, 7,
9, 9, 9, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10,
10, 11, 11, 11, 11, 11, 11, 12, 12
};
static __inline uint32_t mv_bits(int32_t component, const uint32_t iFcode)
{
if (component == 0)
return 1;
if (component < 0)
component = -component;
if (iFcode == 1)
{
if (component > 32)
component = 32;
return mvtab[component] + 1;
}
component += (1 << (iFcode - 1)) - 1;
component >>= (iFcode - 1);
if (component > 32)
component = 32;
return mvtab[component] + 1 + iFcode - 1;
}
static __inline uint32_t calc_delta_16(const int32_t dx, const int32_t dy, const uint32_t iFcode)
{
return NEIGH_TEND_16X16 * (mv_bits(dx, iFcode) + mv_bits(dy, iFcode));
}
static __inline uint32_t calc_delta_8(const int32_t dx, const int32_t dy, const uint32_t iFcode)
{
return NEIGH_TEND_8X8 * (mv_bits(dx, iFcode) + mv_bits(dy, iFcode));
}
/* calculate the min/max range (in halfpixels)
relative to the _MACROBLOCK_ position
*/
static void __inline get_range(
int32_t * const min_dx, int32_t * const max_dx,
int32_t * const min_dy, int32_t * const max_dy,
const uint32_t x, const uint32_t y,
const uint32_t block_sz, // block dimension, 8 or 16
const uint32_t width, const uint32_t height,
const uint32_t fcode)
{
const int search_range = 32 << (fcode - 1);
const int high = search_range - 1;
const int low = -search_range;
// convert full-pixel measurements to half pixel
const int hp_width = 2 * width;
const int hp_height = 2 * height;
const int hp_edge = 2 * block_sz;
const int hp_x = 2 * (x) * block_sz; // we need _right end_ of block, not x-coordinate
const int hp_y = 2 * (y) * block_sz; // same for _bottom end_
*max_dx = MIN(high, hp_width - hp_x);
*max_dy = MIN(high, hp_height - hp_y);
*min_dx = MAX(low, -(hp_edge + hp_x));
*min_dy = MAX(low, -(hp_edge + hp_y));
}
/*
* getref: calculate reference image pointer
* the decision to use interpolation h/v/hv or the normal image is
* based on dx & dy.
*/
static __inline const uint8_t * get_ref(
const uint8_t * const refn,
const uint8_t * const refh,
const uint8_t * const refv,
const uint8_t * const refhv,
const uint32_t x, const uint32_t y,
const uint32_t block, // block dimension, 8 or 16
const int32_t dx, const int32_t dy,
const uint32_t stride)
{
switch ( ((dx&1)<<1) + (dy&1) ) // ((dx%2)?2:0)+((dy%2)?1:0)
{
case 0 : return refn + (x*block+dx/2) + (y*block+dy/2)*stride;
case 1 : return refv + (x*block+dx/2) + (y*block+(dy-1)/2)*stride;
case 2 : return refh + (x*block+(dx-1)/2) + (y*block+dy/2)*stride;
default :
case 3 : return refhv + (x*block+(dx-1)/2) + (y*block+(dy-1)/2)*stride;
}
}
/* This is somehow a copy of get_ref, but with MV instead of X,Y */
static __inline const uint8_t * get_ref_mv(
const uint8_t * const refn,
const uint8_t * const refh,
const uint8_t * const refv,
const uint8_t * const refhv,
const uint32_t x, const uint32_t y,
const uint32_t block, // block dimension, 8 or 16
const VECTOR* mv, // measured in half-pel!
const uint32_t stride)
{
switch ( (((mv->x)&1)<<1) + ((mv->y)&1) )
{
case 0 : return refn + (x*block+(mv->x)/2) + (y*block+(mv->y)/2)*stride;
case 1 : return refv + (x*block+(mv->x)/2) + (y*block+((mv->y)-1)/2)*stride;
case 2 : return refh + (x*block+((mv->x)-1)/2) + (y*block+(mv->y)/2)*stride;
default :
case 3 : return refhv + (x*block+((mv->x)-1)/2) + (y*block+((mv->y)-1)/2)*stride;
}
}
#ifndef SEARCH16
#define SEARCH16 PMVfastSearch16
//#define SEARCH16 FullSearch16
//#define SEARCH16 EPZSSearch16
#endif
#ifndef SEARCH8
#define SEARCH8 PMVfastSearch8
//#define SEARCH8 EPZSSearch8
#endif
bool MotionEstimation(
MACROBLOCK * const pMBs,
MBParam * const pParam,
const IMAGE * const pRef,
const IMAGE * const pRefH,
const IMAGE * const pRefV,
const IMAGE * const pRefHV,
IMAGE * const pCurrent,
const uint32_t iLimit)
{
const uint32_t iWcount = pParam->mb_width;
const uint32_t iHcount = pParam->mb_height;
uint32_t i, j, iIntra = 0;
VECTOR mv16;
VECTOR pmv16;
int32_t sad8 = 0;
int32_t sad16;
int32_t deviation;
if (sadInit)
(*sadInit)();
// note: i==horizontal, j==vertical
for (i = 0; i < iHcount; i++)
for (j = 0; j < iWcount; j++)
{
MACROBLOCK *pMB = &pMBs[j + i * iWcount];
sad16 = SEARCH16(pRef->y, pRefH->y, pRefV->y, pRefHV->y, pCurrent,
j, i, pParam->motion_flags,
pParam, pMBs, &mv16, &pmv16);
pMB->sad16=sad16;
/* decide: MODE_INTER or MODE_INTRA
if (dev_intra < sad_inter - 2 * nb) use_intra
*/
deviation = dev16(pCurrent->y + j*16 + i*16*pParam->edged_width, pParam->edged_width);
if (deviation < (sad16 - INTER_BIAS))
{
pMB->mode = MODE_INTRA;
pMB->mvs[0].x = pMB->mvs[1].x = pMB->mvs[2].x = pMB->mvs[3].x = 0;
pMB->mvs[0].y = pMB->mvs[1].y = pMB->mvs[2].y = pMB->mvs[3].y = 0;
iIntra++;
if(iIntra >= iLimit)
return 1;
continue;
}
if (pParam->global_flags & XVID_INTER4V)
{
pMB->sad8[0] = SEARCH8(pRef->y, pRefH->y, pRefV->y, pRefHV->y, pCurrent,
2 * j, 2 * i, mv16.x, mv16.y, pParam->motion_flags,
pParam, pMBs, &pMB->mvs[0], &pMB->pmvs[0]);
pMB->sad8[1] = SEARCH8(pRef->y, pRefH->y, pRefV->y, pRefHV->y, pCurrent,
2 * j + 1, 2 * i, mv16.x, mv16.y, pParam->motion_flags,
pParam, pMBs, &pMB->mvs[1], &pMB->pmvs[1]);
pMB->sad8[2] = SEARCH8(pRef->y, pRefH->y, pRefV->y, pRefHV->y, pCurrent,
2 * j, 2 * i + 1, mv16.x, mv16.y, pParam->motion_flags,
pParam, pMBs, &pMB->mvs[2], &pMB->pmvs[2]);
pMB->sad8[3] = SEARCH8(pRef->y, pRefH->y, pRefV->y, pRefHV->y, pCurrent,
2 * j + 1, 2 * i + 1, mv16.x, mv16.y, pParam->motion_flags,
pParam, pMBs, &pMB->mvs[3], &pMB->pmvs[3]);
sad8 = pMB->sad8[0] + pMB->sad8[1] + pMB->sad8[2] + pMB->sad8[3];
}
/* decide: MODE_INTER or MODE_INTER4V
mpeg4: if (sad8 < sad16 - nb/2+1) use_inter4v
*/
if (pMB->dquant == NO_CHANGE) {
if (((pParam->global_flags & XVID_INTER4V)==0) ||
(sad16 < (sad8 + (int32_t)(IMV16X16 * pParam->quant)))) {
sad8 = sad16;
pMB->mode = MODE_INTER;
pMB->mvs[0].x = pMB->mvs[1].x = pMB->mvs[2].x = pMB->mvs[3].x = mv16.x;
pMB->mvs[0].y = pMB->mvs[1].y = pMB->mvs[2].y = pMB->mvs[3].y = mv16.y;
pMB->pmvs[0].x = pmv16.x;
pMB->pmvs[0].y = pmv16.y;
}
else
pMB->mode = MODE_INTER4V;
}
else
{
sad8 = sad16;
pMB->mode = MODE_INTER;
pMB->mvs[0].x = pMB->mvs[1].x = pMB->mvs[2].x = pMB->mvs[3].x = mv16.x;
pMB->mvs[0].y = pMB->mvs[1].y = pMB->mvs[2].y = pMB->mvs[3].y = mv16.y;
pMB->pmvs[0].x = pmv16.x;
pMB->pmvs[0].y = pmv16.y;
}
}
return 0;
}
#define MVzero(A) ( ((A).x)==(0) && ((A).y)==(0) )
#define MVequal(A,B) ( ((A).x)==((B).x) && ((A).y)==((B).y) )
#define CHECK_MV16_ZERO {\
if ( (0 <= max_dx) && (0 >= min_dx) \
&& (0 <= max_dy) && (0 >= min_dy) ) \
{ \
iSAD = sad16( cur, get_ref(pRef, pRefH, pRefV, pRefHV, x, y, 16, 0, 0 , iEdgedWidth), iEdgedWidth, MV_MAX_ERROR); \
iSAD += calc_delta_16(-pmv[0].x, -pmv[0].y, (uint8_t)iFcode) * iQuant;\
if (iSAD <= iQuant * 96) \
iSAD -= MV16_00_BIAS; \
if (iSAD < iMinSAD) \
{ iMinSAD=iSAD; currMV->x=0; currMV->y=0; } } \
}
#define NOCHECK_MV16_CANDIDATE(X,Y) { \
iSAD = sad16( cur, get_ref(pRef, pRefH, pRefV, pRefHV, x, y, 16, X, Y, iEdgedWidth),iEdgedWidth, iMinSAD); \
iSAD += calc_delta_16((X) - pmv[0].x, (Y) - pmv[0].y, (uint8_t)iFcode) * iQuant;\
if (iSAD < iMinSAD) \
{ iMinSAD=iSAD; currMV->x=(X); currMV->y=(Y); } \
}
#define CHECK_MV16_CANDIDATE(X,Y) { \
if ( ((X) <= max_dx) && ((X) >= min_dx) \
&& ((Y) <= max_dy) && ((Y) >= min_dy) ) \
{ \
iSAD = sad16( cur, get_ref(pRef, pRefH, pRefV, pRefHV, x, y, 16, X, Y, iEdgedWidth),iEdgedWidth, iMinSAD); \
iSAD += calc_delta_16((X) - pmv[0].x, (Y) - pmv[0].y, (uint8_t)iFcode) * iQuant;\
if (iSAD < iMinSAD) \
{ iMinSAD=iSAD; currMV->x=(X); currMV->y=(Y); } } \
}
#define CHECK_MV16_CANDIDATE_DIR(X,Y,D) { \
if ( ((X) <= max_dx) && ((X) >= min_dx) \
&& ((Y) <= max_dy) && ((Y) >= min_dy) ) \
{ \
iSAD = sad16( cur, get_ref(pRef, pRefH, pRefV, pRefHV, x, y, 16, X, Y, iEdgedWidth),iEdgedWidth, iMinSAD); \
iSAD += calc_delta_16((X) - pmv[0].x, (Y) - pmv[0].y, (uint8_t)iFcode) * iQuant;\
if (iSAD < iMinSAD) \
{ iMinSAD=iSAD; currMV->x=(X); currMV->y=(Y); iDirection=(D); } } \
}
#define CHECK_MV16_CANDIDATE_FOUND(X,Y,D) { \
if ( ((X) <= max_dx) && ((X) >= min_dx) \
&& ((Y) <= max_dy) && ((Y) >= min_dy) ) \
{ \
iSAD = sad16( cur, get_ref(pRef, pRefH, pRefV, pRefHV, x, y, 16, X, Y, iEdgedWidth),iEdgedWidth, iMinSAD); \
iSAD += calc_delta_16((X) - pmv[0].x, (Y) - pmv[0].y, (uint8_t)iFcode) * iQuant;\
if (iSAD < iMinSAD) \
{ iMinSAD=iSAD; currMV->x=(X); currMV->y=(Y); iDirection=(D); iFound=0; } } \
}
#define CHECK_MV8_ZERO {\
iSAD = sad8( cur, get_ref(pRef, pRefH, pRefV, pRefHV, x, y, 8, 0, 0 , iEdgedWidth), iEdgedWidth); \
iSAD += calc_delta_8(-pmv[0].x, -pmv[0].y, (uint8_t)iFcode) * iQuant;\
if (iSAD < iMinSAD) \
{ iMinSAD=iSAD; currMV->x=0; currMV->y=0; } \
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -