[x265] [PATCH] Improved scenecut detection

gopi.satykrishna at multicorewareinc.com gopi.satykrishna at multicorewareinc.com
Tue Oct 15 08:55:22 CEST 2019


# HG changeset patch
# User gopi Satykrishna Akisetty
# Date 1571121977 -19800
#      Tue Oct 15 12:16:17 2019 +0530
# Node ID deaecadc43060ba37a85d9724a1a306a86433432
# Parent  37648fca915b389bafe923d8443818359e80ebf2
Improved scenecut detection

This patch does the following.
1. identifies scenecuts using sad of edge and chroma histogram based thresholding.
2. 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.
3. Removed duplicate edgefilter code and created a global function for use in scene cut detection and aq in Lookahead.

diff -r 37648fca915b -r deaecadc4306 doc/reST/cli.rst
--- a/doc/reST/cli.rst	Fri Oct 11 12:45:52 2019 +0530
+++ b/doc/reST/cli.rst	Tue Oct 15 12:16:17 2019 +0530
@@ -1427,6 +1427,18 @@
 	intra cost of a frame used in scenecut detection. For example, a value of 5 indicates,
 	if the inter cost of a frame is greater than or equal to 95 percent of the intra cost of the frame,
 	then detect this frame as scenecut. Values between 5 and 15 are recommended. Default 5.
+
+.. option:: --hist-scenecut, --no-hist-scenecut
+
+	indicates that I-frames need to be inserted using edge and color histogram based scenecut algorithm.
+	option: `--hist-scencut` 1 enables adaptive I frame placement using this method and disables the default scene cut algorithm.
+	option:`--no-hist-scenecut` adaptive I frame placement.
+	
+.. option:: --hist-threshold <0.0..2.0>
+
+	This value represents the threshold for SAD for edge histograms used in scenecut detection. This requires hist-scenecut to be enabled.
+	For example, a value of 0.2 indicates that a frame with SAD value greater than 0.2 against the previous frame  as scenecut.
+	Values between 0.0 and 2.0 are recommended. Default 0.1.
 	
 .. option:: --radl <integer>
 	
diff -r 37648fca915b -r deaecadc4306 source/CMakeLists.txt
--- a/source/CMakeLists.txt	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/CMakeLists.txt	Tue Oct 15 12:16:17 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 180)
+set(X265_BUILD 181)
 configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
                "${PROJECT_BINARY_DIR}/x265.def")
 configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff -r 37648fca915b -r deaecadc4306 source/common/CMakeLists.txt
--- a/source/common/CMakeLists.txt	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/common/CMakeLists.txt	Tue Oct 15 12:16:17 2019 +0530
@@ -151,4 +151,5 @@
     predict.cpp  predict.h
     scalinglist.cpp scalinglist.h
     quant.cpp quant.h contexts.h
-    deblock.cpp deblock.h)
+    deblock.cpp deblock.h
+    scenecut.h scenecut.cpp)
diff -r 37648fca915b -r deaecadc4306 source/common/common.h
--- a/source/common/common.h	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/common/common.h	Tue Oct 15 12:16:17 2019 +0530
@@ -129,14 +129,20 @@
 typedef uint64_t sum2_t;
 typedef uint64_t pixel4;
 typedef int64_t  ssum2_t;
+#define HISTOGRAM_SIZE 1024
+#define SHIFT 1
 #else
 typedef uint8_t  pixel;
 typedef uint16_t sum_t;
 typedef uint32_t sum2_t;
 typedef uint32_t pixel4;
 typedef int32_t  ssum2_t; // Signed sum
+#define HISTOGRAM_SIZE 256
+#define SHIFT 0
 #endif // if HIGH_BIT_DEPTH
 
+#define PI 3.14159265
+
 #if X265_DEPTH < 10
 typedef uint32_t sse_t;
 #else
diff -r 37648fca915b -r deaecadc4306 source/common/param.cpp
--- a/source/common/param.cpp	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/common/param.cpp	Tue Oct 15 12:16:17 2019 +0530
@@ -167,6 +167,8 @@
     param->bFrameAdaptive = X265_B_ADAPT_TRELLIS;
     param->bBPyramid = 1;
     param->scenecutThreshold = 40; /* Magic number pulled in from x264 */
+    param->edgeTransitionThreshold = 0.01;
+    param->bHistbasedScenecut = false;
     param->lookaheadSlices = 8;
     param->lookaheadThreads = 0;
     param->scenecutBias = 5.0;
@@ -567,6 +569,7 @@
             param->bframes = 0;
             param->lookaheadDepth = 0;
             param->scenecutThreshold = 0;
+            param->bHistbasedScenecut = false;
             param->rc.cuTree = 0;
             param->frameNumThreads = 1;
         }
@@ -609,7 +612,7 @@
     return 0;
 }
 
-static int x265_atobool(const char* str, bool& bError)
+static bool x265_atobool(const char* str, bool& bError)
 {
     if (!strcmp(str, "1") ||
         !strcmp(str, "true") ||
@@ -920,6 +923,7 @@
         {
             bError = false;
             p->scenecutThreshold = atoi(value);
+            p->bHistbasedScenecut = false;
         }
     }
     OPT("temporal-layers") p->bEnableTemporalSubLayers = atobool(value);
@@ -1186,6 +1190,31 @@
         OPT("opt-ref-list-length-pps") p->bOptRefListLengthPPS = atobool(value);
         OPT("multi-pass-opt-rps") p->bMultiPassOptRPS = atobool(value);
         OPT("scenecut-bias") p->scenecutBias = atof(value);
+        OPT("hist-scenecut")
+        {
+            p->bHistbasedScenecut = atobool(value);
+
+            if (bError)
+            {
+                bError = false;
+                p->bHistbasedScenecut = false;
+            }
+
+            if (p->bHistbasedScenecut) {
+                bError = false;
+                p->scenecutThreshold = 0;
+            }
+
+        }
+        OPT("hist-threshold") {
+            p->edgeTransitionThreshold = atof(value);
+            if (bError)
+            {
+                bError = false;
+                p->edgeTransitionThreshold = 0.01;
+                x265_log(p, X265_LOG_INFO, "using  default threshold %.2lf for scene cut detection\n", p->edgeTransitionThreshold);
+            }
+        }
         OPT("lookahead-threads") p->lookaheadThreads = atoi(value);
         OPT("opt-cu-delta-qp") p->bOptCUDeltaQP = atobool(value);
         OPT("multi-pass-opt-analysis") p->analysisMultiPassRefine = atobool(value);
@@ -1623,8 +1652,14 @@
           "Valid Logging level -1:none 0:error 1:warning 2:info 3:debug 4:full");
     CHECK(param->scenecutThreshold < 0,
           "scenecutThreshold must be greater than 0");
-    CHECK(param->scenecutBias < 0 || 100 < param->scenecutBias,
-           "scenecut-bias must be between 0 and 100");
+    if (param->scenecutThreshold) {
+        CHECK(param->scenecutBias < 0 || 100 < param->scenecutBias,
+            "scenecut-bias must be between 0 and 100");
+    }
+    else if (param->bHistbasedScenecut) {
+        CHECK(param->edgeTransitionThreshold < 0.0 || 2.0 < param->edgeTransitionThreshold,
+            "hist-threshold must be between 0.0 and 2.0");
+    }
     CHECK(param->radl < 0 || param->radl > param->bframes,
           "radl must be between 0 and bframes");
     CHECK(param->rdPenalty < 0 || param->rdPenalty > 2,
@@ -1780,10 +1815,20 @@
         x265_log(param, X265_LOG_INFO, "ME / range / subpel / merge         : %s / %d / %d / %d\n",
             x265_motion_est_names[param->searchMethod], param->searchRange, param->subpelRefine, param->maxNumMergeCand);
 
-    if (param->keyframeMax != INT_MAX || param->scenecutThreshold)
-        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);
+    if (param->scenecutThreshold && param->keyframeMax != INT_MAX) 
+        param->edgeTransitionThreshold = 0.0;
+    else if (param->bHistbasedScenecut && param->keyframeMax != INT_MAX) 
+        param->scenecutBias = 0.0;
+    else if (param->keyframeMax != INT_MAX) {
+        param->edgeTransitionThreshold = 0.0;
+        param->scenecutBias = 0.0;
+    }
+        
+    if (param->keyframeMax == INT_MAX)
+        x265_log(param, X265_LOG_INFO, "Keyframe min / max / scenecut       : disabled\n");
     else
-        x265_log(param, X265_LOG_INFO, "Keyframe min / max / scenecut       : disabled\n");
+        x265_log(param, X265_LOG_INFO, "Keyframe min / max / scenecut / bias / threshold  : %d / %d / %d / %.2lf / %.2lf\n", param->keyframeMin, param->keyframeMax, ( param->bHistbasedScenecut || param->scenecutThreshold ), param->scenecutBias * 100, param->edgeTransitionThreshold);
+
 
     if (param->cbQpOffset || param->crQpOffset)
         x265_log(param, X265_LOG_INFO, "Cb/Cr QP Offset                     : %d / %d\n", param->cbQpOffset, param->crQpOffset);
@@ -1949,6 +1994,8 @@
     s += sprintf(s, " rc-lookahead=%d", p->lookaheadDepth);
     s += sprintf(s, " lookahead-slices=%d", p->lookaheadSlices);
     s += sprintf(s, " scenecut=%d", p->scenecutThreshold);
+    s += sprintf(s, " hist-scenecut=%d", p->bHistbasedScenecut);
+    s += sprintf(s, " hist-threshold=%.2f", p->edgeTransitionThreshold);
     s += sprintf(s, " radl=%d", p->radl);
     BOOL(p->bEnableHRDConcatFlag, "splice");
     BOOL(p->bIntraRefresh, "intra-refresh");
@@ -2096,6 +2143,8 @@
     BOOL(p->bOptRefListLengthPPS, "opt-ref-list-length-pps");
     BOOL(p->bMultiPassOptRPS, "multi-pass-opt-rps");
     s += sprintf(s, " scenecut-bias=%.2f", p->scenecutBias);
+    s += sprintf(s, " hist-threshold=%.2f", p->edgeTransitionThreshold);
+    
     BOOL(p->bOptCUDeltaQP, "opt-cu-delta-qp");
     BOOL(p->bAQMotion, "aq-motion");
     BOOL(p->bEmitHDRSEI, "hdr");
@@ -2246,6 +2295,7 @@
     dst->lookaheadSlices = src->lookaheadSlices;
     dst->lookaheadThreads = src->lookaheadThreads;
     dst->scenecutThreshold = src->scenecutThreshold;
+    dst->bHistbasedScenecut = src->bHistbasedScenecut;
     dst->bIntraRefresh = src->bIntraRefresh;
     dst->maxCUSize = src->maxCUSize;
     dst->minCUSize = src->minCUSize;
@@ -2403,6 +2453,7 @@
     dst->bOptRefListLengthPPS = src->bOptRefListLengthPPS;
     dst->bMultiPassOptRPS = src->bMultiPassOptRPS;
     dst->scenecutBias = src->scenecutBias;
+    dst->edgeTransitionThreshold = src->edgeTransitionThreshold;
     dst->gopLookahead = src->lookaheadDepth;
     dst->bOptCUDeltaQP = src->bOptCUDeltaQP;
     dst->analysisMultiPassDistortion = src->analysisMultiPassDistortion;
diff -r 37648fca915b -r deaecadc4306 source/common/scenecut.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/common/scenecut.cpp	Tue Oct 15 12:16:17 2019 +0530
@@ -0,0 +1,674 @@
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <algorithm>
+#include <math.h>
+#include "encoder.h"
+#include "scenecut.h"
+#include "slicetype.h"
+
+using namespace std;
+using namespace X265_NS;
+
+#define EDGE_PLANE_COUNT 1
+
+namespace X265_NS {
+
+void computeEdge(pixel * edgePic, pixel *refPic, pixel * edgeTheta, intptr_t stride, int height, int width)
+    {
+        float gradientH = 0, gradientV = 0, radians = 0, theta = 0;
+        float gradientMagnitude = 0;
+        pixel blackPixel = 0;
+
+        //Applying Sobel filter
+        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 ]*/
+
+                    const intptr_t rowOne = (rowNum - 1)*stride, colOne = colNum - 1;
+                    const intptr_t rowTwo = rowNum * stride, colTwo = colNum;
+                    const intptr_t rowThree = (rowNum + 1)*stride, colThree = colNum + 1;
+                    const intptr_t index = (rowNum*stride) + colNum;
+
+                    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]);
+                    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]);
+
+                    gradientMagnitude = sqrtf(gradientH * gradientH + gradientV * gradientV);
+                    radians = atan2(gradientV, gradientH);
+                    theta = (float)((radians * 180) / PI);
+                    if (theta < 0)
+                        theta = 180 + theta;
+                    edgeTheta[(rowNum*stride) + colNum] = (pixel)theta;
+
+                    edgePic[index] = (pixel)(gradientMagnitude >= edge_threshold ? whitePixel : blackPixel);
+                }
+            }
+        }
+    }
+
+    Histogram::Histogram() 
+    {
+        memset(frequency_distribution, 0, HISTOGRAM_SIZE * sizeof(int32_t));
+    }
+
+    Histogram::Histogram(Histogram const& hist) {
+        memset(frequency_distribution, 0, HISTOGRAM_SIZE * sizeof(int32_t));
+        for (int i = 0; i < HISTOGRAM_SIZE; i++) {
+            frequency_distribution[i] = hist.frequency_distribution[i];
+        }
+    }
+
+    Histogram & Histogram::operator=(Histogram const& hist)
+    {
+        memset(frequency_distribution, 0, HISTOGRAM_SIZE * sizeof(int32_t));
+        memcpy(frequency_distribution, hist.frequency_distribution, sizeof(int32_t)*HISTOGRAM_SIZE);
+        return *this;
+    }
+
+    Histogram::~Histogram() {}
+
+    YuvHistogram::YuvHistogram() {}
+
+    void YuvHistogram::initHistograms(int32_t planecount) {
+        this->plane_count = planecount;
+        m_isalloc = false;
+        bisUpdated = false;
+        param = NULL;
+
+        yuv_hist = edge_hist = NULL;
+        plane_sizes = plane_heights = plane_widths = NULL;
+        edgePic =  edgeThetaPic = NULL;
+
+        plane_sizes = X265_MALLOC(int32_t, plane_count);
+        plane_heights = X265_MALLOC(int32_t, plane_count);
+        plane_widths = X265_MALLOC(int32_t, plane_count);
+
+        if (!plane_sizes || !plane_heights || !plane_widths) {
+            x265_log(param, X265_LOG_ERROR, "unable to allocate memory for plane dimensions\n");
+            m_isalloc &= false;
+        }
+        else {
+            memset(plane_sizes, 0, plane_count * sizeof(int32_t));
+            memset(plane_heights, 0, plane_count * sizeof(int32_t));
+            memset(plane_widths, 0, plane_count * sizeof(int32_t));
+            m_isalloc &= true;
+        }
+
+        yuv_hist = X265_MALLOC(Histogram, plane_count);
+        edge_hist = X265_MALLOC(Histogram, plane_count);
+
+        if (!yuv_hist || !edge_hist) {
+            m_isalloc &= false;
+            x265_log(param, X265_LOG_ERROR, "unable to allocate memory for histograms\n");
+        }
+
+    }
+
+    void YuvHistogram::initHistograms(x265_param *p) {
+        param = p;
+        plane_count = x265_cli_csps[param->internalCsp].planes;
+        bisUpdated = false;
+        m_isalloc = false;
+
+        yuv_hist = edge_hist = NULL;
+        plane_sizes = plane_heights = plane_widths = NULL;
+        edgePic = edgeThetaPic = NULL;
+
+        plane_sizes = X265_MALLOC(int32_t, plane_count);
+        plane_heights = X265_MALLOC(int32_t, plane_count);
+        plane_widths = X265_MALLOC(int32_t, plane_count);
+
+        if (!plane_sizes || !plane_heights || !plane_widths) {
+            x265_log(param, X265_LOG_ERROR, "unable to allocate memory for plane dimensions\n");
+            m_isalloc &= false;
+        }
+        else {
+            memset(plane_sizes, 0, plane_count * sizeof(int32_t));
+            memset(plane_heights, 0, plane_count * sizeof(int32_t));
+            memset(plane_widths, 0, plane_count * sizeof(int32_t));
+            m_isalloc &= true;
+        }
+
+        yuv_hist = X265_MALLOC(Histogram, plane_count);
+        edge_hist = X265_MALLOC(Histogram, plane_count);
+
+        if (!yuv_hist || !edge_hist) {
+            m_isalloc &= false;
+            x265_log(param, X265_LOG_ERROR, "unable to allocate memory for histograms\n");
+        }
+
+    }
+
+    bool YuvHistogram::allocHistogramBuffers() {
+        //allocate memory for edge filter output and histograms
+        bool isalloc = true;
+
+        edgePic = X265_MALLOC(pixel*, plane_count);
+        edgeThetaPic = X265_MALLOC(pixel*, plane_count);
+
+        if (!edgePic || !edgeThetaPic) {
+            isalloc &= false;
+            x265_log(param, X265_LOG_ERROR, "unable to allocate memory for edge buffers\n");
+            return isalloc;
+        }
+
+         for (int i = 0; i < plane_count; i++) {
+
+            edgePic[i] = edgeThetaPic[i] = NULL;
+            edgePic[i] = X265_MALLOC(pixel, plane_sizes[i]);
+            edgeThetaPic[i] = X265_MALLOC(pixel, plane_sizes[i]);
+
+            if (edgePic[i] && edgeThetaPic[i]) {
+                memset(edgePic[i], 0, plane_sizes[i] * sizeof(pixel));
+                memset(edgeThetaPic[i], 0, plane_sizes[i] * sizeof(pixel));
+                isalloc &= true;
+            }
+            else
+                isalloc &= false;
+        }
+        return isalloc;
+    }
+
+    YuvHistogram::~YuvHistogram()
+    {
+        freeHistogramBuffers(); //change implementation based on allocation changes 
+    }
+
+    YuvHistogram::YuvHistogram(YuvHistogram const& hist) {
+
+        maxuv_hist = hist.maxuv_hist;
+        plane_count = hist.plane_count;
+        bisUpdated = hist.bisUpdated;
+        param = hist.param;
+        memcpy(plane_sizes, hist.plane_sizes, plane_count * sizeof(int32_t));
+        memcpy(plane_heights, hist.plane_heights, plane_count * sizeof(int32_t));
+        memcpy(plane_widths, hist.plane_widths, plane_count * sizeof(int32_t));
+        memcpy(yuv_hist, hist.yuv_hist, plane_count * sizeof(Histogram));
+        memcpy(edge_hist, hist.edge_hist, plane_count * sizeof(Histogram));
+
+        if (!m_isalloc) {
+            m_isalloc = false;
+            m_isalloc = allocHistogramBuffers();
+        }
+
+        if (m_isalloc) {
+            for (int i = 0; i < plane_count; i++) {
+                if (edgePic[i] && edgeThetaPic[i]) {
+                    memcpy(edgePic[i], hist.edgePic[i], plane_sizes[i] * sizeof(pixel));
+                    memcpy(edgeThetaPic[i], hist.edgeThetaPic[i], plane_sizes[i] * sizeof(pixel));
+                }
+            }
+        }
+
+    }
+
+    YuvHistogram & YuvHistogram ::operator=(const YuvHistogram & copy_hist)
+    {
+        maxuv_hist = copy_hist.maxuv_hist;
+        plane_count = copy_hist.plane_count;
+        bisUpdated = copy_hist.bisUpdated;
+        param = copy_hist.param;
+        memcpy(plane_sizes, copy_hist.plane_sizes, plane_count * sizeof(int32_t));
+        memcpy(plane_heights, copy_hist.plane_heights, plane_count * sizeof(int32_t));
+        memcpy(plane_widths, copy_hist.plane_widths, plane_count * sizeof(int32_t));
+        memcpy(yuv_hist, copy_hist.yuv_hist, plane_count * sizeof(Histogram));
+        memcpy(edge_hist, copy_hist.edge_hist, plane_count * sizeof(Histogram));
+
+        if (!m_isalloc) {
+            m_isalloc = false;
+            m_isalloc = allocHistogramBuffers();
+        }
+
+        if (m_isalloc) {
+            for (int i = 0; i < plane_count; i++) {
+                if (edgePic[i] && edgeThetaPic[i]) {
+                    memcpy(edgePic[i], copy_hist.edgePic[i], plane_sizes[i] * sizeof(pixel));
+                    memcpy(edgeThetaPic[i], copy_hist.edgeThetaPic[i], plane_sizes[i] * sizeof(pixel));
+                }
+            }
+        }
+
+        return *this;
+    }
+
+    void YuvHistogram::initFrameDimensions(x265_picture & pic) {
+
+        for (int i = 0; i < plane_count; i++) {
+            plane_widths[i] = pic.width;
+            plane_heights[i] = pic.height >> x265_cli_csps[pic.colorSpace].height[i];
+            plane_sizes[i] = plane_widths[i] * plane_heights[i];
+        }
+    }
+
+    void YuvHistogram::freeHistogramBuffers() {
+        //de allocate memory for histograms and edge filtered output 
+        if (edgePic && edgeThetaPic) {
+            for (int i = 0; i < plane_count; i++) {
+                if (edgePic[i] && edgeThetaPic[i]) {
+                    X265_FREE_ZERO(edgePic[i]);
+                    X265_FREE_ZERO(edgeThetaPic[i]);
+                }
+            }
+            X265_FREE_ZERO(edgePic);
+            X265_FREE_ZERO(edgeThetaPic);
+        }
+
+        if (plane_sizes && plane_heights && plane_widths) {
+            X265_FREE_ZERO(plane_sizes);
+            X265_FREE_ZERO(plane_heights);
+            X265_FREE_ZERO(plane_widths);
+        }
+ 
+        if (yuv_hist && edge_hist) {
+            X265_FREE_ZERO(yuv_hist);
+            X265_FREE_ZERO(edge_hist);
+        }
+    }
+
+    bool YuvHistogram::edgeFilter(x265_picture *frame) {
+
+        if (!m_isalloc) {
+            initFrameDimensions(*frame);
+            m_isalloc = allocHistogramBuffers();
+        }
+
+        if (m_isalloc) {
+            for (int idx = 0; idx < EDGE_PLANE_COUNT; idx++) {
+
+               memset(edgePic[idx], 0, sizeof(pixel) * plane_sizes[idx]);
+               memset(edgeThetaPic[idx], 0, sizeof(pixel) * plane_sizes[idx]);
+
+                pixel *src = (pixel*)frame->planes[idx];
+                pixel *edge_pic = edgePic[idx];
+                pixel *ref_pic = src;
+                pixel *edge_theta = edgeThetaPic[idx];
+
+                assert(edge_pic != NULL);
+                assert(ref_pic != NULL);
+                memcpy(edge_pic, src, plane_sizes[idx] * sizeof(pixel));
+                memcpy(ref_pic, src, plane_sizes[idx] * sizeof(pixel));
+
+                computeEdge(edge_pic, ref_pic, edge_theta,plane_widths[idx], plane_heights[idx], plane_widths[idx]);
+            }
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    bool YuvHistogram::computeHistograms(x265_picture &cur_frame) {
+
+        bool bsuccess = false;
+        bsuccess = computeLumaEdgeHistogram(cur_frame);
+        if (bsuccess) {
+            if (plane_count > 1) {
+                bsuccess &= computeChromaHistogram(cur_frame);
+            }
+            return bsuccess;
+        }
+        else {
+            return bsuccess;
+        }
+
+    }
+
+    bool YuvHistogram::computeLumaEdgeHistogram(x265_picture &frame) {
+
+        pixel pixel_val = 0;
+
+        memset(edge_hist[0].frequency_distribution, 0, HISTOGRAM_SIZE * sizeof(int32_t));
+
+        int size = frame.height*(frame.stride[0] >> SHIFT);
+
+        for (int i = 0; i < size; i++) {
+            pixel_val = edgePic[0][i];
+            edge_hist[0].frequency_distribution[pixel_val]++;
+        }
+        return true;
+    }
+
+    bool YuvHistogram::computeChromaHistogram(x265_picture &frame) {
+        /*u hist calculation*/
+        pixel pixel_val = 0;
+        int32_t pixel_ucount = 0, pixel_vcount = 0;
+
+        int u_height = (frame.height >> x265_cli_csps[frame.colorSpace].height[1]);
+        int size = u_height * (frame.stride[1] >> SHIFT);
+        memset(yuv_hist[1].frequency_distribution, 0, HISTOGRAM_SIZE * sizeof(int32_t));
+
+        for (int i = 0; i < size; i++) {
+            pixel_val = *((pixel *)frame.planes[1] + i);
+            yuv_hist[1].frequency_distribution[pixel_val]++;
+            pixel_ucount++;
+        }
+
+        /*v hist calculation for independent uv planes */
+
+        if (plane_count == 3) {
+            pixel_val = 0;
+            int v_height = (frame.height >> x265_cli_csps[frame.colorSpace].height[2]);
+            size = v_height * (frame.stride[2] >> SHIFT);
+            memset(yuv_hist[2].frequency_distribution, 0, HISTOGRAM_SIZE * sizeof(int32_t));
+ 
+            for (int i = 0; i < size; i++) {
+                pixel_val = *((pixel *)frame.planes[2] + i);
+                yuv_hist[2].frequency_distribution[pixel_val]++;
+                pixel_vcount++;
+            }
+
+            for (int i = 0; i < HISTOGRAM_SIZE; i++) {
+                maxuv_hist.frequency_distribution[i] = max(yuv_hist[1].frequency_distribution[i], yuv_hist[2].frequency_distribution[i]);
+            }
+
+        }
+        else {
+            maxuv_hist = yuv_hist[1]; //for two planes scenario
+        }
+
+        return true;
+    }
+
+    bool YuvHistogram::isUpdated() {
+        return bisUpdated;
+    }
+
+    void YuvHistogram::setUpdateFlag(bool flag) {
+        bisUpdated = flag;
+    }
+
+    bool YuvHistogram::getUpdateFlag() {
+        return bisUpdated;
+    }
+
+    SadYuv::SadYuv() { }
+
+    void SadYuv::initSadYuv(int planecount) {
+        this->plane_count = planecount;
+        sad_yuv = NULL;
+        psad_yuv = NULL;
+        sad_yuv = X265_MALLOC(int32_t, plane_count);
+        psad_yuv = X265_MALLOC(double, plane_count);
+        if (sad_yuv && psad_yuv) {
+            memset(sad_yuv, 0, plane_count * sizeof(int32_t));
+            memset(psad_yuv, 0, sizeof(double) * plane_count);
+        }
+    }
+
+    SadYuv & SadYuv::operator=(SadYuv const& sad_val) {
+        this->plane_count = sad_val.plane_count;
+        if (!sad_yuv && !psad_yuv) {
+            sad_yuv = NULL;
+            psad_yuv = NULL;
+            sad_yuv = X265_MALLOC(int32_t, plane_count);
+            psad_yuv = X265_MALLOC(double, plane_count);
+            if (sad_yuv && psad_yuv) {
+                memcpy(sad_yuv, sad_val.sad_yuv, plane_count * sizeof(int32_t));
+                memcpy(psad_yuv, sad_val.psad_yuv, sizeof(double) * plane_count);
+            }
+        }
+        else {
+            if (sad_yuv)
+                memcpy(sad_yuv, sad_val.sad_yuv, plane_count * sizeof(int32_t));
+            if (psad_yuv)
+                memcpy(psad_yuv, sad_val.psad_yuv, sizeof(double) * plane_count);
+        }
+        return *this;
+    }
+
+    SadYuv::~SadYuv() {
+        if (sad_yuv && psad_yuv) {
+            X265_FREE(sad_yuv);
+            X265_FREE(psad_yuv);
+        }
+    }
+
+    int sad_stats::frames_scanned=0;
+    int sad_stats::line_number=0;
+
+    sad_stats::sad_stats(int planecount, double threshold) {
+        this->plane_count = planecount;
+        calculateThresholds(threshold);
+        allocateBuffers();
+    }
+
+    void sad_stats::calculateThresholds(double threshold) {
+        edge_hist_threshold = threshold;
+        strength_factor = 2.0;
+        chroma_hist_threshold = threshold * 10.0;
+        scaled_edge_threshold = edge_hist_threshold * strength_factor;
+        scaled_chroma_threshold = chroma_hist_threshold * strength_factor;
+    }
+
+    void sad_stats::init() {
+        bscene_cut = NULL;
+        bdrop_frame = NULL;
+        sad_vals = NULL;
+        maxuv_sad_vals = NULL;
+        edge_sad_vals = NULL;
+        prev_hist = NULL;
+    }
+
+    sad_stats::~sad_stats() {
+        releaseBuffers();
+    }
+
+    void sad_stats::allocateBuffers() {
+
+        init();
+        sad_vals = new SadYuv[DUP_BUFFER]();
+        maxuv_sad_vals = new SadYuv[DUP_BUFFER]();
+        edge_sad_vals = new SadYuv[DUP_BUFFER]();
+        prev_hist = new YuvHistogram();
+        prev_hist->initHistograms(plane_count);
+
+        for (int i = 0; i < DUP_BUFFER; i++) {
+            sad_vals[i].initSadYuv(plane_count);
+            maxuv_sad_vals[i].initSadYuv(plane_count);
+            edge_sad_vals[i].initSadYuv(plane_count);
+        }
+
+        bscene_cut = new bool[DUP_BUFFER];
+        bdrop_frame = new bool[DUP_BUFFER];
+
+        if (!sad_vals || !maxuv_sad_vals || !edge_sad_vals || !bscene_cut || !bdrop_frame) {
+            x265_log(NULL, X265_LOG_ERROR, "Heap Error !");
+            exit(101);
+        }
+        else {
+            memset(bscene_cut, false, 2 * sizeof(bool));
+            memset(bdrop_frame, false, 2 * sizeof(bool));
+        }
+
+    }
+
+    void sad_stats::releaseBuffers() {
+        if (sad_vals && maxuv_sad_vals && edge_sad_vals && bscene_cut && bdrop_frame && prev_hist) {
+            delete[] sad_vals;
+            delete[] maxuv_sad_vals;
+            delete[] edge_sad_vals;
+            delete[] bscene_cut;
+            delete[] bdrop_frame;
+            delete prev_hist;
+        }
+    }
+
+    bool sad_stats::computeSadValue(YuvHistogram *input_frames, int32_t* plane_sizes) {
+
+        int32_t   *yuv_sad_val = NULL, *edge_sad_val = NULL, *maxuv_sad_val = NULL;
+
+        double *maxuv_normalized_sad = NULL, *yuv_norm_sad = NULL, *edge_normalized_sads = NULL;
+
+        YuvHistogram * ref_hist = NULL, *cur_hist = NULL;
+
+        /*inorder to process frames as per poc's updated by frame duplication */
+        if (frames_scanned > 0) {
+           
+            if (!input_frames[0].isUpdated() && input_frames[1].isUpdated()) {
+                ref_hist = prev_hist;
+                cur_hist = input_frames + 1;
+
+                yuv_sad_val = sad_vals[1].sad_yuv,
+                edge_sad_val = edge_sad_vals[1].sad_yuv,
+                maxuv_sad_val = maxuv_sad_vals[1].sad_yuv;
+                maxuv_normalized_sad = maxuv_sad_vals[1].psad_yuv,
+                yuv_norm_sad = sad_vals[1].psad_yuv,
+                edge_normalized_sads = edge_sad_vals[1].psad_yuv;
+                input_frames[1].setUpdateFlag(false);
+
+            }
+            else if (input_frames[0].isUpdated() && input_frames[1].isUpdated()) {
+                ref_hist = prev_hist;
+                cur_hist = input_frames;
+
+                yuv_sad_val = sad_vals[0].sad_yuv,
+                edge_sad_val = edge_sad_vals[0].sad_yuv,
+                maxuv_sad_val = maxuv_sad_vals[0].sad_yuv;
+                maxuv_normalized_sad = maxuv_sad_vals[0].psad_yuv,
+                yuv_norm_sad = sad_vals[0].psad_yuv,
+                edge_normalized_sads = edge_sad_vals[0].psad_yuv;
+                input_frames[0].setUpdateFlag(false);
+
+            }
+            else if (input_frames[0].isUpdated() && !input_frames[1].isUpdated()) {
+                ref_hist = prev_hist;
+                cur_hist = input_frames;
+
+                yuv_sad_val = sad_vals[0].sad_yuv,
+                edge_sad_val = edge_sad_vals[0].sad_yuv,
+                maxuv_sad_val = maxuv_sad_vals[0].sad_yuv;
+                maxuv_normalized_sad = maxuv_sad_vals[0].psad_yuv,
+                yuv_norm_sad = sad_vals[0].psad_yuv,
+                edge_normalized_sads = edge_sad_vals[0].psad_yuv;
+                input_frames[0].setUpdateFlag(false);
+            }
+            else {
+                return true;
+            }
+        }
+        else {
+            cur_hist = input_frames;
+ 
+            yuv_sad_val = sad_vals[0].sad_yuv,
+            edge_sad_val = edge_sad_vals[0].sad_yuv,
+            maxuv_sad_val = maxuv_sad_vals[0].sad_yuv;
+
+            maxuv_normalized_sad = maxuv_sad_vals[0].psad_yuv,
+            yuv_norm_sad = sad_vals[0].psad_yuv,
+            edge_normalized_sads = edge_sad_vals[0].psad_yuv;
+            input_frames[0].setUpdateFlag(false);
+        }
+
+        if (frames_scanned == 0) { //first frame is scenecut by default no sad computation for the same.
+
+            maxuv_sad_val[0] = 0;
+            maxuv_normalized_sad[0] = 0.0;
+            memset(yuv_sad_val, 0 , plane_count * sizeof(int32_t));
+            memset(edge_sad_val, 0, plane_count * sizeof(int32_t));
+            memset(edge_normalized_sads, 0, plane_count * sizeof(double));
+            memset(yuv_norm_sad, 0, plane_count * sizeof(double));
+
+        }
+        else {
+            int32_t freq_diff[3];
+            int32_t maxuv_freq_diff[1];
+            int32_t edge_freq_diff[3];
+            double color_probability_diff[3], edge_probability_diff[3];
+
+            memset(yuv_sad_val, 0, plane_count*sizeof(int32_t));
+            memset(edge_sad_val, 0, plane_count*sizeof(int32_t));
+
+            memset(yuv_norm_sad, 0, plane_count * sizeof(double));
+            memset(edge_normalized_sads, 0, plane_count * sizeof(double));
+            memset(color_probability_diff, 0, plane_count * sizeof(double));
+            memset(edge_probability_diff, 0, plane_count * sizeof(double));
+
+            maxuv_normalized_sad[0] = 0.0;
+            maxuv_sad_val[0] = 0;
+
+            memset(freq_diff, 0, 3 * sizeof(int32_t));
+            memset(maxuv_freq_diff, 0, sizeof(int32_t));
+            memset(edge_freq_diff, 0, 3 * sizeof(int32_t));
+
+            for (int i = 0; i < plane_count; i++) {
+                {
+                    for (int j = 0; j < HISTOGRAM_SIZE; j++) {
+
+                        if (i == 0 && plane_count >= 1) {
+                            maxuv_freq_diff[i] = (abs(cur_hist->maxuv_hist.frequency_distribution[j] - ref_hist->maxuv_hist.frequency_distribution[j]));
+                            maxuv_sad_val[i] += maxuv_freq_diff[i];
+                            maxuv_normalized_sad[i] += (double)maxuv_freq_diff[i] / plane_sizes[i];
+                            edge_freq_diff[i] = abs(cur_hist->edge_hist[i].frequency_distribution[j] - ref_hist->edge_hist[i].frequency_distribution[j]);
+                            edge_probability_diff[i] = double(edge_freq_diff[i]) / plane_sizes[i];
+                            edge_sad_val[i] += edge_freq_diff[i];
+                            edge_normalized_sads[i] += edge_probability_diff[i];
+                        }
+                        else {
+                            freq_diff[i] = abs(cur_hist->yuv_hist[i].frequency_distribution[j] - ref_hist->yuv_hist[i].frequency_distribution[j]);
+                            color_probability_diff[i] = (double)freq_diff[i] / plane_sizes[i];
+                            yuv_sad_val[i] += freq_diff[i];
+                            yuv_norm_sad[i] += color_probability_diff[i];
+                        }
+
+                    }
+
+                }
+            }
+
+        }
+
+        *prev_hist = *cur_hist;
+
+        frames_scanned++;
+
+        return  true;
+    }
+
+    void sad_stats::findSceneCuts(x265_picture * picList, bool& bdup) { 
+
+            if (frames_scanned == 1) {
+                //for first frame
+                bscene_cut[0] = true;
+                bdrop_frame[0] = false;
+                picList->analysisData.bScenecut = (int)getSceneCutflag(0);
+                bdup = getDropflag(0);
+                picList->analysisData.edgeSadValue = edge_sad_vals[0].psad_yuv[0];
+                picList->analysisData.chromaSadValue = maxuv_sad_vals[0].psad_yuv[0];
+            }
+            else {
+                 bscene_cut[1] = bdrop_frame[1] = false;
+                if (edge_sad_vals[1].psad_yuv[0] == 0) {
+                    bdrop_frame[1] = true;
+                }
+                else if (edge_sad_vals[1].psad_yuv[0] > edge_hist_threshold || maxuv_sad_vals[1].psad_yuv[0] >= chroma_hist_threshold) {
+                    bscene_cut[1] = true;
+                    bdrop_frame[1] = false;
+                }
+                else if (edge_sad_vals[1].psad_yuv[0] >  scaled_edge_threshold || maxuv_sad_vals[1].psad_yuv[0] >= scaled_chroma_threshold) {
+                    bscene_cut[1] = true;
+                    bdrop_frame[1] = false;
+                }
+                picList->analysisData.bScenecut = (int)getSceneCutflag(1);
+                bdup = getDropflag(1);
+                picList->analysisData.edgeSadValue = edge_sad_vals[1].psad_yuv[0];
+                picList->analysisData.chromaSadValue = maxuv_sad_vals[1].psad_yuv[0];
+            }
+     }
+
+    bool sad_stats::getDropflag(int i) {
+        return bdrop_frame[i];
+    }
+
+    bool sad_stats::getSceneCutflag(int i) {
+        return bscene_cut[i];
+    }
+
+}
\ No newline at end of file
diff -r 37648fca915b -r deaecadc4306 source/common/scenecut.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/common/scenecut.h	Tue Oct 15 12:16:17 2019 +0530
@@ -0,0 +1,147 @@
+#ifndef SCENECUT_H
+#define SCENECUT_H
+
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <algorithm>
+#include <math.h>
+
+#include "yuv.h"
+#include "common.h"
+
+#ifdef HIGH_BIT_DEPTH
+#define edge_threshold 1023.0
+#define whitePixel 1023.0
+#else
+#define edge_threshold 255.0
+#define pixel whitePixel 255.0
+#endif
+
+using namespace std;
+
+namespace X265_NS {
+
+    class Histogram {
+
+    public:
+        int32_t frequency_distribution[HISTOGRAM_SIZE];
+
+        Histogram();
+
+        Histogram(Histogram const& hist);
+
+        Histogram & operator=(Histogram const& hist);
+
+        ~Histogram();
+
+    };
+
+    class YuvHistogram {
+    public:
+        Histogram *yuv_hist;
+        Histogram *edge_hist;
+        int32_t *plane_sizes;
+        int32_t *plane_heights;
+        int32_t *plane_widths;
+
+        Histogram maxuv_hist;
+        int32_t plane_count;
+        bool bisUpdated;
+
+        pixel** edgePic;
+        pixel** edgeThetaPic;
+
+        x265_param * param; /*for handling various color spaces*/
+        bool m_isalloc;
+
+        YuvHistogram();
+
+        void initHistograms(int32_t plane_count);
+
+        void initHistograms(x265_param *p);
+
+        bool allocHistogramBuffers();
+
+        YuvHistogram(YuvHistogram const& hist);
+
+        YuvHistogram & operator=(const YuvHistogram & copy_hist);
+
+        ~YuvHistogram();
+
+        void initFrameDimensions(x265_picture & pic);
+
+        void freeHistogramBuffers();
+
+        bool edgeFilter(x265_picture *frame);
+
+        bool computeHistograms(x265_picture &cur_frame);
+
+        bool computeLumaEdgeHistogram(x265_picture &frame);
+
+        bool computeChromaHistogram(x265_picture &frame);
+
+        bool isUpdated();
+
+        void setUpdateFlag(bool flag);
+
+        bool getUpdateFlag();
+
+    };
+
+ struct SadYuv {
+        int32_t *sad_yuv;
+        double *psad_yuv;
+        int plane_count;
+        ~SadYuv();
+        SadYuv();
+        void initSadYuv(int plane_count);
+        SadYuv & operator=(SadYuv const& sad_val);
+};
+
+    class sad_stats {
+
+        bool *bscene_cut;
+        bool *bdrop_frame;
+        SadYuv * sad_vals;
+        SadYuv * maxuv_sad_vals;
+        SadYuv * edge_sad_vals;
+        int plane_count;
+        static int line_number;
+        static int frames_scanned;
+        YuvHistogram *prev_hist;
+        double edge_hist_threshold;
+        double chroma_hist_threshold;
+        double scaled_chroma_threshold;
+        double scaled_edge_threshold;
+        double strength_factor;
+
+    public:
+        sad_stats(int plane_count, double threshold);
+
+        ~sad_stats();
+        
+        void init();
+        
+        void allocateBuffers();
+        
+        void releaseBuffers();
+        
+        void calculateThresholds(double threshold);
+        
+        bool computeSadValue(YuvHistogram *frames, int32_t* plane_sizes);
+        
+        void findSceneCuts(x265_picture * piclist,bool & bdup);
+        
+        bool getDropflag(int i);
+        
+        bool getSceneCutflag(int i);
+
+    };
+
+void computeEdge(pixel * edgePic, pixel *refPic, pixel * edgeTheta, intptr_t stride, int height, int width);
+
+}
+
+#endif
diff -r 37648fca915b -r deaecadc4306 source/encoder/api.cpp
--- a/source/encoder/api.cpp	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/encoder/api.cpp	Tue Oct 15 12:16:17 2019 +0530
@@ -31,6 +31,7 @@
 #include "nal.h"
 #include "bitcost.h"
 #include "svt.h"
+#include "scenecut.h"
 
 #if ENABLE_LIBVMAF
 #include "libvmaf.h"
@@ -117,7 +118,10 @@
     x265_log(param, X265_LOG_INFO, "build info %s\n", PFX(build_info_str));
 
     encoder = new Encoder;
-
+    encoder->m_sad_stats = new sad_stats(x265_cli_csps[p->internalCsp].planes,param->edgeTransitionThreshold);
+    encoder->m_hist_of_adj_frames = new YuvHistogram[2];
+    encoder->m_hist_of_adj_frames[0].initHistograms(p);
+    encoder->m_hist_of_adj_frames[1].initHistograms(p);
 #ifdef SVT_HEVC
 
     if (param->bEnableSvtHevc)
@@ -809,6 +813,7 @@
             CHECKED_MALLOC_ZERO(interData->ref, int32_t, analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU * numDir);
     }
     analysis->interData = interData;
+    analysis->bScenecut = false;
 
     return;
 
@@ -924,6 +929,7 @@
     pic->rpu.payloadSize = 0;
     pic->rpu.payload = NULL;
     pic->picStruct = 0;
+    pic->bufUpdated = false;
 
     if ((param->analysisSave || param->analysisLoad) || (param->bAnalysisType == AVC_INFO))
     {
@@ -933,7 +939,9 @@
         uint32_t numCUsInFrame   = widthInCU * heightInCU;
         pic->analysisData.numCUsInFrame = numCUsInFrame;
         pic->analysisData.numPartitions = param->num4x4Partitions;
+        pic->analysisData.bScenecut = false;
     }
+
 }
 
 void x265_picture_free(x265_picture *p)
@@ -955,7 +963,8 @@
 {
     if (param && param->rc.zonefileCount) {
         for (int i = 0; i < param->rc.zonefileCount; i++)
-            x265_free(param->rc.zones[i].zoneParam);
+            if(param->rc.zones[i].zoneParam)
+              x265_free(param->rc.zones[i].zoneParam);
     }
     if (param && (param->rc.zoneCount || param->rc.zonefileCount))
         x265_free(param->rc.zones);
diff -r 37648fca915b -r deaecadc4306 source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/encoder/encoder.cpp	Tue Oct 15 12:16:17 2019 +0530
@@ -119,6 +119,9 @@
         m_frameEncoder[i] = NULL;
     for (uint32_t i = 0; i < DUP_BUFFER; i++)
         m_dupBuffer[i] = NULL;
+    
+    m_hist_of_adj_frames = NULL;
+    m_sad_stats = NULL;
     MotionEstimate::initScales();
 
 #if ENABLE_HDR10_PLUS
@@ -162,7 +165,9 @@
     int rows = (p->sourceHeight + p->maxCUSize - 1) >> g_log2Size[p->maxCUSize];
     int cols = (p->sourceWidth  + p->maxCUSize - 1) >> g_log2Size[p->maxCUSize];
 
-    if (m_param->bEnableFrameDuplication)
+
+
+    if (m_param->bEnableFrameDuplication || m_param->bHistbasedScenecut)
     {
         size_t framesize = 0;
         int pixelbytes = p->sourceBitDepth > 8 ? 2 : 1;
@@ -184,6 +189,7 @@
             m_dupBuffer[i]->dupPlane = NULL;
             m_dupBuffer[i]->dupPlane = X265_MALLOC(char, framesize);
             m_dupBuffer[i]->dupPic->planes[0] = m_dupBuffer[i]->dupPlane;
+ 			m_dupBuffer[i]->bufUpdated = false;
             m_dupBuffer[i]->bOccupied = false;
             m_dupBuffer[i]->bDup = false;
         }
@@ -820,7 +826,7 @@
         m_exportedPic = NULL;
     }
 
-    if (m_param->bEnableFrameDuplication)
+    if (m_param->bEnableFrameDuplication || m_param->bHistbasedScenecut)
     {
         for (uint32_t i = 0; i < DUP_BUFFER; i++)
         {
@@ -1280,6 +1286,33 @@
     return psnrWeight = (psnrY * 6 + psnrU + psnrV) / 8;
 }
 
+void Encoder::updateSceneCutAndFrameDuplicateFlags() {
+    /* SCD computation and drop flag*/
+    for (int i = 0; i < DUP_BUFFER; i++) {
+        if (m_dupBuffer[i]->bufUpdated) {
+            m_hist_of_adj_frames[i].setUpdateFlag(true);
+            m_hist_of_adj_frames[i].edgeFilter(m_dupBuffer[i]->dupPic);
+            m_hist_of_adj_frames[i].computeHistograms(*m_dupBuffer[i]->dupPic);
+            m_sad_stats->computeSadValue(m_hist_of_adj_frames, m_hist_of_adj_frames->plane_sizes);
+            m_sad_stats->findSceneCuts(m_dupBuffer[i]->dupPic, m_dupBuffer[i]->bDup);
+
+            if (m_dupBuffer[i]->dupPic->analysisData.bScenecut) {
+                x265_log(m_param, X265_LOG_DEBUG, "scene cut at %d edge hist sad: %0.4lf maxuv hist sad: %0.4lf\n",
+                   m_dupBuffer[i]->dupPic->poc,m_dupBuffer[i]->dupPic->analysisData.edgeSadValue,m_dupBuffer[i]->dupPic->analysisData.chromaSadValue);
+            }
+
+            if (m_dupBuffer[1]->bufUpdated)
+                m_hist_of_adj_frames[0] = m_hist_of_adj_frames[1];
+        }
+    }
+
+ }
+
+/* TBD 
+- 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.
+- benefits avoid function and use language features appropriately.
+*/
+
 void Encoder::copyPicture(x265_picture *dest, const x265_picture *src)
 {
     dest->poc = src->poc;
@@ -1299,6 +1332,25 @@
     memcpy(dest->planes[0], src->planes[0], src->framesize * sizeof(char));
     dest->planes[1] = (char*)dest->planes[0] + src->stride[0] * src->height;
     dest->planes[2] = (char*)dest->planes[1] + src->stride[1] * (src->height >> x265_cli_csps[src->colorSpace].height[1]);
+    memcpy(&dest->analysisData, &src->analysisData, sizeof(src->analysisData));
+ 
+}
+
+void Encoder::setPictureFlags(int idx) {
+        m_dupBuffer[idx]->bOccupied = true;
+        m_dupBuffer[idx]->bufUpdated = true;
+        m_dupBuffer[idx]->bDup = false;
+}
+
+void Encoder::unsetPictureFlags(int idx) {
+    if (idx == 1) {
+        m_dupBuffer[idx]->bOccupied = false;
+        m_dupBuffer[idx]->bufUpdated = false;
+        m_dupBuffer[idx]->bDup = false;
+    }
+    else if (idx == 0) {
+        m_dupBuffer[idx]->bufUpdated = false;
+    }
 }
 
 /**
@@ -1327,7 +1379,9 @@
     const x265_picture* inputPic = NULL;
     static int written = 0, read = 0;
     bool dontRead = false;
-
+    bool isScenecutEnabled = m_param->bHistbasedScenecut;
+    bool dropflag = false;
+ 
     if (m_exportedPic)
     {
         if (!m_param->bUseAnalysisFile && m_param->analysisSave)
@@ -1338,7 +1392,7 @@
     }
     if ((pic_in && (!m_param->chunkEnd || (m_encodedFrameNum < m_param->chunkEnd))) || (m_param->bEnableFrameDuplication && !pic_in && (read < written)))
     {
-        if ((m_param->bEnableFrameDuplication && !pic_in && (read < written)))
+        if ((m_param->bEnableFrameDuplication && !pic_in && (read < written)) || (isScenecutEnabled && !pic_in && (read < written)))
             dontRead = true;
         else
         {
@@ -1361,7 +1415,7 @@
             }
         }
 
-        if (m_param->bEnableFrameDuplication)
+        if (m_param->bEnableFrameDuplication || isScenecutEnabled )
         {
             double psnrWeight = 0;
 
@@ -1372,6 +1426,11 @@
                     copyPicture(m_dupBuffer[0]->dupPic, pic_in);
                     m_dupBuffer[0]->bOccupied = true;
                     written++;
+                    if (m_param->bHistbasedScenecut) {
+                        setPictureFlags(0);
+                        updateSceneCutAndFrameDuplicateFlags();
+                        unsetPictureFlags(0);
+                    }
                     return 0;
                 }
                 else if (!m_dupBuffer[1]->bOccupied)
@@ -1379,31 +1438,54 @@
                     copyPicture(m_dupBuffer[1]->dupPic, pic_in);
                     m_dupBuffer[1]->bOccupied = true;
                     written++;
+                    if (m_param->bHistbasedScenecut) {
+                        setPictureFlags(1);
+                        updateSceneCutAndFrameDuplicateFlags();
+                        unsetPictureFlags(1);
+                    }
                 }
 
-                psnrWeight = ComputePSNR(m_dupBuffer[0]->dupPic, m_dupBuffer[1]->dupPic, m_param);
-
-                if (psnrWeight >= m_param->dupThreshold)
-                {
-                    if (m_dupBuffer[0]->bDup)
-                    {
-                        m_dupBuffer[0]->dupPic->picStruct = tripling;
-                        m_dupBuffer[0]->bDup = false;
-                        read++;
+                if (m_param->bEnableFrameDuplication && m_param->bHistbasedScenecut) {
+                    if (m_dupBuffer[1]->bDup == false && m_dupBuffer[1]->dupPic->analysisData.bScenecut == false) {
+                        psnrWeight = ComputePSNR(m_dupBuffer[0]->dupPic, m_dupBuffer[1]->dupPic, m_param);
+                        if (psnrWeight >= m_param->dupThreshold)
+                            dropflag = true;
                     }
-                    else
-                    {
-                        m_dupBuffer[0]->dupPic->picStruct = doubling;
-                        m_dupBuffer[0]->bDup = true;
-                        m_dupBuffer[1]->bOccupied = false;
-                        read++;
-                        return 0;
+                    else {
+                        dropflag = true;
                     }
                 }
-                else if (m_dupBuffer[0]->bDup)
+                else if (m_param->bEnableFrameDuplication) {
+                    psnrWeight = ComputePSNR(m_dupBuffer[0]->dupPic, m_dupBuffer[1]->dupPic, m_param);
+                    if (psnrWeight >= m_param->dupThreshold)
+                        dropflag = true;
+                }
+
+                if (m_param->bEnableFrameDuplication)
+                {
+                    if (dropflag)
+                    {
+                        if (m_dupBuffer[0]->bDup)
+                        {
+                            m_dupBuffer[0]->dupPic->picStruct = tripling;
+                            m_dupBuffer[0]->bDup = false;
+                            read++;
+                        }
+                        else
+                        {
+                            m_dupBuffer[0]->dupPic->picStruct = doubling;
+                            m_dupBuffer[0]->bDup = true;
+                            m_dupBuffer[1]->bOccupied = false;
+                            read++;
+                            return 0;
+                        }
+                    }
+                    else if (m_dupBuffer[0]->bDup)
                     m_dupBuffer[0]->bDup = false;
-                else
-                    m_dupBuffer[0]->dupPic->picStruct = 0;
+                    else
+                        m_dupBuffer[0]->dupPic->picStruct = 0;
+                }
+
             }
 
             if (read < written)
@@ -1485,7 +1567,10 @@
 
         inFrame->m_poc       = ++m_pocLast;
         inFrame->m_userData  = inputPic->userData;
-        inFrame->m_pts       = inputPic->pts;
+        inFrame->m_pts = inputPic->pts;
+        if (m_param->bHistbasedScenecut) {
+           inFrame->m_lowres.bScenecut = inputPic->analysisData.bScenecut;
+        }
         inFrame->m_forceqp   = inputPic->forceqp;
         inFrame->m_param     = (m_reconfigure || m_reconfigureRc) ? m_latestParam : m_param;
         inFrame->m_picStruct = inputPic->picStruct;
@@ -1613,7 +1698,7 @@
             m_param->bUseRcStats = 0;
         }
 
-        if (m_param->bEnableFrameDuplication && ((read < written) || (m_dupBuffer[0]->dupPic->picStruct == tripling && (read <= written))))
+        if ( (m_param->bEnableFrameDuplication || isScenecutEnabled) && ((read < written) || (m_dupBuffer[0]->dupPic->picStruct == tripling && (read <= written))))
         {
             if (m_dupBuffer[0]->dupPic->picStruct == tripling)
                 m_dupBuffer[0]->bOccupied = m_dupBuffer[1]->bOccupied = false;
@@ -3162,6 +3247,7 @@
          * adaptive I frame placement */
         p->keyframeMax = INT_MAX;
         p->scenecutThreshold = 0;
+        p->bHistbasedScenecut = 0;
     }
     else if (p->keyframeMax <= 1)
     {
@@ -3175,6 +3261,7 @@
         p->lookaheadDepth = 0;
         p->bframes = 0;
         p->scenecutThreshold = 0;
+        p->bHistbasedScenecut = 0;
         p->bFrameAdaptive = 0;
         p->rc.cuTree = 0;
         p->bEnableWeightedPred = 0;
@@ -3828,6 +3915,20 @@
             m_param->searchMethod = m_param->hmeSearchMethod[2];
         }
     }
+
+    if (p->bHistbasedScenecut && p->scenecutThreshold) {
+        p->scenecutThreshold = 0;
+        p->bHistbasedScenecut = false;
+        x265_log(p, X265_LOG_WARNING, "Amibigious choice. disabling scene cut detection \n");
+    }
+    else if (p->scenecutThreshold && p->edgeTransitionThreshold != 0.01) {
+        x265_log(p, X265_LOG_WARNING, "using  scenecut-bias %d for scene cut detection\n",p->scenecutBias);
+    }
+    else if (p->bHistbasedScenecut && p->edgeTransitionThreshold == 0.0) {
+        p->edgeTransitionThreshold = 0.01;
+        x265_log(p, X265_LOG_INFO, "using  default threshold %.2lf for scene cut detection\n", p->edgeTransitionThreshold);
+    }
+
 }
 
 void Encoder::readAnalysisFile(x265_analysis_data* analysis, int curPoc, const x265_picture* picIn, int paramBytes)
diff -r 37648fca915b -r deaecadc4306 source/encoder/encoder.h
--- a/source/encoder/encoder.h	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/encoder/encoder.h	Tue Oct 15 12:16:17 2019 +0530
@@ -32,6 +32,8 @@
 #include "nal.h"
 #include "framedata.h"
 #include "svt.h"
+#include "scenecut.h"
+
 #ifdef ENABLE_HDR10_PLUS
     #include "dynamicHDR10/hdr10plus.h"
 #endif
@@ -154,6 +156,9 @@
 
     //Flag to check whether the picture has duplicated.
     bool bDup;
+
+    bool bufUpdated;
+
 };
 
 
@@ -195,6 +200,9 @@
 
     ThreadPool*        m_threadPool;
     FrameEncoder*      m_frameEncoder[X265_MAX_FRAME_THREADS];
+
+    YuvHistogram*      m_hist_of_adj_frames;
+    sad_stats*         m_sad_stats;
     DPB*               m_dpb;
     Frame*             m_exportedPic;
     FILE*              m_analysisFileIn;
@@ -279,6 +287,10 @@
         if (m_prevTonemapPayload.payload != NULL)
             X265_FREE(m_prevTonemapPayload.payload);
 #endif
+        delete m_sad_stats;
+        m_sad_stats = NULL;
+        delete[] m_hist_of_adj_frames;
+        m_hist_of_adj_frames = NULL;
     };
 
     void create();
@@ -349,6 +361,12 @@
 
     void copyPicture(x265_picture *dest, const x265_picture *src);
 
+    void unsetPictureFlags(int index);
+
+    void setPictureFlags(int index);
+
+    void updateSceneCutAndFrameDuplicateFlags();
+
     void initRefIdx();
     void analyseRefIdx(int *numRefIdx);
     void updateRefIdx();
@@ -364,6 +382,7 @@
     void initSPS(SPS *sps);
     void initPPS(PPS *pps);
 };
+
 }
 
 #endif // ifndef X265_ENCODER_H
diff -r 37648fca915b -r deaecadc4306 source/encoder/ratecontrol.cpp
--- a/source/encoder/ratecontrol.cpp	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/encoder/ratecontrol.cpp	Tue Oct 15 12:16:17 2019 +0530
@@ -493,6 +493,7 @@
                 CMP_OPT_FIRST_PASS("open-gop", m_param->bOpenGOP);
                 CMP_OPT_FIRST_PASS(" keyint", m_param->keyframeMax);
                 CMP_OPT_FIRST_PASS("scenecut", m_param->scenecutThreshold);
+                CMP_OPT_FIRST_PASS("hist-threshold", m_param->edgeTransitionThreshold);
                 CMP_OPT_FIRST_PASS("intra-refresh", m_param->bIntraRefresh);
                 if (m_param->bMultiPassOptRPS)
                 {
@@ -1183,6 +1184,7 @@
             m_param->rc.bStatRead = 0;
             m_param->bFrameAdaptive = 0;
             m_param->scenecutThreshold = 0;
+            m_param->bHistbasedScenecut = false;
             m_param->rc.cuTree = 0;
             if (m_param->bframes > 1)
                 m_param->bframes = 1;
@@ -2173,7 +2175,7 @@
     if (m_isVbv && m_currentSatd > 0 && curFrame)
     {
         if (m_param->lookaheadDepth || m_param->rc.cuTree ||
-            m_param->scenecutThreshold ||
+            (m_param->scenecutThreshold || m_param->bHistbasedScenecut) ||
             (m_param->bFrameAdaptive && m_param->bframes))
         {
            /* Lookahead VBV: If lookahead is done, raise the quantizer as necessary
diff -r 37648fca915b -r deaecadc4306 source/encoder/slicetype.cpp
--- a/source/encoder/slicetype.cpp	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/encoder/slicetype.cpp	Tue Oct 15 12:16:17 2019 +0530
@@ -30,6 +30,7 @@
 #include "primitives.h"
 #include "lowres.h"
 #include "mv.h"
+#include "scenecut.h"
 
 #include "slicetype.h"
 #include "motion.h"
@@ -114,8 +115,8 @@
     //Applying Gaussian filter on the picture
     src = (pixel*)curFrame->m_fencPic->m_picOrg[0];
     refPic = curFrame->m_gaussianPic + curFrame->m_fencPic->m_lumaMarginY * stride + curFrame->m_fencPic->m_lumaMarginX;
+    edgePic = pic1 + 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++)
@@ -127,7 +128,8 @@
                  1  [4   9   12  9   4]
                 --- [5   12  15  12  5]
                 159 [4   9   12  9   4]
-                    [2   4   5   4   2]*/
+                    [2   4   5   4   2]
+			    */
 
                 const intptr_t rowOne = (rowNum - 2)*stride, colOne = colNum - 2;
                 const intptr_t rowTwo = (rowNum - 1)*stride, colTwo = colNum - 1;
@@ -145,52 +147,7 @@
             }
         }
     }
-
-#if HIGH_BIT_DEPTH //10-bit build
-    float threshold = 1023;
-    pixel whitePixel = 1023;
-#else
-    float threshold = 255;
-    pixel whitePixel = 255;
-#endif
-#define PI 3.14159265 
-
-    float gradientH = 0, gradientV = 0, radians = 0, theta = 0;
-    float gradientMagnitude = 0;
-    pixel blackPixel = 0;
-    edgePic = curFrame->m_edgePic + 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 ]*/
-
-                const intptr_t rowOne = (rowNum - 1)*stride, colOne = colNum -1;
-                const intptr_t rowTwo = rowNum * stride, colTwo = colNum;
-                const intptr_t rowThree = (rowNum + 1)*stride, colThree = colNum + 1;
-                const intptr_t index = (rowNum*stride) + colNum;
-
-                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]);
-                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]);
-
-                gradientMagnitude = sqrtf(gradientH * gradientH + gradientV * gradientV);
-                radians = atan2(gradientV, gradientH);
-                theta = (float)((radians * 180) / PI);
-                if (theta < 0)
-                    theta = 180 + theta;
-                edgeTheta[(rowNum*stride) + colNum] = (pixel)theta;
-
-                edgePic[index] = gradientMagnitude >= threshold ? whitePixel : blackPixel;
-            }
-        }
-    }
+    computeEdge(edgePic, refPic, edgeTheta, stride, height, width);
 }
 
 //Find the angle of a block by averaging the pixel angles 
@@ -1471,7 +1428,7 @@
 
     if (m_lastNonB && !m_param->rc.bStatRead &&
         ((m_param->bFrameAdaptive && m_param->bframes) ||
-         m_param->rc.cuTree || m_param->scenecutThreshold ||
+         m_param->rc.cuTree || m_param->scenecutThreshold || m_param->bHistbasedScenecut ||
          (m_param->lookaheadDepth && m_param->rc.vbvBufferSize)))
     {
         slicetypeAnalyse(frames, false);
@@ -1962,10 +1919,15 @@
 
     int numBFrames = 0;
     int numAnalyzed = numFrames;
-    bool isScenecut = scenecut(frames, 0, 1, true, origNumFrames);
+    bool isScenecut = false;
 
     /* When scenecut threshold is set, use scenecut detection for I frame placements */
-    if (m_param->scenecutThreshold && isScenecut)
+    if (m_param->scenecutThreshold)
+        isScenecut = scenecut(frames, 0, 1, true, origNumFrames);
+    else if (m_param->bHistbasedScenecut)
+        isScenecut = frames[1]->bScenecut;
+
+    if (isScenecut)
     {
         frames[1]->sliceType = X265_TYPE_I;
         return;
@@ -1976,14 +1938,24 @@
         m_extendGopBoundary = false;
         for (int i = m_param->bframes + 1; i < origNumFrames; i += m_param->bframes + 1)
         {
-            scenecut(frames, i, i + 1, true, origNumFrames);
+            if (m_param->scenecutThreshold)
+               scenecut(frames, i, i + 1, true, origNumFrames);
+
             for (int j = i + 1; j <= X265_MIN(i + m_param->bframes + 1, origNumFrames); j++)
             {
-                if (frames[j]->bScenecut && scenecutInternal(frames, j - 1, j, true) )
-                {
-                    m_extendGopBoundary = true;
-                    break;
-                }
+                if (m_param->scenecutThreshold)
+                    {
+                        if (frames[j]->bScenecut && scenecutInternal(frames, j - 1, j, true))
+                        {
+                               m_extendGopBoundary = true;
+                               break;
+                        }
+                    }
+                    else if(m_param->bHistbasedScenecut && frames[j]->bScenecut)
+                    {
+                            m_extendGopBoundary = true;
+                            break;
+                    }
             }
             if (m_extendGopBoundary)
                 break;
@@ -2088,13 +2060,23 @@
         {
             for (int j = 1; j < numBFrames + 1; j++)
             {
-                if (scenecut(frames, j, j + 1, false, origNumFrames) || 
-                    (bForceRADL && (frames[j]->frameNum == preRADL)))
-                {
-                    frames[j]->sliceType = X265_TYPE_P;
-                    numAnalyzed = j;
-                    break;
+                if (m_param->bHistbasedScenecut) {
+                    if (frames[j]->bScenecut || (bForceRADL && (frames[j]->frameNum == preRADL)))
+                    {
+                        frames[j]->sliceType = X265_TYPE_P;
+                        numAnalyzed = j;
+                        break;
+                    }
                 }
+                else if (m_param->scenecutThreshold){
+                    if ( scenecut(frames, j, j + 1, false, origNumFrames) || (bForceRADL && (frames[j]->frameNum == preRADL)) )
+                    {
+                        frames[j]->sliceType = X265_TYPE_P;
+                        numAnalyzed = j;
+                        break;
+                    }
+                }
+
             }
         }
         resetStart = bKeyframe ? 1 : X265_MIN(numBFrames + 2, numAnalyzed + 1);
diff -r 37648fca915b -r deaecadc4306 source/encoder/slicetype.h
--- a/source/encoder/slicetype.h	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/encoder/slicetype.h	Tue Oct 15 12:16:17 2019 +0530
@@ -43,6 +43,14 @@
 #define AQ_EDGE_BIAS 0.5
 #define EDGE_INCLINATION 45
 
+#ifdef HIGH_BIT_DEPTH
+#define edge_threshold 1023.0
+#define whitePixel 1023.0
+#else
+#define edge_threshold 255.0
+#define pixel whitePixel 255.0
+#endif
+
 /* Thread local data for lookahead tasks */
 struct LookaheadTLD
 {
diff -r 37648fca915b -r deaecadc4306 source/test/regression-tests.txt
--- a/source/test/regression-tests.txt	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/test/regression-tests.txt	Tue Oct 15 12:16:17 2019 +0530
@@ -157,6 +157,9 @@
 ducks_take_off_420_720p50.y4m,--preset medium --aq-mode 4 --crf 22 --no-cutree
 ducks_take_off_420_1_720p50.y4m,--preset medium --selective-sao 4 --sao --crf 20
 Traffic_4096x2048_30p.y4m, --preset medium --frame-dup --dup-threshold 60 --hrd --bitrate 10000 --vbv-bufsize 15000 --vbv-maxrate 12000
+sintel_trailer_2k_1920x1080_24.yuv, --preset medium --hist-scenecut --hist-threshold 0.01
+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
+sintel_trailer_2k_1920x1080_24.yuv, --preset medium --scenecut 40 --scenecut-bias 20
 
 # Main12 intraCost overflow bug test
 720p50_parkrun_ter.y4m,--preset medium
diff -r 37648fca915b -r deaecadc4306 source/x265.h
--- a/source/x265.h	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/x265.h	Tue Oct 15 12:16:17 2019 +0530
@@ -210,7 +210,9 @@
     uint32_t                          numCUsInFrame;
     uint32_t                          numPartitions;
     uint32_t                          depthBytes;
-    int                               bScenecut;
+    bool                               bScenecut;
+    double                             edgeSadValue;
+    double                             chromaSadValue;
     x265_weight_param*                wt;
     x265_analysis_inter_data*         interData;
     x265_analysis_intra_data*         intraData;
@@ -291,6 +293,9 @@
     char             sliceType;
     int              bScenecut;
     double           ipCostRatio;
+    double           yedgeSadValue;
+    double           chromaSadValue;
+
     int              frameLatency;
     x265_cu_stats    cuStats;
     x265_pu_stats    puStats;
@@ -465,6 +470,9 @@
     //Dolby Vision RPU metadata
     x265_dolby_vision_rpu rpu;
 
+    //Flag to determine the latest frame in the buffer
+    bool bufUpdated;
+
     int fieldNum;
 
     //SEI picture structure message
@@ -1017,8 +1025,9 @@
      * decisions. Default is 0 - disabled. 1 is the same as 0. Max 16 */
     int       lookaheadSlices;
 
-    /* An arbitrary threshold which determines how aggressively the lookahead
-     * should detect scene cuts. The default (40) is recommended. */
+    /*  An arbitrary threshold which determines how aggressively the lookahead
+     * should detect scene cuts. The default (40) is recommended.
+     * Used for encoding cost based scenecut detection */
     int       scenecutThreshold;
 
     /* Replace keyframes by using a column of intra blocks that move across the video
@@ -1803,6 +1812,7 @@
 
     /*Emit content light level info SEI*/
     int         bEmitCLL;
+	
 
     /*
     * Signals picture structure SEI timing message for every frame
@@ -1819,6 +1829,17 @@
 
     /*Input sequence bit depth. It can be either 8bit, 10bit or 12bit.*/
     int       sourceBitDepth;
+	
+     /* A genuine threshold which determines whether a frame is a scenecut or not
+      * when compared against edge and color sad values of a frames histograms.Default 0.01
+      * Range:real number in range (0,2)
+      * Used for histogram based scene cut detection */
+      double    edgeTransitionThreshold;
+
+    /*enables improved scenecut detection algorithm to detect scenecuts for slice type
+      decision and rate control */
+      bool      bHistbasedScenecut;
+
 } x265_param;
 /* x265_param_alloc:
  *  Allocates an x265_param instance. The returned param structure is not
diff -r 37648fca915b -r deaecadc4306 source/x265cli.h
--- a/source/x265cli.h	Fri Oct 11 12:45:52 2019 +0530
+++ b/source/x265cli.h	Tue Oct 15 12:16:17 2019 +0530
@@ -129,6 +129,9 @@
     { "scenecut",       required_argument, NULL, 0 },
     { "no-scenecut",          no_argument, NULL, 0 },
     { "scenecut-bias",  required_argument, NULL, 0 },
+    { "hist-scenecut",  no_argument, NULL, 0},
+    { "no-hist-scenecut", no_argument, NULL, 0},
+    { "hist-threshold", required_argument, NULL, 0},
     { "fades",                no_argument, NULL, 0 },
     { "no-fades",             no_argument, NULL, 0 },
     { "radl",           required_argument, NULL, 0 },
@@ -485,7 +488,10 @@
     H0("   --gop-lookahead <integer>     Extends gop boundary if a scenecut is found within this from keyint boundary. Default 0\n");
     H0("   --no-scenecut                 Disable adaptive I-frame decision\n");
     H0("   --scenecut <integer>          How aggressively to insert extra I-frames. Default %d\n", param->scenecutThreshold);
-    H1("   --scenecut-bias <0..100.0>    Bias for scenecut detection. Default %.2f\n", param->scenecutBias);
+    H0("   --hist-scenecut .....         Enables improved scene-cut detection using histogram based algorithm.");
+    H0("   --no-hist-scenecut            Disables improved scene-cut detection using histogram based algorithm. ");
+    H0("   --scenecut-bias <0..100.0>    Bias for scenecut detection. Default %.2f\n", param->scenecutBias); 
+    H0("   --hist-threshold <0.0..2.0>   Threshold for histogram based scenecut detection Default %.2f\n", param->edgeTransitionThreshold);
     H0("   --[no-]fades                  Enable detection and handling of fade-in regions. Default %s\n", OPT(param->bEnableFades));
     H0("   --radl <integer>              Number of RADL pictures allowed in front of IDR. Default %d\n", param->radl);
     H0("   --intra-refresh               Use Periodic Intra Refresh instead of IDR frames\n");
-------------- next part --------------
A non-text attachment was scrubbed...
Name: x265.patch
Type: text/x-patch
Size: 68481 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20191015/750d2eb9/attachment-0001.bin>


More information about the x265-devel mailing list