Tweak 4:4:4 chroma QM levels with `tune=ssimulacra2` At the same level, chroma QMs are flatter in 4:4:4 chroma subsampling mode relative to 4:2:0, so this commit introduces a formula that picks a chroma QM level for a given frame qindex when encoding 4:4:4 content. Approximate BD-Rate gains over no chroma QM adjustment - cpu-used=6 (Daala's subset1): - SSIMULACRA2 60: -0.3% - SSIMULACRA2 70: -0.6% - SSIMULACRA2 80: -0.5% - SSIMULACRA2 90: -0.4% Bug: aomedia:375221136 Change-Id: I358e2701e327520583107aa860eae80fe32f2d1a
diff --git a/aom/aomcx.h b/aom/aomcx.h index 26e3697..2534349 100644 --- a/aom/aomcx.h +++ b/aom/aomcx.h
@@ -1672,6 +1672,8 @@ * Setting the tuning option to AOM_TUNE_SSIMULACRA2 causes the following * options to be set (expressed as command-line options): * * --enable-qm=1 + * * --qm-min=2 + * * --qm-max=10 * * --sharpness=7 * * --dist-metric=qm-psnr * * --enable-cdef=3
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c index 5d951da..6fd6edc 100644 --- a/av1/av1_cx_iface.c +++ b/av1/av1_cx_iface.c
@@ -1790,8 +1790,11 @@ if (extra_cfg->tuning == AOM_TUNE_SSIMULACRA2) { if (ctx->cfg.g_usage != AOM_USAGE_ALL_INTRA) return AOM_CODEC_INCAPABLE; // Enable QMs as they've been found to be beneficial for images, when used - // with an alternative QM formula (see aom_get_qmlevel_allintra()). + // with alternative QM formulas (see aom_get_qmlevel_allintra() and + // aom_get_qmlevel_444_chroma_ssimulacra2()). extra_cfg->enable_qm = 1; + extra_cfg->qm_min = QM_FIRST_SSIMULACRA2; + extra_cfg->qm_max = QM_LAST_SSIMULACRA2; // We can turn on loop filter sharpness, as frames do not have to serve as // references to others. extra_cfg->sharpness = 7;
diff --git a/av1/common/quant_common.h b/av1/common/quant_common.h index 6d2fb97..ed4f23a 100644 --- a/av1/common/quant_common.h +++ b/av1/common/quant_common.h
@@ -38,6 +38,8 @@ #define DEFAULT_QM_LAST 9 #define DEFAULT_QM_FIRST_ALLINTRA 4 #define DEFAULT_QM_LAST_ALLINTRA 10 +#define QM_FIRST_SSIMULACRA2 2 +#define QM_LAST_SSIMULACRA2 10 #define LOSSLESS_Q_STEP 4 // this should equal to dc/ac_qlookup_QTX[0] struct AV1Common; @@ -60,11 +62,18 @@ return first + (qindex * (last + 1 - first)) / QINDEX_RANGE; } -// QM levels tuned for allintra mode (including still images) +// QM levels tuned for all intra 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 +// maximizes SSIMULACRA 2 scores, and a final subjective visual quality pass // as a sanity check. This is a decreasing function in qindex. +// There are a total of 16 luma QM levels, and the higher the level, the +// flatter these QMs are. +// QM level 15 is a completely-flat matrix and level 0 is the steepest. +// This formula only uses levels 4 through 10, unless qm-min and qm-max are +// both set below or above this range. +// For more information on quantization matrices, please refer to +// https://arxiv.org/pdf/2008.06091, section F. static inline int aom_get_qmlevel_allintra(int qindex, int first, int last) { int qm_level = 0; @@ -87,6 +96,45 @@ return clamp(qm_level, first, last); } +// Chroma QM levels for 4:4:4 subsampling tuned for SSIMULACRA 2 tune +// This formula was empirically derived by encoding Daala's subset1 validation +// testset for each QP/QM tuple, and building a convex hull that maximizes +// SSIMULACRA 2 scores, and a final subjective visual quality pass as a sanity +// check. This is a decreasing function in qindex. +// Like with luma QMs, there are a total of 16 chroma QM levels, and the higher +// the level, the flatter these QMs are. +// QM level 15 is a completely-flat matrix and level 0 is the steepest. +// This formula only uses levels 2 through 10, unless qm-min and qm-max are +// both set below or above this range. +// For more information on quantization matrices, please refer to +// https://arxiv.org/pdf/2008.06091, section F. +static inline int aom_get_qmlevel_444_chroma_ssimulacra2(int qindex, int first, + int last) { + int chroma_qm_level = 0; + + if (qindex <= 12) { + chroma_qm_level = 10; + } else if (qindex <= 24) { + chroma_qm_level = 9; + } else if (qindex <= 32) { + chroma_qm_level = 8; + } else if (qindex <= 36) { + chroma_qm_level = 7; + } else if (qindex <= 44) { + chroma_qm_level = 6; + } else if (qindex <= 48) { + chroma_qm_level = 5; + } else if (qindex <= 56) { + chroma_qm_level = 4; + } else if (qindex <= 88) { + chroma_qm_level = 3; + } else { + chroma_qm_level = 2; + } + + return clamp(chroma_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 c4fa7df..f948405 100644 --- a/av1/encoder/av1_quantize.c +++ b/av1/encoder/av1_quantize.c
@@ -943,26 +943,42 @@ } } - // Select the best QM formula based on whether we're encoding in allintra mode - // or any other mode - int (*get_qmlevel)(int, int, int); + // Select the best luma and chroma QM formulas based on encoding mode and + // tuning + int (*get_luma_qmlevel)(int, int, int); + int (*get_chroma_qmlevel)(int, int, int); if (is_allintra) { - get_qmlevel = aom_get_qmlevel_allintra; + get_luma_qmlevel = aom_get_qmlevel_allintra; } else { - get_qmlevel = aom_get_qmlevel; + get_luma_qmlevel = aom_get_qmlevel; + } + + if (is_allintra) { + if (tuning == AOM_TUNE_SSIMULACRA2 && cm->seq_params->subsampling_x == 0 && + cm->seq_params->subsampling_y == 0) { + // 4:4:4 subsampling mode has 4x the number of chroma coefficients + // compared to 4:2:0 (2x on each dimension). This means the encoder should + // use lower chroma QM levels that more closely match the scaling of an + // equivalent 4:2:0 chroma QM. + get_chroma_qmlevel = aom_get_qmlevel_444_chroma_ssimulacra2; + } else { + get_chroma_qmlevel = aom_get_qmlevel_allintra; + } + } else { + get_chroma_qmlevel = aom_get_qmlevel; } quant_params->qmatrix_level_y = - get_qmlevel(quant_params->base_qindex, min_qmlevel, max_qmlevel); + get_luma_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); + get_chroma_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 = - get_qmlevel(quant_params->base_qindex + quant_params->v_ac_delta_q, - min_qmlevel, max_qmlevel); + quant_params->qmatrix_level_v = get_chroma_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; }