Index: source/libvpx/vp8/encoder/onyx_if.c |
=================================================================== |
--- source/libvpx/vp8/encoder/onyx_if.c (revision 290053) |
+++ source/libvpx/vp8/encoder/onyx_if.c (working copy) |
@@ -613,6 +613,24 @@ |
while(block_count && i != cpi->cyclic_refresh_mode_index); |
cpi->cyclic_refresh_mode_index = i; |
+ |
+#if CONFIG_TEMPORAL_DENOISING |
+ if (cpi->denoiser.denoiser_mode == kDenoiserOnYUVAggressive && |
+ Q < (int)cpi->denoiser.denoise_pars.qp_thresh) { |
+ // Under aggressive denoising mode, use segmentation to turn off loop |
+ // filter below some qp thresh. The loop filter is turned off for all |
+ // blocks that have been encoded as ZEROMV LAST x frames in a row, |
+ // where x is set by cpi->denoiser.denoise_pars.consec_zerolast. |
+ // This is to avoid "dot" artifacts that can occur from repeated |
+ // loop filtering on noisy input source. |
+ cpi->cyclic_refresh_q = Q; |
+ lf_adjustment = -MAX_LOOP_FILTER; |
+ for (i = 0; i < mbs_in_frame; ++i) { |
+ seg_map[i] = (cpi->consec_zero_last[i] > |
+ cpi->denoiser.denoise_pars.consec_zerolast) ? 1 : 0; |
+ } |
+ } |
+#endif |
} |
/* Activate segmentation. */ |
@@ -1264,7 +1282,8 @@ |
if (cpi->oxcf.noise_sensitivity > 0) { |
vp8_denoiser_free(&cpi->denoiser); |
vp8_denoiser_allocate(&cpi->denoiser, width, height, |
- cm->mb_rows, cm->mb_cols); |
+ cm->mb_rows, cm->mb_cols, |
+ cpi->oxcf.noise_sensitivity); |
} |
#endif |
} |
@@ -1760,7 +1779,8 @@ |
int width = (cpi->oxcf.Width + 15) & ~15; |
int height = (cpi->oxcf.Height + 15) & ~15; |
vp8_denoiser_allocate(&cpi->denoiser, width, height, |
- cpi->common.mb_rows, cpi->common.mb_cols); |
+ cm->mb_rows, cm->mb_cols, |
+ cpi->oxcf.noise_sensitivity); |
} |
} |
#endif |
@@ -1887,6 +1907,13 @@ |
*/ |
cpi->cyclic_refresh_mode_enabled = cpi->oxcf.error_resilient_mode; |
cpi->cyclic_refresh_mode_max_mbs_perframe = (cpi->common.mb_rows * cpi->common.mb_cols) / 5; |
+ if (cpi->oxcf.number_of_layers == 1) { |
+ cpi->cyclic_refresh_mode_max_mbs_perframe = |
+ (cpi->common.mb_rows * cpi->common.mb_cols) / 20; |
+ } else if (cpi->oxcf.number_of_layers == 2) { |
+ cpi->cyclic_refresh_mode_max_mbs_perframe = |
+ (cpi->common.mb_rows * cpi->common.mb_cols) / 10; |
+ } |
cpi->cyclic_refresh_mode_index = 0; |
cpi->cyclic_refresh_q = 32; |
@@ -1897,6 +1924,9 @@ |
else |
cpi->cyclic_refresh_map = (signed char *) NULL; |
+ CHECK_MEM_ERROR(cpi->consec_zero_last, |
+ vpx_calloc(cpi->common.mb_rows * cpi->common.mb_cols, 1)); |
+ |
#ifdef VP8_ENTROPY_STATS |
init_context_counters(); |
#endif |
@@ -2417,6 +2447,7 @@ |
vpx_free(cpi->mb.ss); |
vpx_free(cpi->tok); |
vpx_free(cpi->cyclic_refresh_map); |
+ vpx_free(cpi->consec_zero_last); |
vp8_remove_common(&cpi->common); |
vpx_free(cpi); |
@@ -3119,10 +3150,8 @@ |
cm->alt_fb_idx = cm->gld_fb_idx = cm->new_fb_idx; |
-#if CONFIG_MULTI_RES_ENCODING |
cpi->current_ref_frames[GOLDEN_FRAME] = cm->current_video_frame; |
cpi->current_ref_frames[ALTREF_FRAME] = cm->current_video_frame; |
-#endif |
} |
else /* For non key frames */ |
{ |
@@ -3134,9 +3163,7 @@ |
cm->yv12_fb[cm->alt_fb_idx].flags &= ~VP8_ALTR_FRAME; |
cm->alt_fb_idx = cm->new_fb_idx; |
-#if CONFIG_MULTI_RES_ENCODING |
cpi->current_ref_frames[ALTREF_FRAME] = cm->current_video_frame; |
-#endif |
} |
else if (cm->copy_buffer_to_arf) |
{ |
@@ -3150,10 +3177,8 @@ |
yv12_fb[cm->alt_fb_idx].flags &= ~VP8_ALTR_FRAME; |
cm->alt_fb_idx = cm->lst_fb_idx; |
-#if CONFIG_MULTI_RES_ENCODING |
cpi->current_ref_frames[ALTREF_FRAME] = |
cpi->current_ref_frames[LAST_FRAME]; |
-#endif |
} |
} |
else /* if (cm->copy_buffer_to_arf == 2) */ |
@@ -3164,10 +3189,8 @@ |
yv12_fb[cm->alt_fb_idx].flags &= ~VP8_ALTR_FRAME; |
cm->alt_fb_idx = cm->gld_fb_idx; |
-#if CONFIG_MULTI_RES_ENCODING |
cpi->current_ref_frames[ALTREF_FRAME] = |
cpi->current_ref_frames[GOLDEN_FRAME]; |
-#endif |
} |
} |
} |
@@ -3180,9 +3203,7 @@ |
cm->yv12_fb[cm->gld_fb_idx].flags &= ~VP8_GOLD_FRAME; |
cm->gld_fb_idx = cm->new_fb_idx; |
-#if CONFIG_MULTI_RES_ENCODING |
cpi->current_ref_frames[GOLDEN_FRAME] = cm->current_video_frame; |
-#endif |
} |
else if (cm->copy_buffer_to_gf) |
{ |
@@ -3196,10 +3217,8 @@ |
yv12_fb[cm->gld_fb_idx].flags &= ~VP8_GOLD_FRAME; |
cm->gld_fb_idx = cm->lst_fb_idx; |
-#if CONFIG_MULTI_RES_ENCODING |
cpi->current_ref_frames[GOLDEN_FRAME] = |
cpi->current_ref_frames[LAST_FRAME]; |
-#endif |
} |
} |
else /* if (cm->copy_buffer_to_gf == 2) */ |
@@ -3210,10 +3229,8 @@ |
yv12_fb[cm->gld_fb_idx].flags &= ~VP8_GOLD_FRAME; |
cm->gld_fb_idx = cm->alt_fb_idx; |
-#if CONFIG_MULTI_RES_ENCODING |
cpi->current_ref_frames[GOLDEN_FRAME] = |
cpi->current_ref_frames[ALTREF_FRAME]; |
-#endif |
} |
} |
} |
@@ -3225,9 +3242,7 @@ |
cm->yv12_fb[cm->lst_fb_idx].flags &= ~VP8_LAST_FRAME; |
cm->lst_fb_idx = cm->new_fb_idx; |
-#if CONFIG_MULTI_RES_ENCODING |
cpi->current_ref_frames[LAST_FRAME] = cm->current_video_frame; |
-#endif |
} |
#if CONFIG_TEMPORAL_DENOISING |
@@ -3268,12 +3283,119 @@ |
&cpi->denoiser.yv12_running_avg[LAST_FRAME]); |
} |
} |
+ if (cpi->oxcf.noise_sensitivity == 4) |
+ vp8_yv12_copy_frame(cpi->Source, &cpi->denoiser.yv12_last_source); |
} |
#endif |
} |
+static void process_denoiser_mode_change(VP8_COMP *cpi) { |
+ const VP8_COMMON *const cm = &cpi->common; |
+ int i, j; |
+ int total = 0; |
+ int num_blocks = 0; |
+ // Number of blocks skipped along row/column in computing the |
+ // nmse (normalized mean square error) of source. |
+ int skip = 2; |
+ // Only select blocks for computing nmse that have been encoded |
+ // as ZERO LAST min_consec_zero_last frames in a row. |
+ int min_consec_zero_last = 10; |
+ // Decision is tested for changing the denoising mode every |
+ // num_mode_change times this function is called. Note that this |
+ // function called every 8 frames, so (8 * num_mode_change) is number |
+ // of frames where denoising mode change is tested for switch. |
+ int num_mode_change = 15; |
+ // Framerate factor, to compensate for larger mse at lower framerates. |
+ // TODO(marpan): Adjust this factor, |
+ int fac_framerate = cpi->output_framerate < 25.0f ? 80 : 100; |
+ int tot_num_blocks = cm->mb_rows * cm->mb_cols; |
+ int ystride = cpi->Source->y_stride; |
+ unsigned char *src = cpi->Source->y_buffer; |
+ unsigned char *dst = cpi->denoiser.yv12_last_source.y_buffer; |
+ static const unsigned char const_source[16] = { |
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, |
+ 128, 128, 128}; |
+ |
+ // Loop through the Y plane, every skip blocks along rows and columns, |
+ // summing the normalized mean square error, only for blocks that have |
+ // been encoded as ZEROMV LAST at least min_consec_zero_last least frames in |
+ // a row and have small sum difference between current and previous frame. |
+ // Normalization here is by the contrast of the current frame block. |
+ for (i = 0; i < cm->Height; i += 16 * skip) { |
+ int block_index_row = (i >> 4) * cm->mb_cols; |
+ for (j = 0; j < cm->Width; j += 16 * skip) { |
+ int index = block_index_row + (j >> 4); |
+ if (cpi->consec_zero_last[index] >= min_consec_zero_last) { |
+ unsigned int sse; |
+ const unsigned int mse = vp8_mse16x16(src + j, |
+ ystride, |
+ dst + j, |
+ ystride, |
+ &sse); |
+ const unsigned int var = vp8_variance16x16(src + j, |
+ ystride, |
+ dst + j, |
+ ystride, |
+ &sse); |
+ // Only consider this block as valid for noise measurement |
+ // if the sum_diff average of the current and previous frame |
+ // is small (to avoid effects from lighting change). |
+ if ((mse - var) < 256) { |
+ const unsigned int act = vp8_variance16x16(src + j, |
+ ystride, |
+ const_source, |
+ 0, |
+ &sse); |
+ if (act > 0) |
+ total += mse / act; |
+ num_blocks++; |
+ } |
+ } |
+ } |
+ src += 16 * skip * ystride; |
+ dst += 16 * skip * ystride; |
+ } |
+ total = total * fac_framerate / 100; |
+ |
+ // Only consider this frame as valid sample if we have computed nmse over |
+ // at least ~1/16 blocks, and Total > 0 (Total == 0 can happen if the |
+ // application inputs duplicate frames, or contrast is all zero). |
+ if (total > 0 && |
+ (num_blocks > (tot_num_blocks >> 4))) { |
+ // Update the recursive mean square source_diff. |
+ if (cpi->denoiser.nmse_source_diff_count == 0) |
+ // First sample in new interval. |
+ cpi->denoiser.nmse_source_diff = total; |
+ else |
+ // For subsequent samples, use average with weight ~1/4 for new sample. |
+ cpi->denoiser.nmse_source_diff = (int)((total >> 2) + |
+ 3 * (cpi->denoiser.nmse_source_diff >> 2)); |
+ cpi->denoiser.nmse_source_diff_count++; |
+ } |
+ // Check for changing the denoiser mode, when we have obtained #samples = |
+ // num_mode_change. |
+ if (cpi->denoiser.nmse_source_diff_count == num_mode_change) { |
+ // Check for going up: from normal to aggressive mode. |
+ if ((cpi->denoiser.denoiser_mode = kDenoiserOnYUV) && |
+ (cpi->denoiser.nmse_source_diff > |
+ cpi->denoiser.threshold_aggressive_mode)) { |
+ vp8_denoiser_set_parameters(&cpi->denoiser, kDenoiserOnYUVAggressive); |
+ } else { |
+ // Check for going down: from aggressive to normal mode. |
+ if ((cpi->denoiser.denoiser_mode = kDenoiserOnYUVAggressive) && |
+ (cpi->denoiser.nmse_source_diff < |
+ cpi->denoiser.threshold_aggressive_mode)) { |
+ vp8_denoiser_set_parameters(&cpi->denoiser, kDenoiserOnYUV); |
+ } |
+ } |
+ // Reset metric and counter for next interval. |
+ cpi->denoiser.nmse_source_diff = 0; |
+ cpi->denoiser.nmse_source_diff_count = 0; |
+ } |
+} |
+ |
void vp8_loopfilter_frame(VP8_COMP *cpi, VP8_COMMON *cm) |
{ |
const FRAME_TYPE frame_type = cm->frame_type; |
@@ -3430,6 +3552,12 @@ |
{ |
/* Key frame from VFW/auto-keyframe/first frame */ |
cm->frame_type = KEY_FRAME; |
+#if CONFIG_TEMPORAL_DENOISING |
+ if (cpi->oxcf.noise_sensitivity == 4) { |
+ // For adaptive mode, reset denoiser to normal mode on key frame. |
+ vp8_denoiser_set_parameters(&cpi->denoiser, kDenoiserOnYUV); |
+ } |
+#endif |
} |
#if CONFIG_MULTI_RES_ENCODING |
@@ -3463,6 +3591,31 @@ |
} |
#endif |
+ // Find the reference frame closest to the current frame. |
+ cpi->closest_reference_frame = LAST_FRAME; |
+ if (cm->frame_type != KEY_FRAME) { |
+ int i; |
+ MV_REFERENCE_FRAME closest_ref = INTRA_FRAME; |
+ if (cpi->ref_frame_flags & VP8_LAST_FRAME) { |
+ closest_ref = LAST_FRAME; |
+ } else if (cpi->ref_frame_flags & VP8_GOLD_FRAME) { |
+ closest_ref = GOLDEN_FRAME; |
+ } else if (cpi->ref_frame_flags & VP8_ALTR_FRAME) { |
+ closest_ref = ALTREF_FRAME; |
+ } |
+ for (i = 1; i <= 3; i++) { |
+ vpx_ref_frame_type_t ref_frame_type = (vpx_ref_frame_type_t) |
+ ((i == 3) ? 4 : i); |
+ if (cpi->ref_frame_flags & ref_frame_type) { |
+ if ((cm->current_video_frame - cpi->current_ref_frames[i]) < |
+ (cm->current_video_frame - cpi->current_ref_frames[closest_ref])) { |
+ closest_ref = i; |
+ } |
+ } |
+ } |
+ cpi->closest_reference_frame = closest_ref; |
+ } |
+ |
/* Set various flags etc to special state if it is a key frame */ |
if (cm->frame_type == KEY_FRAME) |
{ |
@@ -3479,6 +3632,9 @@ |
{ |
cpi->mb.rd_thresh_mult[i] = 128; |
} |
+ |
+ // Reset the zero_last counter to 0 on key frame. |
+ vpx_memset(cpi->consec_zero_last, 0, cm->mb_rows * cm->mb_cols); |
} |
#if 0 |
@@ -3900,6 +4056,7 @@ |
#endif |
+ |
#ifdef OUTPUT_YUV_SRC |
vp8_write_yuv_frame(yuv_file, cpi->Source); |
#endif |
@@ -3995,6 +4152,8 @@ |
else |
disable_segmentation(cpi); |
} |
+ // Reset the consec_zero_last counter on key frame. |
+ vpx_memset(cpi->consec_zero_last, 0, cm->mb_rows * cm->mb_cols); |
vp8_set_quantizer(cpi, Q); |
} |
@@ -4383,7 +4542,8 @@ |
{ |
for (mb_col = 0; mb_col < cm->mb_cols; mb_col ++) |
{ |
- if(tmp->mbmi.mode == ZEROMV) |
+ if (tmp->mbmi.mode == ZEROMV && |
+ tmp->mbmi.ref_frame == LAST_FRAME) |
cpi->zeromv_count++; |
tmp++; |
} |
@@ -4425,6 +4585,21 @@ |
cm->frame_to_show = &cm->yv12_fb[cm->new_fb_idx]; |
+#if CONFIG_TEMPORAL_DENOISING |
+ // For the adaptive denoising mode (noise_sensitivity == 4), sample the mse |
+ // of source diff (between current and previous frame), and determine if we |
+ // should switch the denoiser mode. Sampling refers to computing the mse for |
+ // a sub-sample of the frame (i.e., skip x blocks along row/column), and |
+ // only for blocks in that set that have used ZEROMV LAST, along with some |
+ // constraint on the sum diff between blocks. This process is called every |
+ // ~8 frames, to further reduce complexity. |
+ if (cpi->oxcf.noise_sensitivity == 4 && |
+ cpi->frames_since_key % 8 == 0 && |
+ cm->frame_type != KEY_FRAME) { |
+ process_denoiser_mode_change(cpi); |
+ } |
+#endif |
+ |
#if CONFIG_MULTITHREAD |
if (cpi->b_multi_threaded) |
{ |