[x265] [PATCH]Add: Auto AQ Mode
Niranjan Bala
niranjan at multicorewareinc.com
Thu Feb 27 09:59:18 CET 2020
# HG changeset patch
# User Niranjan <niranjan at multicorewareinc.com>
# Date 1582622787 -19800
# Tue Feb 25 14:56:27 2020 +0530
# Node ID 2ec7d27dc576cd993fd0bfe4b116514e8f2ddc0c
# Parent 1a47c3802fa38bdb4606e3d8203b73b4474d1ce6
Add: Auto AQ Mode
This patch does the following:
1. Automatically decides the AQ Mode for each frame, using its scene
statistics,
such as luma intensity and edge density.
2. Add option "--auto-aq" to enable auto detection of AQ Mode per frame.
diff -r 1a47c3802fa3 -r 2ec7d27dc576 doc/reST/cli.rst
--- a/doc/reST/cli.rst Mon Feb 24 12:51:41 2020 +0530
+++ b/doc/reST/cli.rst Tue Feb 25 14:56:27 2020 +0530
@@ -1721,6 +1721,12 @@
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>
Adjust the strength of the adaptive quantization offsets. Setting
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/CMakeLists.txt
--- a/source/CMakeLists.txt Mon Feb 24 12:51:41 2020 +0530
+++ b/source/CMakeLists.txt Tue Feb 25 14:56:27 2020 +0530
@@ -29,7 +29,7 @@
option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF)
mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)
# X265_BUILD must be incremented each time the public API is changed
-set(X265_BUILD 189)
+set(X265_BUILD 190)
configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
"${PROJECT_BINARY_DIR}/x265.def")
configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/common/common.h
--- a/source/common/common.h Mon Feb 24 12:51:41 2020 +0530
+++ b/source/common/common.h Tue Feb 25 14:56:27 2020 +0530
@@ -131,6 +131,7 @@
typedef int64_t ssum2_t;
#define SHIFT_TO_BITPLANE 9
#define HISTOGRAM_BINS 1024
+#define BRIGHTNESS_THRESHOLD 120 // The threshold above which a pixel is
bright
#else
typedef uint8_t pixel;
typedef uint16_t sum_t;
@@ -139,6 +140,7 @@
typedef int32_t ssum2_t; // Signed sum
#define SHIFT_TO_BITPLANE 7
#define HISTOGRAM_BINS 256
+#define BRIGHTNESS_THRESHOLD 30 // The threshold above which a pixel is
bright
#endif // if HIGH_BIT_DEPTH
#if X265_DEPTH < 10
@@ -163,6 +165,8 @@
#define MIN_QPSCALE 0.21249999999999999
#define MAX_MAX_QPSCALE 615.46574234477100
+#define FRAME_BRIGHTNESS_THRESHOLD 50.0 // Min % of pixels in a frame,
that are above BRIGHTNESS_THRESHOLD for it to be considered a bright frame
+#define FRAME_EDGE_THRESHOLD 10.0 // Min % of edge pixels in a frame, for
it to be considered to have high edge density
template<typename T>
inline T x265_min(T a, T b) { return a < b ? a : b; }
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/common/frame.cpp
--- a/source/common/frame.cpp Mon Feb 24 12:51:41 2020 +0530
+++ b/source/common/frame.cpp Tue Feb 25 14:56:27 2020 +0530
@@ -63,6 +63,7 @@
m_thetaPic = NULL;
m_edgeBitPlane = NULL;
m_edgeBitPic = NULL;
+ m_frameAq = X265_AQ_NONE;
}
bool Frame::create(x265_param *param, float* quantOffsets)
@@ -103,7 +104,7 @@
CHECKED_MALLOC_ZERO(m_classifyCount, uint32_t, size);
}
- if (param->rc.aqMode == X265_AQ_EDGE || param->rc.aqMode ==
X265_AQ_EDGE_BIASED || (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;
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/common/frame.h
--- a/source/common/frame.h Mon Feb 24 12:51:41 2020 +0530
+++ b/source/common/frame.h Tue Feb 25 14:56:27 2020 +0530
@@ -141,6 +141,9 @@
pixel* m_edgeBitPlane;
pixel* m_edgeBitPic;
+ /* AQ mode for each frame */
+ int m_frameAq;
+
Frame();
bool create(x265_param *param, float* quantOffsets);
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/common/lowres.cpp
--- a/source/common/lowres.cpp Mon Feb 24 12:51:41 2020 +0530
+++ b/source/common/lowres.cpp Tue Feb 25 14:56:27 2020 +0530
@@ -190,6 +190,9 @@
}
}
+ if (param->rc.bAutoAq)
+ lowresEdgePlane = X265_MALLOC(pixel, lumaStride * (lines +
(origPic->m_lumaMarginY * 2)));
+
return true;
fail:
@@ -235,6 +238,7 @@
X265_FREE(edgeInclined);
X265_FREE(qpAqMotionOffset);
X265_FREE(blockVariance);
+ X265_FREE(lowresEdgePlane);
if (maxAQDepth > 0)
{
for (uint32_t d = 0; d < 4; d++)
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/common/lowres.h
--- a/source/common/lowres.h Mon Feb 24 12:51:41 2020 +0530
+++ b/source/common/lowres.h Tue Feb 25 14:56:27 2020 +0530
@@ -44,6 +44,9 @@
pixel* fpelLowerResPlane[3];
pixel* lowerResPlane[4];
+ /* Edge Plane in Lowres */
+ pixel* lowresEdgePlane;
+
bool isWeighted;
bool isLowres;
bool isHMELowres;
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/common/param.cpp
--- a/source/common/param.cpp Mon Feb 24 12:51:41 2020 +0530
+++ b/source/common/param.cpp Tue Feb 25 14:56:27 2020 +0530
@@ -286,6 +286,7 @@
param->rc.bEnableConstVbv = 0;
param->bResetZoneConfig = 1;
param->reconfigWindowSize = 0;
+ param->rc.bAutoAq = 0;
/* Video Usability Information (VUI) */
param->vui.aspectRatioIdc = 0;
@@ -1229,6 +1230,7 @@
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")
{
@@ -1877,9 +1879,12 @@
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");
@@ -2187,6 +2192,7 @@
s += sprintf(s, " hist-threshold=%.2f", p->edgeTransitionThreshold);
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");
@@ -2468,6 +2474,7 @@
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 -r 1a47c3802fa3 -r 2ec7d27dc576 source/encoder/slicetype.cpp
--- a/source/encoder/slicetype.cpp Mon Feb 24 12:51:41 2020 +0530
+++ b/source/encoder/slicetype.cpp Tue Feb 25 14:56:27 2020 +0530
@@ -473,9 +473,9 @@
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)
{
@@ -516,18 +516,18 @@
double bias_strength = 0.f;
double strength = 0.f;
- if (param->rc.aqMode == X265_AQ_EDGE || param->rc.aqMode
== X265_AQ_EDGE_BIASED)
+ 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->bHistBasedSceneCut && param->enableRecursionSkip >=
EDGE_BASED_RSKIP)
+ if (curFrame->m_frameAq == X265_AQ_EDGE &&
!param->bHistBasedSceneCut && param->enableRecursionSkip >=
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 || param->rc.aqMode
== X265_AQ_EDGE_BIASED)
+ 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)
@@ -536,7 +536,7 @@
{
uint32_t energy, edgeDensity, avgAngle;
energy = acEnergyCu(curFrame, blockX, blockY,
param->internalCsp, param->rc.qgSize);
- if (param->rc.aqMode == X265_AQ_EDGE ||
param->rc.aqMode == X265_AQ_EDGE_BIASED)
+ 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)
@@ -576,17 +576,17 @@
{
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];
@@ -595,7 +595,7 @@
else
qp_adj = strength * (qp_adj - avg_adj);
}
- else if (param->rc.aqMode == X265_AQ_EDGE_BIASED)
+ else if (curFrame->m_frameAq ==
X265_AQ_EDGE_BIASED)
{
inclinedEdge =
curFrame->m_lowres.edgeInclined[blockXY];
qp_adj =
curFrame->m_lowres.qpCuTreeOffset[blockXY];
@@ -1387,6 +1387,44 @@
}
}
+double computeBrightnessIntensity(pixel *inPlane, int width, int height,
intptr_t stride)
+{
+ pixel* rowStart = inPlane;
+ double 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;
+ double 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;
+}
+
void PreLookaheadGroup::processTasks(int workerThreadID)
{
if (workerThreadID < 0)
@@ -1401,6 +1439,36 @@
ProfileScopeEvent(prelookahead);
m_lock.release();
preFrame->m_lowres.init(preFrame->m_fencPic, preFrame->m_poc);
+
+ /* Auto AQ */
+ if (preFrame->m_param->rc.bAutoAq)
+ {
+ 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_EDGE_BIASED;
+ }
+ else
+ preFrame->m_frameAq = preFrame->m_param->rc.aqMode;
+
if (m_lookahead.m_bAdaptiveQuant)
tld.calcAdaptiveQuantFrame(preFrame, m_lookahead.m_param);
tld.lowresIntraEstimate(preFrame->m_lowres,
m_lookahead.m_param->rc.qgSize);
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/test/regression-tests.txt
--- a/source/test/regression-tests.txt Mon Feb 24 12:51:41 2020 +0530
+++ b/source/test/regression-tests.txt Tue Feb 25 14:56:27 2020 +0530
@@ -171,6 +171,7 @@
crowd_run_1080p50.yuv, --preset slow --ctu 32 --rskip 3
--rskip-edge-threshold 5 --hist-scenecut --hist-threshold 0.1
crowd_run_1080p50.yuv, --preset slower --ctu 16 --rskip 3
--rskip-edge-threshold 5 --hist-scenecut --hist-threshold 0.1 --aq-mode 4
sintel_trailer_2k_1920x1080_24.yuv, --preset medium --aq-mode 5
+ducks_take_off_420_720p50.y4m, --preset slow --auto-aq --aq-strength 1.5
--aq-motion
# Main12 intraCost overflow bug test
720p50_parkrun_ter.y4m,--preset medium
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/x265.h
--- a/source/x265.h Mon Feb 24 12:51:41 2020 +0530
+++ b/source/x265.h Tue Feb 25 14:56:27 2020 +0530
@@ -1477,6 +1477,9 @@
/* internally enable if tune grain is set */
int bEnableConstVbv;
+ /* automatically switch AQ mode for each frame */
+ int bAutoAq;
+
} rc;
/*== Video Usability Information ==*/
diff -r 1a47c3802fa3 -r 2ec7d27dc576 source/x265cli.h
--- a/source/x265cli.h Mon Feb 24 12:51:41 2020 +0530
+++ b/source/x265cli.h Tue Feb 25 14:56:27 2020 +0530
@@ -172,6 +172,8 @@
{ "qp", required_argument, NULL, 'q' },
{ "aq-mode", required_argument, NULL, 0 },
{ "aq-strength", required_argument, NULL, 0 },
+ { "auto-aq", no_argument, NULL, 0 },
+ { "no-auto-aq", no_argument, NULL, 0 },
{ "rc-grain", no_argument, NULL, 0 },
{ "no-rc-grain", no_argument, NULL, 0 },
{ "ipratio", required_argument, NULL, 0 },
@@ -594,6 +596,7 @@
H0(" --aq-strength <float> Reduces blocking and blurring in
flat and textured areas (0 to 3.0). Default %.2f\n", param->rc.aqStrength);
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));
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));
H0(" --[no-]rc-grain Enable ratecontrol mode to handle
grains specifically. turned on with tune grain. Default %s\n",
OPT(param->rc.bEnableGrain));
Thanks & Regards
*Niranjan Kumar B*
Video Codec Engineer
Media & AI Analytics
+91 958 511 1449
<https://multicorewareinc.com/>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20200227/9e2b3802/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: AutoAQ.patch
Type: application/octet-stream
Size: 18398 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20200227/9e2b3802/attachment-0001.obj>
More information about the x265-devel
mailing list