Further change to mv reference search.
authorPaul Wilkins <paulwilkins@google.com>
Thu, 3 Jan 2013 15:14:36 +0000 (15:14 +0000)
committerPaul Wilkins <paulwilkins@google.com>
Fri, 4 Jan 2013 15:18:10 +0000 (15:18 +0000)
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

vp9/common/vp9_mvref_common.c

index bbed055..4b576e8 100644 (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;
+
+  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 a best matching candidate refenence motion vector
-// from the given mode info structure (if available)
-static int get_candidate_mvref(
+// 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;
-
-    // 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;
-    }
-
-  // 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;
+  // 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;
     }
 
-  // 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.
+    // 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;
+      c2_mv->as_int = candidate_mi->mbmi.mv[1].as_int;
     }
-
-  // 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 (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_distance_weight[i] + 16);
+      }
+    }
+  }
 
-      // 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 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)) {
 
-        addmv_and_shuffle(candidate_mvs, candidate_scores,
-                          &index, c_refmv, ref_weight);
+        candidate_mi = here + mv_ref_search[i][0] +
+                       (mv_ref_search[i][1] * xd->mode_info_stride);
 
-        // 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);
+        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);
     }
   }