[x265] [x265 patch] New AQ mode with Variance and Edge information
Akil
akil at multicorewareinc.com
Fri Jul 12 14:12:11 CEST 2019
# HG changeset patch
# User Akil Ayyappan<akil at multicorewareinc.com>
# Date 1562928744 -19800
# Fri Jul 12 16:22:24 2019 +0530
# Node ID 08666ebfcb13887b914b5b245ead42c3c755c759
# Parent 147fb92c5ed50365a69959e6832b1d8368f0ea87
AQ: New AQ mode with Variance and Edge information
diff -r 147fb92c5ed5 -r 08666ebfcb13 doc/reST/cli.rst
--- a/doc/reST/cli.rst Mon Jul 08 10:39:27 2019 +0530
+++ b/doc/reST/cli.rst Fri Jul 12 16:22:24 2019 +0530
@@ -1,3 +1,4 @@
+
*********************
Command Line Options
*********************
@@ -1645,7 +1646,7 @@
ignored. Slower presets will generally achieve better compression
efficiency (and generate smaller bitstreams). Default disabled.
-.. option:: --aq-mode <0|1|2|3>
+.. option:: --aq-mode <0|1|2|3|4>
Adaptive Quantization operating mode. Raise or lower per-block
quantization based on complexity analysis of the source image. The
@@ -1659,6 +1660,7 @@
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.
.. option:: --aq-strength <float>
diff -r 147fb92c5ed5 -r 08666ebfcb13 source/CMakeLists.txt
--- a/source/CMakeLists.txt Mon Jul 08 10:39:27 2019 +0530
+++ b/source/CMakeLists.txt Fri Jul 12 16:22:24 2019 +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 177)
+set(X265_BUILD 178)
configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
"${PROJECT_BINARY_DIR}/x265.def")
configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff -r 147fb92c5ed5 -r 08666ebfcb13 source/common/lowres.cpp
--- a/source/common/lowres.cpp Mon Jul 08 10:39:27 2019 +0530
+++ b/source/common/lowres.cpp Fri Jul 12 16:22:24 2019 +0530
@@ -80,6 +80,7 @@
CHECKED_MALLOC_ZERO(qpCuTreeOffset, double, cuCountFullRes);
if (qgSize == 8)
CHECKED_MALLOC_ZERO(invQscaleFactor8x8, int, cuCount);
+ CHECKED_MALLOC_ZERO(edgeInclined, int, cuCountFullRes);
}
if (origPic->m_param->bAQMotion)
@@ -231,6 +232,7 @@
X265_FREE(qpCuTreeOffset);
X265_FREE(propagateCost);
X265_FREE(invQscaleFactor8x8);
+ X265_FREE(edgeInclined);
X265_FREE(qpAqMotionOffset);
X265_FREE(blockVariance);
if (maxAQDepth > 0)
diff -r 147fb92c5ed5 -r 08666ebfcb13 source/common/lowres.h
--- a/source/common/lowres.h Mon Jul 08 10:39:27 2019 +0530
+++ b/source/common/lowres.h Fri Jul 12 16:22:24 2019 +0530
@@ -220,6 +220,8 @@
uint64_t wp_ssd[3]; // This is different than SSDY, this is
sum(pixel^2) - sum(pixel)^2 for entire frame
uint64_t wp_sum[3];
double frameVariance;
+ int* edgeInclined;
+
/* cutree intermediate data */
PicQPAdaptationLayer* pAQLayer;
diff -r 147fb92c5ed5 -r 08666ebfcb13 source/common/param.cpp
--- a/source/common/param.cpp Mon Jul 08 10:39:27 2019 +0530
+++ b/source/common/param.cpp Fri Jul 12 16:22:24 2019 +0530
@@ -1546,7 +1546,7 @@
"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_AUTO_VARIANCE_BIASED
< param->rc.aqMode,
+ CHECK(param->rc.aqMode < X265_AQ_NONE || X265_AQ_EDGE <
param->rc.aqMode,
"Aq-Mode is out of range");
CHECK(param->rc.aqStrength < 0 || param->rc.aqStrength > 3,
"Aq-Strength is out of range");
diff -r 147fb92c5ed5 -r 08666ebfcb13 source/encoder/slicetype.cpp
--- a/source/encoder/slicetype.cpp Mon Jul 08 10:39:27 2019 +0530
+++ b/source/encoder/slicetype.cpp Fri Jul 12 16:22:24 2019 +0530
@@ -85,6 +85,129 @@
} // end anonymous namespace
+void edgeFilter(Frame *curFrame, pixel *pic1, pixel *pic2, pixel *pic3,
intptr_t stride, int height, int width)
+{
+ pixel *src = (pixel*)curFrame->m_fencPic->m_picOrg[0];
+ pixel *edgePic = pic1 + curFrame->m_fencPic->m_lumaMarginY * stride +
curFrame->m_fencPic->m_lumaMarginX;
+ pixel *refPic = pic2 + curFrame->m_fencPic->m_lumaMarginY * stride +
curFrame->m_fencPic->m_lumaMarginX;
+ pixel *edgeTheta = pic3 + curFrame->m_fencPic->m_lumaMarginY * stride
+ curFrame->m_fencPic->m_lumaMarginX;
+
+ for (int i = 0; i < height; i++)
+ {
+ memcpy(edgePic, src, width * sizeof(pixel));
+ memcpy(refPic, src, width * sizeof(pixel));
+ src += stride;
+ edgePic += stride;
+ refPic += stride;
+ }
+
+ //Applying Gaussian filter on the picture
+ src = (pixel*)curFrame->m_fencPic->m_picOrg[0];
+ refPic = pic2 + curFrame->m_fencPic->m_lumaMarginY * stride +
curFrame->m_fencPic->m_lumaMarginX;
+ pixel pixelValue = 0;
+ for (int rowNum = 0; rowNum < height; rowNum++)
+ {
+ for (int colNum = 0; colNum < width; colNum++)
+ {
+ if ((rowNum >= 2) && (colNum >= 2) && (rowNum != height - 2)
&& (colNum != width - 2)) //Ignoring the border pixels of the picture
+ {
+ /* 5x5 Gaussian filter
+ [2 4 5 4 2]
+ 1 [4 9 12 9 4]
+ --- [5 12 15 12 5]
+ 159 [4 9 12 9 4]
+ [2 4 5 4 2]*/
+ pixelValue = ((2 * src[(rowNum - 2)*stride + (colNum - 2)]
+ 4 * src[(rowNum - 2)*stride + (colNum - 1)] + 5 * src[(rowNum - 2)*stride
+ (colNum)] + 4 * src[(rowNum - 2)*stride + (colNum + 1)] + 2 * src[(rowNum
- 2)*stride + (colNum + 2)] +
+ 4 * src[(rowNum - 1)*stride + (colNum - 2)] + 9 *
src[(rowNum - 1)*stride + (colNum - 1)] + 12 * src[(rowNum - 1)*stride +
(colNum)] + 9 * src[(rowNum - 1)*stride + (colNum + 1)] + 4 * src[(rowNum -
1)*stride + (colNum + 2)] +
+ 5 * src[(rowNum)*stride + (colNum - 2)] + 12 *
src[(rowNum)*stride + (colNum - 1)] + 15 * src[(rowNum)*stride + (colNum)]
+ 12 * src[(rowNum)*stride + (colNum + 1)] + 5 * src[(rowNum)*stride +
(colNum + 2)] +
+ 4 * src[(rowNum + 1)*stride + (colNum - 2)] + 9 *
src[(rowNum + 1)*stride + (colNum - 1)] + 12 * src[(rowNum + 1)*stride +
(colNum)] + 9 * src[(rowNum + 1)*stride + (colNum + 1)] + 4 * src[(rowNum +
1)*stride + (colNum + 2)] +
+ 2 * src[(rowNum + 2)*stride + (colNum - 2)] + 4 *
src[(rowNum + 2)*stride + (colNum - 1)] + 5 * src[(rowNum + 2)*stride +
(colNum)] + 4 * src[(rowNum + 2)*stride + (colNum + 1)] + 2 * src[(rowNum +
2)*stride + (colNum + 2)]) / 159);
+ refPic[(rowNum*stride) + colNum] = pixelValue;
+ }
+ }
+ }
+
+#if HIGH_BIT_DEPTH //10-bit build
+ float_t threshold = 1023;
+ pixel whitePixel = 1023;
+#else
+ float_t threshold = 255;
+ pixel whitePixel = 255;
+#endif
+#define PI 3.14159265
+
+ float_t gradientH = 0, gradientV = 0, radians = 0, theta = 0;
+ float_t gradientMagnitude = 0;
+ pixel blackPixel = 0;
+ edgePic = pic1 + curFrame->m_fencPic->m_lumaMarginY * stride +
curFrame->m_fencPic->m_lumaMarginX;
+ //Applying Sobel filter on the gaussian filtered picture
+ for (int rowNum = 0; rowNum < height; rowNum++)
+ {
+ for (int colNum = 0; colNum < width; colNum++)
+ {
+ edgeTheta[(rowNum*stride) + colNum] = 0;
+ if ((rowNum != 0) && (colNum != 0) && (rowNum != height - 1)
&& (colNum != width - 1)) //Ignoring the border pixels of the picture
+ {
+ /*Horizontal and vertical gradients
+ [ -3 0 3 ] [-3 -10 -3 ]
+ gH = [ -10 0 10] gV = [ 0 0 0 ]
+ [ -3 0 3 ] [ 3 10 3 ]*/
+
+ gradientH = (float_t)(-3 * refPic[(rowNum - 1)*stride +
(colNum - 1)] + 3 * refPic[(rowNum - 1)*stride + (colNum + 1)] - 10 *
refPic[rowNum*stride + (colNum - 1)] + 10 * refPic[rowNum*stride + (colNum
+ 1)] - 3 * refPic[(rowNum + 1)*stride + (colNum - 1)] + 3 * refPic[(rowNum
+ 1)*stride + (colNum + 1)]);
+ gradientV = (float_t)(-3 * refPic[(rowNum - 1)*stride +
(colNum - 1)] - 10 * refPic[(rowNum - 1)*stride + colNum] - 3 *
refPic[(rowNum - 1)*stride + (colNum + 1)] + 3 * refPic[(rowNum + 1)*stride
+ (colNum - 1)] + 10 * refPic[(rowNum + 1)*stride + colNum] + 3 *
refPic[(rowNum + 1)*stride + (colNum + 1)]);
+
+ gradientMagnitude = sqrtf(gradientH * gradientH +
gradientV * gradientV);
+ radians = atan2(gradientV, gradientH);
+ theta = (float_t)((radians * 180) / PI);
+ if (theta < 0)
+ theta = 180 + theta;
+ edgeTheta[(rowNum*stride) + colNum] = (pixel)theta;
+
+ if (gradientMagnitude >= threshold)
+ edgePic[(rowNum*stride) + colNum] = whitePixel;
+ else
+ edgePic[(rowNum*stride) + colNum] = blackPixel;
+ }
+ }
+ }
+}
+
+//Find the angle of a block by averaging the pixel angles
+inline void findAvgAngle(const pixel* block, intptr_t stride, uint32_t
size, uint32_t &angle)
+{
+ int sum = 0;
+ for (uint32_t y = 0; y < size; y++)
+ {
+ for (uint32_t x = 0; x < size; x++)
+ {
+ sum += block[x];
+ }
+ block += stride;
+ }
+ angle = sum / (size*size);
+}
+
+uint32_t LookaheadTLD::edgeDensityCu(Frame* curFrame,pixel *edgeImage,
pixel *edgeTheta, uint32_t &avgAngle, uint32_t blockX, uint32_t blockY,
uint32_t qgSize)
+{
+ intptr_t srcStride = curFrame->m_fencPic->m_stride;
+ intptr_t blockOffsetLuma = blockX + (blockY * srcStride);
+ int plane = 0; // Sobel filter is applied only on Y component
+ uint32_t var;
+
+ if (qgSize == 8)
+ {
+ findAvgAngle(edgeTheta + blockOffsetLuma, srcStride, qgSize,
avgAngle);
+ var = acEnergyVar(curFrame, primitives.cu[BLOCK_8x8].var(edgeImage
+ blockOffsetLuma, srcStride), 6, plane);
+ }
+ else
+ {
+ findAvgAngle(edgeTheta + blockOffsetLuma, srcStride, 16, avgAngle);
+ var = acEnergyVar(curFrame, primitives.cu[BLOCK_16x16].var(edgeImage
+ blockOffsetLuma, srcStride), 8, plane);
+ }
+ x265_emms();
+ return var;
+}
+
/* Find the total AC energy of each block in all planes */
uint32_t LookaheadTLD::acEnergyCu(Frame* curFrame, uint32_t blockX,
uint32_t blockY, int csp, uint32_t qgSize)
{
@@ -342,20 +465,55 @@
}
else
{
- int blockXY = 0;
+#define AQ_EDGE_BIAS 0.5
+#define EDGE_INCLINATION 45
+ uint32_t numCuInHeight = (maxRow + param->maxCUSize - 1) /
param->maxCUSize;
+ int maxHeight = numCuInHeight * param->maxCUSize;
+ intptr_t stride = curFrame->m_fencPic->m_stride;
+ pixel *edgePic = X265_MALLOC(pixel, stride * (maxHeight +
(curFrame->m_fencPic->m_lumaMarginY * 2)));
+ pixel *gaussianPic = X265_MALLOC(pixel, stride * (maxHeight +
(curFrame->m_fencPic->m_lumaMarginY * 2)));
+ pixel *thetaPic = X265_MALLOC(pixel, stride * (maxHeight +
(curFrame->m_fencPic->m_lumaMarginY * 2)));
+ memset(edgePic, 0, stride * (maxHeight +
(curFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));
+ memset(gaussianPic, 0, stride * (maxHeight +
(curFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));
+ memset(thetaPic, 0, stride * (maxHeight +
(curFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));
+ if (param->rc.aqMode == X265_AQ_EDGE)
+ edgeFilter(curFrame, edgePic, gaussianPic, thetaPic,
stride, maxRow, maxCol);
+
+ int blockXY = 0, inclinedEdge = 0;
double avg_adj_pow2 = 0, avg_adj = 0, qp_adj = 0;
double bias_strength = 0.f;
double strength = 0.f;
- if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE ||
param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED)
+ if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE ||
param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED || param->rc.aqMode ==
X265_AQ_EDGE)
{
double bit_depth_correction = 1.f / (1 << (2 * (X265_DEPTH
- 8)));
-
for (int blockY = 0; blockY < maxRow; blockY += loopIncr)
{
for (int blockX = 0; blockX < maxCol; blockX +=
loopIncr)
{
- uint32_t energy = acEnergyCu(curFrame, blockX,
blockY, param->internalCsp, param->rc.qgSize);
- qp_adj = pow(energy * bit_depth_correction + 1,
0.1);
+ uint32_t energy, edgeDensity, avgAngle;
+ energy = acEnergyCu(curFrame, blockX, blockY,
param->internalCsp, param->rc.qgSize);
+ if (param->rc.aqMode == X265_AQ_EDGE)
+ {
+ pixel *edgeImage = edgePic +
curFrame->m_fencPic->m_lumaMarginY * stride +
curFrame->m_fencPic->m_lumaMarginX;
+ pixel *edgeTheta = thetaPic +
curFrame->m_fencPic->m_lumaMarginY * stride +
curFrame->m_fencPic->m_lumaMarginX;
+ edgeDensity = edgeDensityCu(curFrame,
edgeImage, edgeTheta, avgAngle, blockX, blockY, param->rc.qgSize);
+ if (edgeDensity)
+ {
+ qp_adj = pow(edgeDensity *
bit_depth_correction + 1, 0.1);
+ //Increasing the QP of a block if its edge
orientation lies around the multiples of 45 degree
+ if ((avgAngle >= EDGE_INCLINATION - 15 &&
avgAngle <= EDGE_INCLINATION + 15) || (avgAngle >= EDGE_INCLINATION + 75 &&
avgAngle <= EDGE_INCLINATION + 105))
+
curFrame->m_lowres.edgeInclined[blockXY] = 1;
+ else
+
curFrame->m_lowres.edgeInclined[blockXY] = 0;
+ }
+ else
+ {
+ qp_adj = pow(energy * bit_depth_correction
+ 1, 0.1);
+ curFrame->m_lowres.edgeInclined[blockXY] =
0;
+ }
+ }
+ else
+ qp_adj = pow(energy * bit_depth_correction +
1, 0.1);
curFrame->m_lowres.qpCuTreeOffset[blockXY] =
qp_adj;
avg_adj += qp_adj;
avg_adj_pow2 += qp_adj * qp_adj;
@@ -371,6 +529,9 @@
else
strength = param->rc.aqStrength * 1.0397f;
+ X265_FREE(edgePic);
+ X265_FREE(gaussianPic);
+ X265_FREE(thetaPic);
blockXY = 0;
for (int blockY = 0; blockY < maxRow; blockY += loopIncr)
{
@@ -386,6 +547,15 @@
qp_adj =
curFrame->m_lowres.qpCuTreeOffset[blockXY];
qp_adj = strength * (qp_adj - avg_adj);
}
+ else if (param->rc.aqMode == X265_AQ_EDGE)
+ {
+ 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));
+ else
+ qp_adj = strength * (qp_adj - avg_adj);
+ }
else
{
uint32_t energy = acEnergyCu(curFrame, blockX,
blockY, param->internalCsp, param->rc.qgSize);
diff -r 147fb92c5ed5 -r 08666ebfcb13 source/encoder/slicetype.h
--- a/source/encoder/slicetype.h Mon Jul 08 10:39:27 2019 +0530
+++ b/source/encoder/slicetype.h Fri Jul 12 16:22:24 2019 +0530
@@ -92,6 +92,7 @@
protected:
uint32_t acEnergyCu(Frame* curFrame, uint32_t blockX, uint32_t blockY,
int csp, uint32_t qgSize);
+ uint32_t edgeDensityCu(Frame*curFrame, pixel *edgeImage, pixel
*edgeTheta, uint32_t &avgAngle, uint32_t blockX, uint32_t blockY, uint32_t
qgSize);
uint32_t lumaSumCu(Frame* curFrame, uint32_t blockX, uint32_t blockY,
uint32_t qgSize);
uint32_t weightCostLuma(Lowres& fenc, Lowres& ref, WeightParam& wp);
bool allocWeightedRef(Lowres& fenc);
diff -r 147fb92c5ed5 -r 08666ebfcb13 source/test/regression-tests.txt
--- a/source/test/regression-tests.txt Mon Jul 08 10:39:27 2019 +0530
+++ b/source/test/regression-tests.txt Fri Jul 12 16:22:24 2019 +0530
@@ -154,6 +154,7 @@
BasketballDrive_1920x1080_50.y4m, --preset medium --no-open-gop --keyint
50 --min-keyint 50 --radl 2 --vbv-maxrate 5000 --vbv-bufsize 5000
big_buck_bunny_360p24.y4m, --bitrate 500 --fades
720p50_parkrun_ter.y4m,--preset medium --bitrate 400 --hme
+ducks_take_off_420_1_720p50.y4m,--preset medium --aq-mode 4 --crf 22
--no-cutree
# Main12 intraCost overflow bug test
720p50_parkrun_ter.y4m,--preset medium
diff -r 147fb92c5ed5 -r 08666ebfcb13 source/x265.h
--- a/source/x265.h Mon Jul 08 10:39:27 2019 +0530
+++ b/source/x265.h Fri Jul 12 16:22:24 2019 +0530
@@ -561,6 +561,7 @@
#define X265_AQ_VARIANCE 1
#define X265_AQ_AUTO_VARIANCE 2
#define X265_AQ_AUTO_VARIANCE_BIASED 3
+#define X265_AQ_EDGE 4
#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 */
diff -r 147fb92c5ed5 -r 08666ebfcb13 source/x265cli.h
--- a/source/x265cli.h Mon Jul 08 10:39:27 2019 +0530
+++ b/source/x265cli.h Fri Jul 12 16:22:24 2019 +0530
@@ -554,7 +554,7 @@
" - 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. Default %d\n", param->rc.aqMode);
+ 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(" --[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(" --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);
On Thu, Jul 11, 2019 at 6:40 PM Aruna Matheswaran <
aruna at multicorewareinc.com> wrote:
> Akil, Could you please rebase the patch on latest default tip (147fb92c5ed5)
> and resend?
>
> On Wed, Jul 10, 2019 at 3:41 PM Akil <akil at multicorewareinc.com> wrote:
>
>> # HG changeset patch
>> # User Akil Ayyappan<akil at multicorewareinc.com>
>> # Date 1561035091 -19800
>> # Thu Jun 20 18:21:31 2019 +0530
>> # Node ID d25c33cc2b748401c5e908af445a0a110e26c3cf
>> # Parent 4f6dde51a5db4f9229bddb60db176f16ac98f505
>> AQ: New AQ mode with Variance and Edge information
>>
>> diff -r 4f6dde51a5db -r d25c33cc2b74 doc/reST/cli.rst
>> --- a/doc/reST/cli.rst Fri Jul 05 10:47:15 2019 +0530
>> +++ b/doc/reST/cli.rst Thu Jun 20 18:21:31 2019 +0530
>> @@ -1,3 +1,4 @@
>> +
>> *********************
>> Command Line Options
>> *********************
>> @@ -1633,7 +1634,7 @@
>> ignored. Slower presets will generally achieve better compression
>> efficiency (and generate smaller bitstreams). Default disabled.
>>
>> -.. option:: --aq-mode <0|1|2|3>
>> +.. option:: --aq-mode <0|1|2|3|4>
>>
>> Adaptive Quantization operating mode. Raise or lower per-block
>> quantization based on complexity analysis of the source image. The
>> @@ -1647,6 +1648,7 @@
>> 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.
>>
>> .. option:: --aq-strength <float>
>>
>> diff -r 4f6dde51a5db -r d25c33cc2b74 source/CMakeLists.txt
>> --- a/source/CMakeLists.txt Fri Jul 05 10:47:15 2019 +0530
>> +++ b/source/CMakeLists.txt Thu Jun 20 18:21:31 2019 +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 176)
>> +set(X265_BUILD 177)
>> configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
>> "${PROJECT_BINARY_DIR}/x265.def")
>> configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
>> diff -r 4f6dde51a5db -r d25c33cc2b74 source/common/lowres.cpp
>> --- a/source/common/lowres.cpp Fri Jul 05 10:47:15 2019 +0530
>> +++ b/source/common/lowres.cpp Thu Jun 20 18:21:31 2019 +0530
>> @@ -78,6 +78,7 @@
>> CHECKED_MALLOC_ZERO(qpCuTreeOffset, double, cuCountFullRes);
>> if (qgSize == 8)
>> CHECKED_MALLOC_ZERO(invQscaleFactor8x8, int, cuCount);
>> + CHECKED_MALLOC_ZERO(edgeInclined, int, cuCountFullRes);
>> }
>>
>> if (origPic->m_param->bAQMotion)
>> @@ -190,6 +191,7 @@
>> X265_FREE(qpCuTreeOffset);
>> X265_FREE(propagateCost);
>> X265_FREE(invQscaleFactor8x8);
>> + X265_FREE(edgeInclined);
>> X265_FREE(qpAqMotionOffset);
>> X265_FREE(blockVariance);
>> if (maxAQDepth > 0)
>> diff -r 4f6dde51a5db -r d25c33cc2b74 source/common/lowres.h
>> --- a/source/common/lowres.h Fri Jul 05 10:47:15 2019 +0530
>> +++ b/source/common/lowres.h Thu Jun 20 18:21:31 2019 +0530
>> @@ -197,6 +197,8 @@
>> uint64_t wp_ssd[3]; // This is different than SSDY, this is
>> sum(pixel^2) - sum(pixel)^2 for entire frame
>> uint64_t wp_sum[3];
>> double frameVariance;
>> + int* edgeInclined;
>> +
>>
>> /* cutree intermediate data */
>> PicQPAdaptationLayer* pAQLayer;
>> diff -r 4f6dde51a5db -r d25c33cc2b74 source/common/param.cpp
>> --- a/source/common/param.cpp Fri Jul 05 10:47:15 2019 +0530
>> +++ b/source/common/param.cpp Thu Jun 20 18:21:31 2019 +0530
>> @@ -1522,7 +1522,7 @@
>> "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_AUTO_VARIANCE_BIASED < param->rc.aqMode,
>> + CHECK(param->rc.aqMode < X265_AQ_NONE || X265_AQ_EDGE <
>> param->rc.aqMode,
>> "Aq-Mode is out of range");
>> CHECK(param->rc.aqStrength < 0 || param->rc.aqStrength > 3,
>> "Aq-Strength is out of range");
>> diff -r 4f6dde51a5db -r d25c33cc2b74 source/encoder/slicetype.cpp
>> --- a/source/encoder/slicetype.cpp Fri Jul 05 10:47:15 2019 +0530
>> +++ b/source/encoder/slicetype.cpp Thu Jun 20 18:21:31 2019 +0530
>> @@ -85,6 +85,118 @@
>>
>> } // end anonymous namespace
>>
>> +void edgeFilter(Frame *curFrame, pixel *pic1, pixel *pic2, pixel *pic3,
>> intptr_t stride, int height, int width)
>> +{
>> + pixel *src = (pixel*)curFrame->m_fencPic->m_picOrg[0];
>> + pixel *edgePic = pic1 + curFrame->m_fencPic->m_lumaMarginY * stride
>> + curFrame->m_fencPic->m_lumaMarginX;
>> + pixel *refPic = pic2 + curFrame->m_fencPic->m_lumaMarginY * stride +
>> curFrame->m_fencPic->m_lumaMarginX;
>> + pixel *edgeTheta = pic3 + curFrame->m_fencPic->m_lumaMarginY *
>> stride + curFrame->m_fencPic->m_lumaMarginX;
>> +
>> + for (int i = 0; i < height; i++)
>> + {
>> + memcpy(edgePic, src, width * sizeof(pixel));
>> + memcpy(refPic, src, width * sizeof(pixel));
>> + src += stride;
>> + edgePic += stride;
>> + refPic += stride;
>> + }
>> +
>> + //Applying Gaussian filter on the picture
>> + src = (pixel*)curFrame->m_fencPic->m_picOrg[0];
>> + refPic = pic2 + curFrame->m_fencPic->m_lumaMarginY * stride +
>> curFrame->m_fencPic->m_lumaMarginX;
>> + pixel pixelValue = 0;
>> + for (int rowNum = 0; rowNum < height; rowNum++)
>> + {
>> + for (int colNum = 0; colNum < width; colNum++)
>> + {
>> + if ((rowNum >= 2) && (colNum >= 2) && (rowNum != height - 2)
>> && (colNum != width - 2)) //Ignoring the border pixels of the picture
>> + {
>> + pixelValue = ((2 * src[(rowNum - 2)*stride + (colNum -
>> 2)] + 4 * src[(rowNum - 2)*stride + (colNum - 1)] + 5 * src[(rowNum -
>> 2)*stride + (colNum)] + 4 * src[(rowNum - 2)*stride + (colNum + 1)] + 2 *
>> src[(rowNum - 2)*stride + (colNum + 2)] +
>> + 4 * src[(rowNum - 1)*stride + (colNum - 2)] + 9 *
>> src[(rowNum - 1)*stride + (colNum - 1)] + 12 * src[(rowNum - 1)*stride +
>> (colNum)] + 9 * src[(rowNum - 1)*stride + (colNum + 1)] + 4 * src[(rowNum -
>> 1)*stride + (colNum + 2)] +
>> + 5 * src[(rowNum)*stride + (colNum - 2)] + 12 *
>> src[(rowNum)*stride + (colNum - 1)] + 15 * src[(rowNum)*stride + (colNum)]
>> + 12 * src[(rowNum)*stride + (colNum + 1)] + 5 * src[(rowNum)*stride +
>> (colNum + 2)] +
>> + 4 * src[(rowNum + 1)*stride + (colNum - 2)] + 9 *
>> src[(rowNum + 1)*stride + (colNum - 1)] + 12 * src[(rowNum + 1)*stride +
>> (colNum)] + 9 * src[(rowNum + 1)*stride + (colNum + 1)] + 4 * src[(rowNum +
>> 1)*stride + (colNum + 2)] +
>> + 2 * src[(rowNum + 2)*stride + (colNum - 2)] + 4 *
>> src[(rowNum + 2)*stride + (colNum - 1)] + 5 * src[(rowNum + 2)*stride +
>> (colNum)] + 4 * src[(rowNum + 2)*stride + (colNum + 1)] + 2 * src[(rowNum +
>> 2)*stride + (colNum + 2)]) / 159);
>> + refPic[(rowNum*stride) + colNum] = pixelValue;
>> + }
>> + }
>> + }
>> +
>> +#if HIGH_BIT_DEPTH //10-bit build
>> + float_t threshold = 1023;
>> + pixel whitePixel = 1023;
>> +#else
>> + float_t threshold = 255;
>> + pixel whitePixel = 255;
>> +#endif
>> +#define PI 3.14159265
>> +
>> + float_t gradientH = 0, gradientV = 0, radians = 0, theta = 0;
>> + float_t sobelGradient = 0;
>> + pixel blackPixel = 0;
>> + edgePic = pic1 + curFrame->m_fencPic->m_lumaMarginY * stride +
>> curFrame->m_fencPic->m_lumaMarginX;
>> + //Applying Sobel filter on the gaussian filtered picture
>> + for (int rowNum = 0; rowNum < height; rowNum++)
>> + {
>> + for (int colNum = 0; colNum < width; colNum++)
>> + {
>> + edgeTheta[(rowNum*stride) + colNum] = 0;
>> + if ((rowNum != 0) && (colNum != 0) && (rowNum != height - 1)
>> && (colNum != width - 1)) //Ignoring the border pixels of the picture
>> + {
>> + gradientH = (float_t)(-3 * refPic[(rowNum - 1)*stride +
>> (colNum - 1)] + 3 * refPic[(rowNum - 1)*stride + (colNum + 1)] - 10 *
>> refPic[rowNum*stride + (colNum - 1)] + 10 * refPic[rowNum*stride + (colNum
>> + 1)] - 3 * refPic[(rowNum + 1)*stride + (colNum - 1)] + 3 * refPic[(rowNum
>> + 1)*stride + (colNum + 1)]);
>> + gradientV = (float_t)(-3 * refPic[(rowNum - 1)*stride +
>> (colNum - 1)] - 10 * refPic[(rowNum - 1)*stride + colNum] - 3 *
>> refPic[(rowNum - 1)*stride + (colNum + 1)] + 3 * refPic[(rowNum + 1)*stride
>> + (colNum - 1)] + 10 * refPic[(rowNum + 1)*stride + colNum] + 3 *
>> refPic[(rowNum + 1)*stride + (colNum + 1)]);
>> +
>> + sobelGradient = sqrtf(gradientH * gradientH + gradientV
>> * gradientV);
>> + radians = atan2(gradientV, gradientH);
>> + theta = (float_t)((radians * 180) / PI);
>> + if (theta < 0)
>> + theta = 180 + theta;
>> + edgeTheta[(rowNum*stride) + colNum] = (pixel)theta;
>> +
>> + if (sobelGradient >= threshold)
>> + edgePic[(rowNum*stride) + colNum] = whitePixel;
>> + else
>> + edgePic[(rowNum*stride) + colNum] = blackPixel;
>> + }
>> + }
>> + }
>> +}
>> +
>> +//Find the angle of a block by averaging the pixel angles
>> +inline void findAvgAngle(const pixel* block, intptr_t stride, uint32_t
>> size, uint32_t &angle)
>> +{
>> + int sum = 0;
>> + for (uint32_t y = 0; y < size; y++)
>> + {
>> + for (uint32_t x = 0; x < size; x++)
>> + {
>> + sum += block[x];
>> + }
>> + block += stride;
>> + }
>> + angle = sum / (size*size);
>> +}
>> +
>> +uint32_t LookaheadTLD::edgeDensityCu(Frame* curFrame,pixel *edgeImage,
>> pixel *edgeTheta, uint32_t &avgAngle, uint32_t blockX, uint32_t blockY,
>> uint32_t qgSize)
>> +{
>> + intptr_t srcStride = curFrame->m_fencPic->m_stride;
>> + intptr_t blockOffsetLuma = blockX + (blockY * srcStride);
>> + int plane = 0; // Sobel filter is applied only on Y component
>> + uint32_t var;
>> +
>> + if (qgSize == 8)
>> + {
>> + findAvgAngle(edgeTheta + blockOffsetLuma, srcStride, qgSize,
>> avgAngle);
>> + var = acEnergyVar(curFrame, primitives.cu[BLOCK_8x8].var(edgeImage
>> + blockOffsetLuma, srcStride), 6, plane);
>> + }
>> + else
>> + {
>> + findAvgAngle(edgeTheta + blockOffsetLuma, srcStride, 16,
>> avgAngle);
>> + var = acEnergyVar(curFrame, primitives.cu[BLOCK_16x16].var(edgeImage
>> + blockOffsetLuma, srcStride), 8, plane);
>> + }
>> + x265_emms();
>> + return var;
>> +}
>> +
>> /* Find the total AC energy of each block in all planes */
>> uint32_t LookaheadTLD::acEnergyCu(Frame* curFrame, uint32_t blockX,
>> uint32_t blockY, int csp, uint32_t qgSize)
>> {
>> @@ -342,20 +454,55 @@
>> }
>> else
>> {
>> - int blockXY = 0;
>> +#define AQ_EDGE_BIAS 0.5
>> +#define EDGE_INCLINATION 45
>> + uint32_t numCuInHeight = (maxRow + param->maxCUSize - 1) /
>> param->maxCUSize;
>> + int maxHeight = numCuInHeight * param->maxCUSize;
>> + intptr_t stride = curFrame->m_fencPic->m_stride;
>> + pixel *edgePic = X265_MALLOC(pixel, stride * (maxHeight +
>> (curFrame->m_fencPic->m_lumaMarginY * 2)));
>> + pixel *gaussianPic = X265_MALLOC(pixel, stride * (maxHeight
>> + (curFrame->m_fencPic->m_lumaMarginY * 2)));
>> + pixel *thetaPic = X265_MALLOC(pixel, stride * (maxHeight +
>> (curFrame->m_fencPic->m_lumaMarginY * 2)));
>> + memset(edgePic, 0, stride * (maxHeight +
>> (curFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));
>> + memset(gaussianPic, 0, stride * (maxHeight +
>> (curFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));
>> + memset(thetaPic, 0, stride * (maxHeight +
>> (curFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));
>> + if (param->rc.aqMode == X265_AQ_EDGE)
>> + edgeFilter(curFrame, edgePic, gaussianPic, thetaPic,
>> stride, maxRow, maxCol);
>> +
>> + int blockXY = 0, inclinedEdge = 0;
>> double avg_adj_pow2 = 0, avg_adj = 0, qp_adj = 0;
>> double bias_strength = 0.f;
>> double strength = 0.f;
>> - if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE ||
>> param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED)
>> + if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE ||
>> param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED || param->rc.aqMode ==
>> X265_AQ_EDGE)
>> {
>> double bit_depth_correction = 1.f / (1 << (2 *
>> (X265_DEPTH - 8)));
>> -
>> for (int blockY = 0; blockY < maxRow; blockY += loopIncr)
>> {
>> for (int blockX = 0; blockX < maxCol; blockX +=
>> loopIncr)
>> {
>> - uint32_t energy = acEnergyCu(curFrame, blockX,
>> blockY, param->internalCsp, param->rc.qgSize);
>> - qp_adj = pow(energy * bit_depth_correction + 1,
>> 0.1);
>> + uint32_t energy, edgeDensity, avgAngle;
>> + energy = acEnergyCu(curFrame, blockX, blockY,
>> param->internalCsp, param->rc.qgSize);
>> + if (param->rc.aqMode == X265_AQ_EDGE)
>> + {
>> + pixel *edgeImage = edgePic +
>> curFrame->m_fencPic->m_lumaMarginY * stride +
>> curFrame->m_fencPic->m_lumaMarginX;
>> + pixel *edgeTheta = thetaPic +
>> curFrame->m_fencPic->m_lumaMarginY * stride +
>> curFrame->m_fencPic->m_lumaMarginX;
>> + edgeDensity = edgeDensityCu(curFrame,
>> edgeImage, edgeTheta, avgAngle, blockX, blockY, param->rc.qgSize);
>> + if (edgeDensity)
>> + {
>> + qp_adj = pow(edgeDensity *
>> bit_depth_correction + 1, 0.1);
>> + //Increasing the QP of a block if its
>> edge orientation lies around the multiples of 45 degree
>> + if ((avgAngle >= EDGE_INCLINATION - 15
>> && avgAngle <= EDGE_INCLINATION + 15) || (avgAngle >= EDGE_INCLINATION + 75
>> && avgAngle <= EDGE_INCLINATION + 105))
>> +
>> curFrame->m_lowres.edgeInclined[blockXY] = 1;
>> + else
>> +
>> curFrame->m_lowres.edgeInclined[blockXY] = 0;
>> + }
>> + else
>> + {
>> + qp_adj = pow(energy *
>> bit_depth_correction + 1, 0.1);
>> + curFrame->m_lowres.edgeInclined[blockXY]
>> = 0;
>> + }
>> + }
>> + else
>> + qp_adj = pow(energy * bit_depth_correction +
>> 1, 0.1);
>> curFrame->m_lowres.qpCuTreeOffset[blockXY] =
>> qp_adj;
>> avg_adj += qp_adj;
>> avg_adj_pow2 += qp_adj * qp_adj;
>> @@ -371,6 +518,9 @@
>> else
>> strength = param->rc.aqStrength * 1.0397f;
>>
>> + X265_FREE(edgePic);
>> + X265_FREE(gaussianPic);
>> + X265_FREE(thetaPic);
>> blockXY = 0;
>> for (int blockY = 0; blockY < maxRow; blockY += loopIncr)
>> {
>> @@ -386,6 +536,15 @@
>> qp_adj =
>> curFrame->m_lowres.qpCuTreeOffset[blockXY];
>> qp_adj = strength * (qp_adj - avg_adj);
>> }
>> + else if (param->rc.aqMode == X265_AQ_EDGE)
>> + {
>> + 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));
>> + else
>> + qp_adj = strength * (qp_adj - avg_adj);
>> + }
>> else
>> {
>> uint32_t energy = acEnergyCu(curFrame, blockX,
>> blockY, param->internalCsp, param->rc.qgSize);
>> diff -r 4f6dde51a5db -r d25c33cc2b74 source/encoder/slicetype.h
>> --- a/source/encoder/slicetype.h Fri Jul 05 10:47:15 2019 +0530
>> +++ b/source/encoder/slicetype.h Thu Jun 20 18:21:31 2019 +0530
>> @@ -92,6 +92,7 @@
>> protected:
>>
>> uint32_t acEnergyCu(Frame* curFrame, uint32_t blockX, uint32_t
>> blockY, int csp, uint32_t qgSize);
>> + uint32_t edgeDensityCu(Frame*curFrame, pixel *edgeImage, pixel
>> *edgeTheta, uint32_t &avgAngle, uint32_t blockX, uint32_t blockY, uint32_t
>> qgSize);
>> uint32_t lumaSumCu(Frame* curFrame, uint32_t blockX, uint32_t
>> blockY, uint32_t qgSize);
>> uint32_t weightCostLuma(Lowres& fenc, Lowres& ref, WeightParam& wp);
>> bool allocWeightedRef(Lowres& fenc);
>> diff -r 4f6dde51a5db -r d25c33cc2b74 source/x265.h
>> --- a/source/x265.h Fri Jul 05 10:47:15 2019 +0530
>> +++ b/source/x265.h Thu Jun 20 18:21:31 2019 +0530
>> @@ -561,6 +561,7 @@
>> #define X265_AQ_VARIANCE 1
>> #define X265_AQ_AUTO_VARIANCE 2
>> #define X265_AQ_AUTO_VARIANCE_BIASED 3
>> +#define X265_AQ_EDGE 4
>> #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 */
>> diff -r 4f6dde51a5db -r d25c33cc2b74 source/x265cli.h
>> --- a/source/x265cli.h Fri Jul 05 10:47:15 2019 +0530
>> +++ b/source/x265cli.h Thu Jun 20 18:21:31 2019 +0530
>> @@ -549,7 +549,7 @@
>> " - 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. Default %d\n", param->rc.aqMode);
>> + 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(" --[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(" --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);
>>
>> --
>> *Regards,*
>> *Akil R*
>> _______________________________________________
>> x265-devel mailing list
>> x265-devel at videolan.org
>> https://mailman.videolan.org/listinfo/x265-devel
>>
>
>
> --
> Regards,
> Aruna
> _______________________________________________
> x265-devel mailing list
> x265-devel at videolan.org
> https://mailman.videolan.org/listinfo/x265-devel
>
--
*Regards,*
*Akil R*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20190712/c10a9394/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Improved AQ with Sobel edge detection final.patch
Type: application/octet-stream
Size: 18987 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20190712/c10a9394/attachment-0001.obj>
More information about the x265-devel
mailing list