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);