>From 671506e40fbbb0aed825013e434c7c39cec2bb93 Mon Sep 17 00:00:00 2001 From: Pooja Venkatesan <po...@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 <po...@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 < > po...@multicorewareinc.com> wrote: > >> From 2777c2e3389eaf556f3420bc0717171bbcf97e52 Mon Sep 17 00:00:00 2001 >> From: Pooja Venkatesan <po...@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 >> >>
SCD1-final.diff
Description: Binary data
_______________________________________________ x265-devel mailing list x265-devel@videolan.org https://mailman.videolan.org/listinfo/x265-devel