<div dir="ltr"># HG changeset patch<br># User Akil Ayyappan<<a href="mailto:akil@multicorewareinc.com">akil@multicorewareinc.com</a>><br># Date 1561035091 -19800<br>#      Thu Jun 20 18:21:31 2019 +0530<br># Node ID d25c33cc2b748401c5e908af445a0a110e26c3cf<br># Parent  4f6dde51a5db4f9229bddb60db176f16ac98f505<br>AQ: New AQ mode with Variance and Edge information<br><br>diff -r 4f6dde51a5db -r d25c33cc2b74 doc/reST/cli.rst<br>--- a/doc/reST/cli.rst       Fri Jul 05 10:47:15 2019 +0530<br>+++ b/doc/reST/cli.rst  Thu Jun 20 18:21:31 2019 +0530<br>@@ -1,3 +1,4 @@<br>+<br> *********************<br> Command Line Options<br> *********************<br>@@ -1633,7 +1634,7 @@<br>  ignored. Slower presets will generally achieve better compression<br>    efficiency (and generate smaller bitstreams). Default disabled.<br> <br>-.. option:: --aq-mode <0|1|2|3><br>+.. option:: --aq-mode <0|1|2|3|4><br> <br>    Adaptive Quantization operating mode. Raise or lower per-block<br>       quantization based on complexity analysis of the source image. The<br>@@ -1647,6 +1648,7 @@<br>    3. AQ enabled with auto-variance and bias to dark scenes. This is <br>   recommended for 8-bit encodes or low-bitrate 10-bit encodes, to <br>     prevent color banding/blocking. <br>+     4. AQ enabled with auto-variance and edge information.<br> <br> .. option:: --aq-strength <float><br> <br>diff -r 4f6dde51a5db -r d25c33cc2b74 source/CMakeLists.txt<br>--- a/source/CMakeLists.txt      Fri Jul 05 10:47:15 2019 +0530<br>+++ b/source/CMakeLists.txt     Thu Jun 20 18:21:31 2019 +0530<br>@@ -29,7 +29,7 @@<br> option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF)<br> mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)<br> # X265_BUILD must be incremented each time the public API is changed<br>-set(X265_BUILD 176)<br>+set(X265_BUILD 177)<br> configure_file("${PROJECT_SOURCE_DIR}/<a href="http://x265.def.in">x265.def.in</a>"<br>                "${PROJECT_BINARY_DIR}/x265.def")<br> configure_file("${PROJECT_SOURCE_DIR}/<a href="http://x265_config.h.in">x265_config.h.in</a>"<br>diff -r 4f6dde51a5db -r d25c33cc2b74 source/common/lowres.cpp<br>--- a/source/common/lowres.cpp      Fri Jul 05 10:47:15 2019 +0530<br>+++ b/source/common/lowres.cpp  Thu Jun 20 18:21:31 2019 +0530<br>@@ -78,6 +78,7 @@<br>         CHECKED_MALLOC_ZERO(qpCuTreeOffset, double, cuCountFullRes);<br>         if (qgSize == 8)<br>             CHECKED_MALLOC_ZERO(invQscaleFactor8x8, int, cuCount);<br>+        CHECKED_MALLOC_ZERO(edgeInclined, int, cuCountFullRes);<br>     }<br> <br>     if (origPic->m_param->bAQMotion)<br>@@ -190,6 +191,7 @@<br>     X265_FREE(qpCuTreeOffset);<br>     X265_FREE(propagateCost);<br>     X265_FREE(invQscaleFactor8x8);<br>+    X265_FREE(edgeInclined);<br>     X265_FREE(qpAqMotionOffset);<br>     X265_FREE(blockVariance);<br>     if (maxAQDepth > 0)<br>diff -r 4f6dde51a5db -r d25c33cc2b74 source/common/lowres.h<br>--- a/source/common/lowres.h Fri Jul 05 10:47:15 2019 +0530<br>+++ b/source/common/lowres.h    Thu Jun 20 18:21:31 2019 +0530<br>@@ -197,6 +197,8 @@<br>     uint64_t  wp_ssd[3];       // This is different than SSDY, this is sum(pixel^2) - sum(pixel)^2 for entire frame<br>     uint64_t  wp_sum[3];<br>     double    frameVariance;<br>+    int* edgeInclined;<br>+<br> <br>     /* cutree intermediate data */<br>     PicQPAdaptationLayer* pAQLayer;<br>diff -r 4f6dde51a5db -r d25c33cc2b74 source/common/param.cpp<br>--- a/source/common/param.cpp     Fri Jul 05 10:47:15 2019 +0530<br>+++ b/source/common/param.cpp   Thu Jun 20 18:21:31 2019 +0530<br>@@ -1522,7 +1522,7 @@<br>           "Lookahead depth must be less than 256");<br>     CHECK(param->lookaheadSlices > 16 || param->lookaheadSlices < 0,<br>           "Lookahead slices must between 0 and 16");<br>-    CHECK(param->rc.aqMode < X265_AQ_NONE || X265_AQ_AUTO_VARIANCE_BIASED < param->rc.aqMode,<br>+    CHECK(param->rc.aqMode < X265_AQ_NONE || X265_AQ_EDGE < param->rc.aqMode,<br>           "Aq-Mode is out of range");<br>     CHECK(param->rc.aqStrength < 0 || param->rc.aqStrength > 3,<br>           "Aq-Strength is out of range");<br>diff -r 4f6dde51a5db -r d25c33cc2b74 source/encoder/slicetype.cpp<br>--- a/source/encoder/slicetype.cpp        Fri Jul 05 10:47:15 2019 +0530<br>+++ b/source/encoder/slicetype.cpp      Thu Jun 20 18:21:31 2019 +0530<br>@@ -85,6 +85,118 @@<br> <br> } // end anonymous namespace<br> <br>+void edgeFilter(Frame *curFrame, pixel *pic1, pixel *pic2, pixel *pic3, intptr_t stride, int height, int width)<br>+{<br>+    pixel *src = (pixel*)curFrame->m_fencPic->m_picOrg[0];<br>+    pixel *edgePic = pic1 + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;<br>+    pixel *refPic = pic2 + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;<br>+    pixel *edgeTheta = pic3 + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;<br>+<br>+    for (int i = 0; i < height; i++)<br>+    {<br>+        memcpy(edgePic, src, width * sizeof(pixel));<br>+        memcpy(refPic, src, width * sizeof(pixel));<br>+        src += stride;<br>+        edgePic += stride;<br>+        refPic += stride;<br>+    }<br>+<br>+    //Applying Gaussian filter on the picture<br>+    src = (pixel*)curFrame->m_fencPic->m_picOrg[0];<br>+    refPic = pic2 + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;<br>+    pixel pixelValue = 0;<br>+    for (int rowNum = 0; rowNum < height; rowNum++)<br>+    {<br>+        for (int colNum = 0; colNum < width; colNum++)<br>+        {<br>+            if ((rowNum >= 2) && (colNum >= 2) && (rowNum != height - 2) && (colNum != width - 2)) //Ignoring the border pixels of the picture<br>+            {<br>+                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)] +<br>+                    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)] +<br>+                    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)] +<br>+                    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)] +<br>+                    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);<br>+                refPic[(rowNum*stride) + colNum] = pixelValue;<br>+            }<br>+        }<br>+    }<br>+<br>+#if HIGH_BIT_DEPTH //10-bit build<br>+    float_t threshold = 1023;<br>+    pixel whitePixel = 1023;<br>+#else<br>+    float_t threshold = 255;<br>+    pixel whitePixel = 255;<br>+#endif<br>+#define PI 3.14159265 <br>+<br>+    float_t gradientH = 0, gradientV = 0, radians = 0, theta = 0;<br>+    float_t sobelGradient = 0;<br>+    pixel blackPixel = 0;<br>+    edgePic = pic1 + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;<br>+    //Applying Sobel filter on the gaussian filtered picture<br>+    for (int rowNum = 0; rowNum < height; rowNum++)<br>+    {<br>+        for (int colNum = 0; colNum < width; colNum++)<br>+        {<br>+            edgeTheta[(rowNum*stride) + colNum] = 0;<br>+            if ((rowNum != 0) && (colNum != 0) && (rowNum != height - 1) && (colNum != width - 1)) //Ignoring the border pixels of the picture<br>+            {<br>+                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)]);<br>+                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)]);<br>+<br>+                sobelGradient = sqrtf(gradientH * gradientH + gradientV * gradientV);<br>+                radians = atan2(gradientV, gradientH);<br>+                theta = (float_t)((radians * 180) / PI);<br>+                if (theta < 0)<br>+                    theta = 180 + theta;<br>+                edgeTheta[(rowNum*stride) + colNum] = (pixel)theta;<br>+<br>+                if (sobelGradient >= threshold)<br>+                    edgePic[(rowNum*stride) + colNum] = whitePixel;<br>+                else<br>+                    edgePic[(rowNum*stride) + colNum] = blackPixel;<br>+            }<br>+        }<br>+    }<br>+}<br>+<br>+//Find the angle of a block by averaging the pixel angles <br>+inline void findAvgAngle(const pixel* block, intptr_t stride, uint32_t size, uint32_t &angle)<br>+{<br>+    int sum = 0;<br>+    for (uint32_t y = 0; y < size; y++)<br>+    {<br>+        for (uint32_t x = 0; x < size; x++)<br>+        {<br>+            sum += block[x];<br>+        }<br>+        block += stride;<br>+    }<br>+    angle = sum / (size*size);<br>+}<br>+<br>+uint32_t LookaheadTLD::edgeDensityCu(Frame* curFrame,pixel *edgeImage, pixel *edgeTheta, uint32_t &avgAngle, uint32_t blockX, uint32_t blockY, uint32_t qgSize)<br>+{<br>+    intptr_t srcStride = curFrame->m_fencPic->m_stride;<br>+    intptr_t blockOffsetLuma = blockX + (blockY * srcStride);<br>+    int plane = 0; // Sobel filter is applied only on Y component<br>+    uint32_t var;<br>+<br>+    if (qgSize == 8)<br>+    {<br>+        findAvgAngle(edgeTheta + blockOffsetLuma, srcStride, qgSize, avgAngle);<br>+        var = acEnergyVar(curFrame, <a href="http://primitives.cu">primitives.cu</a>[BLOCK_8x8].var(edgeImage + blockOffsetLuma, srcStride), 6, plane);<br>+    }<br>+    else<br>+    {<br>+        findAvgAngle(edgeTheta + blockOffsetLuma, srcStride, 16, avgAngle);<br>+        var = acEnergyVar(curFrame, <a href="http://primitives.cu">primitives.cu</a>[BLOCK_16x16].var(edgeImage + blockOffsetLuma, srcStride), 8, plane);<br>+    }<br>+    x265_emms();<br>+    return var;<br>+}<br>+<br> /* Find the total AC energy of each block in all planes */<br> uint32_t LookaheadTLD::acEnergyCu(Frame* curFrame, uint32_t blockX, uint32_t blockY, int csp, uint32_t qgSize)<br> {<br>@@ -342,20 +454,55 @@<br>         }<br>         else<br>         {<br>-            int blockXY = 0;<br>+#define AQ_EDGE_BIAS 0.5<br>+#define EDGE_INCLINATION 45<br>+            uint32_t numCuInHeight = (maxRow + param->maxCUSize - 1) / param->maxCUSize;<br>+            int maxHeight = numCuInHeight * param->maxCUSize;<br>+            intptr_t stride = curFrame->m_fencPic->m_stride;<br>+            pixel *edgePic = X265_MALLOC(pixel, stride * (maxHeight + (curFrame->m_fencPic->m_lumaMarginY * 2)));<br>+            pixel *gaussianPic = X265_MALLOC(pixel, stride * (maxHeight + (curFrame->m_fencPic->m_lumaMarginY * 2)));<br>+            pixel *thetaPic = X265_MALLOC(pixel, stride * (maxHeight + (curFrame->m_fencPic->m_lumaMarginY * 2)));<br>+            memset(edgePic, 0, stride * (maxHeight + (curFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));<br>+            memset(gaussianPic, 0, stride * (maxHeight + (curFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));<br>+            memset(thetaPic, 0, stride * (maxHeight + (curFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));<br>+            if (param->rc.aqMode == X265_AQ_EDGE)<br>+                edgeFilter(curFrame, edgePic, gaussianPic, thetaPic, stride, maxRow, maxCol);<br>+<br>+            int blockXY = 0, inclinedEdge = 0;<br>             double avg_adj_pow2 = 0, avg_adj = 0, qp_adj = 0;<br>             double bias_strength = 0.f;<br>             double strength = 0.f;<br>-            if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE || param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED)<br>+            if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE || param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED || param->rc.aqMode == X265_AQ_EDGE)<br>             {<br>                 double bit_depth_correction = 1.f / (1 << (2 * (X265_DEPTH - 8)));<br>-<br>                 for (int blockY = 0; blockY < maxRow; blockY += loopIncr)<br>                 {<br>                     for (int blockX = 0; blockX < maxCol; blockX += loopIncr)<br>                     {<br>-                        uint32_t energy = acEnergyCu(curFrame, blockX, blockY, param->internalCsp, param->rc.qgSize);<br>-                        qp_adj = pow(energy * bit_depth_correction + 1, 0.1);<br>+                        uint32_t energy, edgeDensity, avgAngle;<br>+                        energy = acEnergyCu(curFrame, blockX, blockY, param->internalCsp, param->rc.qgSize);<br>+                        if (param->rc.aqMode == X265_AQ_EDGE)<br>+                        {<br>+                            pixel *edgeImage = edgePic + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;<br>+                            pixel *edgeTheta = thetaPic + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;<br>+                            edgeDensity = edgeDensityCu(curFrame, edgeImage, edgeTheta, avgAngle, blockX, blockY, param->rc.qgSize);<br>+                            if (edgeDensity)<br>+                            {<br>+                                qp_adj = pow(edgeDensity * bit_depth_correction + 1, 0.1);<br>+                                //Increasing the QP of a block if its edge orientation lies around the multiples of 45 degree<br>+                                if ((avgAngle >= EDGE_INCLINATION - 15 && avgAngle <= EDGE_INCLINATION + 15) || (avgAngle >= EDGE_INCLINATION + 75 && avgAngle <= EDGE_INCLINATION + 105))<br>+                                    curFrame->m_lowres.edgeInclined[blockXY] = 1;<br>+                                else<br>+                                    curFrame->m_lowres.edgeInclined[blockXY] = 0;<br>+                            }<br>+                            else<br>+                            {<br>+                                qp_adj = pow(energy * bit_depth_correction + 1, 0.1);<br>+                                curFrame->m_lowres.edgeInclined[blockXY] = 0;<br>+                            }<br>+                        }<br>+                        else<br>+                            qp_adj = pow(energy * bit_depth_correction + 1, 0.1);<br>                         curFrame->m_lowres.qpCuTreeOffset[blockXY] = qp_adj;<br>                         avg_adj += qp_adj;<br>                         avg_adj_pow2 += qp_adj * qp_adj;<br>@@ -371,6 +518,9 @@<br>             else<br>                 strength = param->rc.aqStrength * 1.0397f;<br> <br>+            X265_FREE(edgePic);<br>+            X265_FREE(gaussianPic);<br>+            X265_FREE(thetaPic);<br>             blockXY = 0;<br>             for (int blockY = 0; blockY < maxRow; blockY += loopIncr)<br>             {<br>@@ -386,6 +536,15 @@<br>                         qp_adj = curFrame->m_lowres.qpCuTreeOffset[blockXY];<br>                         qp_adj = strength * (qp_adj - avg_adj);<br>                     }<br>+                    else if (param->rc.aqMode == X265_AQ_EDGE)<br>+                    {<br>+                        inclinedEdge = curFrame->m_lowres.edgeInclined[blockXY];<br>+                        qp_adj = curFrame->m_lowres.qpCuTreeOffset[blockXY];<br>+                        if(inclinedEdge && (qp_adj - avg_adj > 0))<br>+                            qp_adj = ((strength + AQ_EDGE_BIAS) * (qp_adj - avg_adj));<br>+                        else<br>+                            qp_adj = strength * (qp_adj - avg_adj);<br>+                    }<br>                     else<br>                     {<br>                         uint32_t energy = acEnergyCu(curFrame, blockX, blockY, param->internalCsp, param->rc.qgSize);<br>diff -r 4f6dde51a5db -r d25c33cc2b74 source/encoder/slicetype.h<br>--- a/source/encoder/slicetype.h    Fri Jul 05 10:47:15 2019 +0530<br>+++ b/source/encoder/slicetype.h        Thu Jun 20 18:21:31 2019 +0530<br>@@ -92,6 +92,7 @@<br> protected:<br> <br>     uint32_t acEnergyCu(Frame* curFrame, uint32_t blockX, uint32_t blockY, int csp, uint32_t qgSize);<br>+    uint32_t edgeDensityCu(Frame*curFrame, pixel *edgeImage, pixel *edgeTheta, uint32_t &avgAngle, uint32_t blockX, uint32_t blockY, uint32_t qgSize);<br>     uint32_t lumaSumCu(Frame* curFrame, uint32_t blockX, uint32_t blockY, uint32_t qgSize);<br>     uint32_t weightCostLuma(Lowres& fenc, Lowres& ref, WeightParam& wp);<br>     bool     allocWeightedRef(Lowres& fenc);<br>diff -r 4f6dde51a5db -r d25c33cc2b74 source/x265.h<br>--- a/source/x265.h       Fri Jul 05 10:47:15 2019 +0530<br>+++ b/source/x265.h     Thu Jun 20 18:21:31 2019 +0530<br>@@ -561,6 +561,7 @@<br> #define X265_AQ_VARIANCE             1<br> #define X265_AQ_AUTO_VARIANCE        2<br> #define X265_AQ_AUTO_VARIANCE_BIASED 3<br>+#define X265_AQ_EDGE                 4<br> #define x265_ADAPT_RD_STRENGTH   4<br> #define X265_REFINE_INTER_LEVELS 3<br> /* NOTE! For this release only X265_CSP_I420 and X265_CSP_I444 are supported */<br>diff -r 4f6dde51a5db -r d25c33cc2b74 source/x265cli.h<br>--- a/source/x265cli.h     Fri Jul 05 10:47:15 2019 +0530<br>+++ b/source/x265cli.h  Thu Jun 20 18:21:31 2019 +0530<br>@@ -549,7 +549,7 @@<br>         "                                    - 0 : Disabled.\n"<br>         "                                    - 1 : Store/Load ctu distortion to/from the file specified in analysis-save/load.\n"<br>         "                                Default 0 - Disabled\n");<br>-    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);<br>+    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);<br>     H0("   --[no-]hevc-aq                Mode for HEVC Adaptive Quantization. Default %s\n", OPT(param->rc.hevcAq));<br>     H0("   --aq-strength <float>         Reduces blocking and blurring in flat and textured areas (0 to 3.0). Default %.2f\n", param->rc.aqStrength);<br>     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);<br><div><br></div>-- <br><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><i><b>Regards,</b></i><div><i><b>Akil R</b></i></div></div></div></div>