Take neighboring motion vectors into account.
Added a static inline method which looks at the up and left neighbors
(relative to current) of a motion vector and accounts for similarities
where the compression will be higher.
The scale_factor of 1.2 and mv_scale_factor of 5.0 are experimentally
chosen to yield a performance improvement. Against the baseline with the
CONFIG_BITRATE_ACCURACY flag off, these settings improve the overall
PSNR by 1.33% and the abs_rc_error by 8.70%.
Updated tpl_model_test accordingly and added more test cases.
BUG=aomedia:3045
Change-Id: I0b02ba786998e8c967d1ddd0d7f0fc8816904c06
diff --git a/av1/encoder/tpl_model.c b/av1/encoder/tpl_model.c
index a129342..0c1f112 100644
--- a/av1/encoder/tpl_model.c
+++ b/av1/encoder/tpl_model.c
@@ -2111,6 +2111,49 @@
return total_mv_bits * mv_scale_factor;
}
+// Use upper and left neighbor block as the reference MVs.
+// Compute the minimum difference between current MV and reference MV.
+int_mv av1_compute_mv_difference(const TplDepFrame *tpl_frame, int row, int col,
+ int step, int tpl_stride, int right_shift) {
+ const TplDepStats *tpl_stats =
+ &tpl_frame
+ ->tpl_stats_ptr[av1_tpl_ptr_pos(row, col, tpl_stride, right_shift)];
+ int_mv current_mv = tpl_stats->mv[tpl_stats->ref_frame_index[0]];
+ int current_mv_magnitude =
+ abs(current_mv.as_mv.row) + abs(current_mv.as_mv.col);
+
+ // Retrieve the up and left neighbors.
+ int up_error = INT_MAX;
+ int_mv up_mv_diff;
+ if (row - step >= 0) {
+ tpl_stats = &tpl_frame->tpl_stats_ptr[av1_tpl_ptr_pos(
+ row - step, col, tpl_stride, right_shift)];
+ up_mv_diff = tpl_stats->mv[tpl_stats->ref_frame_index[0]];
+ up_mv_diff.as_mv.row = current_mv.as_mv.row - up_mv_diff.as_mv.row;
+ up_mv_diff.as_mv.col = current_mv.as_mv.col - up_mv_diff.as_mv.col;
+ up_error = abs(up_mv_diff.as_mv.row) + abs(up_mv_diff.as_mv.col);
+ }
+
+ int left_error = INT_MAX;
+ int_mv left_mv_diff;
+ if (col - step >= 0) {
+ tpl_stats = &tpl_frame->tpl_stats_ptr[av1_tpl_ptr_pos(
+ row, col - step, tpl_stride, right_shift)];
+ left_mv_diff = tpl_stats->mv[tpl_stats->ref_frame_index[0]];
+ left_mv_diff.as_mv.row = current_mv.as_mv.row - left_mv_diff.as_mv.row;
+ left_mv_diff.as_mv.col = current_mv.as_mv.col - left_mv_diff.as_mv.col;
+ left_error = abs(left_mv_diff.as_mv.row) + abs(left_mv_diff.as_mv.col);
+ }
+
+ // Return the MV with the minimum distance from current.
+ if (up_error < left_error && up_error < current_mv_magnitude) {
+ return up_mv_diff;
+ } else if (left_error < up_error && left_error < current_mv_magnitude) {
+ return left_mv_diff;
+ }
+ return current_mv;
+}
+
/* Compute the entropy of motion vectors for a single frame. */
double av1_tpl_compute_frame_mv_entropy(const TplDepFrame *tpl_frame,
uint8_t right_shift) {
@@ -2127,9 +2170,8 @@
for (int row = 0; row < tpl_frame->mi_rows; row += step) {
for (int col = 0; col < tpl_frame->mi_cols; col += step) {
- const TplDepStats *tpl_stats = &tpl_frame->tpl_stats_ptr[av1_tpl_ptr_pos(
- row, col, tpl_stride, right_shift)];
- int_mv mv = tpl_stats->mv[tpl_stats->ref_frame_index[0]];
+ int_mv mv = av1_compute_mv_difference(tpl_frame, row, col, step,
+ tpl_stride, right_shift);
count_row[clamp(mv.as_mv.row, 0, 499)] += 1;
count_col[clamp(mv.as_mv.row, 0, 499)] += 1;
n += 1;
diff --git a/av1/encoder/tpl_model.h b/av1/encoder/tpl_model.h
index bcbfcf2..fb1029e 100644
--- a/av1/encoder/tpl_model.h
+++ b/av1/encoder/tpl_model.h
@@ -250,7 +250,7 @@
vbr_rc_info->show_frame_count = show_frame_count;
vbr_rc_info->keyframe_bitrate = 0;
vbr_rc_info->scale_factor = 1.2;
- vbr_rc_info->mv_scale_factor = 4.0;
+ vbr_rc_info->mv_scale_factor = 5.0;
}
static INLINE void vbr_rc_set_gop_bit_budget(VBR_RATECTRL_INFO *vbr_rc_info,
@@ -584,6 +584,21 @@
double av1_tpl_compute_mv_bits(const TplParams *tpl_data, int gf_group_size,
int gf_frame_index, double mv_scale_factor);
+/*!\brief Improve the motion vector estimation by taking neighbors into account.
+ *
+ * Use the upper and left neighbor block as the reference MVs.
+ * Compute the minimum difference between current MV and reference MV.
+ *
+ * \param[in] tpl_frame Tpl frame struct
+ * \param[in] row Current row
+ * \param[in] col Current column
+ * \param[in] step Step parameter for av1_tpl_ptr_pos
+ * \param[in] tpl_stride Stride parameter for av1_tpl_ptr_pos
+ * \param[in] right_shift Right shift parameter for av1_tpl_ptr_pos
+ */
+int_mv av1_compute_mv_difference(const TplDepFrame *tpl_frame, int row, int col,
+ int step, int tpl_stride, int right_shift);
+
/*!\brief Compute the entropy of motion vectors for a single frame.
*
* \param[in] tpl_frame TPL frame struct
diff --git a/test/tpl_model_test.cc b/test/tpl_model_test.cc
index 16ec433..bc8dbff 100644
--- a/test/tpl_model_test.cc
+++ b/test/tpl_model_test.cc
@@ -306,6 +306,50 @@
}
}
+TEST(TplModelTest, ComputeMVDifferenceTest) {
+ TplDepFrame tpl_frame_small;
+ tpl_frame_small.is_valid = true;
+ tpl_frame_small.mi_rows = 4;
+ tpl_frame_small.mi_cols = 4;
+ tpl_frame_small.stride = 1;
+ uint8_t right_shift_small = 1;
+ int step_small = 1 << right_shift_small;
+
+ // Test values for motion vectors.
+ int mv_vals_small[4] = { 1, 2, 3, 4 };
+ int index = 0;
+
+ // 4x4 blocks means we need to allocate a 4 size array.
+ // According to av1_tpl_ptr_pos:
+ // (row >> right_shift) * stride + (col >> right_shift)
+ // (4 >> 1) * 1 + (4 >> 1) = 4
+ TplDepStats stats_buf_small[4];
+ tpl_frame_small.tpl_stats_ptr = stats_buf_small;
+
+ for (int row = 0; row < tpl_frame_small.mi_rows; row += step_small) {
+ for (int col = 0; col < tpl_frame_small.mi_cols; col += step_small) {
+ TplDepStats tpl_stats;
+ tpl_stats.ref_frame_index[0] = 0;
+ int_mv mv;
+ mv.as_mv.row = mv_vals_small[index];
+ mv.as_mv.col = mv_vals_small[index];
+ index++;
+ tpl_stats.mv[0] = mv;
+ tpl_frame_small.tpl_stats_ptr[av1_tpl_ptr_pos(
+ row, col, tpl_frame_small.stride, right_shift_small)] = tpl_stats;
+ }
+ }
+
+ int_mv result_mv =
+ av1_compute_mv_difference(&tpl_frame_small, 1, 1, step_small,
+ tpl_frame_small.stride, right_shift_small);
+
+ // Expect the result to be exactly equal to 1 because this is the difference
+ // between neighboring motion vectors in this instance.
+ EXPECT_EQ(result_mv.as_mv.row, 1);
+ EXPECT_EQ(result_mv.as_mv.col, 1);
+}
+
TEST(TplModelTest, ComputeMVBitsTest) {
TplDepFrame tpl_frame;
tpl_frame.is_valid = true;
@@ -314,7 +358,11 @@
tpl_frame.stride = 24;
uint8_t right_shift = 2;
int step = 1 << right_shift;
- int mv_val = 0; // Example values for motion vectors.
+ // Test values for motion vectors.
+ int mv_vals_ordered[16] = { 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16 };
+ int mv_vals[16] = { 1, 16, 2, 15, 3, 14, 4, 13, 5, 12, 6, 11, 7, 10, 8, 9 };
+ int index = 0;
// 16x16 blocks means we need to allocate a 100 size array.
// According to av1_tpl_ptr_pos:
@@ -328,9 +376,9 @@
TplDepStats tpl_stats;
tpl_stats.ref_frame_index[0] = 0;
int_mv mv;
- mv.as_mv.row = mv_val;
- mv.as_mv.col = mv_val;
- mv_val++;
+ mv.as_mv.row = mv_vals_ordered[index];
+ mv.as_mv.col = mv_vals_ordered[index];
+ index++;
tpl_stats.mv[0] = mv;
tpl_frame.tpl_stats_ptr[av1_tpl_ptr_pos(row, col, tpl_frame.stride,
right_shift)] = tpl_stats;
@@ -338,7 +386,31 @@
}
double result = av1_tpl_compute_frame_mv_entropy(&tpl_frame, right_shift);
- EXPECT_EQ(result, 128);
+
+ // Expect the result to be low because the motion vectors are ordered.
+ // The estimation algorithm takes this into account and reduces the cost.
+ EXPECT_NEAR(result, 20, 5);
+
+ index = 0;
+ for (int row = 0; row < tpl_frame.mi_rows; row += step) {
+ for (int col = 0; col < tpl_frame.mi_cols; col += step) {
+ TplDepStats tpl_stats;
+ tpl_stats.ref_frame_index[0] = 0;
+ int_mv mv;
+ mv.as_mv.row = mv_vals[index];
+ mv.as_mv.col = mv_vals[index];
+ index++;
+ tpl_stats.mv[0] = mv;
+ tpl_frame.tpl_stats_ptr[av1_tpl_ptr_pos(row, col, tpl_frame.stride,
+ right_shift)] = tpl_stats;
+ }
+ }
+
+ result = av1_tpl_compute_frame_mv_entropy(&tpl_frame, right_shift);
+
+ // Expect the result to be higher because the vectors are not ordered.
+ // Neighboring vectors will have different values, increasing the cost.
+ EXPECT_NEAR(result, 70, 5);
}
} // namespace