[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