<div dir="ltr">  # HG changeset patch<br># User Srikanth Kurapati <<a href="mailto:srikanth.kurapati@multicorewareinc.com" target="_blank">srikanth.kurapati@multicorewareinc.com</a>><br># Date 1571319729 -19800<br>#      Thu Oct 17 19:12:09 2019 +0530<br># Node ID c6d2c44753634202f399033021cf064bdd9efbb7<br># Parent  978a57943c8f622de41ddb1931504d6df4ebafc1<br>Integration of Histogram based scenecut detection and frame duplication features in encoder.<br>1. Add option "--hist-scenecut" and "--hist-threshold' to enable improved scenecut method for slice type decisions,rate control and a threshold for determining scene-cuts.<br>2. Identifies scenecuts using sad of edge and chroma histogram based thresholding in encoder.<br>3. Removes duplicate edgefilter code and uses global definition for use in scene cut detection and aq in Lookahead.<br><br>diff -r 978a57943c8f -r c6d2c4475363 doc/reST/cli.rst<br>--- a/doc/reST/cli.rst Thu Oct 17 18:43:11 2019 +0530<br>+++ b/doc/reST/cli.rst Thu Oct 17 19:12:09 2019 +0530<br>@@ -1426,7 +1426,20 @@<br>  This value represents the percentage difference between the inter cost and<br>  intra cost of a frame used in scenecut detection. For example, a value of 5 indicates,<br>  if the inter cost of a frame is greater than or equal to 95 percent of the intra cost of the frame,<br>- then detect this frame as scenecut. Values between 5 and 15 are recommended. Default 5.<br>+ then detect this frame as scenecut. Values between 5 and 15 are recommended.<br>+ This value is evaluated only when --scenecut is enabled else it is ignored. Default 5.<br>+<br>+.. option:: --hist-scenecut, --no-hist-scenecut<br>+<br>+ indicates that I-frames need to be inserted using edge and color histogram based scenecut algorithm.<br>+ option: `--hist-scencut` enables adaptive I frame placement using this method and disables the default scene cut algorithm.<br>+ option:`--no-hist-scenecut` adaptive I frame placement.<br>+<br>+.. option:: --hist-threshold <0.0..2.0><br>+<br>+ This value represents the threshold for SAD of edge histograms used in scenecut detection. This requires hist-scenecut to be enabled.<br>+ For example, a value of 0.2 indicates that a frame with SAD value greater than 0.2 against the previous frame  as scenecut.<br>+ Values between 0.0 and 2.0 are recommended. This value is evaluated only when --hist-scenecut is enabled. Default 0.01.<br> <br> .. option:: --radl <integer><br> <br>diff -r 978a57943c8f -r c6d2c4475363 source/CMakeLists.txt<br>--- a/source/CMakeLists.txt Thu Oct 17 18:43:11 2019 +0530<br>+++ b/source/CMakeLists.txt Thu Oct 17 19:12:09 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 180)<br>+set(X265_BUILD 181)<br> configure_file("${PROJECT_SOURCE_DIR}/<a href="http://x265.def.in/" target="_blank">x265.def.in</a>"<br>                "${PROJECT_BINARY_DIR}/x265.def")<br> configure_file("${PROJECT_SOURCE_DIR}/<a href="http://x265_config.h.in/" target="_blank">x265_config.h.in</a>"<br>diff -r 978a57943c8f -r c6d2c4475363 source/common/param.cpp<br>--- a/source/common/param.cpp Thu Oct 17 18:43:11 2019 +0530<br>+++ b/source/common/param.cpp Thu Oct 17 19:12:09 2019 +0530<br>@@ -167,6 +167,8 @@<br>     param->bFrameAdaptive = X265_B_ADAPT_TRELLIS;<br>     param->bBPyramid = 1;<br>     param->scenecutThreshold = 40; /* Magic number pulled in from x264 */<br>+    param->edgeTransitionThreshold = 0.01;<br>+    param->bHistBasedSceneCut = false;<br>     param->lookaheadSlices = 8;<br>     param->lookaheadThreads = 0;<br>     param->scenecutBias = 5.0;<br>@@ -567,6 +569,7 @@<br>             param->bframes = 0;<br>             param->lookaheadDepth = 0;<br>             param->scenecutThreshold = 0;<br>+            param->bHistBasedSceneCut = false;<br>             param->rc.cuTree = 0;<br>             param->frameNumThreads = 1;<br>         }<br>@@ -609,7 +612,7 @@<br>     return 0;<br> }<br> <br>-static int x265_atobool(const char* str, bool& bError)<br>+static bool x265_atobool(const char* str, bool& bError)<br> {<br>     if (!strcmp(str, "1") ||<br>         !strcmp(str, "true") ||<br>@@ -920,6 +923,7 @@<br>         {<br>             bError = false;<br>             p->scenecutThreshold = atoi(value);<br>+            p->bHistBasedSceneCut = false;<br>         }<br>     }<br>     OPT("temporal-layers") p->bEnableTemporalSubLayers = atobool(value);<br>@@ -1186,6 +1190,32 @@<br>         OPT("opt-ref-list-length-pps") p->bOptRefListLengthPPS = atobool(value);<br>         OPT("multi-pass-opt-rps") p->bMultiPassOptRPS = atobool(value);<br>         OPT("scenecut-bias") p->scenecutBias = atof(value);<br>+        OPT("hist-scenecut")<br>+        {<br>+            p->bHistBasedSceneCut = atobool(value);<br>+<br>+            if (bError)<br>+            {<br>+                bError = false;<br>+                p->bHistBasedSceneCut = false;<br>+            }<br>+<br>+            if (p->bHistBasedSceneCut)<br>+            {<br>+                bError = false;<br>+                p->scenecutThreshold = 0;<br>+            }<br>+<br>+        }<br>+        OPT("hist-threshold") {<br>+            p->edgeTransitionThreshold = atof(value);<br>+            if (bError)<br>+            {<br>+                bError = false;<br>+                p->edgeTransitionThreshold = 0.01;<br>+                x265_log(p, X265_LOG_INFO, "using  default threshold %.2lf for scene cut detection\n", p->edgeTransitionThreshold);<br>+            }<br>+        }<br>         OPT("lookahead-threads") p->lookaheadThreads = atoi(value);<br>         OPT("opt-cu-delta-qp") p->bOptCUDeltaQP = atobool(value);<br>         OPT("multi-pass-opt-analysis") p->analysisMultiPassRefine = atobool(value);<br>@@ -1623,8 +1653,16 @@<br>           "Valid Logging level -1:none 0:error 1:warning 2:info 3:debug 4:full");<br>     CHECK(param->scenecutThreshold < 0,<br>           "scenecutThreshold must be greater than 0");<br>-    CHECK(param->scenecutBias < 0 || 100 < param->scenecutBias,<br>-           "scenecut-bias must be between 0 and 100");<br>+    if (param->scenecutThreshold)<br>+    {<br>+        CHECK(param->scenecutBias < 0 || 100 < param->scenecutBias,<br>+            "scenecut-bias must be between 0 and 100");<br>+    }<br>+    else if (param->bHistBasedSceneCut)<br>+    {<br>+        CHECK(param->edgeTransitionThreshold < 0.0 || 2.0 < param->edgeTransitionThreshold,<br>+            "hist-threshold must be between 0.0 and 2.0");<br>+    }<br>     CHECK(param->radl < 0 || param->radl > param->bframes,<br>           "radl must be between 0 and bframes");<br>     CHECK(param->rdPenalty < 0 || param->rdPenalty > 2,<br>@@ -1780,10 +1818,21 @@<br>         x265_log(param, X265_LOG_INFO, "ME / range / subpel / merge         : %s / %d / %d / %d\n",<br>             x265_motion_est_names[param->searchMethod], param->searchRange, param->subpelRefine, param->maxNumMergeCand);<br> <br>-    if (param->keyframeMax != INT_MAX || param->scenecutThreshold)<br>-        x265_log(param, X265_LOG_INFO, "Keyframe min / max / scenecut / bias: %d / %d / %d / %.2lf\n", param->keyframeMin, param->keyframeMax, param->scenecutThreshold, param->scenecutBias * 100);<br>+    if (param->scenecutThreshold && param->keyframeMax != INT_MAX)<br>+        param->edgeTransitionThreshold = 0.0;<br>+    else if (param->bHistBasedSceneCut && param->keyframeMax != INT_MAX)<br>+        param->scenecutBias = 0.0;<br>+    else if (param->keyframeMax != INT_MAX)<br>+    {<br>+        param->edgeTransitionThreshold = 0.0;<br>+        param->scenecutBias = 0.0;<br>+    }<br>+<br>+    if (param->keyframeMax == INT_MAX)<br>+        x265_log(param, X265_LOG_INFO, "Keyframe min / max / scenecut       : disabled\n");<br>     else<br>-        x265_log(param, X265_LOG_INFO, "Keyframe min / max / scenecut       : disabled\n");<br>+        x265_log(param, X265_LOG_INFO, "Keyframe min / max / scenecut / bias / threshold  : %d / %d / %d / %.2lf / %.2lf\n",<br>+        param->keyframeMin, param->keyframeMax, ( param->bHistBasedSceneCut || param->scenecutThreshold ), param->scenecutBias * 100, param->edgeTransitionThreshold);<br> <br>     if (param->cbQpOffset || param->crQpOffset)<br>         x265_log(param, X265_LOG_INFO, "Cb/Cr QP Offset                     : %d / %d\n", param->cbQpOffset, param->crQpOffset);<br>@@ -1949,6 +1998,8 @@<br>     s += sprintf(s, " rc-lookahead=%d", p->lookaheadDepth);<br>     s += sprintf(s, " lookahead-slices=%d", p->lookaheadSlices);<br>     s += sprintf(s, " scenecut=%d", p->scenecutThreshold);<br>+    s += sprintf(s, " hist-scenecut=%d", p->bHistBasedSceneCut);<br>+    s += sprintf(s, " hist-threshold=%.2f", p->edgeTransitionThreshold);<br>     s += sprintf(s, " radl=%d", p->radl);<br>     BOOL(p->bEnableHRDConcatFlag, "splice");<br>     BOOL(p->bIntraRefresh, "intra-refresh");<br>@@ -2096,6 +2147,8 @@<br>     BOOL(p->bOptRefListLengthPPS, "opt-ref-list-length-pps");<br>     BOOL(p->bMultiPassOptRPS, "multi-pass-opt-rps");<br>     s += sprintf(s, " scenecut-bias=%.2f", p->scenecutBias);<br>+    s += sprintf(s, " hist-threshold=%.2f", p->edgeTransitionThreshold);<br>+    <br>     BOOL(p->bOptCUDeltaQP, "opt-cu-delta-qp");<br>     BOOL(p->bAQMotion, "aq-motion");<br>     BOOL(p->bEmitHDRSEI, "hdr");<br>@@ -2246,6 +2299,7 @@<br>     dst->lookaheadSlices = src->lookaheadSlices;<br>     dst->lookaheadThreads = src->lookaheadThreads;<br>     dst->scenecutThreshold = src->scenecutThreshold;<br>+    dst->bHistBasedSceneCut = src->bHistBasedSceneCut;<br>     dst->bIntraRefresh = src->bIntraRefresh;<br>     dst->maxCUSize = src->maxCUSize;<br>     dst->minCUSize = src->minCUSize;<br>@@ -2403,6 +2457,7 @@<br>     dst->bOptRefListLengthPPS = src->bOptRefListLengthPPS;<br>     dst->bMultiPassOptRPS = src->bMultiPassOptRPS;<br>     dst->scenecutBias = src->scenecutBias;<br>+    dst->edgeTransitionThreshold = src->edgeTransitionThreshold;<br>     dst->gopLookahead = src->lookaheadDepth;<br>     dst->bOptCUDeltaQP = src->bOptCUDeltaQP;<br>     dst->analysisMultiPassDistortion = src->analysisMultiPassDistortion;<br>diff -r 978a57943c8f -r c6d2c4475363 source/encoder/api.cpp<br>--- a/source/encoder/api.cpp Thu Oct 17 18:43:11 2019 +0530<br>+++ b/source/encoder/api.cpp Thu Oct 17 19:12:09 2019 +0530<br>@@ -31,6 +31,7 @@<br> #include "nal.h"<br> #include "bitcost.h"<br> #include "svt.h"<br>+#include "histscenecut.h"<br> <br> #if ENABLE_LIBVMAF<br> #include "libvmaf.h"<br>@@ -118,7 +119,10 @@<br>     x265_log(param, X265_LOG_INFO, "build info %s\n", PFX(build_info_str));<br> <br>     encoder = new Encoder;<br>-<br>+    encoder->m_sadStats = new sad_stats(x265_cli_csps[p->internalCsp].planes,param->edgeTransitionThreshold);<br>+    encoder->m_histogramsOfAdjFrames = new YuvHistogram[2];<br>+    encoder->m_histogramsOfAdjFrames[0].initHistograms(p);<br>+    encoder->m_histogramsOfAdjFrames[1].initHistograms(p);<br> #ifdef SVT_HEVC<br> <br>     if (param->bEnableSvtHevc)<br>@@ -810,6 +814,7 @@<br>             CHECKED_MALLOC_ZERO(interData->ref, int32_t, analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU * numDir);<br>     }<br>     analysis->interData = interData;<br>+    analysis->bScenecut = false;<br> <br>     return;<br> <br>@@ -925,6 +930,7 @@<br>     pic->rpu.payloadSize = 0;<br>     pic->rpu.payload = NULL;<br>     pic->picStruct = 0;<br>+    pic->bufUpdated = false;<br> <br>     if ((param->analysisSave || param->analysisLoad) || (param->bAnalysisType == AVC_INFO))<br>     {<br>@@ -934,7 +940,9 @@<br>         uint32_t numCUsInFrame   = widthInCU * heightInCU;<br>         pic->analysisData.numCUsInFrame = numCUsInFrame;<br>         pic->analysisData.numPartitions = param->num4x4Partitions;<br>+        pic->analysisData.bScenecut = false;<br>     }<br>+<br> }<br> <br> void x265_picture_free(x265_picture *p)<br>@@ -956,7 +964,8 @@<br> {<br>     if (param && param->rc.zonefileCount) {<br>         for (int i = 0; i < param->rc.zonefileCount; i++)<br>-            x265_free(param->rc.zones[i].zoneParam);<br>+            if(param->rc.zones[i].zoneParam)<br>+              x265_free(param->rc.zones[i].zoneParam);<br>     }<br>     if (param && (param->rc.zoneCount || param->rc.zonefileCount))<br>         x265_free(param->rc.zones);<br>diff -r 978a57943c8f -r c6d2c4475363 source/encoder/encoder.cpp<br>--- a/source/encoder/encoder.cpp Thu Oct 17 18:43:11 2019 +0530<br>+++ b/source/encoder/encoder.cpp Thu Oct 17 19:12:09 2019 +0530<br>@@ -119,6 +119,9 @@<br>         m_frameEncoder[i] = NULL;<br>     for (uint32_t i = 0; i < DUP_BUFFER; i++)<br>         m_dupBuffer[i] = NULL;<br>+    <br>+    m_histogramsOfAdjFrames = NULL;<br>+    m_sadStats = NULL;<br>     MotionEstimate::initScales();<br> <br> #if ENABLE_HDR10_PLUS<br>@@ -162,7 +165,9 @@<br>     int rows = (p->sourceHeight + p->maxCUSize - 1) >> g_log2Size[p->maxCUSize];<br>     int cols = (p->sourceWidth  + p->maxCUSize - 1) >> g_log2Size[p->maxCUSize];<br> <br>-    if (m_param->bEnableFrameDuplication)<br>+<br>+<br>+    if (m_param->bEnableFrameDuplication || m_param->bHistBasedSceneCut)<br>     {<br>         size_t framesize = 0;<br>         int pixelbytes = p->sourceBitDepth > 8 ? 2 : 1;<br>@@ -184,6 +189,7 @@<br>             m_dupBuffer[i]->dupPlane = NULL;<br>             m_dupBuffer[i]->dupPlane = X265_MALLOC(char, framesize);<br>             m_dupBuffer[i]->dupPic->planes[0] = m_dupBuffer[i]->dupPlane;<br>+ m_dupBuffer[i]->bufUpdated = false;<br>             m_dupBuffer[i]->bOccupied = false;<br>             m_dupBuffer[i]->bDup = false;<br>         }<br>@@ -820,7 +826,7 @@<br>         m_exportedPic = NULL;<br>     }<br> <br>-    if (m_param->bEnableFrameDuplication)<br>+    if (m_param->bEnableFrameDuplication || m_param->bHistBasedSceneCut)<br>     {<br>         for (uint32_t i = 0; i < DUP_BUFFER; i++)<br>         {<br>@@ -1280,6 +1286,37 @@<br>     return psnrWeight = (psnrY * 6 + psnrU + psnrV) / 8;<br> }<br> <br>+void Encoder::updateSceneCutAndFrameDuplicateFlags()<br>+{<br>+    /* SCD computation and drop flag*/<br>+    for (int i = 0; i < DUP_BUFFER; i++)<br>+    {<br>+        if (m_dupBuffer[i]->bufUpdated)<br>+        {<br>+            m_histogramsOfAdjFrames[i].setUpdateFlag(true);<br>+            m_histogramsOfAdjFrames[i].edgeFilter(m_dupBuffer[i]->dupPic);<br>+            m_histogramsOfAdjFrames[i].computeHistograms(*m_dupBuffer[i]->dupPic);<br>+            m_sadStats->computeSadValue(m_histogramsOfAdjFrames, m_histogramsOfAdjFrames->m_planeSizes);<br>+            m_sadStats->findSceneCuts(m_dupBuffer[i]->dupPic, m_dupBuffer[i]->bDup);<br>+<br>+            if (m_dupBuffer[i]->dupPic->analysisData.bScenecut)<br>+            {<br>+                x265_log(m_param, X265_LOG_DEBUG, "scene cut at %d edge hist sad: %0.4lf maxuv hist sad: %0.4lf\n",<br>+                   m_dupBuffer[i]->dupPic->poc,m_dupBuffer[i]->dupPic->analysisData.edgeSadValue,m_dupBuffer[i]->dupPic->analysisData.chromaSadValue);<br>+            }<br>+<br>+            if (m_dupBuffer[1]->bufUpdated)<br>+                m_histogramsOfAdjFrames[0] = m_histogramsOfAdjFrames[1];<br>+        }<br>+    }<br>+<br>+ }<br>+<br>+/* TBD<br>+- to be updated for missing parameters in case of re-use else where and improvised to copy constructor / assignment operator of x265 picture data structure.<br>+- benefits avoid function and use language features appropriately.<br>+*/<br>+<br> void Encoder::copyPicture(x265_picture *dest, const x265_picture *src)<br> {<br>     dest->poc = src->poc;<br>@@ -1299,6 +1336,29 @@<br>     memcpy(dest->planes[0], src->planes[0], src->framesize * sizeof(char));<br>     dest->planes[1] = (char*)dest->planes[0] + src->stride[0] * src->height;<br>     dest->planes[2] = (char*)dest->planes[1] + src->stride[1] * (src->height >> x265_cli_csps[src->colorSpace].height[1]);<br>+    memcpy(&dest->analysisData, &src->analysisData, sizeof(src->analysisData));<br>+<br>+}<br>+<br>+void Encoder::setPictureFlags(int idx)<br>+{<br>+    m_dupBuffer[idx]->bOccupied = true;<br>+    m_dupBuffer[idx]->bufUpdated = true;<br>+    m_dupBuffer[idx]->bDup = false;<br>+}<br>+<br>+void Encoder::unsetPictureFlags(int idx)<br>+{<br>+    if (idx == 1)<br>+    {<br>+        m_dupBuffer[idx]->bOccupied = false;<br>+        m_dupBuffer[idx]->bufUpdated = false;<br>+        m_dupBuffer[idx]->bDup = false;<br>+    }<br>+    else if (idx == 0)<br>+    {<br>+        m_dupBuffer[idx]->bufUpdated = false;<br>+    }<br> }<br> <br> /**<br>@@ -1327,7 +1387,9 @@<br>     const x265_picture* inputPic = NULL;<br>     static int written = 0, read = 0;<br>     bool dontRead = false;<br>-<br>+    bool isScenecutEnabled = m_param->bHistBasedSceneCut;<br>+    bool dropflag = false;<br>+<br>     if (m_exportedPic)<br>     {<br>         if (!m_param->bUseAnalysisFile && m_param->analysisSave)<br>@@ -1336,9 +1398,9 @@<br>         m_exportedPic = NULL;<br>         m_dpb->recycleUnreferenced();<br>     }<br>-    if ((pic_in && (!m_param->chunkEnd || (m_encodedFrameNum < m_param->chunkEnd))) || (m_param->bEnableFrameDuplication && !pic_in && (read < written)))<br>-    {<br>-        if ((m_param->bEnableFrameDuplication && !pic_in && (read < written)))<br>+    if ((pic_in && (!m_param->chunkEnd || (m_encodedFrameNum < m_param->chunkEnd))) || (m_param->bEnableFrameDuplication && !pic_in && (read < written)) || (isScenecutEnabled && !pic_in && (read < written)))<br>+    {<br>+        if ((m_param->bEnableFrameDuplication && !pic_in && (read < written)) || (isScenecutEnabled && !pic_in && (read < written)))<br>             dontRead = true;<br>         else<br>         {<br>@@ -1361,7 +1423,7 @@<br>             }<br>         }<br> <br>-        if (m_param->bEnableFrameDuplication)<br>+        if (m_param->bEnableFrameDuplication || isScenecutEnabled )<br>         {<br>             double psnrWeight = 0;<br> <br>@@ -1372,6 +1434,12 @@<br>                     copyPicture(m_dupBuffer[0]->dupPic, pic_in);<br>                     m_dupBuffer[0]->bOccupied = true;<br>                     written++;<br>+                    if (m_param->bHistBasedSceneCut)<br>+                    {<br>+                        setPictureFlags(0);<br>+                        updateSceneCutAndFrameDuplicateFlags();<br>+                        unsetPictureFlags(0);<br>+                    }<br>                     return 0;<br>                 }<br>                 else if (!m_dupBuffer[1]->bOccupied)<br>@@ -1379,31 +1447,58 @@<br>                     copyPicture(m_dupBuffer[1]->dupPic, pic_in);<br>                     m_dupBuffer[1]->bOccupied = true;<br>                     written++;<br>+                    if (m_param->bHistBasedSceneCut)<br>+                    {<br>+                        setPictureFlags(1);<br>+                        updateSceneCutAndFrameDuplicateFlags();<br>+                        unsetPictureFlags(1);<br>+                    }<br>                 }<br> <br>-                psnrWeight = ComputePSNR(m_dupBuffer[0]->dupPic, m_dupBuffer[1]->dupPic, m_param);<br>-<br>-                if (psnrWeight >= m_param->dupThreshold)<br>+                if (m_param->bEnableFrameDuplication && m_param->bHistBasedSceneCut)<br>                 {<br>-                    if (m_dupBuffer[0]->bDup)<br>+                    if (m_dupBuffer[1]->bDup == false && m_dupBuffer[1]->dupPic->analysisData.bScenecut == false)<br>                     {<br>-                        m_dupBuffer[0]->dupPic->picStruct = tripling;<br>-                        m_dupBuffer[0]->bDup = false;<br>-                        read++;<br>+                        psnrWeight = ComputePSNR(m_dupBuffer[0]->dupPic, m_dupBuffer[1]->dupPic, m_param);<br>+                        if (psnrWeight >= m_param->dupThreshold)<br>+                            dropflag = true;<br>                     }<br>                     else<br>                     {<br>-                        m_dupBuffer[0]->dupPic->picStruct = doubling;<br>-                        m_dupBuffer[0]->bDup = true;<br>-                        m_dupBuffer[1]->bOccupied = false;<br>-                        read++;<br>-                        return 0;<br>+                        dropflag = true;<br>                     }<br>                 }<br>-                else if (m_dupBuffer[0]->bDup)<br>+                else if (m_param->bEnableFrameDuplication)<br>+                {<br>+                    psnrWeight = ComputePSNR(m_dupBuffer[0]->dupPic, m_dupBuffer[1]->dupPic, m_param);<br>+                    if (psnrWeight >= m_param->dupThreshold)<br>+                        dropflag = true;<br>+                }<br>+<br>+                if (m_param->bEnableFrameDuplication)<br>+                {<br>+                    if (dropflag)<br>+                    {<br>+                        if (m_dupBuffer[0]->bDup)<br>+                        {<br>+                            m_dupBuffer[0]->dupPic->picStruct = tripling;<br>+                            m_dupBuffer[0]->bDup = false;<br>+                            read++;<br>+                        }<br>+                        else<br>+                        {<br>+                            m_dupBuffer[0]->dupPic->picStruct = doubling;<br>+                            m_dupBuffer[0]->bDup = true;<br>+                            m_dupBuffer[1]->bOccupied = false;<br>+                            read++;<br>+                            return 0;<br>+                        }<br>+                    }<br>+                    else if (m_dupBuffer[0]->bDup)<br>                     m_dupBuffer[0]->bDup = false;<br>-                else<br>-                    m_dupBuffer[0]->dupPic->picStruct = 0;<br>+                    else<br>+                        m_dupBuffer[0]->dupPic->picStruct = 0;<br>+                }<br>             }<br> <br>             if (read < written)<br>@@ -1485,7 +1580,11 @@<br> <br>         inFrame->m_poc       = ++m_pocLast;<br>         inFrame->m_userData  = inputPic->userData;<br>-        inFrame->m_pts       = inputPic->pts;<br>+        inFrame->m_pts = inputPic->pts;<br>+        if (m_param->bHistBasedSceneCut)<br>+        {<br>+           inFrame->m_lowres.bScenecut = inputPic->analysisData.bScenecut;<br>+        }<br>         inFrame->m_forceqp   = inputPic->forceqp;<br>         inFrame->m_param     = (m_reconfigure || m_reconfigureRc) ? m_latestParam : m_param;<br>         inFrame->m_picStruct = inputPic->picStruct;<br>@@ -1613,7 +1712,7 @@<br>             m_param->bUseRcStats = 0;<br>         }<br> <br>-        if (m_param->bEnableFrameDuplication && ((read < written) || (m_dupBuffer[0]->dupPic->picStruct == tripling && (read <= written))))<br>+        if ( (m_param->bEnableFrameDuplication || isScenecutEnabled) && ((read < written) || (m_dupBuffer[0]->dupPic->picStruct == tripling && (read <= written))))<br>         {<br>             if (m_dupBuffer[0]->dupPic->picStruct == tripling)<br>                 m_dupBuffer[0]->bOccupied = m_dupBuffer[1]->bOccupied = false;<br>@@ -3162,6 +3261,7 @@<br>          * adaptive I frame placement */<br>         p->keyframeMax = INT_MAX;<br>         p->scenecutThreshold = 0;<br>+        p->bHistBasedSceneCut = 0;<br>     }<br>     else if (p->keyframeMax <= 1)<br>     {<br>@@ -3175,6 +3275,7 @@<br>         p->lookaheadDepth = 0;<br>         p->bframes = 0;<br>         p->scenecutThreshold = 0;<br>+        p->bHistBasedSceneCut = 0;<br>         p->bFrameAdaptive = 0;<br>         p->rc.cuTree = 0;<br>         p->bEnableWeightedPred = 0;<br>@@ -3828,6 +3929,20 @@<br>             m_param->searchMethod = m_param->hmeSearchMethod[2];<br>         }<br>     }<br>+<br>+    if (p->bHistBasedSceneCut && p->scenecutThreshold) {<br>+        p->scenecutThreshold = 0;<br>+        p->bHistBasedSceneCut = false;<br>+        x265_log(p, X265_LOG_WARNING, "Amibigious choice. disabling scene cut detection \n");<br>+    }<br>+    else if (p->scenecutThreshold && p->edgeTransitionThreshold != 0.01) {<br>+        x265_log(p, X265_LOG_WARNING, "using  scenecut-bias %.2lf for scene cut detection\n",p->scenecutBias);<br>+    }<br>+    else if (p->bHistBasedSceneCut && p->edgeTransitionThreshold == 0.0) {<br>+        p->edgeTransitionThreshold = 0.01;<br>+        x265_log(p, X265_LOG_INFO, "using  default threshold %.2lf for scene cut detection\n", p->edgeTransitionThreshold);<br>+    }<br>+<br> }<br> <br> void Encoder::readAnalysisFile(x265_analysis_data* analysis, int curPoc, const x265_picture* picIn, int paramBytes)<br>diff -r 978a57943c8f -r c6d2c4475363 source/encoder/encoder.h<br>--- a/source/encoder/encoder.h Thu Oct 17 18:43:11 2019 +0530<br>+++ b/source/encoder/encoder.h Thu Oct 17 19:12:09 2019 +0530<br>@@ -32,6 +32,8 @@<br> #include "nal.h"<br> #include "framedata.h"<br> #include "svt.h"<br>+#include "histscenecut.h"<br>+<br> #ifdef ENABLE_HDR10_PLUS<br>     #include "dynamicHDR10/hdr10plus.h"<br> #endif<br>@@ -154,6 +156,9 @@<br> <br>     //Flag to check whether the picture has duplicated.<br>     bool bDup;<br>+<br>+    bool bufUpdated;<br>+<br> };<br> <br> <br>@@ -195,6 +200,9 @@<br> <br>     ThreadPool*        m_threadPool;<br>     FrameEncoder*      m_frameEncoder[X265_MAX_FRAME_THREADS];<br>+<br>+    YuvHistogram*      m_histogramsOfAdjFrames;<br>+    sad_stats*         m_sadStats;<br>     DPB*               m_dpb;<br>     Frame*             m_exportedPic;<br>     FILE*              m_analysisFileIn;<br>@@ -279,6 +287,10 @@<br>         if (m_prevTonemapPayload.payload != NULL)<br>             X265_FREE(m_prevTonemapPayload.payload);<br> #endif<br>+        delete m_sadStats;<br>+        m_sadStats = NULL;<br>+        delete[] m_histogramsOfAdjFrames;<br>+        m_histogramsOfAdjFrames = NULL;<br>     };<br> <br>     void create();<br>@@ -349,6 +361,12 @@<br> <br>     void copyPicture(x265_picture *dest, const x265_picture *src);<br> <br>+    void unsetPictureFlags(int index);<br>+<br>+    void setPictureFlags(int index);<br>+<br>+    void updateSceneCutAndFrameDuplicateFlags();<br>+<br>     void initRefIdx();<br>     void analyseRefIdx(int *numRefIdx);<br>     void updateRefIdx();<br>@@ -364,6 +382,7 @@<br>     void initSPS(SPS *sps);<br>     void initPPS(PPS *pps);<br> };<br>+<br> }<br> <br> #endif // ifndef X265_ENCODER_H<br>diff -r 978a57943c8f -r c6d2c4475363 source/encoder/ratecontrol.cpp<br>--- a/source/encoder/ratecontrol.cpp Thu Oct 17 18:43:11 2019 +0530<br>+++ b/source/encoder/ratecontrol.cpp Thu Oct 17 19:12:09 2019 +0530<br>@@ -493,6 +493,7 @@<br>                 CMP_OPT_FIRST_PASS("open-gop", m_param->bOpenGOP);<br>                 CMP_OPT_FIRST_PASS(" keyint", m_param->keyframeMax);<br>                 CMP_OPT_FIRST_PASS("scenecut", m_param->scenecutThreshold);<br>+                CMP_OPT_FIRST_PASS("hist-threshold", m_param->edgeTransitionThreshold);<br>                 CMP_OPT_FIRST_PASS("intra-refresh", m_param->bIntraRefresh);<br>                 if (m_param->bMultiPassOptRPS)<br>                 {<br>@@ -1183,6 +1184,7 @@<br>             m_param->rc.bStatRead = 0;<br>             m_param->bFrameAdaptive = 0;<br>             m_param->scenecutThreshold = 0;<br>+            m_param->bHistBasedSceneCut = false;<br>             m_param->rc.cuTree = 0;<br>             if (m_param->bframes > 1)<br>                 m_param->bframes = 1;<br>@@ -2173,7 +2175,7 @@<br>     if (m_isVbv && m_currentSatd > 0 && curFrame)<br>     {<br>         if (m_param->lookaheadDepth || m_param->rc.cuTree ||<br>-            m_param->scenecutThreshold ||<br>+            (m_param->scenecutThreshold || m_param->bHistBasedSceneCut) ||<br>             (m_param->bFrameAdaptive && m_param->bframes))<br>         {<br>            /* Lookahead VBV: If lookahead is done, raise the quantizer as necessary<br>diff -r 978a57943c8f -r c6d2c4475363 source/encoder/slicetype.cpp<br>--- a/source/encoder/slicetype.cpp Thu Oct 17 18:43:11 2019 +0530<br>+++ b/source/encoder/slicetype.cpp Thu Oct 17 19:12:09 2019 +0530<br>@@ -30,6 +30,7 @@<br> #include "primitives.h"<br> #include "lowres.h"<br> #include "mv.h"<br>+#include "histscenecut.h"<br> <br> #include "slicetype.h"<br> #include "motion.h"<br>@@ -114,8 +115,8 @@<br>     //Applying Gaussian filter on the picture<br>     src = (pixel*)curFrame->m_fencPic->m_picOrg[0];<br>     refPic = curFrame->m_gaussianPic + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;<br>+//    edgePic = curFrame->m_edgePic + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;<br>     pixel pixelValue = 0;<br>-<br>     for (int rowNum = 0; rowNum < height; rowNum++)<br>     {<br>         for (int colNum = 0; colNum < width; colNum++)<br>@@ -127,7 +128,7 @@<br>                  1  [4   9   12  9   4]<br>                 --- [5   12  15  12  5]<br>                 159 [4   9   12  9   4]<br>-                    [2   4   5   4   2]*/<br>+                    [2   4   5   4   2] */<br> <br>                 const intptr_t rowOne = (rowNum - 2)*stride, colOne = colNum - 2;<br>                 const intptr_t rowTwo = (rowNum - 1)*stride, colTwo = colNum - 1;<br>@@ -145,52 +146,8 @@<br>             }<br>         }<br>     }<br>-<br>-#if HIGH_BIT_DEPTH //10-bit build<br>-    float threshold = 1023;<br>-    pixel whitePixel = 1023;<br>-#else<br>-    float threshold = 255;<br>-    pixel whitePixel = 255;<br>-#endif<br>-#define PI 3.14159265<br>-<br>-    float gradientH = 0, gradientV = 0, radians = 0, theta = 0;<br>-    float gradientMagnitude = 0;<br>-    pixel blackPixel = 0;<br>-    edgePic = curFrame->m_edgePic + 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>-                /*Horizontal and vertical gradients<br>-                       [ -3   0   3 ]        [-3   -10  -3 ]<br>-                  gH = [ -10  0   10]   gV = [ 0    0    0 ]<br>-                       [ -3   0   3 ]        [ 3    10   3 ]*/<br>-<br>-                const intptr_t rowOne = (rowNum - 1)*stride, colOne = colNum -1;<br>-                const intptr_t rowTwo = rowNum * stride, colTwo = colNum;<br>-                const intptr_t rowThree = (rowNum + 1)*stride, colThree = colNum + 1;<br>-                const intptr_t index = (rowNum*stride) + colNum;<br>-<br>-                gradientH = (float)(-3 * refPic[rowOne + colOne] + 3 * refPic[rowOne + colThree] - 10 * refPic[rowTwo + colOne] + 10 * refPic[rowTwo + colThree] - 3 * refPic[rowThree + colOne] + 3 * refPic[rowThree + colThree]);<br>-                gradientV = (float)(-3 * refPic[rowOne + colOne] - 10 * refPic[rowOne + colTwo] - 3 * refPic[rowOne + colThree] + 3 * refPic[rowThree + colOne] + 10 * refPic[rowThree + colTwo] + 3 * refPic[rowThree + colThree]);<br>-<br>-                gradientMagnitude = sqrtf(gradientH * gradientH + gradientV * gradientV);<br>-                radians = atan2(gradientV, gradientH);<br>-                theta = (float)((radians * 180) / PI);<br>-                if (theta < 0)<br>-                    theta = 180 + theta;<br>-                edgeTheta[(rowNum*stride) + colNum] = (pixel)theta;<br>-<br>-                edgePic[index] = gradientMagnitude >= threshold ? whitePixel : blackPixel;<br>-            }<br>-        }<br>-    }<br>+    if(!computeEdge(edgePic, refPic, edgeTheta, stride, height, width))<br>+        x265_log(NULL, X265_LOG_ERROR, "Failed edge computation!");<br> }<br> <br> //Find the angle of a block by averaging the pixel angles<br>@@ -1471,7 +1428,7 @@<br> <br>     if (m_lastNonB && !m_param->rc.bStatRead &&<br>         ((m_param->bFrameAdaptive && m_param->bframes) ||<br>-         m_param->rc.cuTree || m_param->scenecutThreshold ||<br>+         m_param->rc.cuTree || m_param->scenecutThreshold || m_param->bHistBasedSceneCut ||<br>          (m_param->lookaheadDepth && m_param->rc.vbvBufferSize)))<br>     {<br>         slicetypeAnalyse(frames, false);<br>@@ -1962,10 +1919,15 @@<br> <br>     int numBFrames = 0;<br>     int numAnalyzed = numFrames;<br>-    bool isScenecut = scenecut(frames, 0, 1, true, origNumFrames);<br>+    bool isScenecut = false;<br> <br>     /* When scenecut threshold is set, use scenecut detection for I frame placements */<br>-    if (m_param->scenecutThreshold && isScenecut)<br>+    if (m_param->scenecutThreshold)<br>+        isScenecut = scenecut(frames, 0, 1, true, origNumFrames);<br>+    else if (m_param->bHistBasedSceneCut)<br>+        isScenecut = frames[1]->bScenecut;<br>+<br>+    if (isScenecut)<br>     {<br>         frames[1]->sliceType = X265_TYPE_I;<br>         return;<br>@@ -1976,14 +1938,24 @@<br>         m_extendGopBoundary = false;<br>         for (int i = m_param->bframes + 1; i < origNumFrames; i += m_param->bframes + 1)<br>         {<br>-            scenecut(frames, i, i + 1, true, origNumFrames);<br>+            if (m_param->scenecutThreshold)<br>+               scenecut(frames, i, i + 1, true, origNumFrames);<br>+<br>             for (int j = i + 1; j <= X265_MIN(i + m_param->bframes + 1, origNumFrames); j++)<br>             {<br>-                if (frames[j]->bScenecut && scenecutInternal(frames, j - 1, j, true) )<br>-                {<br>-                    m_extendGopBoundary = true;<br>-                    break;<br>-                }<br>+                if (m_param->scenecutThreshold)<br>+                    {<br>+                        if (frames[j]->bScenecut && scenecutInternal(frames, j - 1, j, true))<br>+                        {<br>+                               m_extendGopBoundary = true;<br>+                               break;<br>+                        }<br>+                    }<br>+                    else if(m_param->bHistBasedSceneCut && frames[j]->bScenecut)<br>+                    {<br>+                            m_extendGopBoundary = true;<br>+                            break;<br>+                    }<br>             }<br>             if (m_extendGopBoundary)<br>                 break;<br>@@ -2088,13 +2060,25 @@<br>         {<br>             for (int j = 1; j < numBFrames + 1; j++)<br>             {<br>-                if (scenecut(frames, j, j + 1, false, origNumFrames) ||<br>-                    (bForceRADL && (frames[j]->frameNum == preRADL)))<br>+                if (m_param->bHistBasedSceneCut)<br>                 {<br>-                    frames[j]->sliceType = X265_TYPE_P;<br>-                    numAnalyzed = j;<br>-                    break;<br>+                    if (frames[j]->bScenecut || (bForceRADL && (frames[j]->frameNum == preRADL)))<br>+                    {<br>+                        frames[j]->sliceType = X265_TYPE_P;<br>+                        numAnalyzed = j;<br>+                        break;<br>+                    }<br>                 }<br>+                else if (m_param->scenecutThreshold)<br>+                {<br>+                    if ( scenecut(frames, j, j + 1, false, origNumFrames) || (bForceRADL && (frames[j]->frameNum == preRADL)) )<br>+                    {<br>+                        frames[j]->sliceType = X265_TYPE_P;<br>+                        numAnalyzed = j;<br>+                        break;<br>+                    }<br>+                }<br>+<br>             }<br>         }<br>         resetStart = bKeyframe ? 1 : X265_MIN(numBFrames + 2, numAnalyzed + 1);<br>diff -r 978a57943c8f -r c6d2c4475363 source/test/regression-tests.txt<br>--- a/source/test/regression-tests.txt Thu Oct 17 18:43:11 2019 +0530<br>+++ b/source/test/regression-tests.txt Thu Oct 17 19:12:09 2019 +0530<br>@@ -158,6 +158,9 @@<br> ducks_take_off_420_1_720p50.y4m,--preset medium --selective-sao 4 --sao --crf 20<br> Traffic_4096x2048_30p.y4m, --preset medium --frame-dup --dup-threshold 60 --hrd --bitrate 10000 --vbv-bufsize 15000 --vbv-maxrate 12000<br> Kimono1_1920x1080_24_400.yuv,--preset superfast --qp 28 --zones 0,139,q=32<br>+sintel_trailer_2k_1920x1080_24.yuv, --preset medium --hist-scenecut --hist-threshold 0.01<br>+Traffic_4096x2048_30p.y4m, --preset medium --frame-dup --dup-threshold 60 --hrd --bitrate 10000 --vbv-bufsize 15000 --vbv-maxrate 12000 --hist-scenecut --hist-threshold 0.01<br>+sintel_trailer_2k_1920x1080_24.yuv, --preset medium --scenecut 40 --scenecut-bias 20<br> <br> # Main12 intraCost overflow bug test<br> 720p50_parkrun_ter.y4m,--preset medium<br>diff -r 978a57943c8f -r c6d2c4475363 source/x265cli.h<br>--- a/source/x265cli.h Thu Oct 17 18:43:11 2019 +0530<br>+++ b/source/x265cli.h Thu Oct 17 19:12:09 2019 +0530<br>@@ -129,6 +129,9 @@<br>     { "scenecut",       required_argument, NULL, 0 },<br>     { "no-scenecut",          no_argument, NULL, 0 },<br>     { "scenecut-bias",  required_argument, NULL, 0 },<br>+    { "hist-scenecut",  no_argument, NULL, 0},<br>+    { "no-hist-scenecut", no_argument, NULL, 0},<br>+    { "hist-threshold", required_argument, NULL, 0},<br>     { "fades",                no_argument, NULL, 0 },<br>     { "no-fades",             no_argument, NULL, 0 },<br>     { "radl",           required_argument, NULL, 0 },<br>@@ -485,7 +488,10 @@<br>     H0("   --gop-lookahead <integer>     Extends gop boundary if a scenecut is found within this from keyint boundary. Default 0\n");<br>     H0("   --no-scenecut                 Disable adaptive I-frame decision\n");<br>     H0("   --scenecut <integer>          How aggressively to insert extra I-frames. Default %d\n", param->scenecutThreshold);<br>-    H1("   --scenecut-bias <0..100.0>    Bias for scenecut detection. Default %.2f\n", param->scenecutBias);<br>+    H0("   --hist-scenecut .....         Enables improved scene-cut detection using histogram based algorithm.");<br>+    H0("   --no-hist-scenecut            Disables improved scene-cut detection using histogram based algorithm. ");<br>+    H0("   --scenecut-bias <0..100.0>    Bias for scenecut detection. Default %.2f\n", param->scenecutBias);<br>+    H0("   --hist-threshold <0.0..2.0>   Threshold for histogram based scenecut detection Default %.2f\n", param->edgeTransitionThreshold);<br>     H0("   --[no-]fades                  Enable detection and handling of fade-in regions. Default %s\n", OPT(param->bEnableFades));<br>     H0("   --radl <integer>              Number of RADL pictures allowed in front of IDR. Default %d\n", param->radl);<br>     H0("   --intra-refresh               Use Periodic Intra Refresh instead of IDR frames\n");<font color="#888888"><br><div><br></div></font><div><br></div>-- <br><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><b style="background-color:rgb(255,255,255)"><font color="#0b5394">With Regards,</font></b><div><b style="background-color:rgb(255,255,255)"><font color="#0b5394">Srikanth Kurapati.</font></b></div></div></div></div>