shithub: libvpx

Download patch

ref: 567746d3f0d39d7c44682df18f6617515540d522
parent: 1349e8634ca1be8a128501b74164ae0b523e0ee0
parent: 4e81ab82d55d44e2b125f00017d2026226c5483a
author: Marco Paniconi <marpan@google.com>
date: Tue May 27 11:32:16 EDT 2014

Merge "Fix to reduce block artifacts from vp8 temporal denoiser."

--- a/vp8/encoder/denoising.c
+++ b/vp8/encoder/denoising.c
@@ -136,8 +136,56 @@
 
     sum_diff_thresh= SUM_DIFF_THRESHOLD;
     if (increase_denoising) sum_diff_thresh = SUM_DIFF_THRESHOLD_HIGH;
-    if (abs(sum_diff) > sum_diff_thresh)
+    if (abs(sum_diff) > sum_diff_thresh) {
+      // Before returning to copy the block (i.e., apply no denoising), check
+      // if we can still apply some (weaker) temporal filtering to this block,
+      // that would otherwise not be denoised at all. Simplest is to apply
+      // an additional adjustment to running_avg_y to bring it closer to sig.
+      // The adjustment is capped by a maximum delta, and chosen such that
+      // in most cases the resulting sum_diff will be within the
+      // accceptable range given by sum_diff_thresh.
+
+      // The delta is set by the excess of absolute pixel diff over threshold.
+      int delta = ((abs(sum_diff) - sum_diff_thresh) >> 8) + 1;
+      // Only apply the adjustment for max delta up to 3.
+      if (delta < 4) {
+        sig -= sig_stride * 16;
+        mc_running_avg_y -= mc_avg_y_stride * 16;
+        running_avg_y -= avg_y_stride * 16;
+        for (r = 0; r < 16; ++r) {
+          for (c = 0; c < 16; ++c) {
+            int diff = mc_running_avg_y[c] - sig[c];
+            int adjustment = abs(diff);
+            if (adjustment > delta)
+              adjustment = delta;
+            if (diff > 0) {
+              // Bring denoised signal down.
+              if (running_avg_y[c] - adjustment < 0)
+                running_avg_y[c] = 0;
+              else
+                running_avg_y[c] = running_avg_y[c] - adjustment;
+              sum_diff -= adjustment;
+            } else if (diff < 0) {
+              // Bring denoised signal up.
+              if (running_avg_y[c] + adjustment > 255)
+                running_avg_y[c] = 255;
+              else
+                running_avg_y[c] = running_avg_y[c] + adjustment;
+              sum_diff += adjustment;
+            }
+          }
+          // TODO(marpan): Check here if abs(sum_diff) has gone below the
+          // threshold sum_diff_thresh, and if so, we can exit the row loop.
+          sig += sig_stride;
+          mc_running_avg_y += mc_avg_y_stride;
+          running_avg_y += avg_y_stride;
+        }
+        if (abs(sum_diff) > sum_diff_thresh)
+          return COPY_BLOCK;
+      } else {
         return COPY_BLOCK;
+      }
+    }
 
     vp8_copy_mem16x16(running_avg_y_start, avg_y_stride, sig_start, sig_stride);
     return FILTER_BLOCK;
--- a/vp8/encoder/x86/denoising_sse2.c
+++ b/vp8/encoder/x86/denoising_sse2.c
@@ -112,9 +112,70 @@
 
         sum_diff_thresh = SUM_DIFF_THRESHOLD;
         if (increase_denoising) sum_diff_thresh = SUM_DIFF_THRESHOLD_HIGH;
-        if (abs(sum_diff) > sum_diff_thresh)
-        {
+        if (abs(sum_diff) > sum_diff_thresh) {
+          // Before returning to copy the block (i.e., apply no denoising),
+          // checK if we can still apply some (weaker) temporal filtering to
+          // this block, that would otherwise not be denoised at all. Simplest
+          // is to apply an additional adjustment to running_avg_y to bring it
+          // closer to sig. The adjustment is capped by a maximum delta, and
+          // chosen such that in most cases the resulting sum_diff will be
+          // within the accceptable range given by sum_diff_thresh.
+
+          // The delta is set by the excess of absolute pixel diff over the
+          // threshold.
+          int delta = ((abs(sum_diff) - sum_diff_thresh) >> 8) + 1;
+          // Only apply the adjustment for max delta up to 3.
+          if (delta < 4) {
+            const __m128i k_delta = _mm_set1_epi8(delta);
+            sig -= sig_stride * 16;
+            mc_running_avg_y -= mc_avg_y_stride * 16;
+            running_avg_y -= avg_y_stride * 16;
+            for (r = 0; r < 16; ++r) {
+              __m128i v_running_avg_y =
+                  _mm_loadu_si128((__m128i *)(&running_avg_y[0]));
+              // Calculate differences.
+              const __m128i v_sig = _mm_loadu_si128((__m128i *)(&sig[0]));
+              const __m128i v_mc_running_avg_y =
+                  _mm_loadu_si128((__m128i *)(&mc_running_avg_y[0]));
+              const __m128i pdiff = _mm_subs_epu8(v_mc_running_avg_y, v_sig);
+              const __m128i ndiff = _mm_subs_epu8(v_sig, v_mc_running_avg_y);
+              // Obtain the sign. FF if diff is negative.
+              const __m128i diff_sign = _mm_cmpeq_epi8(pdiff, k_0);
+              // Clamp absolute difference to delta to get the adjustment.
+              const __m128i adj =
+                  _mm_min_epu8(_mm_or_si128(pdiff, ndiff), k_delta);
+              // Restore the sign and get positive and negative adjustments.
+              __m128i padj, nadj;
+              padj = _mm_andnot_si128(diff_sign, adj);
+              nadj = _mm_and_si128(diff_sign, adj);
+              // Calculate filtered value.
+              v_running_avg_y = _mm_subs_epu8(v_running_avg_y, padj);
+              v_running_avg_y = _mm_adds_epu8(v_running_avg_y, nadj);
+             _mm_storeu_si128((__m128i *)running_avg_y, v_running_avg_y);
+
+             // Accumulate the adjustments.
+             acc_diff = _mm_subs_epi8(acc_diff, padj);
+             acc_diff = _mm_adds_epi8(acc_diff, nadj);
+
+             // Update pointers for next iteration.
+             sig += sig_stride;
+             mc_running_avg_y += mc_avg_y_stride;
+             running_avg_y += avg_y_stride;
+            }
+            {
+              // Update the sum of all pixel differences of this MB.
+              union sum_union s;
+              s.v = acc_diff;
+              sum_diff = s.e[0] + s.e[1] + s.e[2] + s.e[3] + s.e[4] + s.e[5]
+                       + s.e[6] + s.e[7] + s.e[8] + s.e[9] + s.e[10] + s.e[11]
+                       + s.e[12] + s.e[13] + s.e[14] + s.e[15];
+              if (abs(sum_diff) > sum_diff_thresh) {
+                return COPY_BLOCK;
+              }
+            }
+          } else {
             return COPY_BLOCK;
+          }
         }
     }
 
--