From d08b2ba1727a358e8836d4cd6a0993343ce5c12d Mon Sep 17 00:00:00 2001 From: Marco Paniconi Date: Fri, 13 Jun 2014 10:08:09 -0700 Subject: [PATCH] Allow for deblocking temporal-denoised signal. Allow for an option to selectively apply the deblocking loop filter to the denoised raw block, based on the denoised state (no-filter, filter with zero motion, or filter with non-zero motion) of the current block and its upper and left denoised block. This helps to reduce some blocking artifacts from the motion-compensated denoising. Change-Id: I0ac4e70076df69a98c5391979e739a2681e24ae6 --- vp8/encoder/denoising.c | 73 +++++++++++++++++++++++++++++++++++++++++++++-- vp8/encoder/denoising.h | 18 ++++++++++-- vp8/encoder/encodeframe.c | 2 +- vp8/encoder/onyx_if.c | 3 +- vp8/encoder/pickinter.c | 5 +++- vp8/encoder/rdopt.c | 8 ++++-- vp8/encoder/rdopt.h | 5 +++- 7 files changed, 104 insertions(+), 10 deletions(-) diff --git a/vp8/encoder/denoising.c b/vp8/encoder/denoising.c index f26741f..5616bda 100644 --- a/vp8/encoder/denoising.c +++ b/vp8/encoder/denoising.c @@ -191,10 +191,12 @@ int vp8_denoiser_filter_c(unsigned char *mc_running_avg_y, int mc_avg_y_stride, return FILTER_BLOCK; } -int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height) +int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height, + int num_mb_rows, int num_mb_cols) { int i; assert(denoiser); + denoiser->num_mb_cols = num_mb_cols; for (i = 0; i < MAX_REF_FRAMES; i++) { @@ -222,6 +224,10 @@ int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height) vpx_memset(denoiser->yv12_mc_running_avg.buffer_alloc, 0, denoiser->yv12_mc_running_avg.frame_size); + + denoiser->denoise_state = vpx_calloc((num_mb_rows * num_mb_cols), 1); + vpx_memset(denoiser->denoise_state, 0, (num_mb_rows * num_mb_cols)); + return 0; } @@ -243,13 +249,20 @@ void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser, unsigned int best_sse, unsigned int zero_mv_sse, int recon_yoffset, - int recon_uvoffset) + int recon_uvoffset, + loop_filter_info_n *lfi_n, + int mb_row, + int mb_col, + int block_index) { int mv_row; int mv_col; unsigned int motion_magnitude2; unsigned int sse_thresh; int sse_diff_thresh = 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; MV_REFERENCE_FRAME frame = x->best_reference_frame; MV_REFERENCE_FRAME zero_frame = x->best_zeromv_reference_frame; @@ -362,6 +375,8 @@ void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser, running_avg_y, avg_y_stride, x->thismb, 16, motion_magnitude2, x->increase_denoising); + denoiser->denoise_state[block_index] = motion_magnitude2 > 0 ? + kFilterNonZeroMV : kFilterZeroMV; } if (decision == COPY_BLOCK) { @@ -372,5 +387,59 @@ void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser, x->thismb, 16, denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset, denoiser->yv12_running_avg[INTRA_FRAME].y_stride); + denoiser->denoise_state[block_index] = kNoFilter; + } + // Option to selectively deblock the denoised signal. + if (apply_spatial_loop_filter) { + loop_filter_info lfi; + int apply_filter_col = 0; + int apply_filter_row = 0; + int apply_filter = 0; + int y_stride = denoiser->yv12_running_avg[INTRA_FRAME].y_stride; + int uv_stride =denoiser->yv12_running_avg[INTRA_FRAME].uv_stride; + + // Fix filter level to some nominal value for now. + int filter_level = 32; + + int hev_index = lfi_n->hev_thr_lut[INTER_FRAME][filter_level]; + lfi.mblim = lfi_n->mblim[filter_level]; + lfi.blim = lfi_n->blim[filter_level]; + lfi.lim = lfi_n->lim[filter_level]; + lfi.hev_thr = lfi_n->hev_thr[hev_index]; + + // Apply filter if there is a difference in the denoiser filter state + // between the current and left/top block, or if non-zero motion vector + // is used for the motion-compensated filtering. + if (mb_col > 0) { + apply_filter_col = !((denoiser->denoise_state[block_index] == + denoiser->denoise_state[block_index - 1]) && + denoiser->denoise_state[block_index] != kFilterNonZeroMV); + if (apply_filter_col) { + // Filter left vertical edge. + apply_filter = 1; + vp8_loop_filter_mbv( + denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset, + NULL, NULL, y_stride, uv_stride, &lfi); + } + } + if (mb_row > 0) { + apply_filter_row = !((denoiser->denoise_state[block_index] == + denoiser->denoise_state[block_index - denoiser->num_mb_cols]) && + denoiser->denoise_state[block_index] != kFilterNonZeroMV); + if (apply_filter_row) { + // Filter top horizontal edge. + apply_filter = 1; + vp8_loop_filter_mbh( + denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset, + NULL, NULL, y_stride, uv_stride, &lfi); + } + } + if (apply_filter) { + // Update the signal block |x|. Pixel changes are only to top and/or + // left boundary pixels: can we avoid full block copy here. + vp8_copy_mem16x16( + denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset, + y_stride, x->thismb, 16); + } } } diff --git a/vp8/encoder/denoising.h b/vp8/encoder/denoising.h index ae744d2..6db0785 100644 --- a/vp8/encoder/denoising.h +++ b/vp8/encoder/denoising.h @@ -12,6 +12,7 @@ #define VP8_ENCODER_DENOISING_H_ #include "block.h" +#include "vp8/common/loopfilter.h" #ifdef __cplusplus extern "C" { @@ -27,13 +28,22 @@ enum vp8_denoiser_decision FILTER_BLOCK }; +enum vp8_denoiser_filter_state { + kNoFilter, + kFilterZeroMV, + kFilterNonZeroMV +}; + typedef struct vp8_denoiser { YV12_BUFFER_CONFIG yv12_running_avg[MAX_REF_FRAMES]; YV12_BUFFER_CONFIG yv12_mc_running_avg; + unsigned char* denoise_state; + int num_mb_cols; } VP8_DENOISER; -int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height); +int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height, + int num_mb_rows, int num_mb_cols); void vp8_denoiser_free(VP8_DENOISER *denoiser); @@ -42,7 +52,11 @@ void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser, unsigned int best_sse, unsigned int zero_mv_sse, int recon_yoffset, - int recon_uvoffset); + int recon_uvoffset, + loop_filter_info_n *lfi_n, + int mb_row, + int mb_col, + int block_index); #ifdef __cplusplus } // extern "C" diff --git a/vp8/encoder/encodeframe.c b/vp8/encoder/encodeframe.c index b550f6b..e6b0f9b 100644 --- a/vp8/encoder/encodeframe.c +++ b/vp8/encoder/encodeframe.c @@ -1246,7 +1246,7 @@ int vp8cx_encode_inter_macroblock x->zbin_mode_boost_enabled = 0; } vp8_rd_pick_inter_mode(cpi, x, recon_yoffset, recon_uvoffset, &rate, - &distortion, &intra_error); + &distortion, &intra_error, mb_row, mb_col); /* switch back to the regular quantizer for the encode */ if (cpi->sf.improved_quant) diff --git a/vp8/encoder/onyx_if.c b/vp8/encoder/onyx_if.c index 4d16a5f..09854a5 100644 --- a/vp8/encoder/onyx_if.c +++ b/vp8/encoder/onyx_if.c @@ -1751,7 +1751,8 @@ void vp8_change_config(VP8_COMP *cpi, VP8_CONFIG *oxcf) { int width = (cpi->oxcf.Width + 15) & ~15; int height = (cpi->oxcf.Height + 15) & ~15; - vp8_denoiser_allocate(&cpi->denoiser, width, height); + vp8_denoiser_allocate(&cpi->denoiser, width, height, + cpi->common.mb_rows, cpi->common.mb_cols); } } #endif diff --git a/vp8/encoder/pickinter.c b/vp8/encoder/pickinter.c index 817c9ef..3a78ee9 100644 --- a/vp8/encoder/pickinter.c +++ b/vp8/encoder/pickinter.c @@ -1168,6 +1168,7 @@ void vp8_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset, #if CONFIG_TEMPORAL_DENOISING if (cpi->oxcf.noise_sensitivity) { + int block_index = mb_row * cpi->common.mb_cols + mb_col; if (x->best_sse_inter_mode == DC_PRED) { /* No best MV found. */ @@ -1179,7 +1180,9 @@ void vp8_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset, } x->increase_denoising = 0; vp8_denoiser_denoise_mb(&cpi->denoiser, x, best_sse, zero_mv_sse, - recon_yoffset, recon_uvoffset); + recon_yoffset, recon_uvoffset, + &cpi->common.lf_info, mb_row, mb_col, + block_index); /* Reevaluate ZEROMV after denoising. */ diff --git a/vp8/encoder/rdopt.c b/vp8/encoder/rdopt.c index f145d09..4465b5e 100644 --- a/vp8/encoder/rdopt.c +++ b/vp8/encoder/rdopt.c @@ -1935,7 +1935,8 @@ static void update_best_mode(BEST_MODE* best_mode, int this_rd, void vp8_rd_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset, int recon_uvoffset, int *returnrate, - int *returndistortion, int *returnintra) + int *returndistortion, int *returnintra, + int mb_row, int mb_col) { BLOCK *b = &x->block[0]; BLOCKD *d = &x->e_mbd.block[0]; @@ -2510,6 +2511,7 @@ void vp8_rd_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset, #if CONFIG_TEMPORAL_DENOISING if (cpi->oxcf.noise_sensitivity) { + int block_index = mb_row * cpi->common.mb_cols + mb_col; if (x->best_sse_inter_mode == DC_PRED) { /* No best MV found. */ @@ -2520,7 +2522,9 @@ void vp8_rd_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset, best_sse = best_rd_sse; } vp8_denoiser_denoise_mb(&cpi->denoiser, x, best_sse, zero_mv_sse, - recon_yoffset, recon_uvoffset); + recon_yoffset, recon_uvoffset, + &cpi->common.lf_info, mb_row, mb_col, + block_index); /* Reevaluate ZEROMV after denoising. */ diff --git a/vp8/encoder/rdopt.h b/vp8/encoder/rdopt.h index fe21b8e..e0da35e 100644 --- a/vp8/encoder/rdopt.h +++ b/vp8/encoder/rdopt.h @@ -70,7 +70,10 @@ static void insertsortsad(int arr[],int idx[], int len) } extern void vp8_initialize_rd_consts(VP8_COMP *cpi, MACROBLOCK *x, int Qvalue); -extern void vp8_rd_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset, int recon_uvoffset, int *returnrate, int *returndistortion, int *returnintra); +extern void vp8_rd_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, + int recon_yoffset, int recon_uvoffset, + int *returnrate, int *returndistortion, + int *returnintra, int mb_row, int mb_col); extern void vp8_rd_pick_intra_mode(MACROBLOCK *x, int *rate); -- 2.7.4