Chromium Code Reviews| Index: webrtc/modules/video_processing/util/denoiser_filter.cc |
| diff --git a/webrtc/modules/video_processing/util/denoiser_filter.cc b/webrtc/modules/video_processing/util/denoiser_filter.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ef4a31fd2d846c0ca59d098e05a39502a241314c |
| --- /dev/null |
| +++ b/webrtc/modules/video_processing/util/denoiser_filter.cc |
| @@ -0,0 +1,258 @@ |
| +/* |
| + * Copyright (c) 2015 The WebRTC 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 "webrtc/modules/video_processing/util/denoiser_filter.h" |
| +#include "webrtc/modules/video_processing/util/denoiser_filter_neon.h" |
| +#include "webrtc/modules/video_processing/util/denoiser_filter_sse2.h" |
| +#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" |
| + |
| +namespace webrtc { |
| + |
| +const int kMotionMagnitudeThreshold = 8 * 3; |
| +const int kSumDiffThreshold = 16 * 16 * 2; |
| +const int kSumDiffThresholdHigh = 600; |
| + |
| +class DenoiserFilterC : public DenoiserFilter { |
| + public: |
| + DenoiserFilterC() {} |
| + void CopyMem16x16(const uint8_t* src, |
| + int src_stride, |
| + uint8_t* dst, |
| + int dst_stride); |
| + void CopyMem8x8(const uint8_t* src, |
| + int src_stride, |
| + uint8_t* dst, |
| + int dst_stride); |
| + uint32_t Variance16x8(const uint8_t* a, |
| + int a_stride, |
| + const uint8_t* b, |
| + int b_stride, |
| + unsigned int* sse); |
| + DenoiserDecision MbDenoise(uint8_t* mc_running_avg_y, |
| + int mc_avg_y_stride, |
| + uint8_t* running_avg_y, |
| + int avg_y_stride, |
| + const uint8_t* sig, |
| + int sig_stride, |
| + uint8_t motion_magnitude, |
| + int increase_denoising) override; |
| +}; |
| + |
| +DenoiserFilter* DenoiserFilter::Create() { |
| + DenoiserFilter* filter = NULL; |
| + |
| + // If we know the minimum architecture at compile time, avoid CPU detection. |
| +#if defined(WEBRTC_ARCH_X86_FAMILY) |
| +#if defined(__SSE2__) |
|
mflodman
2015/11/25 12:15:19
Can't we skip this? I.e. creating the denoiser is
jackychen
2015/11/25 20:29:05
Done.
|
| + filter = |
| + new DenoiserFilterSSE2(); |
| +#else |
| + // x86 CPU detection required. |
| + if (WebRtc_GetCPUInfo(kSSE2)) { |
| + filter = |
| + new DenoiserFilterSSE2(); |
| + } else { |
| + filter = new DenoiserFilterC(); |
| + } |
| +#endif |
| +#elif defined(WEBRTC_HAS_NEON) |
| + filter = |
| + new DenoiserFilterNEON(); |
|
mflodman
2015/11/25 12:15:19
And same goes for neon detection.
jackychen
2015/11/25 20:29:05
Done.
|
| +#elif defined(WEBRTC_DETECT_NEON) |
| + if (WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) { |
| + filter = new DenoiserFilterNEON(); |
| + } else { |
| + filter = new DenoiserFilterC(); |
| + } |
| +#else |
| + filter = new DenoiserFilterC(); |
| +#endif |
| + |
| + return filter; |
| +} |
| + |
| +void DenoiserFilterC::CopyMem16x16(const uint8_t* src, |
|
mflodman
2015/11/25 12:15:19
I'd prefer to put 'DenoiserFilterC' in its own fil
jackychen
2015/11/25 20:29:05
Done.
|
| + int src_stride, |
| + uint8_t* dst, |
| + int dst_stride) { |
| + for (int i = 0; i < 16; i++) { |
| + memcpy(dst, src, 16); |
| + src += src_stride; |
| + dst += dst_stride; |
| + } |
| +} |
| + |
| +void DenoiserFilterC::CopyMem8x8(const uint8_t* src, |
| + int src_stride, |
| + uint8_t* dst, |
| + int dst_stride) { |
| + for (int i = 0; i < 8; i++) { |
| + memcpy(dst, src, 8); |
| + src += src_stride; |
| + dst += dst_stride; |
| + } |
| +} |
| + |
| +uint32_t DenoiserFilterC::Variance16x8(const uint8_t* a, |
| + int a_stride, |
| + const uint8_t* b, |
| + int b_stride, |
| + uint32_t* sse) { |
| + int sum = 0; |
| + *sse = 0; |
| + a_stride <<= 1; |
| + b_stride <<= 1; |
| + |
| + for (int i = 0; i < 8; i++) { |
| + for (int j = 0; j < 16; j++) { |
| + const int diff = a[j] - b[j]; |
| + sum += diff; |
| + *sse += diff * diff; |
| + } |
| + |
| + a += a_stride; |
| + b += b_stride; |
| + } |
| + return *sse - ((static_cast<int64_t>(sum) * sum) >> 8); |
| +} |
| + |
| +DenoiserDecision DenoiserFilterC::MbDenoise(uint8_t* mc_running_avg_y, |
| + int mc_avg_y_stride, |
| + uint8_t* running_avg_y, |
| + int avg_y_stride, |
| + const uint8_t* sig, |
| + int sig_stride, |
| + uint8_t motion_magnitude, |
| + int increase_denoising) { |
| + int sum_diff_thresh = 0; |
| + int sum_diff = 0; |
| + int adj_val[3] = {3, 4, 6}; |
| + int shift_inc1 = 0; |
| + int shift_inc2 = 1; |
| + int col_sum[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| + if (motion_magnitude <= kMotionMagnitudeThreshold) { |
| + if (increase_denoising) { |
| + shift_inc1 = 1; |
| + shift_inc2 = 2; |
| + } |
| + adj_val[0] += shift_inc2; |
| + adj_val[1] += shift_inc2; |
| + adj_val[2] += shift_inc2; |
| + } |
| + |
| + for (int r = 0; r < 16; ++r) { |
| + for (int c = 0; c < 16; ++c) { |
| + int diff = 0; |
| + int adjustment = 0; |
| + int absdiff = 0; |
| + |
| + diff = mc_running_avg_y[c] - sig[c]; |
| + absdiff = abs(diff); |
| + |
| + // When |diff| <= |3 + shift_inc1|, use pixel value from |
| + // last denoised raw. |
| + if (absdiff <= 3 + shift_inc1) { |
| + running_avg_y[c] = mc_running_avg_y[c]; |
| + col_sum[c] += diff; |
| + } else { |
| + if (absdiff >= 4 + shift_inc1 && absdiff <= 7) |
| + adjustment = adj_val[0]; |
| + else if (absdiff >= 8 && absdiff <= 15) |
| + adjustment = adj_val[1]; |
| + else |
| + adjustment = adj_val[2]; |
| + |
| + if (diff > 0) { |
| + if ((sig[c] + adjustment) > 255) |
| + running_avg_y[c] = 255; |
| + else |
| + running_avg_y[c] = sig[c] + adjustment; |
| + |
| + col_sum[c] += adjustment; |
| + } else { |
| + if ((sig[c] - adjustment) < 0) |
| + running_avg_y[c] = 0; |
| + else |
| + running_avg_y[c] = sig[c] - adjustment; |
| + |
| + col_sum[c] -= adjustment; |
| + } |
| + } |
| + } |
| + |
| + // Update pointers for next iteration. |
| + sig += sig_stride; |
| + mc_running_avg_y += mc_avg_y_stride; |
| + running_avg_y += avg_y_stride; |
| + } |
| + |
| + for (int c = 0; c < 16; ++c) { |
| + if (col_sum[c] >= 128) { |
| + col_sum[c] = 127; |
| + } |
| + sum_diff += col_sum[c]; |
| + } |
| + |
| + sum_diff_thresh = kSumDiffThreshold; |
| + if (increase_denoising) |
| + sum_diff_thresh = kSumDiffThresholdHigh; |
| + if (abs(sum_diff) > sum_diff_thresh) { |
| + 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 (int r = 0; r < 16; ++r) { |
| + for (int 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; |
| + col_sum[c] -= 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; |
| + col_sum[c] += adjustment; |
| + } |
| + } |
| + sig += sig_stride; |
| + mc_running_avg_y += mc_avg_y_stride; |
| + running_avg_y += avg_y_stride; |
| + } |
| + |
| + sum_diff = 0; |
| + for (int c = 0; c < 16; ++c) { |
| + if (col_sum[c] >= 128) { |
| + col_sum[c] = 127; |
| + } |
| + sum_diff += col_sum[c]; |
| + } |
| + |
| + if (abs(sum_diff) > sum_diff_thresh) |
| + return COPY_BLOCK; |
| + } else { |
| + return COPY_BLOCK; |
| + } |
| + } |
| + |
| + return FILTER_BLOCK; |
| +} |
| + |
| +} // namespace webrtc |