Last active
January 14, 2023 10:11
-
-
Save Nomis101/9953a2feb69e9f4b96a73fabca2d5ff8 to your computer and use it in GitHub Desktop.
x265 patch to add an auto-AQ mode, AQ 5 (bias towards dark scenes) and option to adjust the strength of dark scene bias
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst | |
index f1d9fa36c..dd2511ffa 100755 | |
--- a/doc/reST/cli.rst | |
+++ b/doc/reST/cli.rst | |
@@ -1733,7 +1733,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 | |
@@ -1741,13 +1741,21 @@ Quality, rate control and rate distortion options | |
the tendency of the encoder to spend too many bits on complex areas | |
and not enough in flat areas. | |
- 0. disabled | |
- 1. AQ enabled | |
- 2. AQ enabled with auto-variance **(default)** | |
+ 0. disabled. | |
+ 1. Uniform AQ. | |
+ 2. AQ enabled with auto-variance **(default)**. | |
3. AQ enabled with auto-variance and bias to dark scenes. This is | |
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. Same as AQ mode 3, but uses edge density instead of auto-variance. | |
+ i.e, AQ with bias towards dark scenes which have high edge density. | |
+ | |
+.. option:: --auto-aq --no-auto-aq | |
+ | |
+ To enable and disable automatic AQ mode detection per frame. | |
+ This option adaptively sets the AQ mode for each frame between 2, 3, 4 and 5 based on the scene statistics. | |
+ Default: disabled. | |
.. option:: --aq-strength <float> | |
@@ -1759,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 <float> | |
+ | |
+ 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:: --sbrc --no-sbrc | |
To enable and disable segment based rate control.Segment duration depends on the | |
@@ -2428,7 +2443,7 @@ VUI fields must be manually specified. | |
.. option:: --hdr10-opt, --no-hdr10-opt | |
Enable block-level luma and chroma QP optimization for HDR10 content | |
- as suggested in ITU-T H-series Recommendations ñ Supplement 15. | |
+ as suggested in ITU-T H-series Recommendations Supplement 15. | |
Source video should have HDR10 characteristics such as 10-bit depth 4:2:0 | |
with Bt.2020 color primaries and SMPTE ST.2084 transfer characteristics. | |
It is recommended that AQ-mode be enabled along with this feature. Default disabled. | |
diff --git a/source/common/common.h b/source/common/common.h | |
index 37c19ae72..7212f3496 100644 | |
--- a/source/common/common.h | |
+++ b/source/common/common.h | |
@@ -130,6 +130,7 @@ typedef uint64_t sum2_t; | |
typedef uint64_t pixel4; | |
typedef int64_t ssum2_t; | |
#define SHIFT_TO_BITPLANE 9 | |
+#define BRIGHTNESS_THRESHOLD 120 // The threshold above which a pixel is bright | |
#else | |
typedef uint8_t pixel; | |
typedef uint16_t sum_t; | |
@@ -137,6 +138,7 @@ typedef uint32_t sum2_t; | |
typedef uint32_t pixel4; | |
typedef int32_t ssum2_t; // Signed sum | |
#define SHIFT_TO_BITPLANE 7 | |
+#define BRIGHTNESS_THRESHOLD 30 // The threshold above which a pixel is bright | |
#endif // if HIGH_BIT_DEPTH | |
#if X265_DEPTH < 10 | |
diff --git a/source/common/frame.cpp b/source/common/frame.cpp | |
index ae3773e83..dedcb9c84 100644 | |
--- a/source/common/frame.cpp | |
+++ b/source/common/frame.cpp | |
@@ -64,6 +64,7 @@ Frame::Frame() | |
m_edgeBitPlane = NULL; | |
m_edgeBitPic = NULL; | |
m_isInsideWindow = 0; | |
+ m_frameAq = X265_AQ_NONE; | |
// mcstf | |
m_isSubSampled = NULL; | |
@@ -132,7 +133,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.bAutoAq || (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; | |
@@ -342,7 +343,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.bAutoAq || (m_param->rc.zonefileCount && m_param->rc.aqMode != 0)) | |
{ | |
X265_FREE(m_edgePic); | |
X265_FREE(m_gaussianPic); | |
diff --git a/source/common/frame.h b/source/common/frame.h | |
index 9a3a3a4f9..93635b7e5 100644 | |
--- a/source/common/frame.h | |
+++ b/source/common/frame.h | |
@@ -153,7 +153,10 @@ public: | |
/* edge bit plane for rskips 2 and 3 */ | |
pixel* m_edgeBitPlane; | |
pixel* m_edgeBitPic; | |
- | |
+ | |
+ /* AQ mode for each frame */ | |
+ int m_frameAq; | |
+ | |
int m_isInsideWindow; | |
/*Frame's temporal layer info*/ | |
diff --git a/source/common/lowres.cpp b/source/common/lowres.cpp | |
index 857414e91..ad0063997 100644 | |
--- a/source/common/lowres.cpp | |
+++ b/source/common/lowres.cpp | |
@@ -211,6 +211,9 @@ bool Lowres::create(x265_param* param, PicYuv *origPic, uint32_t qgSize) | |
CHECKED_MALLOC(lowerResMvCosts[1][i], int32_t, cuCountLowerRes); | |
} | |
} | |
+ | |
+ if (param->rc.bAutoAq) | |
+ lowresEdgePlane = X265_MALLOC(pixel, lumaStride * (lines + (origPic->m_lumaMarginY * 2))); | |
if (param->bHistBasedSceneCut) | |
{ | |
@@ -288,6 +291,7 @@ void Lowres::destroy(x265_param* param) | |
X265_FREE(invQscaleFactor8x8); | |
X265_FREE(edgeInclined); | |
X265_FREE(qpAqMotionOffset); | |
+ X265_FREE(lowresEdgePlane); | |
if (param->bDynamicRefine || param->bEnableFades) | |
X265_FREE(blockVariance); | |
if (maxAQDepth > 0) | |
diff --git a/source/common/lowres.h b/source/common/lowres.h | |
index c727f488a..4f6399e35 100644 | |
--- a/source/common/lowres.h | |
+++ b/source/common/lowres.h | |
@@ -48,6 +48,9 @@ struct ReferencePlanes | |
pixel* fpelLowerResPlane[3]; | |
pixel* lowerResPlane[4]; | |
+ /* Edge Plane in Lowres */ | |
+ pixel* lowresEdgePlane; | |
+ | |
bool isWeighted; | |
bool isLowres; | |
bool isHMELowres; | |
diff --git a/source/common/param.cpp b/source/common/param.cpp | |
index fe85d8726..d5ac6e751 100755 | |
--- a/source/common/param.cpp | |
+++ b/source/common/param.cpp | |
@@ -281,6 +281,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; | |
@@ -304,6 +305,7 @@ void x265_param_default(x265_param* param) | |
param->rc.bEnableConstVbv = 0; | |
param->bResetZoneConfig = 1; | |
param->reconfigWindowSize = 0; | |
+ param->rc.bAutoAq = 0; | |
param->decoderVbvMaxRate = 0; | |
param->bliveVBV2pass = 0; | |
@@ -952,6 +954,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); | |
@@ -1253,6 +1256,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); | |
@@ -1424,6 +1428,7 @@ int x265_param_parse(x265_param* p, const char* name, const char* value) | |
OPT("multi-pass-opt-analysis") p->analysisMultiPassRefine = atobool(value); | |
OPT("multi-pass-opt-distortion") p->analysisMultiPassDistortion = atobool(value); | |
OPT("aq-motion") p->bAQMotion = atobool(value); | |
+ OPT("auto-aq") p->rc.bAutoAq = atobool(value); | |
OPT("dynamic-rd") p->dynamicRd = atof(value); | |
OPT("analysis-reuse-level") | |
{ | |
@@ -1821,7 +1826,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 || param->rc.aqMode > X265_AQ_EDGE_BIASED, | |
"Aq-Mode is out of range"); | |
CHECK(param->rc.aqStrength < 0 || param->rc.aqStrength > 3, | |
"Aq-Strength is out of range"); | |
@@ -2123,9 +2128,11 @@ void x265_print_params(x265_param* param) | |
param->maxNumReferences, (param->limitReferences & X265_REF_LIMIT_CU) ? "on" : "off", | |
(param->limitReferences & X265_REF_LIMIT_DEPTH) ? "on" : "off"); | |
- if (param->rc.aqMode) | |
+ if (param->rc.aqMode && !param->rc.bAutoAq) | |
x265_log(param, X265_LOG_INFO, "AQ: mode / str / qg-size / cu-tree : %d / %0.1f / %d / %d\n", param->rc.aqMode, | |
param->rc.aqStrength, param->rc.qgSize, param->rc.cuTree); | |
+ else if (param->rc.bAutoAq) | |
+ x265_log(param, X265_LOG_INFO, "AQ: mode / str / qg-size / cu-tree : auto / %0.1f / %d / %d\n", param->rc.aqStrength, param->rc.qgSize, param->rc.cuTree); | |
if (param->bLossless) | |
x265_log(param, X265_LOG_INFO, "Rate Control : Lossless\n"); | |
@@ -2381,6 +2388,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) | |
@@ -2436,6 +2444,7 @@ char *x265_param2string(x265_param* p, int padx, int pady) | |
s += sprintf(s, " scenecut-bias=%.2f", p->scenecutBias); | |
BOOL(p->bOptCUDeltaQP, "opt-cu-delta-qp"); | |
BOOL(p->bAQMotion, "aq-motion"); | |
+ BOOL(p->rc.bAutoAq, "auto-aq"); | |
BOOL(p->bEmitHDR10SEI, "hdr10"); | |
BOOL(p->bHDR10Opt, "hdr10-opt"); | |
BOOL(p->bDhdr10opt, "dhdr10-opt"); | |
@@ -2674,6 +2683,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; | |
@@ -2731,6 +2741,7 @@ void x265_copy_params(x265_param* dst, x265_param* src) | |
dst->rc.bEnableConstVbv = src->rc.bEnableConstVbv; | |
dst->rc.hevcAq = src->rc.hevcAq; | |
dst->rc.qpAdaptationRange = src->rc.qpAdaptationRange; | |
+ dst->rc.bAutoAq = src->rc.bAutoAq; | |
dst->vui.aspectRatioIdc = src->vui.aspectRatioIdc; | |
dst->vui.sarWidth = src->vui.sarWidth; | |
diff --git a/source/encoder/frameencoder.cpp b/source/encoder/frameencoder.cpp | |
index 659b87c89..85791952b 100644 | |
--- a/source/encoder/frameencoder.cpp | |
+++ b/source/encoder/frameencoder.cpp | |
@@ -471,7 +471,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->rc.aqMode != X265_AQ_EDGE && m_param->recursionSkipMode == EDGE_BASED_RSKIP) | |
+ if (m_param->rc.aqMode != X265_AQ_EDGE && m_param->rc.aqMode != X265_AQ_EDGE_BIASED && 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 453ca5b40..e9861359c 100644 | |
--- a/source/encoder/slicetype.cpp | |
+++ b/source/encoder/slicetype.cpp | |
@@ -237,6 +237,44 @@ inline void findAvgAngle(const pixel* block, intptr_t stride, uint32_t size, uin | |
angle = sum / (size*size); | |
} | |
+double computeBrightnessIntensity(pixel *inPlane, int width, int height, intptr_t stride) | |
+{ | |
+ pixel* rowStart = inPlane; | |
+ int count = 0; | |
+ | |
+ for (int i = 0; i < height; i++) | |
+ { | |
+ for (int j = 0; j < width; j++) | |
+ { | |
+ if (rowStart[j] > BRIGHTNESS_THRESHOLD) | |
+ count++; | |
+ } | |
+ rowStart += stride; | |
+ } | |
+ | |
+ /* Returns the brightness percentage of the input plane */ | |
+ return (count / (width * height)) * 100; | |
+} | |
+ | |
+double computeEdgeIntensity(pixel *inPlane, int width, int height, intptr_t stride) | |
+{ | |
+ pixel* rowStart = inPlane; | |
+ int count = 0; | |
+ | |
+ for (int i = 0; i < height; i++) | |
+ { | |
+ for (int j = 0; j < width; j++) | |
+ { | |
+ if (rowStart[j] > 0) | |
+ count++; | |
+ } | |
+ rowStart += stride; | |
+ } | |
+ | |
+ /* Returns the edge percentage of the input plane */ | |
+ return (count / (width * height)) * 100; | |
+} | |
+ | |
uint32_t LookaheadTLD::edgeDensityCu(Frame* curFrame, uint32_t &avgAngle, uint32_t blockX, uint32_t blockY, uint32_t qgSize) | |
{ | |
pixel *edgeImage = curFrame->m_edgePic + curFrame->m_fencPic->m_lumaMarginY * curFrame->m_fencPic->m_stride + curFrame->m_fencPic->m_lumaMarginX; | |
@@ -481,9 +519,9 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame *curFrame, x265_param* param) | |
if (!(param->rc.bStatRead && param->rc.cuTree && IS_REFERENCED(curFrame))) | |
{ | |
/* Calculate Qp offset for each 16x16 or 8x8 block in the frame */ | |
- if (param->rc.aqMode == X265_AQ_NONE || param->rc.aqStrength == 0) | |
+ if (curFrame->m_frameAq == X265_AQ_NONE || param->rc.aqStrength == 0) | |
{ | |
- if (param->rc.aqMode && param->rc.aqStrength == 0) | |
+ if (curFrame->m_frameAq && param->rc.aqStrength == 0) | |
{ | |
if (quantOffsets) | |
{ | |
@@ -524,17 +562,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 (curFrame->m_frameAq == X265_AQ_EDGE || curFrame->m_frameAq == X265_AQ_EDGE_BIASED) | |
edgeFilter(curFrame, param); | |
- if (param->rc.aqMode == X265_AQ_EDGE && param->recursionSkipMode == EDGE_BASED_RSKIP) | |
+ if (curFrame->m_frameAq == X265_AQ_EDGE && 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 (curFrame->m_frameAq == X265_AQ_AUTO_VARIANCE || curFrame->m_frameAq == X265_AQ_AUTO_VARIANCE_BIASED || curFrame->m_frameAq == X265_AQ_EDGE || curFrame->m_frameAq == X265_AQ_EDGE_BIASED) | |
{ | |
double bit_depth_correction = 1.f / (1 << (2 * (X265_DEPTH - 8))); | |
for (int blockY = 0; blockY < maxRow; blockY += loopIncr) | |
@@ -543,7 +581,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 (curFrame->m_frameAq == X265_AQ_EDGE || curFrame->m_frameAq == X265_AQ_EDGE_BIASED) | |
{ | |
edgeDensity = edgeDensityCu(curFrame, avgAngle, blockX, blockY, param->rc.qgSize); | |
if (edgeDensity) | |
@@ -573,7 +611,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; | |
@@ -583,17 +621,17 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame *curFrame, x265_param* param) | |
{ | |
for (int blockX = 0; blockX < maxCol; blockX += loopIncr) | |
{ | |
- if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED) | |
+ if (curFrame->m_frameAq == X265_AQ_AUTO_VARIANCE_BIASED) | |
{ | |
qp_adj = curFrame->m_lowres.qpCuTreeOffset[blockXY]; | |
qp_adj = strength * (qp_adj - avg_adj) + bias_strength * (1.f - modeTwoConst / (qp_adj * qp_adj)); | |
} | |
- else if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE) | |
+ else if (curFrame->m_frameAq == X265_AQ_AUTO_VARIANCE) | |
{ | |
qp_adj = curFrame->m_lowres.qpCuTreeOffset[blockXY]; | |
qp_adj = strength * (qp_adj - avg_adj); | |
} | |
- else if (param->rc.aqMode == X265_AQ_EDGE) | |
+ else if (curFrame->m_frameAq == X265_AQ_EDGE) | |
{ | |
inclinedEdge = curFrame->m_lowres.edgeInclined[blockXY]; | |
qp_adj = curFrame->m_lowres.qpCuTreeOffset[blockXY]; | |
@@ -602,6 +640,15 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame *curFrame, x265_param* param) | |
else | |
qp_adj = strength * (qp_adj - avg_adj); | |
} | |
+ else if (curFrame->m_frameAq == X265_AQ_EDGE_BIASED) | |
+ { | |
+ inclinedEdge = curFrame->m_lowres.edgeInclined[blockXY]; | |
+ qp_adj = curFrame->m_lowres.qpCuTreeOffset[blockXY]; | |
+ if (inclinedEdge && (qp_adj - avg_adj > 0)) | |
+ qp_adj = ((strength + AQ_EDGE_BIAS) * (qp_adj - avg_adj)) + bias_strength * (1.f - modeTwoConst / (qp_adj * qp_adj)); | |
+ else | |
+ qp_adj = strength * (qp_adj - avg_adj) + bias_strength * (1.f - modeTwoConst / (qp_adj * qp_adj)); | |
+ } | |
else | |
{ | |
uint32_t energy = acEnergyCu(curFrame, blockX, blockY, param->internalCsp, param->rc.qgSize); | |
@@ -1712,6 +1759,34 @@ void LookaheadTLD::collectPictureStatistics(Frame *curFrame) | |
curFrame->m_lowres.bHistScenecutAnalyzed = false; | |
} | |
+void LookaheadTLD::calcFrameSegment(Frame *preFrame) | |
+{ | |
+ int heightL = preFrame->m_lowres.lines; | |
+ int widthL = preFrame->m_lowres.width; | |
+ pixel *lumaPlane = preFrame->m_lowres.fpelPlane[0]; | |
+ intptr_t stride = preFrame->m_lowres.lumaStride; | |
+ double brightnessIntensity = 0, edgeIntensity = 0; | |
+ | |
+ /* Edge plane computation */ | |
+ memset(preFrame->m_lowres.lowresEdgePlane, 0, stride * (heightL + (preFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel)); | |
+ pixel* lowresEdgePic = preFrame->m_lowres.lowresEdgePlane + preFrame->m_fencPic->m_lumaMarginY * stride + preFrame->m_fencPic->m_lumaMarginX; | |
+ computeEdge(lowresEdgePic, lumaPlane, NULL, stride, heightL, widthL, false); | |
+ | |
+ /*Frame edge percentage computation */ | |
+ edgeIntensity = computeEdgeIntensity(lowresEdgePic, widthL, heightL, stride); | |
+ | |
+ /* Frame Brightness percentage computation */ | |
+ brightnessIntensity = computeBrightnessIntensity(lumaPlane, widthL, heightL, stride); | |
+ | |
+ /* AQ mode switch */ | |
+ if (edgeIntensity < FRAME_EDGE_THRESHOLD) | |
+ preFrame->m_frameAq = brightnessIntensity > FRAME_BRIGHTNESS_THRESHOLD ? X265_AQ_AUTO_VARIANCE : X265_AQ_AUTO_VARIANCE_BIASED; | |
+ else | |
+ preFrame->m_frameAq = brightnessIntensity > FRAME_BRIGHTNESS_THRESHOLD ? X265_AQ_EDGE : X265_AQ_VARIANCE; | |
+ | |
+ preFrame->m_param->rc.aqMode = preFrame->m_frameAq; | |
+} | |
+ | |
void PreLookaheadGroup::processTasks(int workerThreadID) | |
{ | |
if (workerThreadID < 0) | |
@@ -1726,6 +1801,11 @@ void PreLookaheadGroup::processTasks(int workerThreadID) | |
ProfileScopeEvent(prelookahead); | |
m_lock.release(); | |
preFrame->m_lowres.init(preFrame->m_fencPic, preFrame->m_poc); | |
+ | |
+ /* Auto AQ */ | |
+ if (preFrame->m_param->rc.bAutoAq) | |
+ tld.calcFrameSegment(preFrame); | |
+ | |
if (m_lookahead.m_bAdaptiveQuant) | |
tld.calcAdaptiveQuantFrame(preFrame, m_lookahead.m_param); | |
diff --git a/source/x265.h b/source/x265.h | |
index 9f3abd9d9..7aeb05450 100644 | |
--- a/source/x265.h | |
+++ b/source/x265.h | |
@@ -583,6 +583,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 */ | |
@@ -1677,6 +1678,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; | |
@@ -1761,6 +1765,9 @@ typedef struct x265_param | |
/* internally enable if tune grain is set */ | |
int bEnableConstVbv; | |
+ /* automatically switch AQ mode for each frame */ | |
+ int bAutoAq; | |
+ | |
/* if only the focused frames would be re-encode or not */ | |
int bEncFocusedFramesOnly; | |
diff --git a/source/x265cli.cpp b/source/x265cli.cpp | |
index 4d10076c0..9523ed238 100755 | |
--- a/source/x265cli.cpp | |
+++ b/source/x265cli.cpp | |
@@ -257,11 +257,20 @@ 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 <integer> 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 <integer> Mode for Adaptive Quantization.\n" | |
+ " - 0 : none.\n" | |
+ " - 1 : uniform AQ.\n" | |
+ " - 2 : auto variance.\n" | |
+ " - 3 : auto variance with bias to dark scenes.\n" | |
+ " - 4 : auto variance with edge density.\n" | |
+ " - 5 : auto variance with edge density and bias towards dark scenes.\n" | |
+ " 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 <float> Reduces blocking and blurring in flat and textured areas (0 to 3.0). Default %.2f\n", param->rc.aqStrength); | |
+ H0(" --aq-bias-strength <float> Sets the bias to dark strength in AQ modes 3 and 5. Default %.2f\n", param->rc.aqBiasStrength); | |
H0(" --qp-adaptation-range <float> 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)); | |
+ H1(" --[no-]auto-aq Automatically decides the AQ Mode for each frame, using its scene statistics, such as luma intensity and edge density. Default %s\n", OPT(param->rc.bAutoAq)); | |
H1(" --[no-]sbrc Enables the segment based rate control. Default %s\n", OPT(param->bEnableSBRC)); | |
H0(" --qg-size <int> Specifies the size of the quantization group (64, 32, 16, 8). Default %d\n", param->rc.qgSize); | |
H0(" --[no-]cutree Enable cutree for Adaptive Quantization. Default %s\n", OPT(param->rc.cuTree)); | |
@@ -1223,4 +1232,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 d5b816e80..e594dc34d 100644 | |
--- a/source/x265cli.h | |
+++ b/source/x265cli.h | |
@@ -180,7 +180,10 @@ static const struct option long_options[] = | |
{ "bitrate", required_argument, NULL, 0 }, | |
{ "qp", required_argument, NULL, 'q' }, | |
{ "aq-mode", required_argument, NULL, 0 }, | |
+ { "auto-aq", no_argument, NULL, 0 }, | |
+ { "no-auto-aq", no_argument, NULL, 0 }, | |
{ "aq-strength", required_argument, NULL, 0 }, | |
+ { "aq-bias-strength", required_argument, NULL, 0 }, | |
{ "sbrc", no_argument, NULL, 0 }, | |
{ "no-sbrc", no_argument, NULL, 0 }, | |
{ "rc-grain", no_argument, NULL, 0 }, |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment