diff --git a/vp9/encoder/vp9_denoiser.c b/vp9/encoder/vp9_denoiser.c index edda2b2239b57361615ff609a8f6720a2d26618f..f6393e010408d7ffd43502123425e89c75cf2282 100644 --- a/vp9/encoder/vp9_denoiser.c +++ b/vp9/encoder/vp9_denoiser.c @@ -33,30 +33,66 @@ static void make_grayscale(YV12_BUFFER_CONFIG *yuv); static const int widths[] = {4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 32, 64, 64}; static const int heights[] = {4, 8, 4, 8, 16, 8, 16, 32, 16, 32, 64, 32, 64}; -static VP9_DENOISER_DECISION update_running_avg(const uint8_t *mc_avg, - int mc_avg_stride, - uint8_t *avg, int avg_stride, - const uint8_t *sig, - int sig_stride, - int increase_denoising, - BLOCK_SIZE bs) { +static int absdiff_thresh(BLOCK_SIZE bs, int increase_denoising) { + (void)bs; + return 3 + (increase_denoising ? 1 : 0); +} + +static int delta_thresh(BLOCK_SIZE bs, int increase_denoising) { + (void)bs; + (void)increase_denoising; + return 4; +} + +static int noise_motion_thresh(BLOCK_SIZE bs, int increase_denoising) { + (void)bs; + (void)increase_denoising; + return 25 * 25; +} + +static unsigned int sse_thresh(BLOCK_SIZE bs, int increase_denoising) { + return widths[bs] * heights[bs] * (increase_denoising ? 60 : 40); +} + +static int sse_diff_thresh(BLOCK_SIZE bs, int increase_denoising, + int mv_row, int mv_col) { + if (mv_row * mv_row + mv_col * mv_col > + noise_motion_thresh(bs, increase_denoising)) { + return 0; + } else { + return widths[bs] * heights[bs] * 20; + } +} + +static int total_adj_strong_thresh(BLOCK_SIZE bs, int increase_denoising) { + return widths[bs] * heights[bs] * (increase_denoising ? 3 : 2); +} + +static int total_adj_weak_thresh(BLOCK_SIZE bs, int increase_denoising) { + return widths[bs] * heights[bs] * (increase_denoising ? 3 : 2); +} + +static VP9_DENOISER_DECISION denoiser_filter(const uint8_t *sig, int sig_stride, + const uint8_t *mc_avg, + int mc_avg_stride, + uint8_t *avg, int avg_stride, + int increase_denoising, + BLOCK_SIZE bs) { int r, c; - int diff, adj, absdiff; - int shift_inc1 = 0, shift_inc2 = 1; + const uint8_t *sig_start = sig; + const uint8_t *mc_avg_start = mc_avg; + uint8_t *avg_start = avg; + int diff, adj, absdiff, delta; int adj_val[] = {3, 4, 6}; int total_adj = 0; - if (increase_denoising) { - shift_inc1 = 1; - shift_inc2 = 2; - } - + // First attempt to apply a strong temporal denoising filter. for (r = 0; r < heights[bs]; ++r) { for (c = 0; c < widths[bs]; ++c) { diff = mc_avg[c] - sig[c]; absdiff = abs(diff); - if (absdiff <= 3 + shift_inc1) { + if (absdiff <= absdiff_thresh(bs, increase_denoising)) { avg[c] = mc_avg[c]; total_adj += diff; } else { @@ -84,7 +120,47 @@ static VP9_DENOISER_DECISION update_running_avg(const uint8_t *mc_avg, avg += avg_stride; mc_avg += mc_avg_stride; } - return total_adj; + + // If the strong filter did not modify the signal too much, we're all set. + if (abs(total_adj) <= total_adj_strong_thresh(bs, increase_denoising)) { + return FILTER_BLOCK; + } + + // Otherwise, we try to dampen the filter if the delta is not too high. + delta = ((abs(total_adj) - total_adj_strong_thresh(bs, increase_denoising)) + >> 8) + 1; + if (delta > delta_thresh(bs, increase_denoising)) { + return COPY_BLOCK; + } + + mc_avg = mc_avg_start; + avg = avg_start; + sig = sig_start; + for (r = 0; r < heights[bs]; ++r) { + for (c = 0; c < widths[bs]; ++c) { + diff = mc_avg[c] - sig[c]; + adj = abs(diff); + if (adj > delta) { + adj = delta; + } + if (diff > 0) { + avg[c] = MAX(0, avg[c] - adj); + total_adj += adj; + } else { + avg[c] = MIN(UINT8_MAX, avg[c] + adj); + total_adj -= adj; + } + } + sig += sig_stride; + avg += avg_stride; + mc_avg += mc_avg_stride; + } + + // We can use the filter if it has been sufficiently dampened + if (abs(total_adj) <= total_adj_weak_thresh(bs, increase_denoising)) { + return FILTER_BLOCK; + } + return COPY_BLOCK; } static uint8_t *block_start(uint8_t *framebuf, int stride, @@ -108,18 +184,8 @@ static VP9_DENOISER_DECISION perform_motion_compensation(VP9_DENOISER *denoiser, int increase_denoising, int mi_row, int mi_col) { - // constants - // TODO(tkopp): empirically determine good constants, or functions of block - // size. - int NOISE_MOTION_THRESHOLD = 25 * 25; - int SSE_DIFF_THRESHOLD = heights[bs] * widths[bs] * 20; - unsigned int SSE_THRESH = heights[bs] * widths[bs] * 40; - unsigned int SSE_THRESH_HI = heights[bs] * widths[bs] * 60; - int mv_col, mv_row; int sse_diff = denoiser->zero_mv_sse - denoiser->best_sse; - int sse_diff_thresh; - int sse_thresh; MV_REFERENCE_FRAME frame; MACROBLOCKD *filter_mbd = &mb->e_mbd; MB_MODE_INFO *mbmi = &filter_mbd->mi[0]->mbmi; @@ -131,20 +197,15 @@ static VP9_DENOISER_DECISION perform_motion_compensation(VP9_DENOISER *denoiser, saved_pre[0] = filter_mbd->plane[0].pre[0]; saved_pre[1] = filter_mbd->plane[0].pre[1]; - // Decide the threshold for sum squared error. mv_col = denoiser->best_sse_mv.as_mv.col; mv_row = denoiser->best_sse_mv.as_mv.row; - if (mv_row * mv_row + mv_col * mv_col > NOISE_MOTION_THRESHOLD) { - sse_diff_thresh = 0; - } else { - sse_diff_thresh = SSE_DIFF_THRESHOLD; - } frame = denoiser->best_reference_frame; // If the best reference frame uses inter-prediction and there is enough of a // difference in sum-squared-error, use it. - if (frame != INTRA_FRAME && sse_diff > sse_diff_thresh) { + if (frame != INTRA_FRAME && + sse_diff > sse_diff_thresh(bs, increase_denoising, mv_row, mv_col)) { mbmi->ref_frame[0] = denoiser->best_reference_frame; mbmi->mode = denoiser->best_sse_inter_mode; mbmi->mv[0] = denoiser->best_sse_mv; @@ -227,11 +288,12 @@ static VP9_DENOISER_DECISION perform_motion_compensation(VP9_DENOISER *denoiser, mv_row = denoiser->best_sse_mv.as_mv.row; mv_col = denoiser->best_sse_mv.as_mv.col; - sse_thresh = denoiser->increase_denoising ? SSE_THRESH_HI : SSE_THRESH; - // TODO(tkopp) why 8? - if (denoiser->best_sse > sse_thresh || - mv_row * mv_row + mv_col * mv_col > 8 * NOISE_MOTION_THRESHOLD) { + if (denoiser->best_sse > sse_thresh(bs, increase_denoising)) { + return COPY_BLOCK; + } + if (mv_row * mv_row + mv_col * mv_col > + 8 * noise_motion_thresh(bs, increase_denoising)) { return COPY_BLOCK; } return FILTER_BLOCK; @@ -240,7 +302,6 @@ static VP9_DENOISER_DECISION perform_motion_compensation(VP9_DENOISER *denoiser, void vp9_denoiser_denoise(VP9_DENOISER *denoiser, MACROBLOCK *mb, int mi_row, int mi_col, BLOCK_SIZE bs) { VP9_DENOISER_DECISION decision = FILTER_BLOCK; - YV12_BUFFER_CONFIG avg = denoiser->running_avg_y[INTRA_FRAME]; YV12_BUFFER_CONFIG mc_avg = denoiser->mc_running_avg_y; uint8_t *avg_start = block_start(avg.y_buffer, avg.y_stride, mi_row, mi_col); @@ -251,13 +312,17 @@ void vp9_denoiser_denoise(VP9_DENOISER *denoiser, MACROBLOCK *mb, decision = perform_motion_compensation(denoiser, mb, bs, denoiser->increase_denoising, mi_row, mi_col); - update_running_avg(mc_avg_start, mc_avg.y_stride, avg_start, avg.y_stride, - mb->plane[0].src.buf, mb->plane[0].src.stride, 0, bs); if (decision == FILTER_BLOCK) { - // TODO(tkopp) + decision = denoiser_filter(src.buf, src.stride, + mc_avg_start, mc_avg.y_stride, + avg_start, avg.y_stride, + 0, bs); } - if (decision == COPY_BLOCK) { + + if (decision == FILTER_BLOCK) { + copy_block(src.buf, src.stride, avg_start, avg.y_stride, bs); + } else { // COPY_BLOCK copy_block(avg_start, avg.y_stride, src.buf, src.stride, bs); } }