blob: b9e6b8c859679789fb34efcbe867b5bf7fafac83 [file] [log] [blame]
/*
* 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 <arm_neon.h>
#include "webrtc/modules/video_processing/util/denoiser_filter_neon.h"
namespace webrtc {
const int kSumDiffThresholdHighNeon = 600;
static int HorizontalAddS16x8(const int16x8_t v_16x8) {
const int32x4_t a = vpaddlq_s16(v_16x8);
const int64x2_t b = vpaddlq_s32(a);
const int32x2_t c = vadd_s32(vreinterpret_s32_s64(vget_low_s64(b)),
vreinterpret_s32_s64(vget_high_s64(b)));
return vget_lane_s32(c, 0);
}
static int HorizontalAddS32x4(const int32x4_t v_32x4) {
const int64x2_t b = vpaddlq_s32(v_32x4);
const int32x2_t c = vadd_s32(vreinterpret_s32_s64(vget_low_s64(b)),
vreinterpret_s32_s64(vget_high_s64(b)));
return vget_lane_s32(c, 0);
}
static void VarianceNeonW8(const uint8_t* a,
int a_stride,
const uint8_t* b,
int b_stride,
int w,
int h,
uint32_t* sse,
int64_t* sum) {
int16x8_t v_sum = vdupq_n_s16(0);
int32x4_t v_sse_lo = vdupq_n_s32(0);
int32x4_t v_sse_hi = vdupq_n_s32(0);
for (int i = 0; i < h; ++i) {
for (int j = 0; j < w; j += 8) {
const uint8x8_t v_a = vld1_u8(&a[j]);
const uint8x8_t v_b = vld1_u8(&b[j]);
const uint16x8_t v_diff = vsubl_u8(v_a, v_b);
const int16x8_t sv_diff = vreinterpretq_s16_u16(v_diff);
v_sum = vaddq_s16(v_sum, sv_diff);
v_sse_lo =
vmlal_s16(v_sse_lo, vget_low_s16(sv_diff), vget_low_s16(sv_diff));
v_sse_hi =
vmlal_s16(v_sse_hi, vget_high_s16(sv_diff), vget_high_s16(sv_diff));
}
a += a_stride;
b += b_stride;
}
*sum = HorizontalAddS16x8(v_sum);
*sse =
static_cast<uint32_t>(HorizontalAddS32x4(vaddq_s32(v_sse_lo, v_sse_hi)));
}
void DenoiserFilterNEON::CopyMem16x16(const uint8_t* src,
int src_stride,
uint8_t* dst,
int dst_stride) {
uint8x16_t qtmp;
for (int r = 0; r < 16; r++) {
qtmp = vld1q_u8(src);
vst1q_u8(dst, qtmp);
src += src_stride;
dst += dst_stride;
}
}
uint32_t DenoiserFilterNEON::Variance16x8(const uint8_t* a,
int a_stride,
const uint8_t* b,
int b_stride,
uint32_t* sse) {
int64_t sum = 0;
VarianceNeonW8(a, a_stride << 1, b, b_stride << 1, 16, 8, sse, &sum);
return *sse - ((sum * sum) >> 7);
}
DenoiserDecision DenoiserFilterNEON::MbDenoise(const uint8_t* mc_running_avg_y,
int mc_running_avg_y_stride,
uint8_t* running_avg_y,
int running_avg_y_stride,
const uint8_t* sig,
int sig_stride,
uint8_t motion_magnitude,
int increase_denoising) {
// If motion_magnitude is small, making the denoiser more aggressive by
// increasing the adjustment for each level, level1 adjustment is
// increased, the deltas stay the same.
int shift_inc =
(increase_denoising && motion_magnitude <= kMotionMagnitudeThreshold) ? 1
: 0;
int sum_diff_thresh = 0;
const uint8x16_t v_level1_adjustment = vmovq_n_u8(
(motion_magnitude <= kMotionMagnitudeThreshold) ? 4 + shift_inc : 3);
const uint8x16_t v_delta_level_1_and_2 = vdupq_n_u8(1);
const uint8x16_t v_delta_level_2_and_3 = vdupq_n_u8(2);
const uint8x16_t v_level1_threshold = vmovq_n_u8(4 + shift_inc);
const uint8x16_t v_level2_threshold = vdupq_n_u8(8);
const uint8x16_t v_level3_threshold = vdupq_n_u8(16);
int64x2_t v_sum_diff_total = vdupq_n_s64(0);
// Go over lines.
for (int r = 0; r < 16; ++r) {
// Load inputs.
const uint8x16_t v_sig = vld1q_u8(sig);
const uint8x16_t v_mc_running_avg_y = vld1q_u8(mc_running_avg_y);
// Calculate absolute difference and sign masks.
const uint8x16_t v_abs_diff = vabdq_u8(v_sig, v_mc_running_avg_y);
const uint8x16_t v_diff_pos_mask = vcltq_u8(v_sig, v_mc_running_avg_y);
const uint8x16_t v_diff_neg_mask = vcgtq_u8(v_sig, v_mc_running_avg_y);
// Figure out which level that put us in.
const uint8x16_t v_level1_mask = vcleq_u8(v_level1_threshold, v_abs_diff);
const uint8x16_t v_level2_mask = vcleq_u8(v_level2_threshold, v_abs_diff);
const uint8x16_t v_level3_mask = vcleq_u8(v_level3_threshold, v_abs_diff);
// Calculate absolute adjustments for level 1, 2 and 3.
const uint8x16_t v_level2_adjustment =
vandq_u8(v_level2_mask, v_delta_level_1_and_2);
const uint8x16_t v_level3_adjustment =
vandq_u8(v_level3_mask, v_delta_level_2_and_3);
const uint8x16_t v_level1and2_adjustment =
vaddq_u8(v_level1_adjustment, v_level2_adjustment);
const uint8x16_t v_level1and2and3_adjustment =
vaddq_u8(v_level1and2_adjustment, v_level3_adjustment);
// Figure adjustment absolute value by selecting between the absolute
// difference if in level0 or the value for level 1, 2 and 3.
const uint8x16_t v_abs_adjustment =
vbslq_u8(v_level1_mask, v_level1and2and3_adjustment, v_abs_diff);
// Calculate positive and negative adjustments. Apply them to the signal
// and accumulate them. Adjustments are less than eight and the maximum
// sum of them (7 * 16) can fit in a signed char.
const uint8x16_t v_pos_adjustment =
vandq_u8(v_diff_pos_mask, v_abs_adjustment);
const uint8x16_t v_neg_adjustment =
vandq_u8(v_diff_neg_mask, v_abs_adjustment);
uint8x16_t v_running_avg_y = vqaddq_u8(v_sig, v_pos_adjustment);
v_running_avg_y = vqsubq_u8(v_running_avg_y, v_neg_adjustment);
// Store results.
vst1q_u8(running_avg_y, v_running_avg_y);
// Sum all the accumulators to have the sum of all pixel differences
// for this macroblock.
{
const int8x16_t v_sum_diff =
vqsubq_s8(vreinterpretq_s8_u8(v_pos_adjustment),
vreinterpretq_s8_u8(v_neg_adjustment));
const int16x8_t fe_dc_ba_98_76_54_32_10 = vpaddlq_s8(v_sum_diff);
const int32x4_t fedc_ba98_7654_3210 =
vpaddlq_s16(fe_dc_ba_98_76_54_32_10);
const int64x2_t fedcba98_76543210 = vpaddlq_s32(fedc_ba98_7654_3210);
v_sum_diff_total = vqaddq_s64(v_sum_diff_total, fedcba98_76543210);
}
// Update pointers for next iteration.
sig += sig_stride;
mc_running_avg_y += mc_running_avg_y_stride;
running_avg_y += running_avg_y_stride;
}
// Too much adjustments => copy block.
int64x1_t x = vqadd_s64(vget_high_s64(v_sum_diff_total),
vget_low_s64(v_sum_diff_total));
int sum_diff = vget_lane_s32(vabs_s32(vreinterpret_s32_s64(x)), 0);
sum_diff_thresh =
increase_denoising ? kSumDiffThresholdHighNeon : kSumDiffThreshold;
if (sum_diff > sum_diff_thresh)
return COPY_BLOCK;
// Tell above level that block was filtered.
running_avg_y -= running_avg_y_stride * 16;
sig -= sig_stride * 16;
return FILTER_BLOCK;
}
} // namespace webrtc