diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst index 0a43589dd..23f2d4a83 100755 --- a/doc/reST/cli.rst +++ b/doc/reST/cli.rst @@ -1740,7 +1740,7 @@ Quality, rate control and rate distortion options ignored. Slower presets will generally achieve better compression efficiency (and generate smaller bitstreams). Default disabled. -.. option:: --aq-mode <0|1|2|3|4> +.. option:: --aq-mode <0|1|2|3|4|5> Adaptive Quantization operating mode. Raise or lower per-block quantization based on complexity analysis of the source image. The @@ -1755,6 +1755,7 @@ Quality, rate control and rate distortion options recommended for 8-bit encodes or low-bitrate 10-bit encodes, to prevent color banding/blocking. 4. AQ enabled with auto-variance and edge information. + 5. AQ enabled with auto-variance, edge information, and bias to dark scenes. .. option:: --aq-strength @@ -1766,6 +1767,13 @@ Quality, rate control and rate distortion options Default 1.0. **Range of values:** 0.0 to 3.0 +.. option:: --aq-bias-strength + + Adjust the strength of dark scene bias in AQ modes 3 and 5. Setting this + to 0 will disable the dark scene bias, meaning modes will be equivalent to + their unbiased counterparts (2 and 4). + Default 1.0. + .. option:: --hevc-aq Enable adaptive quantization diff --git a/source/common/frame.cpp b/source/common/frame.cpp index 255882a9d..443f80201 100644 --- a/source/common/frame.cpp +++ b/source/common/frame.cpp @@ -104,7 +104,7 @@ bool Frame::create(x265_param *param, float* quantOffsets) CHECKED_MALLOC_ZERO(m_classifyCount, uint32_t, size); } - if (param->rc.aqMode == X265_AQ_EDGE || (param->rc.zonefileCount && param->rc.aqMode != 0)) + if (param->rc.aqMode == X265_AQ_EDGE || param->rc.aqMode == X265_AQ_EDGE_BIASED || (param->rc.zonefileCount && param->rc.aqMode != 0)) { uint32_t numCuInWidth = (param->sourceWidth + param->maxCUSize - 1) / param->maxCUSize; uint32_t numCuInHeight = (param->sourceHeight + param->maxCUSize - 1) / param->maxCUSize; @@ -277,7 +277,7 @@ void Frame::destroy() X265_FREE_ZERO(m_classifyCount); } - if (m_param->rc.aqMode == X265_AQ_EDGE || (m_param->rc.zonefileCount && m_param->rc.aqMode != 0)) + if (m_param->rc.aqMode == X265_AQ_EDGE || m_param->rc.aqMode == X265_AQ_EDGE_BIASED || (m_param->rc.zonefileCount && m_param->rc.aqMode != 0)) { X265_FREE(m_edgePic); X265_FREE(m_gaussianPic); diff --git a/source/common/param.cpp b/source/common/param.cpp index f3cf9f9bd..a86633054 100755 --- a/source/common/param.cpp +++ b/source/common/param.cpp @@ -272,6 +272,7 @@ void x265_param_default(x265_param* param) param->rc.hevcAq = 0; param->rc.qgSize = 32; param->rc.aqStrength = 1.0; + param->rc.aqBiasStrength = 1.0; param->rc.qpAdaptationRange = 1.0; param->rc.cuTree = 1; param->rc.rfConstantMax = 0; @@ -748,6 +749,7 @@ int x265_zone_param_parse(x265_param* p, const char* name, const char* value) } OPT("aq-mode") p->rc.aqMode = atoi(value); OPT("aq-strength") p->rc.aqStrength = atof(value); + OPT("aq-bias-strength") p->rc.aqBiasStrength = atof(value); OPT("nr-intra") p->noiseReductionIntra = atoi(value); OPT("nr-inter") p->noiseReductionInter = atoi(value); OPT("limit-modes") p->limitModes = atobool(value); @@ -1050,6 +1052,7 @@ int x265_param_parse(x265_param* p, const char* name, const char* value) OPT("qblur") p->rc.qblur = atof(value); OPT("aq-mode") p->rc.aqMode = atoi(value); OPT("aq-strength") p->rc.aqStrength = atof(value); + OPT("aq-bias-strength") p->rc.aqBiasStrength = atof(value); OPT("vbv-maxrate") p->rc.vbvMaxBitrate = atoi(value); OPT("vbv-bufsize") p->rc.vbvBufferSize = atoi(value); OPT("vbv-init") p->rc.vbvBufferInit = atof(value); @@ -1690,7 +1693,7 @@ int x265_check_params(x265_param* param) "Lookahead depth must be less than 256"); CHECK(param->lookaheadSlices > 16 || param->lookaheadSlices < 0, "Lookahead slices must between 0 and 16"); - CHECK(param->rc.aqMode < X265_AQ_NONE || X265_AQ_EDGE < param->rc.aqMode, + CHECK(param->rc.aqMode < X265_AQ_NONE || X265_AQ_EDGE_BIASED < param->rc.aqMode, "Aq-Mode is out of range"); CHECK(param->rc.aqStrength < 0 || param->rc.aqStrength > 3, "Aq-Strength is out of range"); @@ -2239,6 +2242,7 @@ char *x265_param2string(x265_param* p, int padx, int pady) } s += sprintf(s, " aq-mode=%d", p->rc.aqMode); s += sprintf(s, " aq-strength=%.2f", p->rc.aqStrength); + s += sprintf(s, " aq-bias-strength=%.2f", p->rc.aqBiasStrength); BOOL(p->rc.cuTree, "cutree"); s += sprintf(s, " zone-count=%d", p->rc.zoneCount); if (p->rc.zoneCount) @@ -2527,6 +2531,7 @@ void x265_copy_params(x265_param* dst, x265_param* src) dst->rc.qpStep = src->rc.qpStep; dst->rc.aqMode = src->rc.aqMode; dst->rc.aqStrength = src->rc.aqStrength; + dst->rc.aqBiasStrength = src->rc.aqBiasStrength; dst->rc.vbvBufferSize = src->rc.vbvBufferSize; dst->rc.vbvMaxBitrate = src->rc.vbvMaxBitrate; diff --git a/source/encoder/frameencoder.cpp b/source/encoder/frameencoder.cpp index efe85282f..2230bacb7 100644 --- a/source/encoder/frameencoder.cpp +++ b/source/encoder/frameencoder.cpp @@ -450,7 +450,7 @@ void FrameEncoder::compressFrame() m_ssimCnt = 0; memset(&(m_frame->m_encData->m_frameStats), 0, sizeof(m_frame->m_encData->m_frameStats)); - if (!m_param->bHistBasedSceneCut && m_param->rc.aqMode != X265_AQ_EDGE && m_param->recursionSkipMode == EDGE_BASED_RSKIP) + if (!m_param->bHistBasedSceneCut && m_param->rc.aqMode != X265_AQ_EDGE_BIASED && m_param->rc.aqMode != X265_AQ_EDGE && m_param->recursionSkipMode == EDGE_BASED_RSKIP) { int height = m_frame->m_fencPic->m_picHeight; int width = m_frame->m_fencPic->m_picWidth; diff --git a/source/encoder/slicetype.cpp b/source/encoder/slicetype.cpp index 0adb0d0db..ccb44d9e8 100644 --- a/source/encoder/slicetype.cpp +++ b/source/encoder/slicetype.cpp @@ -516,17 +516,17 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame *curFrame, x265_param* param) double bias_strength = 0.f; double strength = 0.f; - if (param->rc.aqMode == X265_AQ_EDGE) + if (param->rc.aqMode == X265_AQ_EDGE || param->rc.aqMode == X265_AQ_EDGE_BIASED) edgeFilter(curFrame, param); - if (param->rc.aqMode == X265_AQ_EDGE && !param->bHistBasedSceneCut && param->recursionSkipMode == EDGE_BASED_RSKIP) + if ((param->rc.aqMode == X265_AQ_EDGE || param->rc.aqMode == X265_AQ_EDGE_BIASED) && !param->bHistBasedSceneCut && param->recursionSkipMode == EDGE_BASED_RSKIP) { pixel* src = curFrame->m_edgePic + curFrame->m_fencPic->m_lumaMarginY * curFrame->m_fencPic->m_stride + curFrame->m_fencPic->m_lumaMarginX; primitives.planecopy_pp_shr(src, curFrame->m_fencPic->m_stride, curFrame->m_edgeBitPic, curFrame->m_fencPic->m_stride, curFrame->m_fencPic->m_picWidth, curFrame->m_fencPic->m_picHeight, SHIFT_TO_BITPLANE); } - if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE || param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED || param->rc.aqMode == X265_AQ_EDGE) + if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE || param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED || param->rc.aqMode == X265_AQ_EDGE || param->rc.aqMode == X265_AQ_EDGE_BIASED) { double bit_depth_correction = 1.f / (1 << (2 * (X265_DEPTH - 8))); for (int blockY = 0; blockY < maxRow; blockY += loopIncr) @@ -535,7 +535,7 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame *curFrame, x265_param* param) { uint32_t energy, edgeDensity, avgAngle; energy = acEnergyCu(curFrame, blockX, blockY, param->internalCsp, param->rc.qgSize); - if (param->rc.aqMode == X265_AQ_EDGE) + if (param->rc.aqMode == X265_AQ_EDGE || param->rc.aqMode == X265_AQ_EDGE_BIASED) { edgeDensity = edgeDensityCu(curFrame, avgAngle, blockX, blockY, param->rc.qgSize); if (edgeDensity) @@ -565,7 +565,7 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame *curFrame, x265_param* param) avg_adj_pow2 /= blockCount; strength = param->rc.aqStrength * avg_adj; avg_adj = avg_adj - 0.5f * (avg_adj_pow2 - modeTwoConst) / avg_adj; - bias_strength = param->rc.aqStrength; + bias_strength = param->rc.aqBiasStrength * param->rc.aqStrength; } else strength = param->rc.aqStrength * 1.0397f; @@ -594,6 +594,17 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame *curFrame, x265_param* param) else qp_adj = strength * (qp_adj - avg_adj); } + else if (param->rc.aqMode == X265_AQ_EDGE_BIASED) + { + inclinedEdge = curFrame->m_lowres.edgeInclined[blockXY]; + qp_adj = curFrame->m_lowres.qpCuTreeOffset[blockXY]; + double dark_bias = bias_strength * (1.f - modeTwoConst / (qp_adj * qp_adj)) / 10.f; + if(inclinedEdge && (qp_adj - avg_adj > 0)) + qp_adj = ((strength + AQ_EDGE_BIAS) * (qp_adj - avg_adj)); + else + qp_adj = strength * (qp_adj - avg_adj); + qp_adj += dark_bias; + } else { uint32_t energy = acEnergyCu(curFrame, blockX, blockY, param->internalCsp, param->rc.qgSize); diff --git a/source/x265.h b/source/x265.h index b064f67c2..1b59bb2da 100644 --- a/source/x265.h +++ b/source/x265.h @@ -581,6 +581,7 @@ typedef enum #define X265_AQ_AUTO_VARIANCE 2 #define X265_AQ_AUTO_VARIANCE_BIASED 3 #define X265_AQ_EDGE 4 +#define X265_AQ_EDGE_BIASED 5 #define x265_ADAPT_RD_STRENGTH 4 #define X265_REFINE_INTER_LEVELS 3 /* NOTE! For this release only X265_CSP_I420 and X265_CSP_I444 are supported */ @@ -1405,6 +1406,9 @@ typedef struct x265_param * AQ is enabled. Default value: 1.0. Acceptable values between 0.0 and 3.0 */ double aqStrength; + /* Sets the bias towards dark scenes in AQ modes 3 and 5. */ + double aqBiasStrength; + /* Delta QP range by QP adaptation based on a psycho-visual model. * Acceptable values between 1.0 to 6.0 */ double qpAdaptationRange; diff --git a/source/x265cli.cpp b/source/x265cli.cpp index c376ca231..eb27c1e9f 100755 --- a/source/x265cli.cpp +++ b/source/x265cli.cpp @@ -279,9 +279,10 @@ namespace X265_NS { " - 0 : Disabled.\n" " - 1 : Store/Load ctu distortion to/from the file specified in analysis-save/load.\n" " Default 0 - Disabled\n"); - H0(" --aq-mode Mode for Adaptive Quantization - 0:none 1:uniform AQ 2:auto variance 3:auto variance with bias to dark scenes 4:auto variance with edge information. Default %d\n", param->rc.aqMode); + H0(" --aq-mode Mode for Adaptive Quantization - 0:none 1:uniform AQ 2:auto variance 3:auto variance with bias to dark scenes 4:auto variance with edge information 5:auto variance with edge information and bias to dark scenes. Default %d\n", param->rc.aqMode); H0(" --[no-]hevc-aq Mode for HEVC Adaptive Quantization. Default %s\n", OPT(param->rc.hevcAq)); H0(" --aq-strength Reduces blocking and blurring in flat and textured areas (0 to 3.0). Default %.2f\n", param->rc.aqStrength); + H0(" --aq-bias-strength Sets the bias to dark strength in AQ modes 3 and 5. Default %.2f\n", param->rc.aqBiasStrength); H0(" --qp-adaptation-range Delta QP range by QP adaptation based on a psycho-visual model (1.0 to 6.0). Default %.2f\n", param->rc.qpAdaptationRange); H0(" --[no-]aq-motion Block level QP adaptation based on the relative motion between the block and the frame. Default %s\n", OPT(param->bAQMotion)); H0(" --qg-size Specifies the size of the quantization group (64, 32, 16, 8). Default %d\n", param->rc.qgSize); @@ -1111,4 +1112,4 @@ namespace X265_NS { #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/source/x265cli.h b/source/x265cli.h index 13336d743..d45bbbddc 100644 --- a/source/x265cli.h +++ b/source/x265cli.h @@ -184,6 +184,7 @@ static const struct option long_options[] = { "qp", required_argument, NULL, 'q' }, { "aq-mode", required_argument, NULL, 0 }, { "aq-strength", required_argument, NULL, 0 }, + { "aq-bias-strength", required_argument, NULL, 0 }, { "rc-grain", no_argument, NULL, 0 }, { "no-rc-grain", no_argument, NULL, 0 }, { "ipratio", required_argument, NULL, 0 },