shithub: libvpx

Download patch

ref: 6b66c01c88387670b2aa2ccd2560e2c1202b528c
parent: e9cacfd66df8f29055fb812681310bd27265eee0
parent: 9c41143d6655f39c3b6f2f0023cd687214af599a
author: Jim Bankoski <jimbankoski@google.com>
date: Tue Mar 13 12:18:57 EDT 2012

Merge "Adds a motion compensated temporal denoiser to the encoder."

--- a/configure
+++ b/configure
@@ -47,6 +47,7 @@
   ${toggle_small}                 favor smaller size over speed
   ${toggle_postproc_visualizer}   macro block / block level visualizers
   ${toggle_multi_res_encoding}    enable multiple-resolution encoding
+  ${toggle_temporal_denoising}    enable temporal denoising and disable the spatial denoiser
 
 Codecs:
   Codecs can be selectively enabled or disabled individually, or by family:
@@ -165,6 +166,7 @@
 enable spatial_resampling
 enable multithread
 enable os_support
+enable temporal_denoising
 
 [ -d ${source_path}/../include ] && enable alt_tree_layout
 for d in vp8; do
@@ -263,6 +265,7 @@
     os_support
     unit_tests
     multi_res_encoding
+    temporal_denoising
 "
 CMDLINE_SELECT="
     extra_warnings
@@ -307,6 +310,7 @@
     postproc_visualizer
     unit_tests
     multi_res_encoding
+    temporal_denoising
 "
 
 process_cmdline() {
--- a/vp8/common/alloccommon.c
+++ b/vp8/common/alloccommon.c
@@ -37,7 +37,6 @@
 void vp8_de_alloc_frame_buffers(VP8_COMMON *oci)
 {
     int i;
-
     for (i = 0; i < NUM_YV12_BUFFERS; i++)
         vp8_yv12_de_alloc_frame_buffer(&oci->yv12_fb[i]);
 
--- a/vp8/common/blockd.h
+++ b/vp8/common/blockd.h
@@ -215,6 +215,12 @@
     MODE_INFO *mode_info_context;
     int mode_info_stride;
 
+#if CONFIG_TEMPORAL_DENOISING
+    MB_PREDICTION_MODE best_sse_inter_mode;
+    int_mv best_sse_mv;
+    unsigned char need_to_clamp_best_mvs;
+#endif
+
     FRAME_TYPE frame_type;
 
     int up_available;
--- a/vp8/common/postproc.c
+++ b/vp8/common/postproc.c
@@ -362,6 +362,7 @@
     vp8_post_proc_down_and_across(source->v_buffer, post->v_buffer, source->uv_stride, post->uv_stride, source->uv_height, source->uv_width, ppl);
 }
 
+#if !(CONFIG_TEMPORAL_DENOISING)
 void vp8_de_noise(YV12_BUFFER_CONFIG         *source,
                   YV12_BUFFER_CONFIG         *post,
                   int                         q,
@@ -398,6 +399,7 @@
         source->uv_width - 4, ppl);
 
 }
+#endif
 
 double vp8_gaussian(double sigma, double mu, double x)
 {
--- /dev/null
+++ b/vp8/encoder/denoising.c
@@ -1,0 +1,212 @@
+/*
+ *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "denoising.h"
+
+#include "vp8/common/reconinter.h"
+#include "vpx/vpx_integer.h"
+#include "vpx_mem/vpx_mem.h"
+#include "vpx_rtcd.h"
+
+const unsigned int NOISE_MOTION_THRESHOLD = 20*20;
+const unsigned int NOISE_DIFF2_THRESHOLD = 75;
+// SSE_DIFF_THRESHOLD is selected as ~95% confidence assuming var(noise) ~= 100.
+const unsigned int SSE_DIFF_THRESHOLD = 16*16*20;
+const unsigned int SSE_THRESHOLD = 16*16*40;
+
+static __inline uint8_t blend(uint8_t state, uint8_t sample, uint8_t factor_q8)
+{
+  return (uint8_t)(
+      (((uint16_t)factor_q8 * ((uint16_t)state) +  // Q8
+        (uint16_t)(256 - factor_q8) * ((uint16_t)sample)) + 128)  // Q8
+      >> 8);
+}
+
+static unsigned int denoiser_motion_compensate(YV12_BUFFER_CONFIG* src,
+                                               YV12_BUFFER_CONFIG* dst,
+                                               MACROBLOCK* x,
+                                               unsigned int best_sse,
+                                               unsigned int zero_mv_sse,
+                                               int recon_yoffset,
+                                               int recon_uvoffset)
+{
+  MACROBLOCKD filter_xd = x->e_mbd;
+  int mv_col;
+  int mv_row;
+  int sse_diff = zero_mv_sse - best_sse;
+  // Compensate the running average.
+  filter_xd.pre.y_buffer = src->y_buffer + recon_yoffset;
+  filter_xd.pre.u_buffer = src->u_buffer + recon_uvoffset;
+  filter_xd.pre.v_buffer = src->v_buffer + recon_uvoffset;
+  // Write the compensated running average to the destination buffer.
+  filter_xd.dst.y_buffer = dst->y_buffer + recon_yoffset;
+  filter_xd.dst.u_buffer = dst->u_buffer + recon_uvoffset;
+  filter_xd.dst.v_buffer = dst->v_buffer + recon_uvoffset;
+  // Use the best MV for the compensation.
+  filter_xd.mode_info_context->mbmi.ref_frame = LAST_FRAME;
+  filter_xd.mode_info_context->mbmi.mode = filter_xd.best_sse_inter_mode;
+  filter_xd.mode_info_context->mbmi.mv = filter_xd.best_sse_mv;
+  filter_xd.mode_info_context->mbmi.need_to_clamp_mvs =
+      filter_xd.need_to_clamp_best_mvs;
+  mv_col = filter_xd.best_sse_mv.as_mv.col;
+  mv_row = filter_xd.best_sse_mv.as_mv.row;
+  if (filter_xd.mode_info_context->mbmi.mode <= B_PRED ||
+      (mv_row*mv_row + mv_col*mv_col <= NOISE_MOTION_THRESHOLD &&
+       sse_diff < SSE_DIFF_THRESHOLD))
+  {
+    // Handle intra blocks as referring to last frame with zero motion and
+    // let the absolute pixel difference affect the filter factor.
+    // Also consider small amount of motion as being random walk due to noise,
+    // if it doesn't mean that we get a much bigger error.
+    // Note that any changes to the mode info only affects the denoising.
+    filter_xd.mode_info_context->mbmi.ref_frame = LAST_FRAME;
+    filter_xd.mode_info_context->mbmi.mode = ZEROMV;
+    filter_xd.mode_info_context->mbmi.mv.as_int = 0;
+    x->e_mbd.best_sse_inter_mode = ZEROMV;
+    x->e_mbd.best_sse_mv.as_int = 0;
+    best_sse = zero_mv_sse;
+  }
+  if (!x->skip)
+  {
+    vp8_build_inter_predictors_mb(&filter_xd);
+  }
+  else
+  {
+    vp8_build_inter16x16_predictors_mb(&filter_xd,
+                                       filter_xd.dst.y_buffer,
+                                       filter_xd.dst.u_buffer,
+                                       filter_xd.dst.v_buffer,
+                                       filter_xd.dst.y_stride,
+                                       filter_xd.dst.uv_stride);
+  }
+  return best_sse;
+}
+
+static void denoiser_filter(YV12_BUFFER_CONFIG* mc_running_avg,
+                            YV12_BUFFER_CONFIG* running_avg,
+                            MACROBLOCK* signal,
+                            unsigned int motion_magnitude2,
+                            int y_offset,
+                            int uv_offset)
+{
+  unsigned char* sig = signal->thismb;
+  int sig_stride = 16;
+  unsigned char* mc_running_avg_y = mc_running_avg->y_buffer + y_offset;
+  int mc_avg_y_stride = mc_running_avg->y_stride;
+  unsigned char* running_avg_y = running_avg->y_buffer + y_offset;
+  int avg_y_stride = running_avg->y_stride;
+  int r, c;
+  for (r = 0; r < 16; r++)
+  {
+    for (c = 0; c < 16; c++)
+    {
+      int diff;
+      int absdiff = 0;
+      unsigned int filter_coefficient;
+      absdiff = sig[c] - mc_running_avg_y[c];
+      absdiff = absdiff > 0 ? absdiff : -absdiff;
+      assert(absdiff >= 0 && absdiff < 256);
+      filter_coefficient = (255 << 8) / (256 + ((absdiff * 330) >> 3));
+      // Allow some additional filtering of static blocks, or blocks with very
+      // small motion vectors.
+      filter_coefficient += filter_coefficient / (3 + (motion_magnitude2 >> 3));
+      filter_coefficient = filter_coefficient > 255 ? 255 : filter_coefficient;
+
+      running_avg_y[c] = blend(mc_running_avg_y[c], sig[c], filter_coefficient);
+      diff = sig[c] - running_avg_y[c];
+
+      if (diff * diff < NOISE_DIFF2_THRESHOLD)
+      {
+        // Replace with mean to suppress the noise.
+        sig[c] = running_avg_y[c];
+      }
+      else
+      {
+        // Replace the filter state with the signal since the change in this
+        // pixel isn't classified as noise.
+        running_avg_y[c] = sig[c];
+      }
+    }
+    sig += sig_stride;
+    mc_running_avg_y += mc_avg_y_stride;
+    running_avg_y += avg_y_stride;
+  }
+}
+
+int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height)
+{
+  assert(denoiser);
+  denoiser->yv12_running_avg.flags = 0;
+  if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_running_avg), width,
+                                  height, VP8BORDERINPIXELS) < 0)
+  {
+      vp8_denoiser_free(denoiser);
+      return 1;
+  }
+  denoiser->yv12_mc_running_avg.flags = 0;
+  if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_mc_running_avg), width,
+                                  height, VP8BORDERINPIXELS) < 0)
+  {
+      vp8_denoiser_free(denoiser);
+      return 1;
+  }
+  vpx_memset(denoiser->yv12_running_avg.buffer_alloc, 0,
+             denoiser->yv12_running_avg.frame_size);
+  vpx_memset(denoiser->yv12_mc_running_avg.buffer_alloc, 0,
+             denoiser->yv12_mc_running_avg.frame_size);
+  return 0;
+}
+
+void vp8_denoiser_free(VP8_DENOISER *denoiser)
+{
+  assert(denoiser);
+  vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_running_avg);
+  vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_mc_running_avg);
+}
+
+void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser,
+                             MACROBLOCK *x,
+                             unsigned int best_sse,
+                             unsigned int zero_mv_sse,
+                             int recon_yoffset,
+                             int recon_uvoffset) {
+  int mv_row;
+  int mv_col;
+  unsigned int motion_magnitude2;
+  // Motion compensate the running average.
+  best_sse = denoiser_motion_compensate(&denoiser->yv12_running_avg,
+                                        &denoiser->yv12_mc_running_avg,
+                                        x,
+                                        best_sse,
+                                        zero_mv_sse,
+                                        recon_yoffset,
+                                        recon_uvoffset);
+
+  mv_row = x->e_mbd.best_sse_mv.as_mv.row;
+  mv_col = x->e_mbd.best_sse_mv.as_mv.col;
+  motion_magnitude2 = mv_row*mv_row + mv_col*mv_col;
+  if (best_sse > SSE_THRESHOLD ||
+      motion_magnitude2 > 8 * NOISE_MOTION_THRESHOLD)
+  {
+    // No filtering of this block since it differs too much from the predictor,
+    // or the motion vector magnitude is considered too big.
+    vp8_copy_mem16x16(x->thismb, 16,
+                      denoiser->yv12_running_avg.y_buffer + recon_yoffset,
+                      denoiser->yv12_running_avg.y_stride);
+    return;
+  }
+  // Filter.
+  denoiser_filter(&denoiser->yv12_mc_running_avg,
+                  &denoiser->yv12_running_avg,
+                  x,
+                  motion_magnitude2,
+                  recon_yoffset,
+                  recon_uvoffset);
+}
--- /dev/null
+++ b/vp8/encoder/denoising.h
@@ -1,0 +1,33 @@
+/*
+ *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef VP8_ENCODER_DENOISING_H_
+#define VP8_ENCODER_DENOISING_H_
+
+#include "block.h"
+
+typedef struct vp8_denoiser
+{
+  YV12_BUFFER_CONFIG yv12_running_avg;
+  YV12_BUFFER_CONFIG yv12_mc_running_avg;
+} VP8_DENOISER;
+
+int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height);
+
+void vp8_denoiser_free(VP8_DENOISER *denoiser);
+
+void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser,
+                             MACROBLOCK *x,
+                             unsigned int best_sse,
+                             unsigned int zero_mv_sse,
+                             int recon_yoffset,
+                             int recon_uvoffset);
+
+#endif  // VP8_ENCODER_DENOISING_H_
--- a/vp8/encoder/encodeframe.c
+++ b/vp8/encoder/encodeframe.c
@@ -1179,6 +1179,13 @@
     else
         x->encode_breakout = cpi->oxcf.encode_breakout;
 
+#if CONFIG_TEMPORAL_DENOISING
+    // Reset the best sse mode/mv for each macroblock.
+    x->e_mbd.best_sse_inter_mode = 0;
+    x->e_mbd.best_sse_mv.as_int = 0;
+    x->e_mbd.need_to_clamp_best_mvs = 0;
+#endif
+
     if (cpi->sf.RD)
     {
         int zbin_mode_boost_enabled = cpi->zbin_mode_boost_enabled;
--- a/vp8/encoder/onyx_if.c
+++ b/vp8/encoder/onyx_if.c
@@ -1680,6 +1680,17 @@
     cpi->alt_ref_source = NULL;
     cpi->is_src_frame_alt_ref = 0;
 
+#if CONFIG_TEMPORAL_DENOISING
+    if (cpi->oxcf.noise_sensitivity)
+    {
+      if (!cpi->denoiser.yv12_mc_running_avg.buffer_alloc)
+      {
+        int width = (cpi->oxcf.Width + 15) & ~15;
+        int height = (cpi->oxcf.Height + 15) & ~15;
+        vp8_denoiser_allocate(&cpi->denoiser, width, height);
+      }
+    }
+#endif
 
 #if 0
     // Experimental RD Code
@@ -2314,6 +2325,9 @@
     vp8cx_remove_encoder_threads(cpi);
 #endif
 
+#if CONFIG_TEMPORAL_DENOISING
+    vp8_denoiser_free(&cpi->denoiser);
+#endif
     dealloc_compressor_data(cpi);
     vpx_free(cpi->mb.ss);
     vpx_free(cpi->tok);
@@ -3133,7 +3147,12 @@
     }
 
     vp8_yv12_extend_frame_borders_ptr(cm->frame_to_show);
-
+#if CONFIG_TEMPORAL_DENOISING
+    if (cpi->oxcf.noise_sensitivity)
+    {
+      vp8_yv12_extend_frame_borders(&cpi->denoiser.yv12_running_avg);
+    }
+#endif
 }
 
 static void encode_frame_to_data_rate
@@ -3589,7 +3608,7 @@
 
 
     scale_and_extend_source(cpi->un_scaled_source, cpi);
-#if !(CONFIG_REALTIME_ONLY) && CONFIG_POSTPROC
+#if !(CONFIG_REALTIME_ONLY) && CONFIG_POSTPROC && !(CONFIG_TEMPORAL_DENOISING)
 
     if (cpi->oxcf.noise_sensitivity > 0)
     {
--- a/vp8/encoder/onyx_int.h
+++ b/vp8/encoder/onyx_int.h
@@ -28,6 +28,9 @@
 #include "mcomp.h"
 #include "vp8/common/findnearmv.h"
 #include "lookahead.h"
+#if CONFIG_TEMPORAL_DENOISING
+#include "vp8/encoder/denoising.h"
+#endif
 
 //#define SPEEDSTATS 1
 #define MIN_GF_INTERVAL             4
@@ -660,6 +663,10 @@
     int force_next_frame_intra; /* force next frame to intra when kf_auto says so */
 
     int droppable;
+
+#if CONFIG_TEMPORAL_DENOISING
+    VP8_DENOISER denoiser;
+#endif
 
     // Coding layer state variables
     unsigned int current_layer;
--- a/vp8/encoder/pickinter.c
+++ b/vp8/encoder/pickinter.c
@@ -24,6 +24,9 @@
 #include "mcomp.h"
 #include "rdopt.h"
 #include "vpx_mem/vpx_mem.h"
+#if CONFIG_TEMPORAL_DENOISING
+#include "denoising.h"
+#endif
 
 extern int VP8_UVSSE(MACROBLOCK *x);
 
@@ -450,7 +453,49 @@
 }
 #endif
 
+static void check_for_encode_breakout(unsigned int sse, MACROBLOCK* x)
+{
+    if (sse < x->encode_breakout)
+    {
+        // Check u and v to make sure skip is ok
+        int sse2 = 0;
 
+        sse2 = VP8_UVSSE(x);
+
+        if (sse2 * 2 < x->encode_breakout)
+            x->skip = 1;
+        else
+            x->skip = 0;
+    }
+}
+
+static int evaluate_inter_mode(unsigned int* sse, int rate2, int* distortion2, VP8_COMP *cpi, MACROBLOCK *x)
+{
+    MB_PREDICTION_MODE this_mode = x->e_mbd.mode_info_context->mbmi.mode;
+    int_mv mv = x->e_mbd.mode_info_context->mbmi.mv;
+    int this_rd;
+    /* Exit early and don't compute the distortion if this macroblock
+     * is marked inactive. */
+    if (cpi->active_map_enabled && x->active_ptr[0] == 0)
+    {
+        *sse = 0;
+        *distortion2 = 0;
+        x->skip = 1;
+        return INT_MAX;
+    }
+
+    if((this_mode != NEWMV) ||
+        !(cpi->sf.half_pixel_search) || cpi->common.full_pixel==1)
+        *distortion2 = get_inter_mbpred_error(x,
+                                              &cpi->fn_ptr[BLOCK_16X16],
+                                              sse, mv);
+
+    this_rd = RDCOST(x->rdmult, x->rddiv, rate2, *distortion2);
+
+    check_for_encode_breakout(*sse, x);
+    return this_rd;
+}
+
 void vp8_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset,
                          int recon_uvoffset, int *returnrate,
                          int *returndistortion, int *returnintra, int mb_row,
@@ -476,7 +521,10 @@
     int distortion2;
     int bestsme = INT_MAX;
     int best_mode_index = 0;
-    unsigned int sse = INT_MAX, best_sse = INT_MAX;
+    unsigned int sse = INT_MAX, best_rd_sse = INT_MAX;
+#if CONFIG_TEMPORAL_DENOISING
+    unsigned int zero_mv_sse = 0, best_sse = INT_MAX;
+#endif
 
     int_mv mvp;
 
@@ -488,9 +536,6 @@
     int ref_frame_map[4];
     int sign_bias = 0;
 
-    int have_subp_search = cpi->sf.half_pixel_search;  /* In real-time mode,
-                                       when Speed >= 15, no sub-pixel search. */
-
 #if CONFIG_MULTI_RES_ENCODING
     int dissim = INT_MAX;
     int parent_ref_frame = 0;
@@ -657,7 +702,7 @@
         {
         case B_PRED:
             /* Pass best so far to pick_intra4x4mby_modes to use as breakout */
-            distortion2 = best_sse;
+            distortion2 = best_rd_sse;
             pick_intra4x4mby_modes(x, &rate, &distortion2);
 
             if (distortion2 == INT_MAX)
@@ -905,43 +950,38 @@
             rate2 += vp8_cost_mv_ref(this_mode, mdcounts);
             x->e_mbd.mode_info_context->mbmi.mv.as_int =
                                                     mode_mv[this_mode].as_int;
+            this_rd = evaluate_inter_mode(&sse, rate2, &distortion2, cpi, x);
 
-            /* Exit early and don't compute the distortion if this macroblock
-             * is marked inactive. */
-            if (cpi->active_map_enabled && x->active_ptr[0] == 0)
-            {
-                sse = 0;
-                distortion2 = 0;
-                x->skip = 1;
-                break;
-            }
-
-            if((this_mode != NEWMV) ||
-                !(have_subp_search) || cpi->common.full_pixel==1)
-                distortion2 = get_inter_mbpred_error(x,
-                                                     &cpi->fn_ptr[BLOCK_16X16],
-                                                     &sse, mode_mv[this_mode]);
-
-            this_rd = RDCOST(x->rdmult, x->rddiv, rate2, distortion2);
-
-            if (sse < x->encode_breakout)
-            {
-                // Check u and v to make sure skip is ok
-                int sse2 = 0;
-
-                sse2 = VP8_UVSSE(x);
-
-                if (sse2 * 2 < x->encode_breakout)
-                    x->skip = 1;
-                else
-                    x->skip = 0;
-            }
-
             break;
         default:
             break;
         }
 
+#if CONFIG_TEMPORAL_DENOISING
+        if (cpi->oxcf.noise_sensitivity)
+        {
+          // Store for later use by denoiser.
+          if (this_mode == ZEROMV &&
+              x->e_mbd.mode_info_context->mbmi.ref_frame == LAST_FRAME)
+          {
+            zero_mv_sse = sse;
+          }
+
+          // Store the best NEWMV in x for later use in the denoiser.
+          // We are restricted to the LAST_FRAME since the denoiser only keeps
+          // one filter state.
+          if (x->e_mbd.mode_info_context->mbmi.mode == NEWMV &&
+              x->e_mbd.mode_info_context->mbmi.ref_frame == LAST_FRAME)
+          {
+            best_sse = sse;
+            x->e_mbd.best_sse_inter_mode = NEWMV;
+            x->e_mbd.best_sse_mv = x->e_mbd.mode_info_context->mbmi.mv;
+            x->e_mbd.need_to_clamp_best_mvs =
+                x->e_mbd.mode_info_context->mbmi.need_to_clamp_mvs;
+          }
+        }
+#endif
+
         if (this_rd < best_rd || x->skip)
         {
             // Note index of best mode
@@ -949,7 +989,7 @@
 
             *returnrate = rate2;
             *returndistortion = distortion2;
-            best_sse = sse;
+            best_rd_sse = sse;
             best_rd = this_rd;
             vpx_memcpy(&best_mbmode, &x->e_mbd.mode_info_context->mbmi,
                        sizeof(MB_MODE_INFO));
@@ -1010,6 +1050,43 @@
 
         cpi->error_bins[this_rdbin] ++;
     }
+
+#if CONFIG_TEMPORAL_DENOISING
+    if (cpi->oxcf.noise_sensitivity)
+    {
+      if (x->e_mbd.best_sse_inter_mode == DC_PRED) {
+        // No best MV found.
+        x->e_mbd.best_sse_inter_mode = best_mbmode.mode;
+        x->e_mbd.best_sse_mv = best_mbmode.mv;
+        x->e_mbd.need_to_clamp_best_mvs = best_mbmode.need_to_clamp_mvs;
+        best_sse = best_rd_sse;
+      }
+      vp8_denoiser_denoise_mb(&cpi->denoiser, x, best_sse, zero_mv_sse,
+                              recon_yoffset, recon_uvoffset);
+
+      // Reevaluate ZEROMV after denoising.
+      if (best_mbmode.ref_frame == INTRA_FRAME)
+      {
+        int this_rd = 0;
+        rate2 = 0;
+        distortion2 = 0;
+        x->e_mbd.mode_info_context->mbmi.ref_frame = LAST_FRAME;
+        rate2 += x->ref_frame_cost[x->e_mbd.mode_info_context->mbmi.ref_frame];
+        this_mode = ZEROMV;
+        rate2 += vp8_cost_mv_ref(this_mode, mdcounts);
+        x->e_mbd.mode_info_context->mbmi.mode = this_mode;
+        x->e_mbd.mode_info_context->mbmi.uv_mode = DC_PRED;
+        x->e_mbd.mode_info_context->mbmi.mv.as_int = 0;
+        this_rd = evaluate_inter_mode(&sse, rate2, &distortion2, cpi, x);
+
+        if (this_rd < best_rd || x->skip)
+        {
+            vpx_memcpy(&best_mbmode, &x->e_mbd.mode_info_context->mbmi,
+                       sizeof(MB_MODE_INFO));
+        }
+      }
+    }
+#endif
 
     if (cpi->is_src_frame_alt_ref &&
         (best_mbmode.mode != ZEROMV || best_mbmode.ref_frame != ALTREF_FRAME))
--- a/vp8/encoder/rdopt.c
+++ b/vp8/encoder/rdopt.c
@@ -33,11 +33,33 @@
 #include "rdopt.h"
 #include "vpx_mem/vpx_mem.h"
 #include "vp8/common/systemdependent.h"
+#if CONFIG_TEMPORAL_DENOISING
+#include "denoising.h"
+#endif
 
 extern void vp8_update_zbin_extra(VP8_COMP *cpi, MACROBLOCK *x);
 
 #define MAXF(a,b)            (((a) > (b)) ? (a) : (b))
 
+typedef struct rate_distortion_struct
+{
+    int rate2;
+    int rate_y;
+    int rate_uv;
+    int distortion2;
+    int distortion_uv;
+} RATE_DISTORTION;
+
+typedef struct best_mode_struct
+{
+  int yrd;
+  int rd;
+  int intra_rd;
+  MB_MODE_INFO mbmode;
+  union b_mode_info bmodes[16];
+  PARTITION_INFO partition;
+} BEST_MODE;
+
 static const int auto_speed_thresh[17] =
 {
     1000,
@@ -1711,7 +1733,182 @@
     }
 }
 
+static int evaluate_inter_mode_rd(int mdcounts[4],
+                                  RATE_DISTORTION* rd,
+                                  int* disable_skip,
+                                  VP8_COMP *cpi, MACROBLOCK *x)
+{
+    MB_PREDICTION_MODE this_mode = x->e_mbd.mode_info_context->mbmi.mode;
+    BLOCK *b = &x->block[0];
+    MACROBLOCKD *xd = &x->e_mbd;
+    int distortion;
+    vp8_build_inter16x16_predictors_mby(&x->e_mbd, x->e_mbd.predictor, 16);
 
+    if (cpi->active_map_enabled && x->active_ptr[0] == 0) {
+        x->skip = 1;
+    }
+    else if (x->encode_breakout)
+    {
+        unsigned int sse;
+        unsigned int var;
+        int threshold = (xd->block[0].dequant[1]
+                    * xd->block[0].dequant[1] >>4);
+
+        if(threshold < x->encode_breakout)
+            threshold = x->encode_breakout;
+
+        var = vp8_variance16x16
+                (*(b->base_src), b->src_stride,
+                x->e_mbd.predictor, 16, &sse);
+
+        if (sse < threshold)
+        {
+             unsigned int q2dc = xd->block[24].dequant[0];
+            /* If theres is no codeable 2nd order dc
+               or a very small uniform pixel change change */
+            if ((sse - var < q2dc * q2dc >>4) ||
+                (sse /2 > var && sse-var < 64))
+            {
+                // Check u and v to make sure skip is ok
+                int sse2=  VP8_UVSSE(x);
+                if (sse2 * 2 < threshold)
+                {
+                    x->skip = 1;
+                    rd->distortion2 = sse + sse2;
+                    rd->rate2 = 500;
+
+                    /* for best_yrd calculation */
+                    rd->rate_uv = 0;
+                    rd->distortion_uv = sse2;
+
+                    *disable_skip = 1;
+                    return RDCOST(x->rdmult, x->rddiv, rd->rate2,
+                                  rd->distortion2);
+                }
+            }
+        }
+    }
+
+
+    //intermodecost[mode_index] = vp8_cost_mv_ref(this_mode, mdcounts);   // Experimental debug code
+
+    // Add in the Mv/mode cost
+    rd->rate2 += vp8_cost_mv_ref(this_mode, mdcounts);
+
+    // Y cost and distortion
+    macro_block_yrd(x, &rd->rate_y, &distortion);
+    rd->rate2 += rd->rate_y;
+    rd->distortion2 += distortion;
+
+    // UV cost and distortion
+    rd_inter16x16_uv(cpi, x, &rd->rate_uv, &rd->distortion_uv,
+                     cpi->common.full_pixel);
+    rd->rate2 += rd->rate_uv;
+    rd->distortion2 += rd->distortion_uv;
+    return INT_MAX;
+}
+
+static int calculate_final_rd_costs(int this_rd,
+                                    RATE_DISTORTION* rd,
+                                    int* other_cost,
+                                    int disable_skip,
+                                    int uv_intra_tteob,
+                                    int intra_rd_penalty,
+                                    VP8_COMP *cpi, MACROBLOCK *x)
+{
+    MB_PREDICTION_MODE this_mode = x->e_mbd.mode_info_context->mbmi.mode;
+    // Where skip is allowable add in the default per mb cost for the no skip case.
+    // where we then decide to skip we have to delete this and replace it with the
+    // cost of signallying a skip
+    if (cpi->common.mb_no_coeff_skip)
+    {
+        *other_cost += vp8_cost_bit(cpi->prob_skip_false, 0);
+        rd->rate2 += *other_cost;
+    }
+
+    /* Estimate the reference frame signaling cost and add it
+     * to the rolling cost variable.
+     */
+    rd->rate2 +=
+        x->ref_frame_cost[x->e_mbd.mode_info_context->mbmi.ref_frame];
+
+    if (!disable_skip)
+    {
+        // Test for the condition where skip block will be activated because there are no non zero coefficients and make any necessary adjustment for rate
+        if (cpi->common.mb_no_coeff_skip)
+        {
+            int i;
+            int tteob;
+            int has_y2_block = (this_mode!=SPLITMV && this_mode!=B_PRED);
+
+            tteob = 0;
+            if(has_y2_block)
+                tteob += x->e_mbd.eobs[24];
+
+            for (i = 0; i < 16; i++)
+                tteob += (x->e_mbd.eobs[i] > has_y2_block);
+
+            if (x->e_mbd.mode_info_context->mbmi.ref_frame)
+            {
+                for (i = 16; i < 24; i++)
+                    tteob += x->e_mbd.eobs[i];
+            }
+            else
+                tteob += uv_intra_tteob;
+
+            if (tteob == 0)
+            {
+                rd->rate2 -= (rd->rate_y + rd->rate_uv);
+                //for best_yrd calculation
+                rd->rate_uv = 0;
+
+                // Back out no skip flag costing and add in skip flag costing
+                if (cpi->prob_skip_false)
+                {
+                    int prob_skip_cost;
+
+                    prob_skip_cost = vp8_cost_bit(cpi->prob_skip_false, 1);
+                    prob_skip_cost -= vp8_cost_bit(cpi->prob_skip_false, 0);
+                    rd->rate2 += prob_skip_cost;
+                    *other_cost += prob_skip_cost;
+                }
+            }
+        }
+        // Calculate the final RD estimate for this mode
+        this_rd = RDCOST(x->rdmult, x->rddiv, rd->rate2, rd->distortion2);
+        if (this_rd < INT_MAX && x->e_mbd.mode_info_context->mbmi.ref_frame
+                                 == INTRA_FRAME)
+            this_rd += intra_rd_penalty;
+    }
+    return this_rd;
+}
+
+static void update_best_mode(BEST_MODE* best_mode, int this_rd,
+                             RATE_DISTORTION* rd, int other_cost, MACROBLOCK *x)
+{
+    MB_PREDICTION_MODE this_mode = x->e_mbd.mode_info_context->mbmi.mode;
+
+    other_cost +=
+    x->ref_frame_cost[x->e_mbd.mode_info_context->mbmi.ref_frame];
+
+    /* Calculate the final y RD estimate for this mode */
+    best_mode->yrd = RDCOST(x->rdmult, x->rddiv, (rd->rate2-rd->rate_uv-other_cost),
+                      (rd->distortion2-rd->distortion_uv));
+
+    best_mode->rd = this_rd;
+    vpx_memcpy(&best_mode->mbmode, &x->e_mbd.mode_info_context->mbmi, sizeof(MB_MODE_INFO));
+    vpx_memcpy(&best_mode->partition, x->partition_info, sizeof(PARTITION_INFO));
+
+    if ((this_mode == B_PRED) || (this_mode == SPLITMV))
+    {
+        int i;
+        for (i = 0; i < 16; i++)
+        {
+            best_mode->bmodes[i] = x->e_mbd.block[i].bmi;
+        }
+    }
+}
+
 void vp8_rd_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset,
                             int recon_uvoffset, int *returnrate,
                             int *returndistortion, int *returnintra)
@@ -1719,9 +1916,6 @@
     BLOCK *b = &x->block[0];
     BLOCKD *d = &x->e_mbd.block[0];
     MACROBLOCKD *xd = &x->e_mbd;
-    union b_mode_info best_bmodes[16];
-    MB_MODE_INFO best_mbmode;
-    PARTITION_INFO best_partition;
     int_mv best_ref_mv_sb[2];
     int_mv mode_mv_sb[2][MB_MODE_COUNT];
     int_mv best_ref_mv;
@@ -1729,21 +1923,16 @@
     MB_PREDICTION_MODE this_mode;
     int num00;
     int best_mode_index = 0;
+    BEST_MODE best_mode;
 
     int i;
     int mode_index;
     int mdcounts[4];
     int rate;
-    int distortion;
-    int best_rd = INT_MAX;
-    int best_intra_rd = INT_MAX;
-    int rate2, distortion2;
+    RATE_DISTORTION rd;
     int uv_intra_rate, uv_intra_distortion, uv_intra_rate_tokenonly;
     int uv_intra_tteob = 0;
     int uv_intra_done = 0;
-    int rate_y, UNINITIALIZED_IS_SAFE(rate_uv);
-    int distortion_uv;
-    int best_yrd = INT_MAX;
 
     MB_PREDICTION_MODE uv_intra_mode = 0;
     int_mv mvp;
@@ -1760,9 +1949,12 @@
 
     mode_mv = mode_mv_sb[sign_bias];
     best_ref_mv.as_int = 0;
+    best_mode.rd = INT_MAX;
+    best_mode.yrd = INT_MAX;
+    best_mode.intra_rd = INT_MAX;
     vpx_memset(mode_mv_sb, 0, sizeof(mode_mv_sb));
-    vpx_memset(&best_mbmode, 0, sizeof(best_mbmode));
-    vpx_memset(&best_bmodes, 0, sizeof(best_bmodes));
+    vpx_memset(&best_mode.mbmode, 0, sizeof(best_mode.mbmode));
+    vpx_memset(&best_mode.bmodes, 0, sizeof(best_mode.bmodes));
 
     /* Setup search priorities */
     get_reference_search_order(cpi, ref_frame_map);
@@ -1799,7 +1991,7 @@
         int this_ref_frame = ref_frame_map[vp8_ref_frame_order[mode_index]];
 
         // Test best rd so far against threshold for trying this mode.
-        if (best_rd <= cpi->rd_threshes[mode_index])
+        if (best_mode.rd <= cpi->rd_threshes[mode_index])
             continue;
 
         if (this_ref_frame < 0)
@@ -1806,8 +1998,8 @@
             continue;
 
         // These variables hold are rolling total cost and distortion for this mode
-        rate2 = 0;
-        distortion2 = 0;
+        rd.rate2 = 0;
+        rd.distortion2 = 0;
 
         this_mode = vp8_mode_order[mode_index];
 
@@ -1907,16 +2099,17 @@
             int tmp_rd;
 
             // Note the rate value returned here includes the cost of coding the BPRED mode : x->mbmode_cost[x->e_mbd.frame_type][BPRED];
-            tmp_rd = rd_pick_intra4x4mby_modes(cpi, x, &rate, &rate_y, &distortion, best_yrd);
-            rate2 += rate;
-            distortion2 += distortion;
+            int distortion;
+            tmp_rd = rd_pick_intra4x4mby_modes(cpi, x, &rate, &rd.rate_y, &distortion, best_mode.yrd);
+            rd.rate2 += rate;
+            rd.distortion2 += distortion;
 
-            if(tmp_rd < best_yrd)
+            if(tmp_rd < best_mode.yrd)
             {
-                rate2 += uv_intra_rate;
-                rate_uv = uv_intra_rate_tokenonly;
-                distortion2 += uv_intra_distortion;
-                distortion_uv = uv_intra_distortion;
+                rd.rate2 += uv_intra_rate;
+                rd.rate_uv = uv_intra_rate_tokenonly;
+                rd.distortion2 += uv_intra_distortion;
+                rd.distortion_uv = uv_intra_distortion;
             }
             else
             {
@@ -1930,24 +2123,25 @@
         {
             int tmp_rd;
             int this_rd_thresh;
+            int distortion;
 
             this_rd_thresh = (vp8_ref_frame_order[mode_index] == 1) ? cpi->rd_threshes[THR_NEW1] : cpi->rd_threshes[THR_NEW3];
             this_rd_thresh = (vp8_ref_frame_order[mode_index] == 2) ? cpi->rd_threshes[THR_NEW2] : this_rd_thresh;
 
             tmp_rd = vp8_rd_pick_best_mbsegmentation(cpi, x, &best_ref_mv,
-                                                     best_yrd, mdcounts,
-                                                     &rate, &rate_y, &distortion, this_rd_thresh) ;
+                                                     best_mode.yrd, mdcounts,
+                                                     &rate, &rd.rate_y, &distortion, this_rd_thresh) ;
 
-            rate2 += rate;
-            distortion2 += distortion;
+            rd.rate2 += rate;
+            rd.distortion2 += distortion;
 
             // If even the 'Y' rd value of split is higher than best so far then dont bother looking at UV
-            if (tmp_rd < best_yrd)
+            if (tmp_rd < best_mode.yrd)
             {
                 // Now work out UV cost and add it in
-                rd_inter4x4_uv(cpi, x, &rate_uv, &distortion_uv, cpi->common.full_pixel);
-                rate2 += rate_uv;
-                distortion2 += distortion_uv;
+                rd_inter4x4_uv(cpi, x, &rd.rate_uv, &rd.distortion_uv, cpi->common.full_pixel);
+                rd.rate2 += rd.rate_uv;
+                rd.distortion2 += rd.distortion_uv;
             }
             else
             {
@@ -1960,18 +2154,21 @@
         case V_PRED:
         case H_PRED:
         case TM_PRED:
+        {
+            int distortion;
             x->e_mbd.mode_info_context->mbmi.ref_frame = INTRA_FRAME;
             vp8_build_intra_predictors_mby
                 (&x->e_mbd);
-            macro_block_yrd(x, &rate_y, &distortion) ;
-            rate2 += rate_y;
-            distortion2 += distortion;
-            rate2 += x->mbmode_cost[x->e_mbd.frame_type][x->e_mbd.mode_info_context->mbmi.mode];
-            rate2 += uv_intra_rate;
-            rate_uv = uv_intra_rate_tokenonly;
-            distortion2 += uv_intra_distortion;
-            distortion_uv = uv_intra_distortion;
-            break;
+            macro_block_yrd(x, &rd.rate_y, &distortion) ;
+            rd.rate2 += rd.rate_y;
+            rd.distortion2 += distortion;
+            rd.rate2 += x->mbmode_cost[x->e_mbd.frame_type][x->e_mbd.mode_info_context->mbmi.mode];
+            rd.rate2 += uv_intra_rate;
+            rd.rate_uv = uv_intra_rate_tokenonly;
+            rd.distortion2 += uv_intra_distortion;
+            rd.distortion_uv = uv_intra_distortion;
+        }
+        break;
 
         case NEWMV:
         {
@@ -2114,7 +2311,7 @@
             mode_mv[NEWMV].as_int = d->bmi.mv.as_int;
 
             // Add the new motion vector cost to our rolling cost variable
-            rate2 += vp8_mv_bit_cost(&mode_mv[NEWMV], &best_ref_mv, x->mvcost, 96);
+            rd.rate2 += vp8_mv_bit_cost(&mode_mv[NEWMV], &best_ref_mv, x->mvcost, 96);
         }
 
         case NEARESTMV:
@@ -2136,152 +2333,50 @@
                 continue;
 
             vp8_set_mbmode_and_mvs(x, this_mode, &mode_mv[this_mode]);
-            vp8_build_inter16x16_predictors_mby(&x->e_mbd, x->e_mbd.predictor, 16);
-
-            if (cpi->active_map_enabled && x->active_ptr[0] == 0) {
-                x->skip = 1;
-            }
-            else if (x->encode_breakout)
-            {
-                unsigned int sse;
-                unsigned int var;
-                int threshold = (xd->block[0].dequant[1]
-                            * xd->block[0].dequant[1] >>4);
-
-                if(threshold < x->encode_breakout)
-                    threshold = x->encode_breakout;
-
-                var = vp8_variance16x16
-                        (*(b->base_src), b->src_stride,
-                        x->e_mbd.predictor, 16, &sse);
-
-                if (sse < threshold)
-                {
-                     unsigned int q2dc = xd->block[24].dequant[0];
-                    /* If theres is no codeable 2nd order dc
-                       or a very small uniform pixel change change */
-                    if ((sse - var < q2dc * q2dc >>4) ||
-                        (sse /2 > var && sse-var < 64))
-                    {
-                        // Check u and v to make sure skip is ok
-                        int sse2=  VP8_UVSSE(x);
-                        if (sse2 * 2 < threshold)
-                        {
-                            x->skip = 1;
-                            distortion2 = sse + sse2;
-                            rate2 = 500;
-
-                            /* for best_yrd calculation */
-                            rate_uv = 0;
-                            distortion_uv = sse2;
-
-                            disable_skip = 1;
-                            this_rd = RDCOST(x->rdmult, x->rddiv, rate2, distortion2);
-
-                            break;
-                        }
-                    }
-                }
-            }
+            this_rd = evaluate_inter_mode_rd(mdcounts, &rd,
+                                             &disable_skip, cpi, x);
+            break;
 
-
-            //intermodecost[mode_index] = vp8_cost_mv_ref(this_mode, mdcounts);   // Experimental debug code
-
-            // Add in the Mv/mode cost
-            rate2 += vp8_cost_mv_ref(this_mode, mdcounts);
-
-            // Y cost and distortion
-            macro_block_yrd(x, &rate_y, &distortion);
-            rate2 += rate_y;
-            distortion2 += distortion;
-
-            // UV cost and distortion
-            rd_inter16x16_uv(cpi, x, &rate_uv, &distortion_uv, cpi->common.full_pixel);
-            rate2 += rate_uv;
-            distortion2 += distortion_uv;
-            break;
-
         default:
             break;
         }
 
-        // Where skip is allowable add in the default per mb cost for the no skip case.
-        // where we then decide to skip we have to delete this and replace it with the
-        // cost of signallying a skip
-        if (cpi->common.mb_no_coeff_skip)
-        {
-            other_cost += vp8_cost_bit(cpi->prob_skip_false, 0);
-            rate2 += other_cost;
-        }
+        this_rd = calculate_final_rd_costs(this_rd, &rd, &other_cost,
+                                           disable_skip, uv_intra_tteob,
+                                           intra_rd_penalty, cpi, x);
 
-        /* Estimate the reference frame signaling cost and add it
-         * to the rolling cost variable.
-         */
-        rate2 +=
-            x->ref_frame_cost[x->e_mbd.mode_info_context->mbmi.ref_frame];
-
-        if (!disable_skip)
+        // Keep record of best intra distortion
+        if ((x->e_mbd.mode_info_context->mbmi.ref_frame == INTRA_FRAME) &&
+            (this_rd < best_mode.intra_rd) )
         {
-            // Test for the condition where skip block will be activated because there are no non zero coefficients and make any necessary adjustment for rate
-            if (cpi->common.mb_no_coeff_skip)
-            {
-                int tteob;
-                int has_y2_block = (this_mode!=SPLITMV && this_mode!=B_PRED);
-
-                tteob = 0;
-                if(has_y2_block)
-                    tteob += x->e_mbd.eobs[24];
-
-                for (i = 0; i < 16; i++)
-                    tteob += (x->e_mbd.eobs[i] > has_y2_block);
-
-                if (x->e_mbd.mode_info_context->mbmi.ref_frame)
-                {
-                    for (i = 16; i < 24; i++)
-                        tteob += x->e_mbd.eobs[i];
-                }
-                else
-                    tteob += uv_intra_tteob;
-
-                if (tteob == 0)
-                {
-                    rate2 -= (rate_y + rate_uv);
-                    //for best_yrd calculation
-                    rate_uv = 0;
-
-                    // Back out no skip flag costing and add in skip flag costing
-                    if (cpi->prob_skip_false)
-                    {
-                        int prob_skip_cost;
-
-                        prob_skip_cost = vp8_cost_bit(cpi->prob_skip_false, 1);
-                        prob_skip_cost -= vp8_cost_bit(cpi->prob_skip_false, 0);
-                        rate2 += prob_skip_cost;
-                        other_cost += prob_skip_cost;
-                    }
-                }
-            }
-            // Calculate the final RD estimate for this mode
-            this_rd = RDCOST(x->rdmult, x->rddiv, rate2, distortion2);
-            if (this_rd < INT_MAX && x->e_mbd.mode_info_context->mbmi.ref_frame
-                                     == INTRA_FRAME)
-                this_rd += intra_rd_penalty;
+          best_mode.intra_rd = this_rd;
+            *returnintra = rd.distortion2 ;
         }
 
-        // Keep record of best intra distortion
-        if ((x->e_mbd.mode_info_context->mbmi.ref_frame == INTRA_FRAME) &&
-            (this_rd < best_intra_rd) )
+#if CONFIG_TEMPORAL_DENOISING
+        if (cpi->oxcf.noise_sensitivity)
         {
-            best_intra_rd = this_rd;
-            *returnintra = distortion2 ;
+          // Store the best NEWMV in x for later use in the denoiser.
+          // We are restricted to the LAST_FRAME since the denoiser only keeps
+          // one filter state.
+          if (x->e_mbd.mode_info_context->mbmi.mode == NEWMV &&
+              x->e_mbd.mode_info_context->mbmi.ref_frame == LAST_FRAME)
+          {
+            x->e_mbd.best_sse_inter_mode = NEWMV;
+            x->e_mbd.best_sse_mv = x->e_mbd.mode_info_context->mbmi.mv;
+            x->e_mbd.need_to_clamp_best_mvs =
+                x->e_mbd.mode_info_context->mbmi.need_to_clamp_mvs;
+          }
         }
+#endif
 
         // Did this mode help.. i.i is it the new best mode
-        if (this_rd < best_rd || x->skip)
+        if (this_rd < best_mode.rd || x->skip)
         {
             // Note index of best mode so far
             best_mode_index = mode_index;
-
+            *returnrate = rd.rate2;
+            *returndistortion = rd.distortion2;
             if (this_mode <= B_PRED)
             {
                 x->e_mbd.mode_info_context->mbmi.uv_mode = uv_intra_mode;
@@ -2288,27 +2383,9 @@
                 /* required for left and above block mv */
                 x->e_mbd.mode_info_context->mbmi.mv.as_int = 0;
             }
+            update_best_mode(&best_mode, this_rd, &rd, other_cost, x);
 
-            other_cost +=
-            x->ref_frame_cost[x->e_mbd.mode_info_context->mbmi.ref_frame];
 
-            /* Calculate the final y RD estimate for this mode */
-            best_yrd = RDCOST(x->rdmult, x->rddiv, (rate2-rate_uv-other_cost),
-                              (distortion2-distortion_uv));
-
-            *returnrate = rate2;
-            *returndistortion = distortion2;
-            best_rd = this_rd;
-            vpx_memcpy(&best_mbmode, &x->e_mbd.mode_info_context->mbmi, sizeof(MB_MODE_INFO));
-            vpx_memcpy(&best_partition, x->partition_info, sizeof(PARTITION_INFO));
-
-            if ((this_mode == B_PRED) || (this_mode == SPLITMV))
-                for (i = 0; i < 16; i++)
-                {
-                    best_bmodes[i] = x->e_mbd.block[i].bmi;
-                }
-
-
             // Testing this mode gave rise to an improvement in best error score. Lower threshold a bit for next time
             cpi->rd_thresh_mult[mode_index] = (cpi->rd_thresh_mult[mode_index] >= (MIN_THRESHMULT + 2)) ? cpi->rd_thresh_mult[mode_index] - 2 : MIN_THRESHMULT;
             cpi->rd_threshes[mode_index] = (cpi->rd_baseline_thresh[mode_index] >> 7) * cpi->rd_thresh_mult[mode_index];
@@ -2359,9 +2436,50 @@
     // Note how often each mode chosen as best
     cpi->mode_chosen_counts[best_mode_index] ++;
 
+#if CONFIG_TEMPORAL_DENOISING
+    if (cpi->oxcf.noise_sensitivity)
+    {
+      if (x->e_mbd.best_sse_inter_mode == DC_PRED) {
+        // No best MV found.
+        x->e_mbd.best_sse_inter_mode = best_mode.mbmode.mode;
+        x->e_mbd.best_sse_mv = best_mode.mbmode.mv;
+        x->e_mbd.need_to_clamp_best_mvs = best_mode.mbmode.need_to_clamp_mvs;
+      }
 
+      // TODO(holmer): No SSEs are calculated in rdopt.c. What else can be used?
+      vp8_denoiser_denoise_mb(&cpi->denoiser, x, 0, 0,
+                              recon_yoffset, recon_uvoffset);
+      // Reevalute ZEROMV if the current mode is INTRA.
+      if (best_mode.mbmode.ref_frame == INTRA_FRAME)
+      {
+        int this_rd = INT_MAX;
+        int disable_skip = 0;
+        int other_cost = 0;
+        vpx_memset(&rd, 0, sizeof(rd));
+        x->e_mbd.mode_info_context->mbmi.ref_frame = LAST_FRAME;
+        rd.rate2 += x->ref_frame_cost[LAST_FRAME];
+        rd.rate2 += vp8_cost_mv_ref(ZEROMV, mdcounts);
+        x->e_mbd.mode_info_context->mbmi.mode = ZEROMV;
+        x->e_mbd.mode_info_context->mbmi.uv_mode = DC_PRED;
+        x->e_mbd.mode_info_context->mbmi.mv.as_int = 0;
+        this_rd = evaluate_inter_mode_rd(mdcounts, &rd, &disable_skip, cpi, x);
+        this_rd = calculate_final_rd_costs(this_rd, &rd, &other_cost,
+                                           disable_skip, uv_intra_tteob,
+                                           intra_rd_penalty, cpi, x);
+        if (this_rd < best_mode.rd || x->skip)
+        {
+            // Note index of best mode so far
+            best_mode_index = mode_index;
+            *returnrate = rd.rate2;
+            *returndistortion = rd.distortion2;
+            update_best_mode(&best_mode, this_rd, &rd, other_cost, x);
+        }
+      }
+    }
+#endif
+
     if (cpi->is_src_frame_alt_ref &&
-        (best_mbmode.mode != ZEROMV || best_mbmode.ref_frame != ALTREF_FRAME))
+        (best_mode.mbmode.mode != ZEROMV || best_mode.mbmode.ref_frame != ALTREF_FRAME))
     {
         x->e_mbd.mode_info_context->mbmi.mode = ZEROMV;
         x->e_mbd.mode_info_context->mbmi.ref_frame = ALTREF_FRAME;
@@ -2370,26 +2488,25 @@
         x->e_mbd.mode_info_context->mbmi.mb_skip_coeff =
                                         (cpi->common.mb_no_coeff_skip);
         x->e_mbd.mode_info_context->mbmi.partitioning = 0;
-
         return;
     }
 
 
     // macroblock modes
-    vpx_memcpy(&x->e_mbd.mode_info_context->mbmi, &best_mbmode, sizeof(MB_MODE_INFO));
+    vpx_memcpy(&x->e_mbd.mode_info_context->mbmi, &best_mode.mbmode, sizeof(MB_MODE_INFO));
 
-    if (best_mbmode.mode == B_PRED)
+    if (best_mode.mbmode.mode == B_PRED)
     {
         for (i = 0; i < 16; i++)
-            xd->mode_info_context->bmi[i].as_mode = best_bmodes[i].as_mode;
+            xd->mode_info_context->bmi[i].as_mode = best_mode.bmodes[i].as_mode;
     }
 
-    if (best_mbmode.mode == SPLITMV)
+    if (best_mode.mbmode.mode == SPLITMV)
     {
         for (i = 0; i < 16; i++)
-            xd->mode_info_context->bmi[i].mv.as_int = best_bmodes[i].mv.as_int;
+            xd->mode_info_context->bmi[i].mv.as_int = best_mode.bmodes[i].mv.as_int;
 
-        vpx_memcpy(x->partition_info, &best_partition, sizeof(PARTITION_INFO));
+        vpx_memcpy(x->partition_info, &best_mode.partition, sizeof(PARTITION_INFO));
 
         x->e_mbd.mode_info_context->mbmi.mv.as_int =
                                       x->partition_info->bmi[15].mv.as_int;
--- a/vp8/vp8_cx_iface.c
+++ b/vp8/vp8_cx_iface.c
@@ -183,13 +183,19 @@
 
     RANGE_CHECK_BOOL(vp8_cfg,               enable_auto_alt_ref);
     RANGE_CHECK(vp8_cfg, cpu_used,           -16, 16);
-
+#if CONFIG_TEMPORAL_DENOISING
+    RANGE_CHECK(vp8_cfg, noise_sensitivity, 0, 1);
+#endif
 #if !(CONFIG_REALTIME_ONLY)
     RANGE_CHECK(vp8_cfg, encoding_mode,      VP8_BEST_QUALITY_ENCODING, VP8_REAL_TIME_ENCODING);
+#if !(CONFIG_TEMPORAL_DENOISING)
     RANGE_CHECK_HI(vp8_cfg, noise_sensitivity,  6);
+#endif
 #else
     RANGE_CHECK(vp8_cfg, encoding_mode,      VP8_REAL_TIME_ENCODING, VP8_REAL_TIME_ENCODING);
+#if !(CONFIG_TEMPORAL_DENOISING)
     RANGE_CHECK(vp8_cfg, noise_sensitivity,  0, 0);
+#endif
 #endif
 
     RANGE_CHECK(vp8_cfg, token_partitions,   VP8_ONE_TOKENPARTITION, VP8_EIGHT_TOKENPARTITION);
--- a/vp8/vp8cx.mk
+++ b/vp8/vp8cx.mk
@@ -47,6 +47,8 @@
 VP8_CX_SRCS-yes += encoder/block.h
 VP8_CX_SRCS-yes += encoder/boolhuff.h
 VP8_CX_SRCS-yes += encoder/bitstream.h
+VP8_CX_SRCS-$(CONFIG_TEMPORAL_DENOISING) += encoder/denoising.h
+VP8_CX_SRCS-$(CONFIG_TEMPORAL_DENOISING) += encoder/denoising.c
 VP8_CX_SRCS-yes += encoder/encodeintra.h
 VP8_CX_SRCS-yes += encoder/encodemb.h
 VP8_CX_SRCS-yes += encoder/encodemv.h