Index: source/libvpx/vp8/encoder/denoising.c |
=================================================================== |
--- source/libvpx/vp8/encoder/denoising.c (revision 281795) |
+++ source/libvpx/vp8/encoder/denoising.c (working copy) |
@@ -191,6 +191,148 @@ |
return FILTER_BLOCK; |
} |
+int vp8_denoiser_filter_uv_c(unsigned char *mc_running_avg_uv, |
+ int mc_avg_uv_stride, |
+ unsigned char *running_avg_uv, |
+ int avg_uv_stride, |
+ unsigned char *sig, |
+ int sig_stride, |
+ unsigned int motion_magnitude, |
+ int increase_denoising) { |
+ unsigned char *running_avg_uv_start = running_avg_uv; |
+ unsigned char *sig_start = sig; |
+ int sum_diff_thresh; |
+ int r, c; |
+ int sum_diff = 0; |
+ int sum_block = 0; |
+ int adj_val[3] = {3, 4, 6}; |
+ int shift_inc1 = 0; |
+ int shift_inc2 = 1; |
+ /* If motion_magnitude is small, making the denoiser more aggressive by |
+ * increasing the adjustment for each level. Add another increment for |
+ * blocks that are labeled for increase denoising. */ |
+ if (motion_magnitude <= MOTION_MAGNITUDE_THRESHOLD_UV) { |
+ if (increase_denoising) { |
+ shift_inc1 = 1; |
+ shift_inc2 = 2; |
+ } |
+ adj_val[0] += shift_inc2; |
+ adj_val[1] += shift_inc2; |
+ adj_val[2] += shift_inc2; |
+ } |
+ |
+ // Avoid denoising color signal if its close to average level. |
+ for (r = 0; r < 8; ++r) { |
+ for (c = 0; c < 8; ++c) { |
+ sum_block += sig[c]; |
+ } |
+ sig += sig_stride; |
+ } |
+ if (abs(sum_block - (128 * 8 * 8)) < SUM_DIFF_FROM_AVG_THRESH_UV) { |
+ return COPY_BLOCK; |
+ } |
+ |
+ sig -= sig_stride * 8; |
+ for (r = 0; r < 8; ++r) { |
+ for (c = 0; c < 8; ++c) { |
+ int diff = 0; |
+ int adjustment = 0; |
+ int absdiff = 0; |
+ |
+ diff = mc_running_avg_uv[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_uv[c] = mc_running_avg_uv[c]; |
+ sum_diff += diff; |
+ } else { |
+ if (absdiff >= 4 && 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_uv[c] = 255; |
+ else |
+ running_avg_uv[c] = sig[c] + adjustment; |
+ sum_diff += adjustment; |
+ } else { |
+ if ((sig[c] - adjustment) < 0) |
+ running_avg_uv[c] = 0; |
+ else |
+ running_avg_uv[c] = sig[c] - adjustment; |
+ sum_diff -= adjustment; |
+ } |
+ } |
+ } |
+ /* Update pointers for next iteration. */ |
+ sig += sig_stride; |
+ mc_running_avg_uv += mc_avg_uv_stride; |
+ running_avg_uv += avg_uv_stride; |
+ } |
+ |
+ sum_diff_thresh= SUM_DIFF_THRESHOLD_UV; |
+ if (increase_denoising) sum_diff_thresh = SUM_DIFF_THRESHOLD_HIGH_UV; |
+ 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 * 8; |
+ mc_running_avg_uv -= mc_avg_uv_stride * 8; |
+ running_avg_uv -= avg_uv_stride * 8; |
+ for (r = 0; r < 8; ++r) { |
+ for (c = 0; c < 8; ++c) { |
+ int diff = mc_running_avg_uv[c] - sig[c]; |
+ int adjustment = abs(diff); |
+ if (adjustment > delta) |
+ adjustment = delta; |
+ if (diff > 0) { |
+ // Bring denoised signal down. |
+ if (running_avg_uv[c] - adjustment < 0) |
+ running_avg_uv[c] = 0; |
+ else |
+ running_avg_uv[c] = running_avg_uv[c] - adjustment; |
+ sum_diff -= adjustment; |
+ } else if (diff < 0) { |
+ // Bring denoised signal up. |
+ if (running_avg_uv[c] + adjustment > 255) |
+ running_avg_uv[c] = 255; |
+ else |
+ running_avg_uv[c] = running_avg_uv[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_uv += mc_avg_uv_stride; |
+ running_avg_uv += avg_uv_stride; |
+ } |
+ if (abs(sum_diff) > sum_diff_thresh) |
+ return COPY_BLOCK; |
+ } else { |
+ return COPY_BLOCK; |
+ } |
+ } |
+ |
+ vp8_copy_mem8x8(running_avg_uv_start, avg_uv_stride, sig_start, |
+ sig_stride); |
+ return FILTER_BLOCK; |
+} |
+ |
int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height, |
int num_mb_rows, int num_mb_cols) |
{ |
@@ -261,6 +403,8 @@ |
unsigned int motion_magnitude2; |
unsigned int sse_thresh; |
int sse_diff_thresh = 0; |
+ // Denoise the UV channel. |
+ int apply_color_denoise = 0; |
// Spatial loop filter: only applied selectively based on |
// temporal filter state of block relative to top/left neighbors. |
int apply_spatial_loop_filter = 1; |
@@ -268,6 +412,8 @@ |
MV_REFERENCE_FRAME zero_frame = x->best_zeromv_reference_frame; |
enum vp8_denoiser_decision decision = FILTER_BLOCK; |
+ enum vp8_denoiser_decision decision_u = FILTER_BLOCK; |
+ enum vp8_denoiser_decision decision_v = FILTER_BLOCK; |
if (zero_frame) |
{ |
@@ -377,11 +523,37 @@ |
/* Filter. */ |
decision = vp8_denoiser_filter(mc_running_avg_y, mc_avg_y_stride, |
- running_avg_y, avg_y_stride, |
- x->thismb, 16, motion_magnitude2, |
- x->increase_denoising); |
+ running_avg_y, avg_y_stride, |
+ x->thismb, 16, motion_magnitude2, |
+ x->increase_denoising); |
denoiser->denoise_state[block_index] = motion_magnitude2 > 0 ? |
kFilterNonZeroMV : kFilterZeroMV; |
+ // Only denoise UV for zero motion, and if y channel was denoised. |
+ if (apply_color_denoise && |
+ motion_magnitude2 == 0 && |
+ decision == FILTER_BLOCK) { |
+ unsigned char *mc_running_avg_u = |
+ denoiser->yv12_mc_running_avg.u_buffer + recon_uvoffset; |
+ unsigned char *running_avg_u = |
+ denoiser->yv12_running_avg[INTRA_FRAME].u_buffer + recon_uvoffset; |
+ unsigned char *mc_running_avg_v = |
+ denoiser->yv12_mc_running_avg.v_buffer + recon_uvoffset; |
+ unsigned char *running_avg_v = |
+ denoiser->yv12_running_avg[INTRA_FRAME].v_buffer + recon_uvoffset; |
+ int mc_avg_uv_stride = denoiser->yv12_mc_running_avg.uv_stride; |
+ int avg_uv_stride = denoiser->yv12_running_avg[INTRA_FRAME].uv_stride; |
+ int signal_stride = x->block[16].src_stride; |
+ decision_u = |
+ vp8_denoiser_filter_uv(mc_running_avg_u, mc_avg_uv_stride, |
+ running_avg_u, avg_uv_stride, |
+ x->block[16].src + *x->block[16].base_src, |
+ signal_stride, motion_magnitude2, 0); |
+ decision_v = |
+ vp8_denoiser_filter_uv(mc_running_avg_v, mc_avg_uv_stride, |
+ running_avg_v, avg_uv_stride, |
+ x->block[20].src + *x->block[20].base_src, |
+ signal_stride, motion_magnitude2, 0); |
+ } |
} |
if (decision == COPY_BLOCK) |
{ |
@@ -394,7 +566,21 @@ |
denoiser->yv12_running_avg[INTRA_FRAME].y_stride); |
denoiser->denoise_state[block_index] = kNoFilter; |
} |
- // Option to selectively deblock the denoised signal. |
+ if (apply_color_denoise) { |
+ if (decision_u == COPY_BLOCK) { |
+ vp8_copy_mem8x8( |
+ x->block[16].src + *x->block[16].base_src, x->block[16].src_stride, |
+ denoiser->yv12_running_avg[INTRA_FRAME].u_buffer + recon_uvoffset, |
+ denoiser->yv12_running_avg[INTRA_FRAME].uv_stride); |
+ } |
+ if (decision_v == COPY_BLOCK) { |
+ vp8_copy_mem8x8( |
+ x->block[20].src + *x->block[20].base_src, x->block[16].src_stride, |
+ denoiser->yv12_running_avg[INTRA_FRAME].v_buffer + recon_uvoffset, |
+ denoiser->yv12_running_avg[INTRA_FRAME].uv_stride); |
+ } |
+ } |
+ // Option to selectively deblock the denoised signal, for y channel only. |
if (apply_spatial_loop_filter) { |
loop_filter_info lfi; |
int apply_filter_col = 0; |