From 644bd87e8e04fe5c70dd47b34f9ac19fe173dab5 Mon Sep 17 00:00:00 2001 From: Paul Wilkins Date: Fri, 22 Nov 2013 17:21:53 +0000 Subject: In frame Q adjustment experiment. The idea here is to allow "in frame" adjustment of the final Q value used to encode each SB64, using segmentation. There is also adjustment of the rd mult in regions of overspend. Activated using aq_mode=2 Change-Id: I2f140cd898c9f877c32cd6d2e667f5e11ada4b1c --- vp9/encoder/vp9_encodeframe.c | 98 +++++++++++++++++++++++++++++++++++++++---- vp9/encoder/vp9_onyx_if.c | 89 ++++++++++++++++++++++++++++++++++++++- vp9/encoder/vp9_onyx_int.h | 3 ++ vp9/encoder/vp9_ratectrl.c | 9 ++++ 4 files changed, 190 insertions(+), 9 deletions(-) (limited to 'vp9/encoder') diff --git a/vp9/encoder/vp9_encodeframe.c b/vp9/encoder/vp9_encodeframe.c index 33839370a..cd02aadb0 100644 --- a/vp9/encoder/vp9_encodeframe.c +++ b/vp9/encoder/vp9_encodeframe.c @@ -360,6 +360,52 @@ void vp9_activity_masking(VP9_COMP *cpi, MACROBLOCK *x) { adjust_act_zbin(cpi, x); } +// Select a segment for the current SB64 +static void select_in_frame_q_segment(VP9_COMP *cpi, + int mi_row, int mi_col, + int output_enabled, int projected_rate) { + VP9_COMMON * const cm = &cpi->common; + int target_rate = cpi->rc.sb64_target_rate << 8; // convert to bits << 8 + + const int mi_offset = mi_row * cm->mi_cols + mi_col; + const int bw = 1 << mi_width_log2(BLOCK_64X64); + const int bh = 1 << mi_height_log2(BLOCK_64X64); + const int xmis = MIN(cm->mi_cols - mi_col, bw); + const int ymis = MIN(cm->mi_rows - mi_row, bh); + int complexity_metric = 64; + int x, y; + + unsigned char segment; + + if (!output_enabled) { + segment = 0; + } else { + // Rate depends on fraction of a SB64 in frame (xmis * ymis / bw * bh). + // It is converted to bits * 256 units + target_rate = (cpi->rc.sb64_target_rate * xmis * ymis * 256) / (bw * bh); + + if (projected_rate < (target_rate / 4)) { + segment = 2; + } else if (projected_rate < (target_rate / 2)) { + segment = 1; + } else { + segment = 0; + } + + complexity_metric = + clamp((int)((projected_rate * 64) / target_rate), 16, 255); + } + + // Fill in the entires in the segment map corresponding to this SB64 + for (y = 0; y < ymis; y++) { + for (x = 0; x < xmis; x++) { + cpi->segmentation_map[mi_offset + y * cm->mi_cols + x] = segment; + cpi->complexity_map[mi_offset + y * cm->mi_cols + x] = + (unsigned char)complexity_metric; + } + } +} + static void update_state(VP9_COMP *cpi, PICK_MODE_CONTEXT *ctx, BLOCK_SIZE bsize, int output_enabled) { int i, x_idx, y; @@ -383,6 +429,11 @@ static void update_state(VP9_COMP *cpi, PICK_MODE_CONTEXT *ctx, assert(mi->mbmi.ref_frame[1] < MAX_REF_FRAMES); assert(mi->mbmi.sb_type == bsize); + // For in frame adaptive Q copy over the chosen segment id into the + // mode innfo context for the chosen mode / partition. + if ((cpi->oxcf.aq_mode == COMPLEXITY_AQ) && output_enabled) + mi->mbmi.segment_id = xd->mi_8x8[0]->mbmi.segment_id; + *mi_addr = *mi; max_plane = is_inter_block(mbmi) ? MAX_MB_PLANE : 1; @@ -405,10 +456,12 @@ static void update_state(VP9_COMP *cpi, PICK_MODE_CONTEXT *ctx, for (y = 0; y < mi_height; y++) for (x_idx = 0; x_idx < mi_width; x_idx++) if ((xd->mb_to_right_edge >> (3 + MI_SIZE_LOG2)) + mi_width > x_idx - && (xd->mb_to_bottom_edge >> (3 + MI_SIZE_LOG2)) + mi_height > y) + && (xd->mb_to_bottom_edge >> (3 + MI_SIZE_LOG2)) + mi_height > y) { xd->mi_8x8[x_idx + y * mis] = mi_addr; + } - if (cpi->oxcf.aq_mode == VARIANCE_AQ) { + if ((cpi->oxcf.aq_mode == VARIANCE_AQ) || + (cpi->oxcf.aq_mode == COMPLEXITY_AQ)) { vp9_mb_init_quantizer(cpi, x); } @@ -557,7 +610,7 @@ static void set_offsets(VP9_COMP *cpi, const TileInfo *const tile, /* segment ID */ if (seg->enabled) { - if (!cpi->oxcf.aq_mode == VARIANCE_AQ) { + if (cpi->oxcf.aq_mode != VARIANCE_AQ) { uint8_t *map = seg->update_map ? cpi->segmentation_map : cm->last_frame_seg_map; mbmi->segment_id = vp9_get_segment_id(cm, map, bsize, mi_row, mi_col); @@ -653,6 +706,14 @@ static void pick_sb_modes(VP9_COMP *cpi, const TileInfo *const tile, if (cpi->oxcf.aq_mode == VARIANCE_AQ) { vp9_clear_system_state(); // __asm emms; x->rdmult = round(x->rdmult * rdmult_ratio); + } else if (cpi->oxcf.aq_mode == COMPLEXITY_AQ) { + const int mi_offset = mi_row * cm->mi_cols + mi_col; + unsigned char complexity = cpi->complexity_map[mi_offset]; + const int is_edge = (mi_row == 0) || (mi_row == (cm->mi_rows - 1)) || + (mi_col == 0) || (mi_col == (cm->mi_cols - 1)); + + if (!is_edge && (complexity > 128)) + x->rdmult = x->rdmult + ((x->rdmult * (complexity - 128)) / 256); } // Find best coding mode & reconstruct the MB so it is available @@ -1261,8 +1322,19 @@ static void rd_use_partition(VP9_COMP *cpi, if ( bsize == BLOCK_64X64) assert(chosen_rate < INT_MAX && chosen_dist < INT_MAX); - if (do_recon) - encode_sb(cpi, tile, tp, mi_row, mi_col, bsize == BLOCK_64X64, bsize); + if (do_recon) { + int output_enabled = (bsize == BLOCK_64X64); + + // Check the projected output rate for this SB against it's target + // and and if necessary apply a Q delta using segmentation to get + // closer to the target. + if ((cpi->oxcf.aq_mode == COMPLEXITY_AQ) && cm->seg.update_map) { + select_in_frame_q_segment(cpi, mi_row, mi_col, + output_enabled, chosen_rate); + } + + encode_sb(cpi, tile, tp, mi_row, mi_col, output_enabled, bsize); + } *rate = chosen_rate; *dist = chosen_dist; @@ -1740,8 +1812,17 @@ static void rd_pick_partition(VP9_COMP *cpi, const TileInfo *const tile, *rate = best_rate; *dist = best_dist; - if (best_rate < INT_MAX && best_dist < INT64_MAX && do_recon) - encode_sb(cpi, tile, tp, mi_row, mi_col, bsize == BLOCK_64X64, bsize); + if (best_rate < INT_MAX && best_dist < INT64_MAX && do_recon) { + int output_enabled = (bsize == BLOCK_64X64); + + // Check the projected output rate for this SB against it's target + // and and if necessary apply a Q delta using segmentation to get + // closer to the target. + if ((cpi->oxcf.aq_mode == COMPLEXITY_AQ) && cm->seg.update_map) { + select_in_frame_q_segment(cpi, mi_row, mi_col, output_enabled, best_rate); + } + encode_sb(cpi, tile, tp, mi_row, mi_col, output_enabled, bsize); + } if (bsize == BLOCK_64X64) { assert(tp_orig < *tp); assert(best_rate < INT_MAX); @@ -2415,7 +2496,8 @@ static void encode_superblock(VP9_COMP *cpi, TOKENEXTRA **t, int output_enabled, const int mis = cm->mode_info_stride; const int mi_width = num_8x8_blocks_wide_lookup[bsize]; const int mi_height = num_8x8_blocks_high_lookup[bsize]; - x->skip_recode = !x->select_txfm_size && mbmi->sb_type >= BLOCK_8X8; + x->skip_recode = !x->select_txfm_size && mbmi->sb_type >= BLOCK_8X8 && + (cpi->oxcf.aq_mode != COMPLEXITY_AQ); x->skip_optimize = ctx->is_coded; ctx->is_coded = 1; x->use_lp32x32fdct = cpi->sf.use_lp32x32fdct; diff --git a/vp9/encoder/vp9_onyx_if.c b/vp9/encoder/vp9_onyx_if.c index 07f0c2ecd..8b2765104 100644 --- a/vp9/encoder/vp9_onyx_if.c +++ b/vp9/encoder/vp9_onyx_if.c @@ -109,6 +109,9 @@ extern unsigned __int64 Sectionbits[500]; extern void vp9_init_quantizer(VP9_COMP *cpi); +static const double in_frame_q_adj_ratio[MAX_SEGMENTS] = + {1.0, 1.5, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + static INLINE void Scale2Ratio(int mode, int *hr, int *hs) { switch (mode) { case NORMAL: @@ -192,6 +195,8 @@ static void dealloc_compressor_data(VP9_COMP *cpi) { vpx_free(cpi->coding_context.last_frame_seg_map_copy); cpi->coding_context.last_frame_seg_map_copy = 0; + vpx_free(cpi->complexity_map); + cpi->complexity_map = 0; vpx_free(cpi->active_map); cpi->active_map = 0; @@ -243,6 +248,79 @@ int vp9_compute_qdelta(VP9_COMP *cpi, double qstart, double qtarget) { return target_index - start_index; } +// Computes a q delta (in "q index" terms) to get from a starting q value +// to a value that should equate to thegiven rate ratio. + +int vp9_compute_qdelta_by_rate(VP9_COMP *cpi, + double base_q_index, double rate_target_ratio) { + int i; + int base_bits_per_mb; + int target_bits_per_mb; + int target_index = cpi->rc.worst_quality; + + // Make SURE use of floating point in this function is safe. + vp9_clear_system_state(); + + // Look up the current projected bits per block for the base index + base_bits_per_mb = vp9_bits_per_mb(cpi->common.frame_type, + base_q_index, 1.0); + + // Find the target bits per mb based on the base value and given ratio. + target_bits_per_mb = rate_target_ratio * base_bits_per_mb; + + // Convert the q target to an index + for (i = cpi->rc.best_quality; i < cpi->rc.worst_quality; i++) { + target_index = i; + if (vp9_bits_per_mb(cpi->common.frame_type, + i, 1.0) <= target_bits_per_mb ) + break; + } + + return target_index - base_q_index; +} + +// This function sets up a set of segments with delta Q values around +// the baseline frame quantizer. +static void setup_in_frame_q_adj(VP9_COMP *cpi) { + VP9_COMMON *cm = &cpi->common; + struct segmentation *seg = &cm->seg; + // double q_ratio; + int segment; + int qindex_delta; + + // Make SURE use of floating point in this function is safe. + vp9_clear_system_state(); + + if (cm->frame_type == KEY_FRAME || + cpi->refresh_alt_ref_frame || + (cpi->refresh_golden_frame && !cpi->is_src_frame_alt_ref)) { + // Clear down the segment map + vpx_memset(cpi->segmentation_map, 0, cm->mi_rows * cm->mi_cols); + + // Clear down the complexity map used for rd + vpx_memset(cpi->complexity_map, 0, cm->mi_rows * cm->mi_cols); + + // Enable segmentation + vp9_enable_segmentation((VP9_PTR)cpi); + vp9_clearall_segfeatures(seg); + + // Select delta coding method + seg->abs_delta = SEGMENT_DELTADATA; + + // Segment 0 "Q" feature is disabled so it defaults to the baseline Q + vp9_disable_segfeature(seg, 0, SEG_LVL_ALT_Q); + + // Use some of the segments for in frame Q adjustment + for (segment = 1; segment < 3; segment++) { + qindex_delta = + vp9_compute_qdelta_by_rate(cpi, cm->base_qindex, + in_frame_q_adj_ratio[segment]); + vp9_enable_segfeature(seg, segment, SEG_LVL_ALT_Q); + vp9_set_segdata(seg, segment, SEG_LVL_ALT_Q, qindex_delta); + } + } +} + static void configure_static_seg_features(VP9_COMP *cpi) { VP9_COMMON *cm = &cpi->common; struct segmentation *seg = &cm->seg; @@ -1446,6 +1524,11 @@ VP9_PTR vp9_create_compressor(VP9_CONFIG *oxcf) { CHECK_MEM_ERROR(cm, cpi->segmentation_map, vpx_calloc(cm->mi_rows * cm->mi_cols, 1)); + // Create a complexity map used for rd adjustment + CHECK_MEM_ERROR(cm, cpi->complexity_map, + vpx_calloc(cm->mi_rows * cm->mi_cols, 1)); + + // And a place holder structure is the coding context // for use if we want to save and restore it CHECK_MEM_ERROR(cm, cpi->coding_context.last_frame_seg_map_copy, @@ -2630,8 +2713,12 @@ static void encode_with_recode_loop(VP9_COMP *cpi, } } + // Variance adaptive and in frame q adjustment experiments are mutually + // exclusive. if (cpi->oxcf.aq_mode == VARIANCE_AQ) { - vp9_vaq_frame_setup(cpi); + vp9_vaq_frame_setup(cpi); + } else if (cpi->oxcf.aq_mode == COMPLEXITY_AQ) { + setup_in_frame_q_adj(cpi); } // transform / motion compensation build reconstruction frame diff --git a/vp9/encoder/vp9_onyx_int.h b/vp9/encoder/vp9_onyx_int.h index 54af75633..52ad1e1c5 100644 --- a/vp9/encoder/vp9_onyx_int.h +++ b/vp9/encoder/vp9_onyx_int.h @@ -293,6 +293,7 @@ typedef struct { // Rate targetting variables int this_frame_target; int projected_frame_size; + int sb64_target_rate; int last_q[2]; // Separate values for Intra/Inter int last_boosted_qindex; // Last boosted GF/KF/ARF q @@ -516,6 +517,8 @@ typedef struct VP9_COMP { // segment threashold for encode breakout int segment_encode_breakout[MAX_SEGMENTS]; + unsigned char *complexity_map; + unsigned char *active_map; unsigned int active_map_enabled; diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c index 1293e860f..6e4c56c1a 100644 --- a/vp9/encoder/vp9_ratectrl.c +++ b/vp9/encoder/vp9_ratectrl.c @@ -242,6 +242,10 @@ static void calc_iframe_target_size(VP9_COMP *cpi) { } cpi->rc.this_frame_target = target; + + // Target rate per SB64 (including partial SB64s. + cpi->rc.sb64_target_rate = (cpi->rc.this_frame_target * 64 * 64) / + (cpi->common.width * cpi->common.height); } @@ -269,6 +273,11 @@ static void calc_pframe_target_size(VP9_COMP *cpi) { cpi->rc.this_frame_target = cpi->rc.per_frame_bandwidth; } + // Target rate per SB64 (including partial SB64s. + cpi->rc.sb64_target_rate = (cpi->rc.this_frame_target * 64 * 64) / + (cpi->common.width * cpi->common.height); + + // Check that the total sum of adjustments is not above the maximum allowed. // That is, having allowed for the KF and GF penalties, we have not pushed // the current inter-frame target too low. If the adjustment we apply here is -- cgit v1.2.3