Add variance based pre-processing parameter selection

Taking into account the variance change to predict the vmaf_neg
distortions during pre-processing stage, this CL improves BD-rate of all
core metrics in mode tune=vmaf_neg:

                 PSNR   SSIM   VMAF   VMAF_NEG
midres          -1.22  -0.56   0.68     -0.54
hdres           -0.96  -0.94   1.02     -0.50
lowres_10bd     -2.70  -1.37   1.64     -1.16
midres_10bd     -1.99  -0.90   2.25     -0.64

Change-Id: I45db6a2ed53972b42cb58ee525c09f54e421970e
diff --git a/aom_dsp/vmaf.c b/aom_dsp/vmaf.c
index 1704dc4..4165343 100644
--- a/aom_dsp/vmaf.c
+++ b/aom_dsp/vmaf.c
@@ -25,7 +25,7 @@
 #endif
 
 #if CONFIG_USE_VMAF_RC
-#include <libvmaf.rc.h>
+#include <libvmaf/libvmaf.rc.h>
 #endif
 
 #include "aom_dsp/blend.h"
diff --git a/av1/encoder/encode_strategy.c b/av1/encoder/encode_strategy.c
index 0ab7d3a..31a5731 100644
--- a/av1/encoder/encode_strategy.c
+++ b/av1/encoder/encode_strategy.c
@@ -1373,7 +1373,7 @@
   if (!is_stat_generation_stage(cpi) &&
       (oxcf->tune_cfg.tuning >= AOM_TUNE_VMAF_WITH_PREPROCESSING &&
        oxcf->tune_cfg.tuning <= AOM_TUNE_VMAF_NEG_MAX_GAIN)) {
-    av1_update_vmaf_curve(cpi, cpi->source, &cpi->common.cur_frame->buf);
+    av1_update_vmaf_curve(cpi);
   }
 #endif
 
diff --git a/av1/encoder/tune_vmaf.c b/av1/encoder/tune_vmaf.c
index 08c3d4e..bbb1ff8 100644
--- a/av1/encoder/tune_vmaf.c
+++ b/av1/encoder/tune_vmaf.c
@@ -22,6 +22,173 @@
 
 static const double kBaselineVmaf = 97.42773;
 
+static void motion_search(AV1_COMP *cpi, const YV12_BUFFER_CONFIG *src,
+                          const YV12_BUFFER_CONFIG *ref,
+                          const BLOCK_SIZE block_size, const int mb_row,
+                          const int mb_col, FULLPEL_MV *ref_mv) {
+  // Block information (ONLY Y-plane is used for motion search).
+  const int mb_height = block_size_high[block_size];
+  const int mb_width = block_size_wide[block_size];
+  const int y_stride = src->y_stride;
+  assert(y_stride == ref->y_stride);
+  const int y_offset = mb_row * mb_height * y_stride + mb_col * mb_width;
+
+  // Save input state.
+  MACROBLOCK *const mb = &cpi->td.mb;
+  MACROBLOCKD *const mbd = &mb->e_mbd;
+  const struct buf_2d ori_src_buf = mb->plane[0].src;
+  const struct buf_2d ori_pre_buf = mbd->plane[0].pre[0];
+
+  // Parameters used for motion search.
+  FULLPEL_MOTION_SEARCH_PARAMS full_ms_params;
+  const SEARCH_METHODS search_method = NSTEP;
+  const search_site_config *search_site_cfg =
+      cpi->mv_search_params.search_site_cfg[SS_CFG_FPF];
+  const int step_param =
+      av1_init_search_range(AOMMAX(src->y_crop_width, src->y_crop_height));
+
+  // Baseline position for motion search (used for rate distortion comparison).
+  const MV baseline_mv = kZeroMv;
+
+  // Setup.
+  mb->plane[0].src.buf = src->y_buffer + y_offset;
+  mb->plane[0].src.stride = y_stride;
+  mbd->plane[0].pre[0].buf = ref->y_buffer + y_offset;
+  mbd->plane[0].pre[0].stride = y_stride;
+
+  // Unused intermediate results for motion search.
+  int cost_list[5];
+
+  // Do motion search.
+  // Only do full search on the entire block.
+  av1_make_default_fullpel_ms_params(&full_ms_params, cpi, mb, block_size,
+                                     &baseline_mv, search_site_cfg,
+                                     /*fine_search_interval=*/0);
+  av1_set_mv_search_method(&full_ms_params, search_site_cfg, search_method);
+  av1_full_pixel_search(*ref_mv, &full_ms_params, step_param,
+                        cond_cost_list(cpi, cost_list), ref_mv, NULL);
+
+  // Restore input state.
+  mb->plane[0].src = ori_src_buf;
+  mbd->plane[0].pre[0] = ori_pre_buf;
+}
+
+static unsigned int residual_variance(const AV1_COMP *cpi,
+                                      const YV12_BUFFER_CONFIG *src,
+                                      const YV12_BUFFER_CONFIG *ref,
+                                      const BLOCK_SIZE block_size,
+                                      const int mb_row, const int mb_col,
+                                      FULLPEL_MV ref_mv, unsigned int *sse) {
+  const int mb_height = block_size_high[block_size];
+  const int mb_width = block_size_wide[block_size];
+  const int y_stride = src->y_stride;
+  assert(y_stride == ref->y_stride);
+  const int y_offset = mb_row * mb_height * y_stride + mb_col * mb_width;
+  const int mv_offset = ref_mv.row * y_stride + ref_mv.col;
+  const unsigned int var =
+      cpi->fn_ptr[block_size].vf(ref->y_buffer + y_offset + mv_offset, y_stride,
+                                 src->y_buffer + y_offset, y_stride, sse);
+  return var;
+}
+
+static double frame_average_variance(const AV1_COMP *const cpi,
+                                     const YV12_BUFFER_CONFIG *const frame) {
+  const uint8_t *const y_buffer = frame->y_buffer;
+  const int y_stride = frame->y_stride;
+  const BLOCK_SIZE block_size = BLOCK_64X64;
+
+  const int block_w = mi_size_wide[block_size] * 4;
+  const int block_h = mi_size_high[block_size] * 4;
+  int row, col;
+  const int bit_depth = cpi->td.mb.e_mbd.bd;
+  double var = 0.0, var_count = 0.0;
+
+  // Loop through each block.
+  for (row = 0; row < frame->y_height / block_h; ++row) {
+    for (col = 0; col < frame->y_width / block_w; ++col) {
+      struct buf_2d buf;
+      const int row_offset_y = row * block_h;
+      const int col_offset_y = col * block_w;
+
+      buf.buf = (uint8_t *)y_buffer + row_offset_y * y_stride + col_offset_y;
+      buf.stride = y_stride;
+
+      if (cpi->common.seq_params.use_highbitdepth) {
+        assert(frame->flags & YV12_FLAG_HIGHBITDEPTH);
+        var += av1_high_get_sby_perpixel_variance(cpi, &buf, block_size,
+                                                  bit_depth);
+      } else {
+        var += av1_get_sby_perpixel_variance(cpi, &buf, block_size);
+      }
+      var_count += 1.0;
+    }
+  }
+  var /= var_count;
+  return var;
+}
+
+static double residual_frame_average_variance(AV1_COMP *cpi,
+                                              const YV12_BUFFER_CONFIG *src,
+                                              const YV12_BUFFER_CONFIG *ref,
+                                              FULLPEL_MV *mvs) {
+  if (ref == NULL) return frame_average_variance(cpi, src);
+  const BLOCK_SIZE block_size = BLOCK_16X16;
+  const int frame_height = src->y_height;
+  const int frame_width = src->y_width;
+  const int mb_height = block_size_high[block_size];
+  const int mb_width = block_size_wide[block_size];
+  const int mb_rows = (frame_height + mb_height - 1) / mb_height;
+  const int mb_cols = (frame_width + mb_width - 1) / mb_width;
+  const int num_planes = av1_num_planes(&cpi->common);
+  const int mi_h = mi_size_high_log2[block_size];
+  const int mi_w = mi_size_wide_log2[block_size];
+  assert(num_planes >= 1 && num_planes <= MAX_MB_PLANE);
+
+  // Save input state.
+  MACROBLOCK *const mb = &cpi->td.mb;
+  MACROBLOCKD *const mbd = &mb->e_mbd;
+  uint8_t *input_buffer[MAX_MB_PLANE];
+  for (int i = 0; i < num_planes; i++) {
+    input_buffer[i] = mbd->plane[i].pre[0].buf;
+  }
+  MB_MODE_INFO **input_mb_mode_info = mbd->mi;
+
+  bool do_motion_search = false;
+  if (mvs == NULL) {
+    do_motion_search = true;
+    mvs = (FULLPEL_MV *)aom_malloc(sizeof(*mvs) * mb_rows * mb_cols);
+    memset(mvs, 0, sizeof(*mvs) * mb_rows * mb_cols);
+  }
+
+  unsigned int variance = 0;
+  // Perform temporal filtering block by block.
+  for (int mb_row = 0; mb_row < mb_rows; mb_row++) {
+    av1_set_mv_row_limits(&cpi->common.mi_params, &mb->mv_limits,
+                          (mb_row << mi_h), (mb_height >> MI_SIZE_LOG2),
+                          cpi->oxcf.border_in_pixels);
+    for (int mb_col = 0; mb_col < mb_cols; mb_col++) {
+      av1_set_mv_col_limits(&cpi->common.mi_params, &mb->mv_limits,
+                            (mb_col << mi_w), (mb_width >> MI_SIZE_LOG2),
+                            cpi->oxcf.border_in_pixels);
+      FULLPEL_MV *ref_mv = &mvs[mb_col + mb_row * mb_cols];
+      if (do_motion_search) {
+        motion_search(cpi, src, ref, block_size, mb_row, mb_col, ref_mv);
+      }
+      unsigned int mv_sse;
+      const unsigned int blk_var = residual_variance(
+          cpi, src, ref, block_size, mb_row, mb_col, *ref_mv, &mv_sse);
+      variance += blk_var;
+    }
+  }
+
+  // Restore input state
+  for (int i = 0; i < num_planes; i++) {
+    mbd->plane[i].pre[0].buf = input_buffer[i];
+  }
+  mbd->mi = input_mb_mode_info;
+  return (double)variance / (double)(mb_rows * mb_cols);
+}
+
 // TODO(sdeng): Add the SIMD implementation.
 static AOM_INLINE void highbd_unsharp_rect(const uint16_t *source,
                                            int source_stride,
@@ -122,49 +289,14 @@
   }
 }
 
-static double frame_average_variance(const AV1_COMP *const cpi,
-                                     const YV12_BUFFER_CONFIG *const frame) {
-  const uint8_t *const y_buffer = frame->y_buffer;
-  const int y_stride = frame->y_stride;
-  const BLOCK_SIZE block_size = BLOCK_64X64;
-
-  const int block_w = mi_size_wide[block_size] * 4;
-  const int block_h = mi_size_high[block_size] * 4;
-  int row, col;
-  const int bit_depth = cpi->td.mb.e_mbd.bd;
-  double var = 0.0, var_count = 0.0;
-
-  // Loop through each block.
-  for (row = 0; row < frame->y_height / block_h; ++row) {
-    for (col = 0; col < frame->y_width / block_w; ++col) {
-      struct buf_2d buf;
-      const int row_offset_y = row * block_h;
-      const int col_offset_y = col * block_w;
-
-      buf.buf = (uint8_t *)y_buffer + row_offset_y * y_stride + col_offset_y;
-      buf.stride = y_stride;
-
-      if (cpi->common.seq_params.use_highbitdepth) {
-        assert(frame->flags & YV12_FLAG_HIGHBITDEPTH);
-        var += av1_high_get_sby_perpixel_variance(cpi, &buf, block_size,
-                                                  bit_depth);
-      } else {
-        var += av1_get_sby_perpixel_variance(cpi, &buf, block_size);
-      }
-      var_count += 1.0;
-    }
-  }
-  var /= var_count;
-  return var;
-}
-
-static double cal_approx_vmaf(const AV1_COMP *const cpi,
+static AOM_INLINE double cal_approx_vmaf(const AV1_COMP *const cpi,
 #if CONFIG_USE_VMAF_RC
-                              VmafContext *vmaf_context, int *vmaf_cal_index,
+                                         VmafContext *vmaf_context,
+                                         int *vmaf_cal_index,
 #endif
-                              double source_variance,
-                              YV12_BUFFER_CONFIG *const source,
-                              YV12_BUFFER_CONFIG *const sharpened) {
+                                         double source_variance,
+                                         YV12_BUFFER_CONFIG *const source,
+                                         YV12_BUFFER_CONFIG *const sharpened) {
   const int bit_depth = cpi->td.mb.e_mbd.bd;
   double new_vmaf;
 
@@ -802,10 +934,11 @@
   return accum / (double)(h * w);
 }
 
-static AOM_INLINE double calc_vmaf_motion_score(
-    const AV1_COMP *const cpi, const AV1_COMMON *const cm,
-    const YV12_BUFFER_CONFIG *const cur, const YV12_BUFFER_CONFIG *const last,
-    const YV12_BUFFER_CONFIG *const next) {
+static double calc_vmaf_motion_score(const AV1_COMP *const cpi,
+                                     const AV1_COMMON *const cm,
+                                     const YV12_BUFFER_CONFIG *const cur,
+                                     const YV12_BUFFER_CONFIG *const last,
+                                     const YV12_BUFFER_CONFIG *const next) {
   const int y_width = cur->y_width;
   const int y_height = cur->y_height;
   YV12_BUFFER_CONFIG blurred_cur, blurred_last, blurred_next;
@@ -865,6 +998,21 @@
   return AOMMIN(motion1, motion2);
 }
 
+static AOM_INLINE void get_neighbor_frames(const AV1_COMP *const cpi,
+                                           YV12_BUFFER_CONFIG **last,
+                                           YV12_BUFFER_CONFIG **next) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const GF_GROUP *gf_group = &cpi->gf_group;
+  const int src_index =
+      cm->show_frame != 0 ? 0 : gf_group->arf_src_offset[gf_group->index];
+  struct lookahead_entry *last_entry =
+      av1_lookahead_peek(cpi->lookahead, src_index - 1, cpi->compressor_stage);
+  struct lookahead_entry *next_entry =
+      av1_lookahead_peek(cpi->lookahead, src_index + 1, cpi->compressor_stage);
+  *next = &next_entry->img;
+  *last = cm->show_frame ? cpi->last_source : &last_entry->img;
+}
+
 // Calculates the new qindex from the VMAF motion score. This is based on the
 // observation: when the motion score becomes higher, the VMAF score of the
 // same source and distorted frames would become higher.
@@ -887,23 +1035,16 @@
   aom_clear_system_state();
   const GF_GROUP *gf_group = &cpi->gf_group;
   YV12_BUFFER_CONFIG *cur_buf = cpi->source;
-  int src_index = 0;
   if (cm->show_frame == 0) {
-    src_index = gf_group->arf_src_offset[gf_group->index];
+    const int src_index = gf_group->arf_src_offset[gf_group->index];
     struct lookahead_entry *cur_entry =
         av1_lookahead_peek(cpi->lookahead, src_index, cpi->compressor_stage);
     cur_buf = &cur_entry->img;
   }
   assert(cur_buf);
 
-  const struct lookahead_entry *last_entry =
-      av1_lookahead_peek(cpi->lookahead, src_index - 1, cpi->compressor_stage);
-  const struct lookahead_entry *next_entry =
-      av1_lookahead_peek(cpi->lookahead, src_index + 1, cpi->compressor_stage);
-  const YV12_BUFFER_CONFIG *next_buf = &next_entry->img;
-  const YV12_BUFFER_CONFIG *last_buf =
-      cm->show_frame ? cpi->last_source : &last_entry->img;
-
+  YV12_BUFFER_CONFIG *next_buf, *last_buf;
+  get_neighbor_frames(cpi, &last_buf, &next_buf);
   assert(last_buf);
 
   const double motion =
@@ -925,73 +1066,102 @@
 }
 
 #if CONFIG_USE_VMAF_RC
-static double cal_approx_score(const AV1_COMP *const cpi,
-                               VmafContext *vmaf_context, int vmaf_cal_index,
-                               YV12_BUFFER_CONFIG *const ref,
-                               YV12_BUFFER_CONFIG *const sharpened) {
+static AOM_INLINE double cal_approx_score(
+    AV1_COMP *const cpi, VmafContext *vmaf_context, int vmaf_cal_index,
+    double src_variance, double new_variance, double src_score,
+    YV12_BUFFER_CONFIG *const src, YV12_BUFFER_CONFIG *const recon_sharpened) {
   double score;
   const uint32_t bit_depth = cpi->td.mb.e_mbd.bd;
-  aom_calc_vmaf_at_index_rc(vmaf_context, cpi->vmaf_info.vmaf_model, ref,
-                            sharpened, bit_depth, vmaf_cal_index, &score);
-  return score;
+  aom_calc_vmaf_at_index_rc(vmaf_context, cpi->vmaf_info.vmaf_model, src,
+                            recon_sharpened, bit_depth, vmaf_cal_index, &score);
+  return src_variance / new_variance * (score - src_score);
 }
 
 static double find_best_frame_unsharp_amount_loop_neg(
-    const AV1_COMP *const cpi, VmafContext *vmaf_context,
-    YV12_BUFFER_CONFIG *const ref, YV12_BUFFER_CONFIG *const source,
-    YV12_BUFFER_CONFIG *const blurred, YV12_BUFFER_CONFIG *const sharpened,
-    double best_score, const double unsharp_amount_start,
+    AV1_COMP *const cpi, VmafContext *vmaf_context, double src_variance,
+    double base_score, YV12_BUFFER_CONFIG *const src,
+    YV12_BUFFER_CONFIG *const recon, YV12_BUFFER_CONFIG *const ref,
+    FULLPEL_MV *mvs, double best_score, const double unsharp_amount_start,
     const double step_size, const int max_loop_count, const double max_amount) {
   const double min_amount = 0.0;
   int loop_count = 0;
   double approx_score = best_score;
   double unsharp_amount = unsharp_amount_start;
   int vmaf_cal_index = 2;
+
+  const AV1_COMMON *const cm = &cpi->common;
+  const int width = recon->y_width;
+  const int height = recon->y_height;
+  const int bit_depth = cpi->td.mb.e_mbd.bd;
+  YV12_BUFFER_CONFIG src_blurred, recon_blurred, src_sharpened, recon_sharpened;
+  memset(&recon_sharpened, 0, sizeof(recon_sharpened));
+  memset(&src_sharpened, 0, sizeof(src_sharpened));
+  memset(&recon_blurred, 0, sizeof(recon_blurred));
+  memset(&src_blurred, 0, sizeof(src_blurred));
+  aom_alloc_frame_buffer(
+      &recon_sharpened, width, height, 1, 1, cm->seq_params.use_highbitdepth,
+      cpi->oxcf.border_in_pixels, cm->features.byte_alignment);
+  aom_alloc_frame_buffer(
+      &src_sharpened, width, height, 1, 1, cm->seq_params.use_highbitdepth,
+      cpi->oxcf.border_in_pixels, cm->features.byte_alignment);
+  aom_alloc_frame_buffer(
+      &recon_blurred, width, height, 1, 1, cm->seq_params.use_highbitdepth,
+      cpi->oxcf.border_in_pixels, cm->features.byte_alignment);
+  aom_alloc_frame_buffer(
+      &src_blurred, width, height, 1, 1, cm->seq_params.use_highbitdepth,
+      cpi->oxcf.border_in_pixels, cm->features.byte_alignment);
+
+  gaussian_blur(bit_depth, recon, &recon_blurred);
+  gaussian_blur(bit_depth, src, &src_blurred);
   do {
     best_score = approx_score;
     unsharp_amount += step_size;
     if (unsharp_amount > max_amount || unsharp_amount < min_amount) break;
-    unsharp(cpi, source, blurred, sharpened, unsharp_amount);
+    unsharp(cpi, recon, &recon_blurred, &recon_sharpened, unsharp_amount);
+    unsharp(cpi, src, &src_blurred, &src_sharpened, unsharp_amount);
+    const double new_variance =
+        residual_frame_average_variance(cpi, &src_sharpened, ref, mvs);
     approx_score =
-        cal_approx_score(cpi, vmaf_context, vmaf_cal_index++, ref, sharpened);
+        cal_approx_score(cpi, vmaf_context, vmaf_cal_index++, src_variance,
+                         new_variance, base_score, src, &recon_sharpened);
 
     loop_count++;
   } while (approx_score > best_score && loop_count < max_loop_count);
   unsharp_amount =
       approx_score > best_score ? unsharp_amount : unsharp_amount - step_size;
+
+  aom_free_frame_buffer(&recon_sharpened);
+  aom_free_frame_buffer(&src_sharpened);
+  aom_free_frame_buffer(&recon_blurred);
+  aom_free_frame_buffer(&src_blurred);
   return AOMMIN(max_amount, AOMMAX(unsharp_amount, min_amount));
 }
 
 static double find_best_frame_unsharp_amount_neg(
-    const AV1_COMP *const cpi, VmafContext *vmaf_context,
-    YV12_BUFFER_CONFIG *const ref, YV12_BUFFER_CONFIG *const source,
-    YV12_BUFFER_CONFIG *const blurred, const double unsharp_amount_start,
+    AV1_COMP *const cpi, VmafContext *vmaf_context,
+    YV12_BUFFER_CONFIG *const src, YV12_BUFFER_CONFIG *const recon,
+    YV12_BUFFER_CONFIG *const ref, const double unsharp_amount_start,
     const double step_size, const int max_loop_count,
     const double max_filter_amount) {
-  const AV1_COMMON *const cm = &cpi->common;
-  const int width = source->y_width;
-  const int height = source->y_height;
+  double base_score = 0.0;
+  aom_calc_vmaf_at_index_rc(vmaf_context, cpi->vmaf_info.vmaf_model, src, recon,
+                            cpi->td.mb.e_mbd.bd, 1, &base_score);
 
-  double best_score = 0.0;
-  aom_calc_vmaf_at_index_rc(vmaf_context, cpi->vmaf_info.vmaf_model, ref,
-                            source, cpi->td.mb.e_mbd.bd, 1, &best_score);
-  YV12_BUFFER_CONFIG sharpened;
-  memset(&sharpened, 0, sizeof(sharpened));
-  aom_alloc_frame_buffer(
-      &sharpened, width, height, 1, 1, cm->seq_params.use_highbitdepth,
-      cpi->oxcf.border_in_pixels, cm->features.byte_alignment);
-
+  FULLPEL_MV *mvs = NULL;
+  const double src_variance =
+      residual_frame_average_variance(cpi, src, ref, mvs);
   const double unsharp_amount = find_best_frame_unsharp_amount_loop_neg(
-      cpi, vmaf_context, ref, source, blurred, &sharpened, best_score,
+      cpi, vmaf_context, src_variance, base_score, src, recon, ref, mvs, 0.0,
       unsharp_amount_start, step_size, max_loop_count, max_filter_amount);
 
-  aom_free_frame_buffer(&sharpened);
+  aom_free(mvs);
   return unsharp_amount;
 }
-#endif
+#endif  // CONFIG_USE_VMAF_RC
 
-void av1_update_vmaf_curve(AV1_COMP *cpi, YV12_BUFFER_CONFIG *source,
-                           YV12_BUFFER_CONFIG *recon) {
+void av1_update_vmaf_curve(AV1_COMP *cpi) {
+  YV12_BUFFER_CONFIG *source = cpi->source;
+  YV12_BUFFER_CONFIG *recon = &cpi->common.cur_frame->buf;
   const int bit_depth = cpi->td.mb.e_mbd.bd;
 #if CONFIG_USE_VMAF_RC
   VmafContext *vmaf_context;
@@ -1004,7 +1174,7 @@
 #else
   aom_calc_vmaf(cpi->oxcf.tune_cfg.vmaf_model_path, source, recon, bit_depth,
                 &cpi->vmaf_info.last_frame_vmaf);
-#endif
+#endif  // CONFIG_USE_VMAF_RC
   if (cpi->common.seq_params.use_highbitdepth) {
     assert(source->flags & YV12_FLAG_HIGHBITDEPTH);
     assert(recon->flags & YV12_FLAG_HIGHBITDEPTH);
@@ -1016,22 +1186,11 @@
 
 #if CONFIG_USE_VMAF_RC
   if (cpi->oxcf.tune_cfg.tuning == AOM_TUNE_VMAF_NEG_MAX_GAIN) {
-    const AV1_COMMON *const cm = &cpi->common;
-    const int width = source->y_width;
-    const int height = source->y_height;
-
-    YV12_BUFFER_CONFIG blurred;
-    memset(&blurred, 0, sizeof(blurred));
-    aom_alloc_frame_buffer(
-        &blurred, width, height, 1, 1, cm->seq_params.use_highbitdepth,
-        cpi->oxcf.border_in_pixels, cm->features.byte_alignment);
-
-    gaussian_blur(bit_depth, recon, &blurred);
+    YV12_BUFFER_CONFIG *last, *next;
+    get_neighbor_frames(cpi, &last, &next);
     cpi->vmaf_info.best_unsharp_amount = find_best_frame_unsharp_amount_neg(
-        cpi, vmaf_context, source, recon, &blurred, 0.0, 0.025, 20, 1.01);
-
-    aom_free_frame_buffer(&blurred);
+        cpi, vmaf_context, source, recon, last, 0.0, 0.025, 20, 1.01);
   }
   aom_close_vmaf_context_rc(vmaf_context);
-#endif
+#endif  // CONFIG_USE_VMAF_RC
 }
diff --git a/av1/encoder/tune_vmaf.h b/av1/encoder/tune_vmaf.h
index c71fee5..6536df2 100644
--- a/av1/encoder/tune_vmaf.h
+++ b/av1/encoder/tune_vmaf.h
@@ -30,7 +30,6 @@
 
 int av1_get_vmaf_base_qindex(const AV1_COMP *cpi, int current_qindex);
 
-void av1_update_vmaf_curve(AV1_COMP *cpi, YV12_BUFFER_CONFIG *source,
-                           YV12_BUFFER_CONFIG *recon);
+void av1_update_vmaf_curve(AV1_COMP *cpi);
 
 #endif  // AOM_AV1_ENCODER_TUNE_VMAF_H_