[x265] [x265 Patch 1 of 2] Histogram based scenecut detection

Srikanth Kurapati srikanth.kurapati at multicorewareinc.com
Thu Oct 17 16:07:23 CEST 2019


  # HG changeset patch
# User Srikanth Kurapati <srikanth.kurapati at multicorewareinc.com>
# Date 1571317991 -19800
#      Thu Oct 17 18:43:11 2019 +0530
# Node ID 978a57943c8f622de41ddb1931504d6df4ebafc1
# Parent  7fc1f6ef2b96aa7c85bbae06c649a02efc44e940
Histogram Based Scenecut Detection
1. Adds infrastructure to identify scenecuts using sad of edge and chroma
histogram based thresholding.

diff -r 7fc1f6ef2b96 -r 978a57943c8f source/common/CMakeLists.txt
--- a/source/common/CMakeLists.txt Mon Oct 14 14:33:16 2019 +0530
+++ b/source/common/CMakeLists.txt Thu Oct 17 18:43:11 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
+    histscenecut.h histscenecut.cpp)
diff -r 7fc1f6ef2b96 -r 978a57943c8f source/common/common.h
--- a/source/common/common.h Mon Oct 14 14:33:16 2019 +0530
+++ b/source/common/common.h Thu Oct 17 18:43:11 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 7fc1f6ef2b96 -r 978a57943c8f source/common/histscenecut.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/common/histscenecut.cpp Thu Oct 17 18:43:11 2019 +0530
@@ -0,0 +1,637 @@
+#include "encoder.h"
+#include "histscenecut.h"
+#include "slicetype.h"
+
+using namespace std;
+using namespace X265_NS;
+
+namespace X265_NS {
+
+bool computeEdge(pixel * edgePic, pixel *refPic, pixel * edgeTheta,
intptr_t stride, int height, int width)
+{
+    if (!edgePic || !refPic || !edgeTheta)
+    {
+        return false;
+    }
+    else
+    {
+        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 >=
edgeThreshold ? whitePixel : blackPixel);
+                }
+            }
+        }
+        return true;
+    }
+}
+
+Histogram::Histogram()
+{
+    memset(m_frequencyDistribution, 0, HISTOGRAM_SIZE * sizeof(int32_t));
+}
+
+void Histogram::copyHistogram(Histogram const& hist)
+{
+    memset(m_frequencyDistribution, 0, HISTOGRAM_SIZE * sizeof(int32_t));
+    memcpy(m_frequencyDistribution, hist.m_frequencyDistribution,
sizeof(int32_t)*HISTOGRAM_SIZE);
+}
+
+Histogram::Histogram(Histogram const& hist)
+{
+    copyHistogram(hist);
+}
+
+Histogram & Histogram::operator=(Histogram const& hist)
+{
+    copyHistogram(hist);
+    return *this;
+}
+
+Histogram::~Histogram() {}
+
+YuvHistogram::YuvHistogram() {}
+
+void YuvHistogram::initHists()
+{
+    bAlloc = false;
+    bisUpdated = false;
+    m_param = NULL;
+    m_yuvHist = m_edgeHist = NULL;
+    m_planeSizes = m_planeHeights = m_planeWidths = NULL;
+    m_edgePic = m_edgeThetaPic = NULL;
+    m_planeSizes = X265_MALLOC(int32_t, m_planeCount);
+    m_planeHeights = X265_MALLOC(int32_t, m_planeCount);
+    m_planeWidths = X265_MALLOC(int32_t, m_planeCount);
+    if (!m_planeSizes || !m_planeHeights || !m_planeWidths)
+    {
+        x265_log(m_param, X265_LOG_ERROR, "unable to allocate memory for
plane dimensions\n");
+        bAlloc &= false;
+    }
+    else
+    {
+        memset(m_planeSizes, 0, m_planeCount * sizeof(int32_t));
+        memset(m_planeHeights, 0, m_planeCount * sizeof(int32_t));
+        memset(m_planeWidths, 0, m_planeCount * sizeof(int32_t));
+        bAlloc &= true;
+    }
+
+    m_yuvHist = X265_MALLOC(Histogram, m_planeCount);
+    m_edgeHist = X265_MALLOC(Histogram, m_planeCount);
+    if (!m_yuvHist || !m_edgeHist)
+    {
+        bAlloc &= false;
+        x265_log(m_param, X265_LOG_ERROR, "unable to allocate memory for
histograms\n");
+    }
+}
+
+void YuvHistogram::initHistograms(int32_t planeCount)
+{
+    this->m_planeCount = planeCount;
+    initHists();
+}
+
+void YuvHistogram::initHistograms(x265_param *p)
+{
+    m_param = p;
+    m_planeCount = x265_cli_csps[m_param->internalCsp].planes;
+    initHists();
+}
+
+bool YuvHistogram::allocEdgeBuffers()
+{
+    //allocate memory for edge filter output and histograms
+    bool isalloc = true;
+    m_edgePic = X265_MALLOC(pixel*, m_planeCount);
+    m_edgeThetaPic = X265_MALLOC(pixel*, m_planeCount);
+    if (!m_edgePic || !m_edgeThetaPic)
+    {
+        isalloc &= false;
+        x265_log(m_param, X265_LOG_ERROR, "unable to allocate memory for
edge buffers\n");
+        return isalloc;
+    }
+    for (int i = 0; i < m_planeCount; i++) {
+        m_edgePic[i] = m_edgeThetaPic[i] = NULL;
+        m_edgePic[i] = X265_MALLOC(pixel, m_planeSizes[i]);
+        m_edgeThetaPic[i] = X265_MALLOC(pixel, m_planeSizes[i]);
+        if (m_edgePic[i] && m_edgeThetaPic[i])
+        {
+          memset(m_edgePic[i], 0, m_planeSizes[i] * sizeof(pixel));
+          memset(m_edgeThetaPic[i], 0, m_planeSizes[i] * sizeof(pixel));
+         isalloc &= true;
+        }
+        else
+        isalloc &= false;
+    }
+    return isalloc;
+}
+
+YuvHistogram::~YuvHistogram()
+{
+    freeHistogramBuffers(); //change implementation based on allocation
changes
+}
+
+void YuvHistogram::copyHist(YuvHistogram const& hist)
+{
+    m_maxuvHist = hist.m_maxuvHist;
+    m_planeCount = hist.m_planeCount;
+    bisUpdated = hist.bisUpdated;
+    m_param = hist.m_param;
+    memcpy(m_planeSizes, hist.m_planeSizes, m_planeCount *
sizeof(int32_t));
+    memcpy(m_planeHeights, hist.m_planeHeights, m_planeCount *
sizeof(int32_t));
+    memcpy(m_planeWidths, hist.m_planeWidths, m_planeCount *
sizeof(int32_t));
+    memcpy(m_yuvHist, hist.m_yuvHist, m_planeCount * sizeof(Histogram));
+    memcpy(m_edgeHist, hist.m_edgeHist, m_planeCount * sizeof(Histogram));
+    if (!bAlloc)
+    {
+        bAlloc = false;
+        bAlloc = allocEdgeBuffers();
+    }
+    if (bAlloc)
+    {
+        for (int i = 0; i < m_planeCount; i++)
+        {
+            if (m_edgePic[i] && m_edgeThetaPic[i]) {
+                memcpy(m_edgePic[i], hist.m_edgePic[i], m_planeSizes[i] *
sizeof(pixel));
+                memcpy(m_edgeThetaPic[i], hist.m_edgeThetaPic[i],
m_planeSizes[i] * sizeof(pixel));
+            }
+        }
+    }
+}
+
+YuvHistogram::YuvHistogram(YuvHistogram const& hist)
+{
+    copyHist(hist);
+}
+
+YuvHistogram & YuvHistogram ::operator=(const YuvHistogram & refHist)
+{
+    copyHist(refHist);
+    return *this;
+}
+
+void YuvHistogram::freeHistogramBuffers()
+{
+    //de allocate memory for histograms and edge filtered output
+    if (m_edgePic && m_edgeThetaPic)
+    {
+        for (int i = 0; i < m_planeCount; i++)
+        {
+            if (m_edgePic[i] && m_edgeThetaPic[i])
+            {
+                X265_FREE_ZERO(m_edgePic[i]);
+                X265_FREE_ZERO(m_edgeThetaPic[i]);
+            }
+        }
+        X265_FREE_ZERO(m_edgePic);
+        X265_FREE_ZERO(m_edgeThetaPic);
+    }
+
+    if (m_planeSizes && m_planeHeights && m_planeWidths)
+    {
+        X265_FREE_ZERO(m_planeSizes);
+        X265_FREE_ZERO(m_planeHeights);
+        X265_FREE_ZERO(m_planeWidths);
+    }
+
+    if (m_yuvHist && m_edgeHist)
+    {
+        X265_FREE_ZERO(m_yuvHist);
+        X265_FREE_ZERO(m_edgeHist);
+    }
+}
+
+bool YuvHistogram::edgeFilter(x265_picture *pic)
+{
+
+    if (!bAlloc)
+    {
+        for (int i = 0; i < m_planeCount; i++)
+        {
+            m_planeWidths[i] = pic->width;
+            m_planeHeights[i] = pic->height >>
x265_cli_csps[pic->colorSpace].height[i];
+            m_planeSizes[i] = m_planeWidths[i] * m_planeHeights[i];
+        }
+        bAlloc = allocEdgeBuffers();
+    }
+    if (bAlloc)
+    {
+        memset(m_edgePic[0], 0, sizeof(pixel) * m_planeSizes[0]);
+        memset(m_edgeThetaPic[0], 0, sizeof(pixel) * m_planeSizes[0]);
+        pixel *src = (pixel*)pic->planes[0];
+        pixel *edge_pic = m_edgePic[0];
+        pixel *ref_pic = src;
+        pixel *edge_theta = m_edgeThetaPic[0];
+        assert(edge_pic != NULL);
+        assert(ref_pic != NULL);
+        memcpy(edge_pic, src, m_planeSizes[0] * sizeof(pixel));
+        memcpy(ref_pic, src, m_planeSizes[0] * sizeof(pixel));
+
+        if (!computeEdge(edge_pic, ref_pic, edge_theta, m_planeWidths[0],
m_planeHeights[0], m_planeWidths[0]))
+           {
+            x265_log(m_param, X265_LOG_ERROR, "Failed edge computation!");
+            return false;
+           }
+    }
+    else
+    {
+        return false;
+    }
+    return true;
+}
+
+bool YuvHistogram::computeHistograms(x265_picture &curFrame)
+{
+    bool bSuccess = false;
+    bSuccess = computeLumaEdgeHistogram(curFrame);
+    if (bSuccess)
+    {
+        if (m_planeCount > 1)
+        {
+            bSuccess &= computeChromaHistograms(curFrame);
+        }
+        return bSuccess;
+    }
+    else
+    {
+        return bSuccess;
+    }
+}
+
+bool YuvHistogram::computeLumaEdgeHistogram(x265_picture &frame)
+{
+    pixel pixelVal = 0;
+    memset(m_edgeHist[0].m_frequencyDistribution, 0, HISTOGRAM_SIZE *
sizeof(int32_t));
+    int size = frame.height*(frame.stride[0] >> SHIFT);
+    for (int i = 0; i < size; i++)
+    {
+        pixelVal = m_edgePic[0][i];
+        m_edgeHist[0].m_frequencyDistribution[pixelVal]++;
+    }
+    return true;
+}
+
+bool YuvHistogram::computeChromaHistograms(x265_picture &frame)
+{
+    /*u hist calculation*/
+    pixel pixelVal = 0;
+    int HeightL = (frame.height >>
x265_cli_csps[frame.colorSpace].height[1]);
+    int size = HeightL * (frame.stride[1] >> SHIFT);
+    memset(m_yuvHist[1].m_frequencyDistribution, 0, HISTOGRAM_SIZE *
sizeof(int32_t));
+
+    for (int i = 0; i < size; i++)
+    {
+        pixelVal = *((pixel *)frame.planes[1] + i);
+        m_yuvHist[1].m_frequencyDistribution[pixelVal]++;
+    }
+
+    /*v hist calculation for independent uv planes */
+    if (m_planeCount == 3)
+    {
+        pixelVal = 0;
+        int heightV = (frame.height >>
x265_cli_csps[frame.colorSpace].height[2]);
+        size = heightV * (frame.stride[2] >> SHIFT);
+        memset(m_yuvHist[2].m_frequencyDistribution, 0, HISTOGRAM_SIZE *
sizeof(int32_t));
+        for (int i = 0; i < size; i++)
+        {
+            pixelVal = *((pixel *)frame.planes[2] + i);
+            m_yuvHist[2].m_frequencyDistribution[pixelVal]++;
+        }
+        for (int i = 0; i < HISTOGRAM_SIZE; i++)
+        {
+            m_maxuvHist.m_frequencyDistribution[i] =
max(m_yuvHist[1].m_frequencyDistribution[i],
m_yuvHist[2].m_frequencyDistribution[i]);
+        }
+    }
+    else
+    {
+        m_maxuvHist = m_yuvHist[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->planeCount = planecount;
+    sadYuv = NULL;
+    psadYuv = NULL;
+    sadYuv = X265_MALLOC(int32_t, planeCount);
+    psadYuv = X265_MALLOC(double, planeCount);
+    if (sadYuv && psadYuv)
+    {
+        memset(sadYuv, 0, planeCount * sizeof(int32_t));
+        memset(psadYuv, 0, sizeof(double) * planeCount);
+    }
+}
+
+SadYuv & SadYuv::operator=(SadYuv const& sadVal)
+{
+    this->planeCount = sadVal.planeCount;
+    if (!sadYuv && !psadYuv)
+    {
+        sadYuv = NULL;
+        psadYuv = NULL;
+        sadYuv = X265_MALLOC(int32_t, planeCount);
+        psadYuv = X265_MALLOC(double, planeCount);
+        if (sadYuv && psadYuv)
+        {
+            memcpy(sadYuv, sadVal.sadYuv, planeCount * sizeof(int32_t));
+            memcpy(psadYuv, sadVal.psadYuv, sizeof(double) * planeCount);
+        }
+    }
+    else
+    {
+        if (sadYuv)
+            memcpy(sadYuv, sadVal.sadYuv, planeCount * sizeof(int32_t));
+        if (psadYuv)
+            memcpy(psadYuv, sadVal.psadYuv, sizeof(double) * planeCount);
+    }
+    return *this;
+}
+
+SadYuv::~SadYuv()
+{
+    if (sadYuv && psadYuv)
+    {
+        X265_FREE(sadYuv);
+        X265_FREE(psadYuv);
+    }
+}
+
+int sad_stats::m_framesRead=0;
+
+sad_stats::sad_stats(int planeCount, double threshold)
+{
+    this->m_planeCount = planeCount;
+    calculateThresholds(threshold);
+    allocateBuffers();
+}
+
+void sad_stats::calculateThresholds(double threshold)
+{
+    m_edgeHistThreshold = threshold;
+    m_strengthFactor = 2.0;
+    m_chromaHistThreshold = threshold * 10.0;
+    m_scaledEdgeThreshold = m_edgeHistThreshold * m_strengthFactor;
+    m_scaledChromaThreshold = m_chromaHistThreshold * m_strengthFactor;
+}
+
+void sad_stats::init()
+{
+    bSceneCut = NULL;
+    bDropFrame = NULL;
+    m_SadVals = NULL;
+    m_maxuvSadVals = NULL;
+    m_edgeSadVals = NULL;
+    m_prevHist = NULL;
+}
+
+sad_stats::~sad_stats()
+{
+    releaseBuffers();
+}
+
+void sad_stats::allocateBuffers()
+{
+    init();
+    m_SadVals = new SadYuv[DUP_BUFFER]();
+    m_maxuvSadVals = new SadYuv[DUP_BUFFER]();
+    m_edgeSadVals = new SadYuv[DUP_BUFFER]();
+    m_prevHist = new YuvHistogram();
+    m_prevHist->initHistograms(m_planeCount);
+    bSceneCut = new bool[DUP_BUFFER];
+    bDropFrame = new bool[DUP_BUFFER];
+
+    for (int i = 0; i < DUP_BUFFER; i++)
+    {
+        m_SadVals[i].initSadYuv(m_planeCount);
+        m_maxuvSadVals[i].initSadYuv(m_planeCount);
+        m_edgeSadVals[i].initSadYuv(m_planeCount);
+    }
+    if (!m_SadVals || !m_maxuvSadVals || !m_edgeSadVals || !bSceneCut ||
!bDropFrame)
+    {
+        x265_log(NULL, X265_LOG_ERROR, "Heap Error !");
+        exit(101);
+    }
+    else
+    {
+        memset(bSceneCut, false, 2 * sizeof(bool));
+        memset(bDropFrame, false, 2 * sizeof(bool));
+    }
+}
+
+void sad_stats::releaseBuffers()
+{
+    if (m_SadVals && m_maxuvSadVals && m_edgeSadVals && bSceneCut &&
bDropFrame && m_prevHist)
+    {
+        delete[] m_SadVals;
+        delete[] m_maxuvSadVals;
+        delete[] m_edgeSadVals;
+        delete[] bSceneCut;
+        delete[] bDropFrame;
+        delete   m_prevHist;
+    }
+}
+
+void sad_stats::updateSadVals(int32_t **yuvSadVal, int32_t **edgeSadVal,
int32_t** maxuvSadVal,
+    double** maxuvNormalizedSad, double** yuvNormalizedSad, double **
edgeNormalizedSad)
+{
+    *yuvSadVal = m_SadVals[0].sadYuv,
+    *edgeSadVal = m_edgeSadVals[0].sadYuv,
+    *maxuvSadVal = m_maxuvSadVals[0].sadYuv;
+    *maxuvNormalizedSad = m_maxuvSadVals[0].psadYuv,
+    *yuvNormalizedSad = m_SadVals[0].psadYuv,
+    *edgeNormalizedSad = m_edgeSadVals[0].psadYuv;
+}
+
+void sad_stats::resetSadVals(int32_t *yuvSadVal, int32_t *edgeSadVal,
int32_t* maxuvSadVal,
+    double* maxuvNormalizedSad, double* yuvNormalizedSad, double *
edgeNormalizedSad)
+{
+    maxuvSadVal[0] = 0;
+    maxuvNormalizedSad[0] = 0.0;
+    memset(yuvSadVal, 0, m_planeCount * sizeof(int32_t));
+    memset(edgeSadVal, 0, m_planeCount * sizeof(int32_t));
+    memset(edgeNormalizedSad, 0, m_planeCount * sizeof(double));
+    memset(yuvNormalizedSad, 0, m_planeCount * sizeof(double));
+}
+
+bool sad_stats::computeSadValue(YuvHistogram *frameHists, int32_t*
planeSizes)
+{
+    int32_t   *yuvSadVal = NULL, *edgeSadVal = NULL, *maxuvSadVal = NULL;
+    double *maxuvNormalizedSad = NULL, *yuvNormalizedSad = NULL,
*edgeNormalizedSad = NULL;
+    YuvHistogram * refHist = NULL, *curHist = NULL;
+
+    /*inorder to process frames as per poc's updated by frame duplication
*/
+    if (m_framesRead > 0)
+    {
+        if (!frameHists[0].isUpdated() && frameHists[1].isUpdated())
+        {
+            refHist = m_prevHist;
+            curHist = frameHists + 1;
+            yuvSadVal = m_SadVals[1].sadYuv,
+            edgeSadVal = m_edgeSadVals[1].sadYuv,
+            maxuvSadVal = m_maxuvSadVals[1].sadYuv;
+            maxuvNormalizedSad = m_maxuvSadVals[1].psadYuv,
+            yuvNormalizedSad = m_SadVals[1].psadYuv,
+            edgeNormalizedSad = m_edgeSadVals[1].psadYuv;
+            frameHists[1].setUpdateFlag(false);
+        }
+        else if (frameHists[0].isUpdated() && frameHists[1].isUpdated())
+        {
+            refHist = m_prevHist;
+            curHist = frameHists;
+            frameHists[0].setUpdateFlag(false);
+            updateSadVals(&yuvSadVal, &edgeSadVal,
&maxuvSadVal,&maxuvNormalizedSad,&yuvNormalizedSad,&edgeNormalizedSad);
+        }
+        else if (frameHists[0].isUpdated() && !frameHists[1].isUpdated())
+        {
+            refHist = m_prevHist;
+            curHist = frameHists;
+            frameHists[0].setUpdateFlag(false);
+            updateSadVals(&yuvSadVal, &edgeSadVal, &maxuvSadVal,
&maxuvNormalizedSad, &yuvNormalizedSad, &edgeNormalizedSad);
+        }
+        else
+        {
+            return true;
+        }
+    }
+    else
+    {
+        curHist = frameHists;
+        updateSadVals(&yuvSadVal, &edgeSadVal, &maxuvSadVal,
&maxuvNormalizedSad, &yuvNormalizedSad, &edgeNormalizedSad);
+        frameHists[0].setUpdateFlag(false);
+    }
+
+    if (m_framesRead == 0)
+    { //first frame is scenecut by default no sad computation for the same.
+        resetSadVals(yuvSadVal, edgeSadVal, maxuvSadVal,
maxuvNormalizedSad, yuvNormalizedSad, edgeNormalizedSad);
+    }
+    else
+    {
+        int32_t freqDiff[3];
+        int32_t edgefreqDiff[3];
+        int32_t maxuvfreqDiff;
+        double chromaProbabilityDiff[3], edgeProbabilityDiff[3];
+        memset(chromaProbabilityDiff, 0, m_planeCount * sizeof(double));
+        memset(edgeProbabilityDiff, 0, m_planeCount * sizeof(double));
+        memset(freqDiff, 0, 3 * sizeof(int32_t));
+        maxuvfreqDiff = 0;
+        memset(edgefreqDiff, 0, 3 * sizeof(int32_t));
+        resetSadVals(yuvSadVal, edgeSadVal, maxuvSadVal,
maxuvNormalizedSad, yuvNormalizedSad, edgeNormalizedSad);
+        for (int i = 0; i < m_planeCount; i++)
+        {
+            for (int j = 0; j < HISTOGRAM_SIZE; j++)
+            {
+                if (i == 0 && m_planeCount >= 1)
+                {
+                    maxuvfreqDiff =
(abs(curHist->m_maxuvHist.m_frequencyDistribution[j] -
refHist->m_maxuvHist.m_frequencyDistribution[j]));
+                    maxuvSadVal[i] += maxuvfreqDiff;
+                    maxuvNormalizedSad[i] += (double)maxuvfreqDiff /
planeSizes[i];
+                    edgefreqDiff[i] =
abs(curHist->m_edgeHist[i].m_frequencyDistribution[j] -
refHist->m_edgeHist[i].m_frequencyDistribution[j]);
+                    edgeProbabilityDiff[i] = double(edgefreqDiff[i]) /
planeSizes[i];
+                    edgeSadVal[i] += edgefreqDiff[i];
+                    edgeNormalizedSad[i] += edgeProbabilityDiff[i];
+                }
+                else
+                {
+                    freqDiff[i] =
abs(curHist->m_yuvHist[i].m_frequencyDistribution[j] -
refHist->m_yuvHist[i].m_frequencyDistribution[j]);
+                    chromaProbabilityDiff[i] = (double)freqDiff[i] /
planeSizes[i];
+                    yuvSadVal[i] += freqDiff[i];
+                    yuvNormalizedSad[i] += chromaProbabilityDiff[i];
+                }
+            }
+        }
+    }
+    *m_prevHist = *curHist;
+    m_framesRead++;
+    return  true;
+}
+
+void sad_stats::findSceneCuts(x265_picture * picList, bool& bDup)
+{
+        if (m_framesRead == 1)
+        {
+            //for first frame
+            bSceneCut[0] = true;
+            bDropFrame[0] = false;
+            picList->analysisData.bScenecut = (int)getSceneCutflag(0);
+            bDup = getDropflag(0);
+            picList->analysisData.edgeSadValue =
m_edgeSadVals[0].psadYuv[0];
+            picList->analysisData.chromaSadValue =
m_maxuvSadVals[0].psadYuv[0];
+        }
+        else
+        {
+            bSceneCut[1] = bDropFrame[1] = false;
+            if (m_edgeSadVals[1].psadYuv[0] == 0)
+            {
+                bDropFrame[1] = true;
+            }
+            else if (m_edgeSadVals[1].psadYuv[0] > m_edgeHistThreshold ||
m_maxuvSadVals[1].psadYuv[0] >= m_chromaHistThreshold)
+            {
+                bSceneCut[1] = true;
+                bDropFrame[1] = false;
+            }
+            else if (m_edgeSadVals[1].psadYuv[0] >  m_scaledEdgeThreshold
|| m_maxuvSadVals[1].psadYuv[0] >= m_scaledChromaThreshold)
+            {
+                bSceneCut[1] = true;
+                bDropFrame[1] = false;
+            }
+            picList->analysisData.bScenecut = (int)getSceneCutflag(1);
+            bDup = getDropflag(1);
+            bDup = getDropflag(1);
+            picList->analysisData.edgeSadValue =
m_edgeSadVals[1].psadYuv[0];
+            picList->analysisData.chromaSadValue =
m_maxuvSadVals[1].psadYuv[0];
+        }
+    }
+
+bool sad_stats::getDropflag(int idx)
+{
+    return bDropFrame[idx];
+}
+
+bool sad_stats::getSceneCutflag(int idx)
+{
+    return bSceneCut[idx];
+}
+
+}
diff -r 7fc1f6ef2b96 -r 978a57943c8f source/common/histscenecut.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/common/histscenecut.h Thu Oct 17 18:43:11 2019 +0530
@@ -0,0 +1,106 @@
+#ifndef SCENECUT_H
+#define SCENECUT_H
+
+#include "yuv.h"
+#include "common.h"
+
+#ifdef HIGH_BIT_DEPTH
+#define edgeThreshold 1023.0
+#define whitePixel 1023.0
+#else
+#define edgeThreshold 255.0
+#define pixel whitePixel 255.0
+#endif
+
+using namespace std;
+
+namespace X265_NS {
+
+class Histogram {
+
+public:
+    int32_t m_frequencyDistribution[HISTOGRAM_SIZE];
+    Histogram();
+    void copyHistogram(Histogram const& hist);
+    Histogram(Histogram const& hist);
+    Histogram & operator=(Histogram const& hist);
+    ~Histogram();
+};
+
+class YuvHistogram {
+public:
+    pixel     **m_edgePic;
+    pixel     **m_edgeThetaPic;
+    Histogram  *m_yuvHist;
+    Histogram  *m_edgeHist;
+    Histogram   m_maxuvHist;
+    int32_t    *m_planeSizes;
+    int32_t    *m_planeHeights;
+    int32_t    *m_planeWidths;
+    int32_t     m_planeCount;
+    x265_param *m_param; /*for handling various color spaces*/
+    bool bisUpdated;
+    bool bAlloc;
+    YuvHistogram();
+    ~YuvHistogram();
+    void initHists();
+    void initHistograms(int32_t planeCount);
+    void initHistograms(x265_param *p);
+    bool allocEdgeBuffers();
+    void copyHist(YuvHistogram const & hist);
+    YuvHistogram(YuvHistogram const& hist);
+    YuvHistogram & operator=(const YuvHistogram & refHist);
+    void freeHistogramBuffers();
+    bool edgeFilter(x265_picture *frame);
+    bool computeHistograms(x265_picture &curFrame);
+    bool computeLumaEdgeHistogram(x265_picture &frame);
+    bool computeChromaHistograms(x265_picture &frame);
+    bool isUpdated();
+    void setUpdateFlag(bool flag);
+    bool getUpdateFlag();
+};
+
+struct SadYuv {
+    int32_t *sadYuv;
+    double  *psadYuv;
+    int      planeCount;
+    SadYuv();
+    ~SadYuv();
+    void initSadYuv(int planeCount);
+    SadYuv & operator=(SadYuv const& sadVal);
+};
+
+class sad_stats {
+    bool            *bSceneCut;
+    bool            *bDropFrame;
+    SadYuv          *m_SadVals;
+    SadYuv          *m_maxuvSadVals;
+    SadYuv          *m_edgeSadVals;
+    int              m_planeCount;
+    YuvHistogram    *m_prevHist;
+    static int       m_framesRead;
+    double           m_edgeHistThreshold;
+    double           m_chromaHistThreshold;
+    double           m_scaledChromaThreshold;
+    double           m_scaledEdgeThreshold;
+    double           m_strengthFactor;
+public:
+    sad_stats(int planeCount, double threshold);
+    ~sad_stats();
+    void init();
+    void allocateBuffers();
+    void releaseBuffers();
+    void updateSadVals(int32_t **yuvSadVal, int32_t **edgeSadVal,
int32_t** maxuvSadVal, double** maxuvNormalizedSad, double**
yuvNormalizedSad, double ** edgeNormalizedSad);
+    void resetSadVals(int32_t *yuvSadVal, int32_t *edgeSadVal, int32_t
*maxuvSadVal, double *maxuvNormalizedSad, double *yuvNormalizedSad, double
*edgeNormalizedSad);
+    void calculateThresholds(double threshold);
+    bool computeSadValue(YuvHistogram *frames, int32_t* planeSizes);
+    void findSceneCuts(x265_picture * picList, bool & bDup);
+    bool getDropflag(int idx);
+    bool getSceneCutflag(int idx);
+};
+
+bool computeEdge(pixel *edgePic, pixel *refPic, pixel *edgeTheta, intptr_t
stride, int height, int width);
+
+}
+
+#endif
diff -r 7fc1f6ef2b96 -r 978a57943c8f source/x265.h
--- a/source/x265.h Mon Oct 14 14:33:16 2019 +0530
+++ b/source/x265.h Thu Oct 17 18:43:11 2019 +0530
@@ -210,7 +210,7 @@
     uint32_t                          numCUsInFrame;
     uint32_t                          numPartitions;
     uint32_t                          depthBytes;
-    int                               bScenecut;
+    bool                               bScenecut;
     x265_weight_param*                wt;
     x265_analysis_inter_data*         interData;
     x265_analysis_intra_data*         intraData;
@@ -219,6 +219,8 @@
     uint8_t*                          modeFlag[2];
     x265_analysis_validate            saveParam;
     x265_analysis_distortion_data*    distortionData;
+    double                             edgeSadValue;
+    double                             chromaSadValue;
 } x265_analysis_data;

 /* cu statistics */
@@ -291,12 +293,15 @@
     char             sliceType;
     int              bScenecut;
     double           ipCostRatio;
+
     int              frameLatency;
     x265_cu_stats    cuStats;
     x265_pu_stats    puStats;
     double           totalFrameTime;
     double           vmafFrameScore;
     double           bufferFillFinal;
+    double           yedgeSadValue;
+    double           chromaSadValue;
 } x265_frame_stats;

 typedef struct x265_ctu_info_t
@@ -471,6 +476,9 @@
     uint32_t picStruct;

     int    width;
+
+    //Flag to determine the latest frame in the buffer
+    bool bufUpdated;
 } x265_picture;

 typedef enum
@@ -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


-- 
*With Regards,*
*Srikanth Kurapati.*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20191017/791df6f0/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 1-HistSceneCut.patch
Type: application/octet-stream
Size: 30137 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20191017/791df6f0/attachment-0001.obj>


More information about the x265-devel mailing list