Add user rating based delta q mode --deltaq-mode=4
The current model is fitted at the near lossless quality.
Change-Id: I0842b34b33f91d3915b3400eb63458c7d704d1e9
diff --git a/aom/aomcx.h b/aom/aomcx.h
index e92c0a7..42b3d66 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -1115,6 +1115,7 @@
* - 1 = use modulation to maximize objective quality (default)
* - 2 = use modulation for local test
* - 3 = use modulation for key frame perceptual quality optimization
+ * - 4 = use modulation for user rating based perceptual quality optimization
*/
AV1E_SET_DELTAQ_MODE = 107,
diff --git a/av1/arg_defs.c b/av1/arg_defs.c
index 663c889..f7a36e3 100644
--- a/av1/arg_defs.c
+++ b/av1/arg_defs.c
@@ -517,7 +517,8 @@
.deltaq_mode =
ARG_DEF(NULL, "deltaq-mode", 1,
"Delta qindex mode (0: off, 1: deltaq objective (default), "
- "2: deltaq placeholder, 3: key frame visual quality). "
+ "2: deltaq placeholder, 3: key frame visual quality, 4: user "
+ "rating based visual quality optimization). "
"Currently this requires enable-tpl-model as a prerequisite."),
.deltalf_mode = ARG_DEF(NULL, "delta-lf-mode", 1,
"Enable delta-lf-mode (0: off (default), 1: on)"),
diff --git a/av1/encoder/allintra_vis.c b/av1/encoder/allintra_vis.c
index 817c33c..510a28f 100644
--- a/av1/encoder/allintra_vis.c
+++ b/av1/encoder/allintra_vis.c
@@ -13,6 +13,7 @@
#include "av1/encoder/allintra_vis.h"
#include "av1/encoder/hybrid_fwd_txfm.h"
+#include "av1/encoder/rdopt_utils.h"
// Process the wiener variance in 16x16 block basis.
static int qsort_comp(const void *elem1, const void *elem2) {
@@ -417,3 +418,84 @@
return qindex;
}
+
+void av1_init_mb_ur_var_buffer(AV1_COMP *cpi) {
+ AV1_COMMON *cm = &cpi->common;
+
+ if (cpi->mb_variance) return;
+
+ CHECK_MEM_ERROR(cm, cpi->mb_variance,
+ aom_calloc(cpi->frame_info.mb_rows * cpi->frame_info.mb_cols,
+ sizeof(*cpi->mb_variance)));
+}
+
+void av1_set_mb_ur_variance(AV1_COMP *cpi) {
+ const CommonModeInfoParams *const mi_params = &cpi->common.mi_params;
+ ThreadData *td = &cpi->td;
+ MACROBLOCK *x = &td->mb;
+ MACROBLOCKD *xd = &x->e_mbd;
+ uint8_t *y_buffer = cpi->source->y_buffer;
+ const int y_stride = cpi->source->y_stride;
+ const int block_size = cpi->common.seq_params->sb_size;
+
+ const int num_mi_w = mi_size_wide[block_size];
+ const int num_mi_h = mi_size_high[block_size];
+ const int num_cols = (mi_params->mi_cols + num_mi_w - 1) / num_mi_w;
+ const int num_rows = (mi_params->mi_rows + num_mi_h - 1) / num_mi_h;
+ const int use_hbd = cpi->source->flags & YV12_FLAG_HIGHBITDEPTH;
+
+ // Loop through each SB block.
+ for (int row = 0; row < num_rows; ++row) {
+ for (int col = 0; col < num_cols; ++col) {
+ double var = 0.0, num_of_var = 0.0;
+ const int index = row * num_cols + col;
+
+ // Loop through each 8x8 block.
+ for (int mi_row = row * num_mi_h;
+ mi_row < mi_params->mi_rows && mi_row < (row + 1) * num_mi_h;
+ mi_row += 2) {
+ for (int mi_col = col * num_mi_w;
+ mi_col < mi_params->mi_cols && mi_col < (col + 1) * num_mi_w;
+ mi_col += 2) {
+ struct buf_2d buf;
+ const int row_offset_y = mi_row << 2;
+ const int col_offset_y = mi_col << 2;
+
+ buf.buf = y_buffer + row_offset_y * y_stride + col_offset_y;
+ buf.stride = y_stride;
+
+ if (use_hbd) {
+ var += av1_high_get_sby_perpixel_variance(cpi, &buf, BLOCK_8X8,
+ xd->bd);
+ } else {
+ var += av1_get_sby_perpixel_variance(cpi, &buf, BLOCK_8X8);
+ }
+
+ num_of_var += 1.0;
+ }
+ }
+ var = var / num_of_var;
+ cpi->mb_variance[index] = var;
+ }
+ }
+}
+
+int av1_get_sbq_user_rating_based(AV1_COMP *const cpi, int mi_row, int mi_col) {
+ const BLOCK_SIZE bsize = cpi->common.seq_params->sb_size;
+ const CommonModeInfoParams *const mi_params = &cpi->common.mi_params;
+ AV1_COMMON *const cm = &cpi->common;
+ const int base_qindex = cm->quant_params.base_qindex;
+ if (base_qindex == 0) return base_qindex;
+ const int num_mi_w = mi_size_wide[bsize];
+ const int num_mi_h = mi_size_high[bsize];
+ const int num_cols = (mi_params->mi_cols + num_mi_w - 1) / num_mi_w;
+ const int index = (mi_row / num_mi_h) * num_cols + (mi_col / num_mi_w);
+ const double var = cpi->mb_variance[index];
+ const double delta_qindex =
+ 4.0 * 23.5 * (1.0 - exp(-0.002 * var)) - 2.0 * 23.5;
+ int qindex = (int)((double)base_qindex + delta_qindex + 0.5);
+ qindex = AOMMIN(qindex, MAXQ);
+ qindex = AOMMAX(qindex, MINQ + 1);
+
+ return qindex;
+}
diff --git a/av1/encoder/allintra_vis.h b/av1/encoder/allintra_vis.h
index db6267e..9798707 100644
--- a/av1/encoder/allintra_vis.h
+++ b/av1/encoder/allintra_vis.h
@@ -23,3 +23,10 @@
int av1_get_sbq_perceptual_ai(AV1_COMP *const cpi, BLOCK_SIZE bsize, int mi_row,
int mi_col);
+
+// User rating based mode
+void av1_init_mb_ur_var_buffer(AV1_COMP *cpi);
+
+void av1_set_mb_ur_variance(AV1_COMP *cpi);
+
+int av1_get_sbq_user_rating_based(AV1_COMP *const cpi, int mi_row, int mi_col);
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index ed99441..60f8e50 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -239,6 +239,8 @@
av1_get_q_for_deltaq_objective(cpi, sb_size, mi_row, mi_col);
} else if (cpi->oxcf.q_cfg.deltaq_mode == DELTA_Q_PERCEPTUAL_AI) {
current_qindex = av1_get_sbq_perceptual_ai(cpi, sb_size, mi_row, mi_col);
+ } else if (cpi->oxcf.q_cfg.deltaq_mode == DELTA_Q_USER_RATING_BASED) {
+ current_qindex = av1_get_sbq_user_rating_based(cpi, mi_row, mi_col);
}
MACROBLOCKD *const xd = &x->e_mbd;
@@ -1358,6 +1360,9 @@
cm->delta_q_info.delta_q_res = DEFAULT_DELTA_Q_RES_PERCEPTUAL;
else if (deltaq_mode == DELTA_Q_PERCEPTUAL_AI)
cm->delta_q_info.delta_q_res = DEFAULT_DELTA_Q_RES_PERCEPTUAL;
+ else if (deltaq_mode == DELTA_Q_USER_RATING_BASED)
+ cm->delta_q_info.delta_q_res = DEFAULT_DELTA_Q_RES_PERCEPTUAL;
+
// Set delta_q_present_flag before it is used for the first time
cm->delta_q_info.delta_lf_res = DEFAULT_DELTA_LF_RES;
cm->delta_q_info.delta_q_present_flag = deltaq_mode != NO_DELTA_Q;
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 86ce61d..16d559d 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -1378,6 +1378,7 @@
sizeof(*cpi->consec_zero_mv)));
cpi->mb_weber_stats = NULL;
+ cpi->mb_variance = NULL;
{
const int bsize = BLOCK_16X16;
@@ -3383,6 +3384,11 @@
av1_set_mb_wiener_variance(cpi);
}
+ if (cpi->oxcf.q_cfg.deltaq_mode == DELTA_Q_USER_RATING_BASED) {
+ av1_init_mb_ur_var_buffer(cpi);
+ av1_set_mb_ur_variance(cpi);
+ }
+
#if CONFIG_INTERNAL_STATS
memset(cpi->mode_chosen_counts, 0,
MAX_MODES * sizeof(*cpi->mode_chosen_counts));
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 5024954..155ee7e 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -154,6 +154,7 @@
DELTA_Q_OBJECTIVE = 1, // Modulation to improve objective quality
DELTA_Q_PERCEPTUAL = 2, // Modulation to improve video perceptual quality
DELTA_Q_PERCEPTUAL_AI = 3, // Perceptual quality opt for all intra mode
+ DELTA_Q_USER_RATING_BASED = 4, // User rating based delta q mode
DELTA_Q_MODE_COUNT // This should always be the last member of the enum
} UENUM1BYTE(DELTAQ_MODE);
@@ -3109,6 +3110,11 @@
int64_t norm_wiener_variance;
/*!
+ * Buffer to store MB variance.
+ */
+ double *mb_variance;
+
+ /*!
* Flag to indicate that current frame is dropped.
*/
bool is_dropped_frame;
diff --git a/av1/encoder/encoder_alloc.h b/av1/encoder/encoder_alloc.h
index 5daf50b..cac6bee 100644
--- a/av1/encoder/encoder_alloc.h
+++ b/av1/encoder/encoder_alloc.h
@@ -289,6 +289,9 @@
aom_free(cpi->mb_weber_stats);
cpi->mb_weber_stats = NULL;
+
+ aom_free(cpi->mb_variance);
+ cpi->mb_variance = NULL;
}
static AOM_INLINE void variance_partition_alloc(AV1_COMP *cpi) {
diff --git a/av1/encoder/encoder_utils.c b/av1/encoder/encoder_utils.c
index 093311d..dac41de 100644
--- a/av1/encoder/encoder_utils.c
+++ b/av1/encoder/encoder_utils.c
@@ -746,7 +746,8 @@
// Force 64x64 superblock size to increase resolution in perceptual
// AQ mode.
if (oxcf->mode == ALLINTRA &&
- oxcf->q_cfg.deltaq_mode == DELTA_Q_PERCEPTUAL_AI)
+ (oxcf->q_cfg.deltaq_mode == DELTA_Q_PERCEPTUAL_AI ||
+ oxcf->q_cfg.deltaq_mode == DELTA_Q_USER_RATING_BASED))
return BLOCK_64X64;
assert(oxcf->tool_cfg.superblock_size == AOM_SUPERBLOCK_SIZE_DYNAMIC);