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