ref: 2b2a757199a9ea84528102778bcdccc54fa6ee54
parent: 47808645a8572dc799795aab45a6be7ee78a84fc
author: Marco Paniconi <marpan@google.com>
date: Mon Jul 30 05:25:14 EDT 2018
vp9: Add flatness metric to cyclic refresh setup. For screen-content with aq-mode = 3: identify spatial flat superblocks in the setup stage and don't mark them as candidates for refresh. Spatially flat blocks are already removed from refresh at a later stage in the encoding (in pick_mode), but doing this at the setup stage of cyclic refresh (before encoding) allows refresh to more quickly hit the text areas. Only drawback is an extra source variance calculation for a set of superblocks on each frame. Adjust the refresh rate: lower it to reduce overshoot since more texture areas are hit faster with this change. Change-Id: I88fa20e52fdbf1a938ae814f9b48c887f1f909d2
--- a/vp9/encoder/vp9_aq_cyclicrefresh.c
+++ b/vp9/encoder/vp9_aq_cyclicrefresh.c
@@ -21,6 +21,14 @@
#include "vp9/encoder/vp9_ratectrl.h"
#include "vp9/encoder/vp9_segmentation.h"
+static const uint8_t VP9_VAR_OFFS[64] = {
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128
+};
+
CYCLIC_REFRESH *vp9_cyclic_refresh_alloc(int mi_rows, int mi_cols) {
size_t last_coded_q_map_size;
CYCLIC_REFRESH *const cr = vpx_calloc(1, sizeof(*cr));
@@ -319,6 +327,28 @@
rc->baseline_gf_interval = 10;
}
+static int is_superblock_flat_static(VP9_COMP *const cpi, int sb_row_index,
+ int sb_col_index) {
+ unsigned int source_variance;
+ const uint8_t *src_y = cpi->Source->y_buffer;
+ const int ystride = cpi->Source->y_stride;
+ unsigned int sse;
+ const BLOCK_SIZE bsize = BLOCK_64X64;
+ src_y += (sb_row_index << 6) * ystride + (sb_col_index << 6);
+ source_variance =
+ cpi->fn_ptr[bsize].vf(src_y, ystride, VP9_VAR_OFFS, 0, &sse);
+ if (source_variance == 0) {
+ uint64_t block_sad;
+ const uint8_t *last_src_y = cpi->Last_Source->y_buffer;
+ const int last_ystride = cpi->Last_Source->y_stride;
+ last_src_y += (sb_row_index << 6) * ystride + (sb_col_index << 6);
+ block_sad =
+ cpi->fn_ptr[bsize].sdf(src_y, ystride, last_src_y, last_ystride);
+ if (block_sad == 0) return 1;
+ }
+ return 0;
+}
+
// Update the segmentation map, and related quantities: cyclic refresh map,
// refresh sb_index, and target number of blocks to be refreshed.
// The map is set to either 0/CR_SEGMENT_ID_BASE (no refresh) or to
@@ -369,8 +399,17 @@
int sb_col_index = i - sb_row_index * sb_cols;
int mi_row = sb_row_index * MI_BLOCK_SIZE;
int mi_col = sb_col_index * MI_BLOCK_SIZE;
+ int flat_static_blocks = 0;
+ int compute_content = 1;
assert(mi_row >= 0 && mi_row < cm->mi_rows);
assert(mi_col >= 0 && mi_col < cm->mi_cols);
+#if CONFIG_VP9_HIGHBITDEPTH
+ if (cpi->common.use_highbitdepth) compute_content = 0;
+#endif
+ if (cpi->Last_Source == NULL ||
+ cpi->Last_Source->y_width != cpi->Source->y_width ||
+ cpi->Last_Source->y_height != cpi->Source->y_height)
+ compute_content = 0;
bl_index = mi_row * cm->mi_cols + mi_col;
// Loop through all 8x8 blocks in superblock and update map.
xmis =
@@ -401,11 +440,21 @@
// Enforce constant segment over superblock.
// If segment is at least half of superblock, set to 1.
if (sum_map >= xmis * ymis / 2) {
- for (y = 0; y < ymis; y++)
- for (x = 0; x < xmis; x++) {
- seg_map[bl_index + y * cm->mi_cols + x] = CR_SEGMENT_ID_BOOST1;
- }
- cr->target_num_seg_blocks += xmis * ymis;
+ // This superblock is a candidate for refresh:
+ // compute spatial variance and exclude blocks that are spatially flat
+ // and stationary. Note: this is currently only done for screne content
+ // mode.
+ if (compute_content && cr->skip_flat_static_blocks)
+ flat_static_blocks =
+ is_superblock_flat_static(cpi, sb_row_index, sb_col_index);
+ if (!flat_static_blocks) {
+ // Label this superblock as segment 1.
+ for (y = 0; y < ymis; y++)
+ for (x = 0; x < xmis; x++) {
+ seg_map[bl_index + y * cm->mi_cols + x] = CR_SEGMENT_ID_BOOST1;
+ }
+ cr->target_num_seg_blocks += xmis * ymis;
+ }
}
i++;
if (i == sbs_in_frame) {
@@ -464,12 +513,16 @@
// For screen-content: keep rate_ratio_qdelta to 2.0 (segment#1 boost) and
// percent_refresh (refresh rate) to 10. But reduce rate boost for segment#2
// (rate_boost_fac = 10 disables segment#2).
- // TODO(marpan): Consider increasing refresh rate after slide change.
if (cpi->oxcf.content == VP9E_CONTENT_SCREEN) {
- cr->percent_refresh = 10;
+ // Only enable feature of skipping flat_static blocks for top layer
+ // under screen content mode.
+ if (cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 1)
+ cr->skip_flat_static_blocks = 1;
+ cr->percent_refresh = (cr->skip_flat_static_blocks) ? 5 : 10;
// Increase the amount of refresh on scene change that is encoded at max Q,
- // increase for a few cycles of the refresh period (~30 frames).
- if (cr->counter_encode_maxq_scene_change < 30) cr->percent_refresh = 15;
+ // increase for a few cycles of the refresh period (~100 / percent_refresh).
+ if (cr->counter_encode_maxq_scene_change < 30)
+ cr->percent_refresh = (cr->skip_flat_static_blocks) ? 10 : 15;
cr->rate_ratio_qdelta = 2.0;
cr->rate_boost_fac = 10;
}
--- a/vp9/encoder/vp9_aq_cyclicrefresh.h
+++ b/vp9/encoder/vp9_aq_cyclicrefresh.h
@@ -69,6 +69,7 @@
double weight_segment;
int apply_cyclic_refresh;
int counter_encode_maxq_scene_change;
+ int skip_flat_static_blocks;
};
struct VP9_COMP;