Introduce qindex -> QM level formula tuned for allintra mode QMs help increase visual quality by effectively allocating more bits towards encoding low and mid frequencies for a given file size, which helps retain overall texture integrity, avoiding cases where local features could collapse into flat patches or 'thin' high-freq basis patterns. The formula was empirically derived by encoding the CID22 validation testset for each QP/QM tuple, and building a convex hull that maximizes SSIMU2 scores, and a final subjective visual quality pass as a sanity check. Approximate BD-Rate gains over enable-qm=0 - cpu-used=6 (subset1): - SSIMU2 60: -4.5% - SSIMU2 70: -3.9% - SSIMU2 80: -3.4% - SSIMU2 90: -6.6% Bug: aomedia:375221136 Change-Id: I29721cad07ee273a56c5d5d4b7e929301083a117
diff --git a/aom/aomcx.h b/aom/aomcx.h index 255a891..1f2f34f 100644 --- a/aom/aomcx.h +++ b/aom/aomcx.h
@@ -731,8 +731,8 @@ * control sets the minimum level of flatness from which the matrices * are determined. * - * By default, the encoder sets this minimum at half the available - * range. + * By default, the encoder sets this minimum at level 5 (4 in allintra + * mode). */ AV1E_SET_QM_MIN = 64, @@ -743,8 +743,8 @@ * As quantisation levels increase, the matrices get flatter. This * control sets the maximum level of flatness possible. * - * By default, the encoder sets this maximum at the top of the - * available range. + * By default, the encoder sets this maximum at level 9 (10 in allintra + * mode) */ AV1E_SET_QM_MAX = 65,
diff --git a/av1/arg_defs.c b/av1/arg_defs.c index 1c06f5b..4f92cbe 100644 --- a/av1/arg_defs.c +++ b/av1/arg_defs.c
@@ -460,10 +460,12 @@ .enable_qm = ARG_DEF(NULL, "enable-qm", 1, "Enable quantisation matrices (0: false (default), 1: true)"), - .qm_min = ARG_DEF(NULL, "qm-min", 1, - "Min quant matrix flatness (0..15), default is 8"), - .qm_max = ARG_DEF(NULL, "qm-max", 1, - "Max quant matrix flatness (0..15), default is 15"), + .qm_min = ARG_DEF( + NULL, "qm-min", 1, + "Min quant matrix flatness (0..15), default is 5 (4 for allintra mode)"), + .qm_max = ARG_DEF( + NULL, "qm-max", 1, + "Max quant matrix flatness (0..15), default is 9 (10 for allintra mode)"), .reduced_tx_type_set = ARG_DEF(NULL, "reduced-tx-type-set", 1, "Use reduced set of transform types"), .use_intra_dct_only =
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c index f10f5fa..071a2fe 100644 --- a/av1/av1_cx_iface.c +++ b/av1/av1_cx_iface.c
@@ -2861,12 +2861,15 @@ } priv->extra_cfg = default_extra_cfg[extra_cfg_idx]; // Special handling: - // By default, if omitted, --enable-cdef = 1. - // Here we set its default value to 0 when --allintra is turned on. - // However, if users set --enable-cdef = 1 from command line, - // The encoder still respects it. + // By default, if omitted: --enable-cdef=1, --qm-min=5, and --qm-max=9 + // Here we set its default values to 0, 4, and 10 respectively when + // --allintra is turned on. + // However, if users set --enable-cdef, --qm-min, or --qm-max, either from + // the command line or aom_codec_control(), the encoder still respects it. if (priv->cfg.g_usage == AOM_USAGE_ALL_INTRA) { priv->extra_cfg.enable_cdef = 0; + priv->extra_cfg.qm_min = DEFAULT_QM_FIRST_ALLINTRA; + priv->extra_cfg.qm_max = DEFAULT_QM_LAST_ALLINTRA; } av1_initialize_enc(priv->cfg.g_usage, priv->cfg.rc_end_usage);
diff --git a/av1/common/quant_common.h b/av1/common/quant_common.h index 7e0a20c..1a7b281 100644 --- a/av1/common/quant_common.h +++ b/av1/common/quant_common.h
@@ -36,6 +36,8 @@ #define DEFAULT_QM_V 12 #define DEFAULT_QM_FIRST 5 #define DEFAULT_QM_LAST 9 +#define DEFAULT_QM_FIRST_ALLINTRA 4 +#define DEFAULT_QM_LAST_ALLINTRA 10 #define LOSSLESS_Q_STEP 4 // this should equal to dc/ac_qlookup_QTX[0] struct AV1Common; @@ -58,6 +60,33 @@ return first + (qindex * (last + 1 - first)) / QINDEX_RANGE; } +// QM levels tuned for allintra mode (including still images) +// This formula was empirically derived by encoding the CID22 validation +// testset for each QP/QM tuple, and building a convex hull that +// maximizes SSIMU2 scores, and a final subjective visual quality pass +// as a sanity check. +static inline int aom_get_qmlevel_allintra(int qindex, int first, int last) { + int qm_level = 0; + + if (qindex <= 40) { + qm_level = 10; + } else if (qindex <= 100) { + qm_level = 9; + } else if (qindex <= 160) { + qm_level = 8; + } else if (qindex <= 200) { + qm_level = 7; + } else if (qindex <= 220) { + qm_level = 6; + } else if (qindex <= 240) { + qm_level = 5; + } else { + qm_level = 4; + } + + return clamp(qm_level, first, last); +} + // Initialize all global quant/dequant matrices. void av1_qm_init(struct CommonQuantParams *quant_params, int num_planes);
diff --git a/av1/encoder/av1_quantize.c b/av1/encoder/av1_quantize.c index d44795d..5de1114 100644 --- a/av1/encoder/av1_quantize.c +++ b/av1/encoder/av1_quantize.c
@@ -864,7 +864,8 @@ } void av1_set_quantizer(AV1_COMMON *const cm, int min_qmlevel, int max_qmlevel, - int q, int enable_chroma_deltaq, int enable_hdr_deltaq) { + int q, int enable_chroma_deltaq, int enable_hdr_deltaq, + bool is_allintra) { // quantizer has to be reinitialized with av1_init_quantizer() if any // delta_q changes. CommonQuantParams *quant_params = &cm->quant_params; @@ -896,18 +897,29 @@ } } - quant_params->qmatrix_level_y = - aom_get_qmlevel(quant_params->base_qindex, min_qmlevel, max_qmlevel); - quant_params->qmatrix_level_u = - aom_get_qmlevel(quant_params->base_qindex + quant_params->u_ac_delta_q, - min_qmlevel, max_qmlevel); + // Select the best QM formula based on whether we're encoding in allintra mode + // or any other mode + int (*get_qmlevel)(int, int, int); - if (!cm->seq_params->separate_uv_delta_q) - quant_params->qmatrix_level_v = quant_params->qmatrix_level_u; - else + if (is_allintra) { + get_qmlevel = aom_get_qmlevel_allintra; + } else { + get_qmlevel = aom_get_qmlevel; + } + + quant_params->qmatrix_level_y = + get_qmlevel(quant_params->base_qindex, min_qmlevel, max_qmlevel); + quant_params->qmatrix_level_u = + get_qmlevel(quant_params->base_qindex + quant_params->u_ac_delta_q, + min_qmlevel, max_qmlevel); + + if (cm->seq_params->separate_uv_delta_q) { quant_params->qmatrix_level_v = - aom_get_qmlevel(quant_params->base_qindex + quant_params->v_ac_delta_q, - min_qmlevel, max_qmlevel); + get_qmlevel(quant_params->base_qindex + quant_params->v_ac_delta_q, + min_qmlevel, max_qmlevel); + } else { + quant_params->qmatrix_level_v = quant_params->qmatrix_level_u; + } } // Table that converts 0-63 Q-range values passed in outside to the Qindex
diff --git a/av1/encoder/av1_quantize.h b/av1/encoder/av1_quantize.h index 35b0fa8..bf3b365 100644 --- a/av1/encoder/av1_quantize.h +++ b/av1/encoder/av1_quantize.h
@@ -12,6 +12,8 @@ #ifndef AOM_AV1_ENCODER_AV1_QUANTIZE_H_ #define AOM_AV1_ENCODER_AV1_QUANTIZE_H_ +#include <stdbool.h> + #include "config/aom_config.h" #include "av1/common/quant_common.h" @@ -120,7 +122,7 @@ void av1_set_quantizer(struct AV1Common *const cm, int min_qmlevel, int max_qmlevel, int q, int enable_chroma_deltaq, - int enable_hdr_deltaq); + int enable_hdr_deltaq, bool is_allintra); int av1_quantizer_to_qindex(int quantizer);
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c index 6df5e2e..4d28bd2 100644 --- a/av1/encoder/encoder.c +++ b/av1/encoder/encoder.c
@@ -2646,7 +2646,8 @@ } av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q, - q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq); + q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, + cpi->oxcf.mode == ALLINTRA); av1_set_speed_features_qindex_dependent(cpi, cpi->oxcf.speed); av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, cm->seq_params->bit_depth); @@ -2660,7 +2661,8 @@ cpi->rc.high_source_sad) { if (av1_encodedframe_overshoot_cbr(cpi, &q)) { av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q, - q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq); + q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, + cpi->oxcf.mode == ALLINTRA); av1_set_speed_features_qindex_dependent(cpi, cpi->oxcf.speed); av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, cm->seq_params->bit_depth); @@ -2964,7 +2966,8 @@ } av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q, - q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq); + q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, + oxcf->mode == ALLINTRA); av1_set_speed_features_qindex_dependent(cpi, oxcf->speed); av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, cm->seq_params->bit_depth);
diff --git a/av1/encoder/encoder_utils.c b/av1/encoder/encoder_utils.c index 32eb2f6..73a0dac 100644 --- a/av1/encoder/encoder_utils.c +++ b/av1/encoder/encoder_utils.c
@@ -1127,7 +1127,8 @@ set_encoding_params_for_screen_content(cpi, pass); av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q_for_screen_content_quick_run, - q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq); + q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, + oxcf->mode == ALLINTRA); av1_set_speed_features_qindex_dependent(cpi, oxcf->speed); av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, cm->seq_params->bit_depth);
diff --git a/av1/encoder/firstpass.c b/av1/encoder/firstpass.c index a81cc29..69d465e 100644 --- a/av1/encoder/firstpass.c +++ b/av1/encoder/firstpass.c
@@ -1368,7 +1368,8 @@ av1_set_quantizer( cm, cpi->oxcf.q_cfg.qm_minlevel, cpi->oxcf.q_cfg.qm_maxlevel, qindex, - cpi->oxcf.q_cfg.enable_chroma_deltaq, cpi->oxcf.q_cfg.enable_hdr_deltaq); + cpi->oxcf.q_cfg.enable_chroma_deltaq, cpi->oxcf.q_cfg.enable_hdr_deltaq, + cpi->oxcf.mode == ALLINTRA); av1_setup_block_planes(xd, seq_params->subsampling_x, seq_params->subsampling_y, num_planes);
diff --git a/av1/encoder/tune_butteraugli.c b/av1/encoder/tune_butteraugli.c index 99fd464..e6061ec 100644 --- a/av1/encoder/tune_butteraugli.c +++ b/av1/encoder/tune_butteraugli.c
@@ -299,7 +299,8 @@ // cpi->sf.part_sf.fixed_partition_size = BLOCK_32X32; av1_set_quantizer(cm, q_cfg->qm_minlevel, q_cfg->qm_maxlevel, q_index, - q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq); + q_cfg->enable_chroma_deltaq, q_cfg->enable_hdr_deltaq, + oxcf->mode == ALLINTRA); av1_set_speed_features_qindex_dependent(cpi, oxcf->speed); av1_init_quantizer(&cpi->enc_quant_dequant_params, &cm->quant_params, cm->seq_params->bit_depth);