[x265] [PATCH 1 of 2] Improvements to hist-based scenecut algorithm.

Pooja Venkatesan pooja at multicorewareinc.com
Mon Jun 29 15:13:10 CEST 2020


>From 671506e40fbbb0aed825013e434c7c39cec2bb93 Mon Sep 17 00:00:00 2001
From: Pooja Venkatesan <pooja at multicorewareinc.com>
Date: Thu, 25 Jun 2020 20:42:50 +0530
Subject: [PATCH] Improvements to hist-based scenecut algorithm.

This patch does the following:
1. Strengthens scenecut detection using threshold intervals, spatial and
temporal properties.
2. Change default value of hist-threshold to 0.03
---
 doc/reST/cli.rst             |  7 +--
 source/common/lowres.cpp     |  3 ++
 source/common/lowres.h       |  5 ++
 source/common/param.cpp      |  2 +-
 source/encoder/encoder.cpp   | 25 ++++++++--
 source/encoder/encoder.h     |  5 +-
 source/encoder/slicetype.cpp | 89 +++++++++++++++++++++++++++---------
 source/encoder/slicetype.h   |  1 +
 source/x265.h                |  2 +-
 9 files changed, 107 insertions(+), 32 deletions(-)

diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst
index b9d795ace..23b74c3d8 100644
--- a/doc/reST/cli.rst
+++ b/doc/reST/cli.rst
@@ -1468,9 +1468,10 @@ Slice decision options
 .. option:: --hist-threshold <0.0..1.0>

  This value represents the threshold for normalized SAD of edge histograms
used in scenecut detection.
- This requires :option:`--hist-scenecut` to be enabled. For example, a
value of 0.2 indicates that a frame with normalized SAD value
- greater than 0.2 against the previous frame as scenecut.
- Default 0.01.
+ This requires :option:`--hist-scenecut` to be enabled. For example, a
value of 0.2 indicates that a frame with normalized SAD value
+ greater than 0.2 against the previous frame as scenecut.
+ Increasing the threshold reduces the number of scenecuts detected.
+ Default 0.03.

 .. option:: --radl <integer>

diff --git a/source/common/lowres.cpp b/source/common/lowres.cpp
index e8dd991bc..db1c2d159 100644
--- a/source/common/lowres.cpp
+++ b/source/common/lowres.cpp
@@ -266,6 +266,9 @@ void Lowres::init(PicYuv *origPic, int poc)
     indB = 0;
     memset(costEst, -1, sizeof(costEst));
     memset(weightedCostDelta, 0, sizeof(weightedCostDelta));
+    interPCostPercDiff = 0.0;
+    intraCostPercDiff = 0.0;
+    m_bIsMaxThres = false;

     if (qpAqOffset && invQscaleFactor)
         memset(costEstAq, -1, sizeof(costEstAq));
diff --git a/source/common/lowres.h b/source/common/lowres.h
index 5c50fad67..200b1f032 100644
--- a/source/common/lowres.h
+++ b/source/common/lowres.h
@@ -234,6 +234,11 @@ struct Lowres : public ReferencePlanes
     uint16_t* propagateCost;
     double    weightedCostDelta[X265_BFRAME_MAX + 2];
     ReferencePlanes weightedRef[X265_BFRAME_MAX + 2];
+    /* For hist-based scenecut */
+    bool   m_bIsMaxThres;
+    double interPCostPercDiff;
+    double intraCostPercDiff;
+
     bool create(x265_param* param, PicYuv *origPic, uint32_t qgSize);
     void destroy();
     void init(PicYuv *origPic, int poc);
diff --git a/source/common/param.cpp b/source/common/param.cpp
index 925f0c460..8c0498efc 100644
--- a/source/common/param.cpp
+++ b/source/common/param.cpp
@@ -168,7 +168,7 @@ void x265_param_default(x265_param* param)
     param->bFrameAdaptive = X265_B_ADAPT_TRELLIS;
     param->bBPyramid = 1;
     param->scenecutThreshold = 40; /* Magic number pulled in from x264 */
-    param->edgeTransitionThreshold = 0.01;
+    param->edgeTransitionThreshold = 0.03;
     param->bHistBasedSceneCut = 0;
     param->lookaheadSlices = 8;
     param->lookaheadThreads = 0;
diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp
index f6bc5408d..0c6fd80bf 100644
--- a/source/encoder/encoder.cpp
+++ b/source/encoder/encoder.cpp
@@ -1528,8 +1528,12 @@ double Encoder::normalizeRange(int32_t value,
int32_t minValue, int32_t maxValue
     return (double)(value - minValue) * (rangeEnd - rangeStart) /
(maxValue - minValue) + rangeStart;
 }

-void Encoder::findSceneCuts(x265_picture *pic, bool& bDup, double
maxUVSad, double edgeSad)
+void Encoder::findSceneCuts(x265_picture *pic, bool& bDup, double
maxUVSad, double edgeSad, bool& isMaxThres)
 {
+    double minEdgeT = m_edgeHistThreshold * MIN_EDGE_FACTOR;
+    double minChromaT = minEdgeT * SCENECUT_CHROMA_FACTOR;
+    double maxEdgeT = m_edgeHistThreshold * MAX_EDGE_FACTOR;
+    double maxChromaT = maxEdgeT * SCENECUT_CHROMA_FACTOR;
     pic->frameData.bScenecut = false;

     if (pic->poc == 0)
@@ -1544,11 +1548,20 @@ void Encoder::findSceneCuts(x265_picture *pic,
bool& bDup, double maxUVSad, doub
         {
             bDup = true;
         }
-        else if (edgeSad > m_scaledEdgeThreshold || maxUVSad >=
m_scaledChromaThreshold || (edgeSad > m_edgeHistThreshold && maxUVSad >=
m_chromaHistThreshold))
+        else if (edgeSad < minEdgeT && maxUVSad < minChromaT)
+        {
+            pic->frameData.bScenecut = false;
+        }
+        else if (edgeSad > maxEdgeT && maxUVSad > maxChromaT)
+        {
+            pic->frameData.bScenecut = true;
+            isMaxThres = true;
+        }
+        else if (edgeSad > m_scaledEdgeThreshold || maxUVSad >=
m_scaledChromaThreshold
+                 || (edgeSad > m_edgeHistThreshold && maxUVSad >=
m_chromaHistThreshold))
         {
             pic->frameData.bScenecut = true;
             bDup = false;
-            x265_log(m_param, X265_LOG_DEBUG, "scene cut at %d \n",
pic->poc);
         }
     }
 }
@@ -1581,6 +1594,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
     bool dontRead = false;
     bool bdropFrame = false;
     bool dropflag = false;
+    bool isMaxThres = false;

     if (m_exportedPic)
     {
@@ -1607,7 +1621,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
             {
                 double maxUVSad = 0.0, edgeSad = 0.0;
                 computeHistogramSAD(&maxUVSad, &edgeSad, pic_in->poc);
-                findSceneCuts(pic, bdropFrame, maxUVSad, edgeSad);
+                findSceneCuts(pic, bdropFrame, maxUVSad, edgeSad,
isMaxThres);
             }
         }

@@ -1786,6 +1800,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
         if (m_param->bHistBasedSceneCut)
         {
             inFrame->m_lowres.bScenecut = (inputPic->frameData.bScenecut
== 1) ? true : false;
+            inFrame->m_lowres.m_bIsMaxThres = isMaxThres;
         }
         if (m_param->bHistBasedSceneCut && m_param->analysisSave)
         {
@@ -4261,7 +4276,7 @@ void Encoder::configure(x265_param *p)

    if (p->bHistBasedSceneCut && !p->edgeTransitionThreshold)
    {
-       p->edgeTransitionThreshold = 0.01;
+       p->edgeTransitionThreshold = 0.03;
        x265_log(p, X265_LOG_WARNING, "using  default threshold %.2lf for
scene cut detection\n", p->edgeTransitionThreshold);
    }

diff --git a/source/encoder/encoder.h b/source/encoder/encoder.h
index fd6b3e72c..507c42d5e 100644
--- a/source/encoder/encoder.h
+++ b/source/encoder/encoder.h
@@ -165,6 +165,9 @@ class FrameData;

 #define MAX_SCENECUT_THRESHOLD 1.0
 #define SCENECUT_STRENGTH_FACTOR 2.0
+#define MIN_EDGE_FACTOR 0.5
+#define MAX_EDGE_FACTOR 1.5
+#define SCENECUT_CHROMA_FACTOR 10.0

 class Encoder : public x265_encoder
 {
@@ -373,7 +376,7 @@ public:
     bool computeHistograms(x265_picture *pic);
     void computeHistogramSAD(double *maxUVNormalizedSAD, double
*edgeNormalizedSAD, int curPoc);
     double normalizeRange(int32_t value, int32_t minValue, int32_t
maxValue, double rangeStart, double rangeEnd);
-    void findSceneCuts(x265_picture *pic, bool& bDup, double
m_maxUVSADVal, double m_edgeSADVal);
+    void findSceneCuts(x265_picture *pic, bool& bDup, double
m_maxUVSADVal, double m_edgeSADVal, bool& isMaxThres);

     void initRefIdx();
     void analyseRefIdx(int *numRefIdx);
diff --git a/source/encoder/slicetype.cpp b/source/encoder/slicetype.cpp
index 0a95e77d2..d3783cfe1 100644
--- a/source/encoder/slicetype.cpp
+++ b/source/encoder/slicetype.cpp
@@ -2001,10 +2001,41 @@ void Lookahead::slicetypeAnalyse(Lowres **frames,
bool bKeyframe)
     int numAnalyzed = numFrames;
     bool isScenecut = false;

-    /* When scenecut threshold is set, use scenecut detection for I frame
placements */
+    /* Temporal computations for scenecut detection */
     if (m_param->bHistBasedSceneCut)
-        isScenecut = frames[1]->bScenecut;
-    else
+    {
+        for (int i = numFrames - 1; i > 0; i--)
+        {
+            if (frames[i]->interPCostPercDiff > 0.0)
+                continue;
+            int64_t interCost = frames[i]->costEst[1][0];
+            int64_t intraCost = frames[i]->costEst[0][0];
+            if (interCost < 0 || intraCost < 0)
+                continue;
+            int times = 0;
+            double averagePcost = 0.0, averageIcost = 0.0;
+            for (int j = i - 1; j >= 0 && times < 5; j--, times++)
+            {
+                if (frames[j]->costEst[0][0] > 0 &&
frames[j]->costEst[1][0] > 0)
+                {
+                    averageIcost += frames[j]->costEst[0][0];
+                    averagePcost += frames[j]->costEst[1][0];
+                }
+                else
+                    times--;
+            }
+            if (times)
+            {
+                averageIcost = averageIcost / times;
+                averagePcost = averagePcost / times;
+                frames[i]->interPCostPercDiff = abs(interCost -
averagePcost) / X265_MIN(interCost, averagePcost) * 100;
+                frames[i]->intraCostPercDiff = abs(intraCost -
averageIcost) / X265_MIN(intraCost, averageIcost) * 100;
+            }
+        }
+    }
+
+    /* When scenecut threshold is set, use scenecut detection for I frame
placements */
+    if (!m_param->bHistBasedSceneCut || (m_param->bHistBasedSceneCut &&
frames[1]->bScenecut))
         isScenecut = scenecut(frames, 0, 1, true, origNumFrames);

     if (isScenecut && (m_param->bHistBasedSceneCut ||
m_param->scenecutThreshold))
@@ -2018,17 +2049,16 @@ void Lookahead::slicetypeAnalyse(Lowres **frames,
bool bKeyframe)
         m_extendGopBoundary = false;
         for (int i = m_param->bframes + 1; i < origNumFrames; i +=
m_param->bframes + 1)
         {
-            if (!m_param->bHistBasedSceneCut)
+            if (!m_param->bHistBasedSceneCut ||
(m_param->bHistBasedSceneCut && frames[i + 1]->bScenecut))
                 scenecut(frames, i, i + 1, true, origNumFrames);

             for (int j = i + 1; j <= X265_MIN(i + m_param->bframes + 1,
origNumFrames); j++)
             {
-                if ((!m_param->bHistBasedSceneCut && frames[j]->bScenecut
&& scenecutInternal(frames, j - 1, j, true)) ||
-                    (m_param->bHistBasedSceneCut && frames[j]->bScenecut))
-                    {
-                        m_extendGopBoundary = true;
-                        break;
-                    }
+                if (frames[j]->bScenecut && scenecutInternal(frames, j -
1, j, true))
+                {
+                    m_extendGopBoundary = true;
+                    break;
+                }
             }
             if (m_extendGopBoundary)
                 break;
@@ -2133,14 +2163,15 @@ void Lookahead::slicetypeAnalyse(Lowres **frames,
bool bKeyframe)
         {
             for (int j = 1; j < numBFrames + 1; j++)
             {
-                if ((!m_param->bHistBasedSceneCut && scenecut(frames, j, j
+ 1, false, origNumFrames)) ||
-                    (m_param->bHistBasedSceneCut && frames[j +
1]->bScenecut) ||
-                    (bForceRADL && (frames[j]->frameNum == preRADL)))
-                    {
-                        frames[j]->sliceType = X265_TYPE_P;
-                        numAnalyzed = j;
-                        break;
-                    }
+                bool isNextScenecut = false;
+                if (!m_param->bHistBasedSceneCut ||
(m_param->bHistBasedSceneCut && frames[j + 1]->bScenecut))
+                    isNextScenecut = scenecut(frames, j, j + 1, false,
origNumFrames);
+                if (isNextScenecut || (bForceRADL && frames[j]->frameNum
== preRADL))
+                {
+                    frames[j]->sliceType = X265_TYPE_P;
+                    numAnalyzed = j;
+                    break;
+                }
             }
         }
         resetStart = bKeyframe ? 1 : X265_MIN(numBFrames + 2, numAnalyzed
+ 1);
@@ -2203,7 +2234,7 @@ bool Lookahead::scenecut(Lowres **frames, int p0, int
p1, bool bRealScenecut, in
          * and not considered a scenecut. */
         for (int cp1 = p1; cp1 <= maxp1; cp1++)
         {
-            if (!scenecutInternal(frames, p0, cp1, false))
+            if (!m_param->bHistBasedSceneCut && !scenecutInternal(frames,
p0, cp1, false))
             {
                 /* Any frame in between p0 and cur_p1 cannot be a real
scenecut. */
                 for (int i = cp1; i > p0; i--)
@@ -2212,7 +2243,7 @@ bool Lookahead::scenecut(Lowres **frames, int p0, int
p1, bool bRealScenecut, in
                     noScenecuts = false;
                 }
             }
-            else if (scenecutInternal(frames, cp1 - 1, cp1, false))
+            else if ((m_param->bHistBasedSceneCut &&
frames[cp1]->m_bIsMaxThres) || scenecutInternal(frames, cp1 - 1, cp1,
false))
             {
                 /* If current frame is a Scenecut from p0 frame as well as
Scenecut from
                  * preceeding frame, mark it as a Scenecut */
@@ -2273,6 +2304,10 @@ bool Lookahead::scenecut(Lowres **frames, int p0,
int p1, bool bRealScenecut, in

     if (!frames[p1]->bScenecut)
         return false;
+    /* Check only scene transitions if max threshold */
+    if (m_param->bHistBasedSceneCut && frames[p1]->m_bIsMaxThres)
+        return frames[p1]->bScenecut;
+
     return scenecutInternal(frames, p0, p1, bRealScenecut);
 }

@@ -2289,7 +2324,19 @@ bool Lookahead::scenecutInternal(Lowres **frames,
int p0, int p1, bool bRealScen
     /* magic numbers pulled out of thin air */
     float threshMin = (float)(threshMax * 0.25);
     double bias = m_param->scenecutBias;
-    if (bRealScenecut)
+    if (m_param->bHistBasedSceneCut)
+    {
+        double minT = TEMPORAL_SCENECUT_THRESHOLD * (1 +
m_param->edgeTransitionThreshold);
+        if (frame->interPCostPercDiff > minT || frame->intraCostPercDiff >
minT)
+        {
+            if (bRealScenecut && frame->bScenecut)
+                x265_log(m_param, X265_LOG_DEBUG, "scene cut at %d \n",
frame->frameNum);
+            return frame->bScenecut;
+        }
+        else
+            return false;
+    }
+    else if (bRealScenecut)
     {
         if (m_param->keyframeMin == m_param->keyframeMax)
             threshMin = threshMax;
diff --git a/source/encoder/slicetype.h b/source/encoder/slicetype.h
index 31cce972b..6484ad8a0 100644
--- a/source/encoder/slicetype.h
+++ b/source/encoder/slicetype.h
@@ -42,6 +42,7 @@ class Lookahead;
 #define LOWRES_COST_SHIFT 14
 #define AQ_EDGE_BIAS 0.5
 #define EDGE_INCLINATION 45
+#define TEMPORAL_SCENECUT_THRESHOLD 50

 #if HIGH_BIT_DEPTH
 #define EDGE_THRESHOLD 1023.0
diff --git a/source/x265.h b/source/x265.h
index 1e6f9ece6..32feb2bca 100644
--- a/source/x265.h
+++ b/source/x265.h
@@ -1860,7 +1860,7 @@ typedef struct x265_param
     /* A genuine threshold used for histogram based scene cut detection.
      * This threshold determines whether a frame is a scenecut or not
      * when compared against the edge and chroma histogram sad values.
-     * Default 0.01. Range: Real number in the interval (0,2). */
+     * Default 0.03. Range: Real number in the interval (0,1). */
     double    edgeTransitionThreshold;

     /* Enables histogram based scenecut detection algorithm to detect
scenecuts. Default disabled */
-- 
2.24.0.windows.2

Regards,
*Pooja Venkatesan*,
Video Codec Engineer,
Media & AI analytics BU



On Mon, Jun 29, 2020 at 4:11 PM Pooja Venkatesan <pooja at multicorewareinc.com>
wrote:

> Hi,
>
> I am working on the review comments on this patch series. Will be sending
> the updated patches soon. Stay tuned!
>
> Regards,
> *Pooja Venkatesan*,
> Video Codec Engineer,
> Media & AI analytics BU
>
>
>
> On Thu, Jun 25, 2020 at 9:00 PM Pooja Venkatesan <
> pooja at multicorewareinc.com> wrote:
>
>> From 2777c2e3389eaf556f3420bc0717171bbcf97e52 Mon Sep 17 00:00:00 2001
>> From: Pooja Venkatesan <pooja at multicorewareinc.com>
>> Date: Thu, 25 Jun 2020 20:42:50 +0530
>> Subject: [PATCH] Improvements to hist-based scenecut algorithm.
>>
>> This patch does the following:
>> 1. Add min and max threshold intervals to detect scenecuts.
>> 2. For those within the range,
>>     Compare colour and edge histogram along with inter and intra
>> satdcosts to detect scenecuts.
>> 3. Handle scene transitions.
>> 4. Change default value of hist-threshold to 0.03
>> ---
>>  doc/reST/cli.rst             |  7 +--
>>  source/common/lowres.cpp     |  2 +
>>  source/common/lowres.h       |  5 ++
>>  source/common/param.cpp      |  2 +-
>>  source/encoder/encoder.cpp   | 25 ++++++++--
>>  source/encoder/encoder.h     |  2 +-
>>  source/encoder/slicetype.cpp | 88 +++++++++++++++++++++++++++---------
>>  source/x265.h                |  2 +-
>>  8 files changed, 101 insertions(+), 32 deletions(-)
>>
>> diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst
>> index b9d795ace..23b74c3d8 100644
>> --- a/doc/reST/cli.rst
>> +++ b/doc/reST/cli.rst
>> @@ -1468,9 +1468,10 @@ Slice decision options
>>  .. option:: --hist-threshold <0.0..1.0>
>>
>>   This value represents the threshold for normalized SAD of edge
>> histograms used in scenecut detection.
>> - This requires :option:`--hist-scenecut` to be enabled. For example, a
>> value of 0.2 indicates that a frame with normalized SAD value
>> - greater than 0.2 against the previous frame as scenecut.
>> - Default 0.01.
>> + This requires :option:`--hist-scenecut` to be enabled. For example, a
>> value of 0.2 indicates that a frame with normalized SAD value
>> + greater than 0.2 against the previous frame as scenecut.
>> + Increasing the threshold reduces the number of scenecuts detected.
>> + Default 0.03.
>>
>>  .. option:: --radl <integer>
>>
>> diff --git a/source/common/lowres.cpp b/source/common/lowres.cpp
>> index e8dd991bc..8e19ac17c 100644
>> --- a/source/common/lowres.cpp
>> +++ b/source/common/lowres.cpp
>> @@ -266,6 +266,8 @@ void Lowres::init(PicYuv *origPic, int poc)
>>      indB = 0;
>>      memset(costEst, -1, sizeof(costEst));
>>      memset(weightedCostDelta, 0, sizeof(weightedCostDelta));
>> +    interPCostPercDiff = 0.0;
>> +    intraCostPercDiff = 0.0;
>>
>>      if (qpAqOffset && invQscaleFactor)
>>          memset(costEstAq, -1, sizeof(costEstAq));
>> diff --git a/source/common/lowres.h b/source/common/lowres.h
>> index 5c50fad67..200b1f032 100644
>> --- a/source/common/lowres.h
>> +++ b/source/common/lowres.h
>> @@ -234,6 +234,11 @@ struct Lowres : public ReferencePlanes
>>      uint16_t* propagateCost;
>>      double    weightedCostDelta[X265_BFRAME_MAX + 2];
>>      ReferencePlanes weightedRef[X265_BFRAME_MAX + 2];
>> +    /* For hist-based scenecut */
>> +    bool   m_bIsMaxThres;
>> +    double interPCostPercDiff;
>> +    double intraCostPercDiff;
>> +
>>      bool create(x265_param* param, PicYuv *origPic, uint32_t qgSize);
>>      void destroy();
>>      void init(PicYuv *origPic, int poc);
>> diff --git a/source/common/param.cpp b/source/common/param.cpp
>> index 925f0c460..8c0498efc 100644
>> --- a/source/common/param.cpp
>> +++ b/source/common/param.cpp
>> @@ -168,7 +168,7 @@ void x265_param_default(x265_param* param)
>>      param->bFrameAdaptive = X265_B_ADAPT_TRELLIS;
>>      param->bBPyramid = 1;
>>      param->scenecutThreshold = 40; /* Magic number pulled in from x264 */
>> -    param->edgeTransitionThreshold = 0.01;
>> +    param->edgeTransitionThreshold = 0.03;
>>      param->bHistBasedSceneCut = 0;
>>      param->lookaheadSlices = 8;
>>      param->lookaheadThreads = 0;
>> diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp
>> index f6bc5408d..bec7ff5c0 100644
>> --- a/source/encoder/encoder.cpp
>> +++ b/source/encoder/encoder.cpp
>> @@ -1528,8 +1528,12 @@ double Encoder::normalizeRange(int32_t value,
>> int32_t minValue, int32_t maxValue
>>      return (double)(value - minValue) * (rangeEnd - rangeStart) /
>> (maxValue - minValue) + rangeStart;
>>  }
>>
>> -void Encoder::findSceneCuts(x265_picture *pic, bool& bDup, double
>> maxUVSad, double edgeSad)
>> +void Encoder::findSceneCuts(x265_picture *pic, bool& isMax, bool& bDup,
>> double maxUVSad, double edgeSad)
>>  {
>> +    double minEdgeT = m_edgeHistThreshold * 0.5;
>> +    double minChromaT = minEdgeT * 10.0;
>> +    double maxEdgeT = m_edgeHistThreshold * 1.5;
>> +    double maxChromaT = maxEdgeT * 10.0;
>>      pic->frameData.bScenecut = false;
>>
>>      if (pic->poc == 0)
>> @@ -1544,11 +1548,20 @@ void Encoder::findSceneCuts(x265_picture *pic,
>> bool& bDup, double maxUVSad, doub
>>          {
>>              bDup = true;
>>          }
>> -        else if (edgeSad > m_scaledEdgeThreshold || maxUVSad >=
>> m_scaledChromaThreshold || (edgeSad > m_edgeHistThreshold && maxUVSad >=
>> m_chromaHistThreshold))
>> +        else if (edgeSad < minEdgeT && maxUVSad < minChromaT)
>> +        {
>> +            pic->frameData.bScenecut = false;
>> +        }
>> +        else if (edgeSad > maxEdgeT && maxUVSad > maxChromaT)
>> +        {
>> +            pic->frameData.bScenecut = true;
>> +            isMax = true;
>> +        }
>> +        else if (edgeSad > m_scaledEdgeThreshold || maxUVSad >=
>> m_scaledChromaThreshold
>> +                 || (edgeSad > m_edgeHistThreshold && maxUVSad >=
>> m_chromaHistThreshold))
>>          {
>>              pic->frameData.bScenecut = true;
>>              bDup = false;
>> -            x265_log(m_param, X265_LOG_DEBUG, "scene cut at %d \n",
>> pic->poc);
>>          }
>>      }
>>  }
>> @@ -1581,6 +1594,7 @@ int Encoder::encode(const x265_picture* pic_in,
>> x265_picture* pic_out)
>>      bool dontRead = false;
>>      bool bdropFrame = false;
>>      bool dropflag = false;
>> +    bool isMaxThreshold = false;
>>
>>      if (m_exportedPic)
>>      {
>> @@ -1607,7 +1621,7 @@ int Encoder::encode(const x265_picture* pic_in,
>> x265_picture* pic_out)
>>              {
>>                  double maxUVSad = 0.0, edgeSad = 0.0;
>>                  computeHistogramSAD(&maxUVSad, &edgeSad, pic_in->poc);
>> -                findSceneCuts(pic, bdropFrame, maxUVSad, edgeSad);
>> +                findSceneCuts(pic, isMaxThreshold, bdropFrame, maxUVSad,
>> edgeSad);
>>              }
>>          }
>>
>> @@ -1786,6 +1800,7 @@ int Encoder::encode(const x265_picture* pic_in,
>> x265_picture* pic_out)
>>          if (m_param->bHistBasedSceneCut)
>>          {
>>              inFrame->m_lowres.bScenecut = (inputPic->frameData.bScenecut
>> == 1) ? true : false;
>> +            inFrame->m_lowres.m_bIsMaxThres = isMaxThreshold;
>>          }
>>          if (m_param->bHistBasedSceneCut && m_param->analysisSave)
>>          {
>> @@ -4261,7 +4276,7 @@ void Encoder::configure(x265_param *p)
>>
>>     if (p->bHistBasedSceneCut && !p->edgeTransitionThreshold)
>>     {
>> -       p->edgeTransitionThreshold = 0.01;
>> +       p->edgeTransitionThreshold = 0.03;
>>         x265_log(p, X265_LOG_WARNING, "using  default threshold %.2lf for
>> scene cut detection\n", p->edgeTransitionThreshold);
>>     }
>>
>> diff --git a/source/encoder/encoder.h b/source/encoder/encoder.h
>> index fd6b3e72c..1d4fe2476 100644
>> --- a/source/encoder/encoder.h
>> +++ b/source/encoder/encoder.h
>> @@ -373,7 +373,7 @@ public:
>>      bool computeHistograms(x265_picture *pic);
>>      void computeHistogramSAD(double *maxUVNormalizedSAD, double
>> *edgeNormalizedSAD, int curPoc);
>>      double normalizeRange(int32_t value, int32_t minValue, int32_t
>> maxValue, double rangeStart, double rangeEnd);
>> -    void findSceneCuts(x265_picture *pic, bool& bDup, double
>> m_maxUVSADVal, double m_edgeSADVal);
>> +    void findSceneCuts(x265_picture *pic, bool& isMax, bool& bDup,
>> double m_maxUVSADVal, double m_edgeSADVal);
>>
>>      void initRefIdx();
>>      void analyseRefIdx(int *numRefIdx);
>> diff --git a/source/encoder/slicetype.cpp b/source/encoder/slicetype.cpp
>> index 0a95e77d2..27052ca4e 100644
>> --- a/source/encoder/slicetype.cpp
>> +++ b/source/encoder/slicetype.cpp
>> @@ -2001,10 +2001,40 @@ void Lookahead::slicetypeAnalyse(Lowres **frames,
>> bool bKeyframe)
>>      int numAnalyzed = numFrames;
>>      bool isScenecut = false;
>>
>> -    /* When scenecut threshold is set, use scenecut detection for I
>> frame placements */
>>      if (m_param->bHistBasedSceneCut)
>> -        isScenecut = frames[1]->bScenecut;
>> -    else
>> +    {
>> +        for (int i = numFrames - 1; i > 0; i--)
>> +        {
>> +            if (frames[i]->interPCostPercDiff > 0.0)
>> +                continue;
>> +            int64_t interCost = frames[i]->costEst[1][0];
>> +            int64_t intraCost = frames[i]->costEst[0][0];
>> +            if (interCost < 0 || intraCost < 0)
>> +                continue;
>> +            int times = 0;
>> +            double averageP = 0.0, averageI = 0.0;
>> +            for (int j = i - 1; j >= 0 && times < 5; j--, times++)
>> +            {
>> +                if (frames[j]->costEst[0][0] > 0 &&
>> frames[j]->costEst[1][0] > 0)
>> +                {
>> +                    averageI += frames[j]->costEst[0][0];
>> +                    averageP += frames[j]->costEst[1][0];
>> +                }
>> +                else
>> +                    times--;
>> +            }
>> +            if (times)
>> +            {
>> +                averageI = averageI / times;
>> +                averageP = averageP / times;
>> +                frames[i]->interPCostPercDiff = abs(interCost -
>> averageP) / X265_MIN(interCost, averageP) * 100;
>> +                frames[i]->intraCostPercDiff = abs(intraCost - averageI)
>> / X265_MIN(intraCost, averageI) * 100;
>> +            }
>> +        }
>> +    }
>> +
>> +    /* When scenecut threshold is set, use scenecut detection for I
>> frame placements */
>> +    if (!m_param->bHistBasedSceneCut || (m_param->bHistBasedSceneCut &&
>> frames[1]->bScenecut))
>>          isScenecut = scenecut(frames, 0, 1, true, origNumFrames);
>>
>>      if (isScenecut && (m_param->bHistBasedSceneCut ||
>> m_param->scenecutThreshold))
>> @@ -2018,17 +2048,16 @@ void Lookahead::slicetypeAnalyse(Lowres **frames,
>> bool bKeyframe)
>>          m_extendGopBoundary = false;
>>          for (int i = m_param->bframes + 1; i < origNumFrames; i +=
>> m_param->bframes + 1)
>>          {
>> -            if (!m_param->bHistBasedSceneCut)
>> +            if (!m_param->bHistBasedSceneCut ||
>> (m_param->bHistBasedSceneCut && frames[i + 1]->bScenecut))
>>                  scenecut(frames, i, i + 1, true, origNumFrames);
>>
>>              for (int j = i + 1; j <= X265_MIN(i + m_param->bframes + 1,
>> origNumFrames); j++)
>>              {
>> -                if ((!m_param->bHistBasedSceneCut &&
>> frames[j]->bScenecut && scenecutInternal(frames, j - 1, j, true)) ||
>> -                    (m_param->bHistBasedSceneCut &&
>> frames[j]->bScenecut))
>> -                    {
>> -                        m_extendGopBoundary = true;
>> -                        break;
>> -                    }
>> +                if (frames[j]->bScenecut && scenecutInternal(frames, j -
>> 1, j, true))
>> +                {
>> +                    m_extendGopBoundary = true;
>> +                    break;
>> +                }
>>              }
>>              if (m_extendGopBoundary)
>>                  break;
>> @@ -2133,14 +2162,15 @@ void Lookahead::slicetypeAnalyse(Lowres **frames,
>> bool bKeyframe)
>>          {
>>              for (int j = 1; j < numBFrames + 1; j++)
>>              {
>> -                if ((!m_param->bHistBasedSceneCut && scenecut(frames, j,
>> j + 1, false, origNumFrames)) ||
>> -                    (m_param->bHistBasedSceneCut && frames[j +
>> 1]->bScenecut) ||
>> -                    (bForceRADL && (frames[j]->frameNum == preRADL)))
>> -                    {
>> -                        frames[j]->sliceType = X265_TYPE_P;
>> -                        numAnalyzed = j;
>> -                        break;
>> -                    }
>> +                bool isNextScenecut = false;
>> +                if (!m_param->bHistBasedSceneCut ||
>> (m_param->bHistBasedSceneCut && frames[j + 1]->bScenecut))
>> +                    isNextScenecut = scenecut(frames, j, j + 1, false,
>> origNumFrames);
>> +                if (isNextScenecut || (bForceRADL && frames[j]->frameNum
>> == preRADL))
>> +                {
>> +                    frames[j]->sliceType = X265_TYPE_P;
>> +                    numAnalyzed = j;
>> +                    break;
>> +                }
>>              }
>>          }
>>          resetStart = bKeyframe ? 1 : X265_MIN(numBFrames + 2,
>> numAnalyzed + 1);
>> @@ -2203,7 +2233,7 @@ bool Lookahead::scenecut(Lowres **frames, int p0,
>> int p1, bool bRealScenecut, in
>>           * and not considered a scenecut. */
>>          for (int cp1 = p1; cp1 <= maxp1; cp1++)
>>          {
>> -            if (!scenecutInternal(frames, p0, cp1, false))
>> +            if (!m_param->bHistBasedSceneCut &&
>> !scenecutInternal(frames, p0, cp1, false))
>>              {
>>                  /* Any frame in between p0 and cur_p1 cannot be a real
>> scenecut. */
>>                  for (int i = cp1; i > p0; i--)
>> @@ -2212,7 +2242,7 @@ bool Lookahead::scenecut(Lowres **frames, int p0,
>> int p1, bool bRealScenecut, in
>>                      noScenecuts = false;
>>                  }
>>              }
>> -            else if (scenecutInternal(frames, cp1 - 1, cp1, false))
>> +            else if ((m_param->bHistBasedSceneCut &&
>> frames[cp1]->m_bIsMaxThres) || scenecutInternal(frames, cp1 - 1, cp1,
>> false))
>>              {
>>                  /* If current frame is a Scenecut from p0 frame as well
>> as Scenecut from
>>                   * preceeding frame, mark it as a Scenecut */
>> @@ -2273,6 +2303,10 @@ bool Lookahead::scenecut(Lowres **frames, int p0,
>> int p1, bool bRealScenecut, in
>>
>>      if (!frames[p1]->bScenecut)
>>          return false;
>> +    /* Check only scene transitions if max threshold */
>> +    if (m_param->bHistBasedSceneCut && frames[p1]->m_bIsMaxThres)
>> +        return frames[p1]->bScenecut;
>> +
>>      return scenecutInternal(frames, p0, p1, bRealScenecut);
>>  }
>>
>> @@ -2289,7 +2323,19 @@ bool Lookahead::scenecutInternal(Lowres **frames,
>> int p0, int p1, bool bRealScen
>>      /* magic numbers pulled out of thin air */
>>      float threshMin = (float)(threshMax * 0.25);
>>      double bias = m_param->scenecutBias;
>> -    if (bRealScenecut)
>> +    if (m_param->bHistBasedSceneCut)
>> +    {
>> +        double minT = 50.0 * (1 + m_param->edgeTransitionThreshold);
>> +        if (frame->interPCostPercDiff > minT || frame->intraCostPercDiff
>> > minT)
>> +        {
>> +            if (bRealScenecut && frame->bScenecut)
>> +                x265_log(m_param, X265_LOG_DEBUG, "scene cut at %d \n",
>> frame->frameNum);
>> +            return frame->bScenecut;
>> +        }
>> +        else
>> +            return false;
>> +    }
>> +    else if (bRealScenecut)
>>      {
>>          if (m_param->keyframeMin == m_param->keyframeMax)
>>              threshMin = threshMax;
>> diff --git a/source/x265.h b/source/x265.h
>> index 1e6f9ece6..32feb2bca 100644
>> --- a/source/x265.h
>> +++ b/source/x265.h
>> @@ -1860,7 +1860,7 @@ typedef struct x265_param
>>      /* A genuine threshold used for histogram based scene cut detection.
>>       * This threshold determines whether a frame is a scenecut or not
>>       * when compared against the edge and chroma histogram sad values.
>> -     * Default 0.01. Range: Real number in the interval (0,2). */
>> +     * Default 0.03. Range: Real number in the interval (0,1). */
>>      double    edgeTransitionThreshold;
>>
>>      /* Enables histogram based scenecut detection algorithm to detect
>> scenecuts. Default disabled */
>> --
>> 2.24.0.windows.2
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20200629/65708764/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: SCD1-final.diff
Type: application/octet-stream
Size: 15575 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20200629/65708764/attachment-0001.obj>


More information about the x265-devel mailing list