From: Debargha Mukherjee Date: Sat, 9 Apr 2016 13:24:18 +0000 (-0700) Subject: Step towards making the 2-pass cq mode perceptual X-Git-Tag: v1.6.0~234^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c47c460f695f9a4fb2ecfed108e615c850035d65;p=libvpx Step towards making the 2-pass cq mode perceptual Uses a metric on fraction of smooth blocks derived from first pass stats in a frame to adjust down the cq_level modestly in the cq mode. The current implementation does not add much complexity, and is fairly light in the adaptation. Change-Id: Ic484e810d5bd51b7bb6b8945f378c7c3d9d27053 --- diff --git a/vp9/encoder/vp9_firstpass.c b/vp9/encoder/vp9_firstpass.c index 3ad175eb2..bb2acccb4 100644 --- a/vp9/encoder/vp9_firstpass.c +++ b/vp9/encoder/vp9_firstpass.c @@ -115,7 +115,7 @@ static void output_stats(FIRSTPASS_STATS *stats, fprintf(fpfile, "%12.0lf %12.4lf %12.0lf %12.0lf %12.0lf %12.4lf %12.4lf" "%12.4lf %12.4lf %12.4lf %12.4lf %12.4lf %12.4lf %12.4lf %12.4lf" - "%12.4lf %12.4lf %12.0lf %12.0lf %12.0lf %12.4lf\n", + "%12.4lf %12.4lf %12.4lf %12.0lf %12.0lf %12.0lf %12.4lf\n", stats->frame, stats->weight, stats->intra_error, @@ -126,6 +126,7 @@ static void output_stats(FIRSTPASS_STATS *stats, stats->pcnt_second_ref, stats->pcnt_neutral, stats->intra_skip_pct, + stats->intra_smooth_pct, stats->inactive_zone_rows, stats->inactive_zone_cols, stats->MVr, @@ -165,6 +166,7 @@ static void zero_stats(FIRSTPASS_STATS *section) { section->pcnt_second_ref = 0.0; section->pcnt_neutral = 0.0; section->intra_skip_pct = 0.0; + section->intra_smooth_pct = 0.0; section->inactive_zone_rows = 0.0; section->inactive_zone_cols = 0.0; section->MVr = 0.0; @@ -193,6 +195,7 @@ static void accumulate_stats(FIRSTPASS_STATS *section, section->pcnt_second_ref += frame->pcnt_second_ref; section->pcnt_neutral += frame->pcnt_neutral; section->intra_skip_pct += frame->intra_skip_pct; + section->intra_smooth_pct += frame->intra_smooth_pct; section->inactive_zone_rows += frame->inactive_zone_rows; section->inactive_zone_cols += frame->inactive_zone_cols; section->MVr += frame->MVr; @@ -219,6 +222,7 @@ static void subtract_stats(FIRSTPASS_STATS *section, section->pcnt_second_ref -= frame->pcnt_second_ref; section->pcnt_neutral -= frame->pcnt_neutral; section->intra_skip_pct -= frame->intra_skip_pct; + section->intra_smooth_pct -= frame->intra_smooth_pct; section->inactive_zone_rows -= frame->inactive_zone_rows; section->inactive_zone_cols -= frame->inactive_zone_cols; section->MVr -= frame->MVr; @@ -493,12 +497,12 @@ static void set_first_pass_params(VP9_COMP *cpi) { // This threshold is used to track blocks where to all intents and purposes // the intra prediction error 0. Though the metric we test against // is technically a sse we are mainly interested in blocks where all the pixels -// int he 8 bit domain have an error of <= 1 (where error = sse) so a +// in the 8 bit domain have an error of <= 1 (where error = sse) so a // linear scaling for 10 and 12 bit gives similar results. #define UL_INTRA_THRESH 50 -#if CONFIG_VP9_HIGHBITDEPTH static int get_ul_intra_threshold(VP9_COMMON *cm) { int ret_val = UL_INTRA_THRESH; +#if CONFIG_VP9_HIGHBITDEPTH if (cm->use_highbitdepth) { switch (cm->bit_depth) { case VPX_BITS_8: @@ -515,9 +519,37 @@ static int get_ul_intra_threshold(VP9_COMMON *cm) { "VPX_BITS_10 or VPX_BITS_12"); } } +#else + (void) cm; +#endif // CONFIG_VP9_HIGHBITDEPTH return ret_val; } + +#define SMOOTH_INTRA_THRESH 4000 +static int get_smooth_intra_threshold(VP9_COMMON *cm) { + int ret_val = SMOOTH_INTRA_THRESH; +#if CONFIG_VP9_HIGHBITDEPTH + if (cm->use_highbitdepth) { + switch (cm->bit_depth) { + case VPX_BITS_8: + ret_val = SMOOTH_INTRA_THRESH; + break; + case VPX_BITS_10: + ret_val = SMOOTH_INTRA_THRESH >> 2; + break; + case VPX_BITS_12: + ret_val = SMOOTH_INTRA_THRESH >> 4; + break; + default: + assert(0 && "cm->bit_depth should be VPX_BITS_8, " + "VPX_BITS_10 or VPX_BITS_12"); + } + } +#else + (void) cm; #endif // CONFIG_VP9_HIGHBITDEPTH + return ret_val; +} #define INVALID_ROW -1 void vp9_first_pass(VP9_COMP *cpi, const struct lookahead_entry *source) { @@ -545,6 +577,7 @@ void vp9_first_pass(VP9_COMP *cpi, const struct lookahead_entry *source) { const int intrapenalty = INTRA_MODE_PENALTY; double neutral_count; int intra_skip_count = 0; + int intra_smooth_count = 0; int image_data_start_row = INVALID_ROW; int new_mv_count = 0; int sum_in_vectors = 0; @@ -716,15 +749,14 @@ void vp9_first_pass(VP9_COMP *cpi, const struct lookahead_entry *source) { // domain). In natural videos this is uncommon, but it is much more // common in animations, graphics and screen content, so may be used // as a signal to detect these types of content. -#if CONFIG_VP9_HIGHBITDEPTH if (this_error < get_ul_intra_threshold(cm)) { -#else - if (this_error < UL_INTRA_THRESH) { -#endif ++intra_skip_count; } else if ((mb_col > 0) && (image_data_start_row == INVALID_ROW)) { image_data_start_row = mb_row; } + if (this_error < get_smooth_intra_threshold(cm)) { + ++intra_smooth_count; + } #if CONFIG_VP9_HIGHBITDEPTH if (cm->use_highbitdepth) { @@ -1090,6 +1122,7 @@ void vp9_first_pass(VP9_COMP *cpi, const struct lookahead_entry *source) { fps.pcnt_second_ref = (double)second_ref_count / num_mbs; fps.pcnt_neutral = (double)neutral_count / num_mbs; fps.intra_skip_pct = (double)intra_skip_count / num_mbs; + fps.intra_smooth_pct = (double)intra_smooth_count / num_mbs; fps.inactive_zone_rows = (double)image_data_start_row; fps.inactive_zone_cols = (double)0; // TODO(paulwilkins): fix @@ -2789,6 +2822,7 @@ void vp9_rc_get_second_pass_params(VP9_COMP *cpi) { // applied when combining MB error values for the frame. twopass->mb_av_energy = log(((this_frame.intra_error * 256.0) / num_mbs) + 1.0); + twopass->mb_smooth_pct = this_frame.intra_smooth_pct; } // Update the total stats remaining structure. diff --git a/vp9/encoder/vp9_firstpass.h b/vp9/encoder/vp9_firstpass.h index 5875a7b9b..7eb44fa13 100644 --- a/vp9/encoder/vp9_firstpass.h +++ b/vp9/encoder/vp9_firstpass.h @@ -52,6 +52,7 @@ typedef struct { double pcnt_second_ref; double pcnt_neutral; double intra_skip_pct; + double intra_smooth_pct; // % of blocks that are smooth double inactive_zone_rows; // Image mask rows top and bottom. double inactive_zone_cols; // Image mask columns at left and right edges. double MVr; @@ -107,6 +108,7 @@ typedef struct { double modified_error_max; double modified_error_left; double mb_av_energy; + double mb_smooth_pct; #if CONFIG_FP_MB_STATS uint8_t *frame_mb_stats_buf; diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c index 545441bef..38711071f 100644 --- a/vp9/encoder/vp9_ratectrl.c +++ b/vp9/encoder/vp9_ratectrl.c @@ -812,8 +812,8 @@ static int rc_pick_q_and_bounds_one_pass_cbr(const VP9_COMP *cpi, return q; } -static int get_active_cq_level(const RATE_CONTROL *rc, - const VP9EncoderConfig *const oxcf) { +static int get_active_cq_level_one_pass( + const RATE_CONTROL *rc, const VP9EncoderConfig *const oxcf) { static const double cq_adjust_threshold = 0.1; int active_cq_level = oxcf->cq_level; if (oxcf->rc_mode == VPX_CQ && @@ -826,13 +826,36 @@ static int get_active_cq_level(const RATE_CONTROL *rc, return active_cq_level; } +#define SMOOTH_PCT_MIN 0.1 +#define SMOOTH_PCT_DIV 0.05 +static int get_active_cq_level_two_pass( + const TWO_PASS *twopass, const RATE_CONTROL *rc, + const VP9EncoderConfig *const oxcf) { + static const double cq_adjust_threshold = 0.1; + int active_cq_level = oxcf->cq_level; + if (oxcf->rc_mode == VPX_CQ) { + if (twopass->mb_smooth_pct > SMOOTH_PCT_MIN) { + active_cq_level -= (twopass->mb_smooth_pct - SMOOTH_PCT_MIN) / + SMOOTH_PCT_DIV; + active_cq_level = VPXMAX(active_cq_level, 0); + } + if (rc->total_target_bits > 0) { + const double x = (double)rc->total_actual_bits / rc->total_target_bits; + if (x < cq_adjust_threshold) { + active_cq_level = (int)(active_cq_level * x / cq_adjust_threshold); + } + } + } + return active_cq_level; +} + static int rc_pick_q_and_bounds_one_pass_vbr(const VP9_COMP *cpi, int *bottom_index, int *top_index) { const VP9_COMMON *const cm = &cpi->common; const RATE_CONTROL *const rc = &cpi->rc; const VP9EncoderConfig *const oxcf = &cpi->oxcf; - const int cq_level = get_active_cq_level(rc, oxcf); + const int cq_level = get_active_cq_level_one_pass(rc, oxcf); int active_best_quality; int active_worst_quality = calc_active_worst_quality_one_pass_vbr(cpi); int q; @@ -1018,7 +1041,7 @@ static int rc_pick_q_and_bounds_two_pass(const VP9_COMP *cpi, const RATE_CONTROL *const rc = &cpi->rc; const VP9EncoderConfig *const oxcf = &cpi->oxcf; const GF_GROUP *gf_group = &cpi->twopass.gf_group; - const int cq_level = get_active_cq_level(rc, oxcf); + const int cq_level = get_active_cq_level_two_pass(&cpi->twopass, rc, oxcf); int active_best_quality; int active_worst_quality = cpi->twopass.active_worst_quality; int q;