Further change to mv reference search.

This experimental change reorders the search so
that all possible references that match the target
reference frame are tested first and these in order
of distance from the current block. These will usually
be the highest scoring candidates.

If we do not find enough good candidates this way
we try non matching cases. These will usually be lower
scoring candidates.

The change in order together with breakouts when
we have found enough candidates should reduce
the computational cost and especially reduce the number
of sort operations.

Quality Results:
Std Hd +0.228%, Hd +0.074%, YT +0.046%, derf +0.137%

This effect is probably due to the fact that more distant
weak candidates are now less likely to get "promoted" over
near candidates even if they are repeated.

Change-Id: Iec37e77d88a48ad0ee1f315b14327a95d63f81f6
This commit is contained in:
Paul Wilkins 2013-01-03 15:14:36 +00:00
parent df7ce5a711
commit c6ba3a3d85

View File

@ -25,6 +25,7 @@ static int sb_mv_ref_search[MVREF_NEIGHBOURS][2] = {
static int sb_ref_distance_weight[MVREF_NEIGHBOURS] =
{ 3, 3, 2, 2, 2, 1, 1, 1 };
#endif
// clamp_mv
#define MV_BORDER (16 << 3) // Allow 16 pels in 1/8th pel units
static void clamp_mv(const MACROBLOCKD *xd, int_mv *mv) {
@ -40,10 +41,29 @@ static void clamp_mv(const MACROBLOCKD *xd, int_mv *mv) {
mv->as_mv.row = xd->mb_to_bottom_edge + MV_BORDER;
}
// Gets a candidate refenence motion vector from the given mode info
// structure if one exists that matches the given reference frame.
static int get_matching_candidate(
const MODE_INFO *candidate_mi,
MV_REFERENCE_FRAME ref_frame,
int_mv *c_mv
) {
int ret_val = TRUE;
// Gets a best matching candidate refenence motion vector
// from the given mode info structure (if available)
static int get_candidate_mvref(
if (ref_frame == candidate_mi->mbmi.ref_frame) {
c_mv->as_int = candidate_mi->mbmi.mv[0].as_int;
} else if (ref_frame == candidate_mi->mbmi.second_ref_frame) {
c_mv->as_int = candidate_mi->mbmi.mv[1].as_int;
} else {
ret_val = FALSE;
}
return ret_val;
}
// Gets candidate refenence motion vector(s) from the given mode info
// structure if they exists and do NOT match the given reference frame.
static void get_non_matching_candidates(
const MODE_INFO *candidate_mi,
MV_REFERENCE_FRAME ref_frame,
MV_REFERENCE_FRAME *c_ref_frame,
@ -52,61 +72,29 @@ static int get_candidate_mvref(
int_mv *c2_mv
) {
int ret_val = FALSE;
c_mv->as_int = 0;
c2_mv->as_int = 0;
*c_ref_frame = INTRA_FRAME;
*c2_ref_frame = INTRA_FRAME;
// Target ref frame matches candidate first ref frame
if (ref_frame == candidate_mi->mbmi.ref_frame) {
c_mv->as_int = candidate_mi->mbmi.mv[0].as_int;
*c_ref_frame = ref_frame;
ret_val = TRUE;
// If first candidate not valid neither will be.
if (candidate_mi->mbmi.ref_frame > INTRA_FRAME) {
// First candidate
if (candidate_mi->mbmi.ref_frame != ref_frame) {
*c_ref_frame = candidate_mi->mbmi.ref_frame;
c_mv->as_int = candidate_mi->mbmi.mv[0].as_int;
}
// Is there a second non zero vector we can use.
// Second candidate
if ((candidate_mi->mbmi.second_ref_frame > INTRA_FRAME) &&
(candidate_mi->mbmi.mv[1].as_int != 0) &&
(candidate_mi->mbmi.mv[1].as_int != c_mv->as_int)) {
c2_mv->as_int = candidate_mi->mbmi.mv[1].as_int;
(candidate_mi->mbmi.second_ref_frame != ref_frame)) { // &&
// (candidate_mi->mbmi.mv[1].as_int != 0) &&
// (candidate_mi->mbmi.mv[1].as_int !=
// candidate_mi->mbmi.mv[0].as_int)) {
*c2_ref_frame = candidate_mi->mbmi.second_ref_frame;
}
// Target ref frame matches candidate second ref frame
} else if (ref_frame == candidate_mi->mbmi.second_ref_frame) {
c_mv->as_int = candidate_mi->mbmi.mv[1].as_int;
*c_ref_frame = ref_frame;
ret_val = TRUE;
// Is there a second non zero vector we can use.
if ((candidate_mi->mbmi.ref_frame > INTRA_FRAME) &&
(candidate_mi->mbmi.mv[0].as_int != 0) &&
(candidate_mi->mbmi.mv[0].as_int != c_mv->as_int)) {
c2_mv->as_int = candidate_mi->mbmi.mv[0].as_int;
*c2_ref_frame = candidate_mi->mbmi.ref_frame;
}
// No ref frame matches so use first ref mv as first choice
} else if (candidate_mi->mbmi.ref_frame > INTRA_FRAME) {
c_mv->as_int = candidate_mi->mbmi.mv[0].as_int;
*c_ref_frame = candidate_mi->mbmi.ref_frame;
ret_val = TRUE;
// Is there a second non zero vector we can use.
if ((candidate_mi->mbmi.second_ref_frame > INTRA_FRAME) &&
(candidate_mi->mbmi.mv[1].as_int != 0) &&
(candidate_mi->mbmi.mv[1].as_int != c_mv->as_int)) {
c2_mv->as_int = candidate_mi->mbmi.mv[1].as_int;
*c2_ref_frame = candidate_mi->mbmi.second_ref_frame;
}
// If only the second ref mv is valid:- (Should not trigger in current code
// base given current possible compound prediction options).
} else if (candidate_mi->mbmi.second_ref_frame > INTRA_FRAME) {
c_mv->as_int = candidate_mi->mbmi.mv[1].as_int;
*c_ref_frame = candidate_mi->mbmi.second_ref_frame;
ret_val = TRUE;
}
return ret_val;
}
// Performs mv adjustment based on reference frame and clamps the MV
@ -240,8 +228,6 @@ void vp9_find_mv_refs(
int candidate_scores[MAX_MV_REF_CANDIDATES];
int index = 0;
int split_count = 0;
int ref_weight = 0;
int valid_mv_ref;
int (*mv_ref_search)[2];
int *ref_distance_weight;
@ -262,8 +248,9 @@ void vp9_find_mv_refs(
mv_ref_search = mb_mv_ref_search;
ref_distance_weight = mb_ref_distance_weight;
#endif
// Populate a list with candidate reference vectors from the
// spatial neighbours.
// We first scan for candidate vectors that match the current reference frame
// Look at nearest neigbours
for (i = 0; i < 2; ++i) {
if (((mv_ref_search[i][0] << 7) >= xd->mb_to_left_edge) &&
((mv_ref_search[i][1] << 7) >= xd->mb_to_top_edge)) {
@ -271,89 +258,88 @@ void vp9_find_mv_refs(
candidate_mi = here + mv_ref_search[i][0] +
(mv_ref_search[i][1] * xd->mode_info_stride);
valid_mv_ref = get_candidate_mvref(candidate_mi, ref_frame,
&c_ref_frame, &c_refmv,
&c2_ref_frame, &c2_refmv);
// If there is a valid MV candidate then add it to the list
if (valid_mv_ref) {
scale_mv(xd, ref_frame, c_ref_frame, &c_refmv, ref_sign_bias );
ref_weight = ref_distance_weight[i] +
((c_ref_frame == ref_frame) << 4);
split_count += (candidate_mi->mbmi.mode == SPLITMV);
if (get_matching_candidate(candidate_mi, ref_frame, &c_refmv)) {
clamp_mv(xd, &c_refmv);
addmv_and_shuffle(candidate_mvs, candidate_scores,
&index, c_refmv, ref_weight);
// If there is a second valid mv then add it as well.
if (c2_ref_frame > INTRA_FRAME) {
scale_mv(xd, ref_frame, c2_ref_frame, &c2_refmv, ref_sign_bias );
ref_weight = ref_distance_weight[i] +
((c2_ref_frame == ref_frame) << 4);
addmv_and_shuffle(candidate_mvs, candidate_scores,
&index, c2_refmv, ref_weight);
}
&index, c_refmv, ref_distance_weight[i] + 16);
}
split_count += (candidate_mi->mbmi.mode == SPLITMV);
}
}
// Look at the corresponding vector in the last frame
// Look in the last frame
candidate_mi = lf_here;
valid_mv_ref = get_candidate_mvref(candidate_mi, ref_frame,
&c_ref_frame, &c_refmv,
&c2_ref_frame, &c2_refmv);
// If there is a valid MV candidate then add it to the list
if (valid_mv_ref) {
scale_mv(xd, ref_frame, c_ref_frame, &c_refmv, ref_sign_bias );
ref_weight = 2 + ((c_ref_frame == ref_frame) << 4);
if (get_matching_candidate(candidate_mi, ref_frame, &c_refmv)) {
clamp_mv(xd, &c_refmv);
addmv_and_shuffle(candidate_mvs, candidate_scores,
&index, c_refmv, ref_weight);
// If there is a second valid mv then add it as well.
if (c2_ref_frame > INTRA_FRAME) {
scale_mv(xd, ref_frame, c2_ref_frame, &c2_refmv, ref_sign_bias );
ref_weight = ref_distance_weight[i] +
((c2_ref_frame == ref_frame) << 4);
addmv_and_shuffle(candidate_mvs, candidate_scores,
&index, c2_refmv, ref_weight);
}
&index, c_refmv, 18);
}
// Populate a list with candidate reference vectors from the
// spatial neighbours.
for (i = 2; (i < MVREF_NEIGHBOURS) && (index < (MAX_MV_REFS - 2)); ++i) {
// More distant neigbours
for (i = 2; (i < MVREF_NEIGHBOURS) &&
(index < (MAX_MV_REF_CANDIDATES - 1)); ++i) {
if (((mv_ref_search[i][0] << 7) >= xd->mb_to_left_edge) &&
((mv_ref_search[i][1] << 7) >= xd->mb_to_top_edge)) {
candidate_mi = here + mv_ref_search[i][0] +
(mv_ref_search[i][1] * xd->mode_info_stride);
valid_mv_ref = get_candidate_mvref(candidate_mi, ref_frame,
&c_ref_frame, &c_refmv,
&c2_ref_frame, &c2_refmv);
// If there is a valid MV candidate then add it to the list
if (valid_mv_ref) {
scale_mv(xd, ref_frame, c_ref_frame, &c_refmv, ref_sign_bias );
ref_weight = ref_distance_weight[i] +
((c_ref_frame == ref_frame) << 4);
if (get_matching_candidate(candidate_mi, ref_frame, &c_refmv)) {
clamp_mv(xd, &c_refmv);
addmv_and_shuffle(candidate_mvs, candidate_scores,
&index, c_refmv, ref_weight);
&index, c_refmv, ref_distance_weight[i] + 16);
}
}
}
// If there is a second valid mv then add it as well.
if (c2_ref_frame > INTRA_FRAME) {
scale_mv(xd, ref_frame, c2_ref_frame, &c2_refmv, ref_sign_bias );
ref_weight = ref_distance_weight[i] +
((c2_ref_frame == ref_frame) << 4);
// If we have not found enough candidates consider ones where the
// reference frame does not match. Break out when we have
// MAX_MV_REF_CANDIDATES candidates.
// Look first at spatial neighbours
if (index < (MAX_MV_REF_CANDIDATES - 1)) {
for (i = 0; i < MVREF_NEIGHBOURS; ++i) {
if (((mv_ref_search[i][0] << 7) >= xd->mb_to_left_edge) &&
((mv_ref_search[i][1] << 7) >= xd->mb_to_top_edge)) {
candidate_mi = here + mv_ref_search[i][0] +
(mv_ref_search[i][1] * xd->mode_info_stride);
get_non_matching_candidates(candidate_mi, ref_frame,
&c_ref_frame, &c_refmv,
&c2_ref_frame, &c2_refmv);
if (c_ref_frame != INTRA_FRAME) {
scale_mv(xd, ref_frame, c_ref_frame, &c_refmv, ref_sign_bias);
addmv_and_shuffle(candidate_mvs, candidate_scores,
&index, c2_refmv, ref_weight);
&index, c_refmv, ref_distance_weight[i]);
}
if (c2_ref_frame != INTRA_FRAME) {
scale_mv(xd, ref_frame, c2_ref_frame, &c2_refmv, ref_sign_bias);
addmv_and_shuffle(candidate_mvs, candidate_scores,
&index, c2_refmv, ref_distance_weight[i]);
}
}
if (index >= (MAX_MV_REF_CANDIDATES - 1)) {
break;
}
}
}
// Look at the last frame
if (index < (MAX_MV_REF_CANDIDATES - 1)) {
candidate_mi = lf_here;
get_non_matching_candidates(candidate_mi, ref_frame,
&c_ref_frame, &c_refmv,
&c2_ref_frame, &c2_refmv);
if (c_ref_frame != INTRA_FRAME) {
scale_mv(xd, ref_frame, c_ref_frame, &c_refmv, ref_sign_bias);
addmv_and_shuffle(candidate_mvs, candidate_scores,
&index, c_refmv, 2);
}
if (c2_ref_frame != INTRA_FRAME) {
scale_mv(xd, ref_frame, c2_ref_frame, &c2_refmv, ref_sign_bias);
addmv_and_shuffle(candidate_mvs, candidate_scores,
&index, c2_refmv, 2);
}
}