[x265] [PATCH] Add Segment based Ratecontrol(SBRC) feature

Niranjan Bala niranjan at multicorewareinc.com
Wed Oct 19 07:00:41 UTC 2022


>From d61dd735c43d122d8d4f86ebf249971ab1391b40 Mon Sep 17 00:00:00 2001
From: Niranjan Kumar <niranjan at multicorewareinc.com>
Date: Thu, 15 Sep 2022 16:05:22 +0530
Subject: [PATCH] Add Segment based Ratecontrol(SBRC) feature

---
 doc/reST/cli.rst             |  5 +++
 source/CMakeLists.txt        |  2 +-
 source/common/common.h       |  4 ++
 source/common/frame.cpp      |  3 +-
 source/common/frame.h        |  4 ++
 source/common/lowres.cpp     |  4 ++
 source/common/lowres.h       |  3 ++
 source/common/param.cpp      |  8 +++-
 source/encoder/slicetype.cpp | 87 ++++++++++++++++++++++++++++++++----
 source/x265.h                |  4 ++
 source/x265cli.cpp           |  1 +
 source/x265cli.h             |  2 +
 12 files changed, 115 insertions(+), 12 deletions(-)

diff --git a/doc/reST/cli.rst b/doc/reST/cli.rst
index 1640c1f8e..be32fc6cc 100755
--- a/doc/reST/cli.rst
+++ b/doc/reST/cli.rst
@@ -1768,6 +1768,11 @@ Quality, rate control and rate distortion options
  Default 1.0.
  **Range of values:** 0.0 to 3.0

+.. option:: --sbrc --no-sbrc
+
+ To enable and disable segment based rate control.
+ Default: disabled.
+
 .. option:: --hevc-aq

  Enable adaptive quantization
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 13e4750de..8a93a8bc4 100755
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -29,7 +29,7 @@ option(NATIVE_BUILD "Target the build CPU" OFF)
 option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF)
 mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)
 # X265_BUILD must be incremented each time the public API is changed
-set(X265_BUILD 204)
+set(X265_BUILD 205)
 configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
                "${PROJECT_BINARY_DIR}/x265.def")
 configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
diff --git a/source/common/common.h b/source/common/common.h
index a245c7dae..9a578590a 100644
--- a/source/common/common.h
+++ b/source/common/common.h
@@ -131,6 +131,7 @@ typedef uint64_t pixel4;
 typedef int64_t  ssum2_t;
 #define SHIFT_TO_BITPLANE 9
 #define HISTOGRAM_BINS 1024
+#define BRIGHTNESS_THRESHOLD 120 // The threshold above which a pixel is
bright
 #else
 typedef uint8_t  pixel;
 typedef uint16_t sum_t;
@@ -139,6 +140,7 @@ typedef uint32_t pixel4;
 typedef int32_t  ssum2_t; // Signed sum
 #define SHIFT_TO_BITPLANE 7
 #define HISTOGRAM_BINS 256
+#define BRIGHTNESS_THRESHOLD 30 // The threshold above which a pixel is
bright
 #endif // if HIGH_BIT_DEPTH

 #if X265_DEPTH < 10
@@ -162,6 +164,8 @@ typedef uint64_t sse_t;

 #define MIN_QPSCALE     0.21249999999999999
 #define MAX_MAX_QPSCALE 615.46574234477100
+#define FRAME_BRIGHTNESS_THRESHOLD  50.0 // Min % of pixels in a frame,
that are above BRIGHTNESS_THRESHOLD for it to be considered a bright frame
+#define FRAME_EDGE_THRESHOLD  10.0 // Min % of edge pixels in a frame, for
it to be considered to have high edge density


 template<typename T>
diff --git a/source/common/frame.cpp b/source/common/frame.cpp
index 255882a9d..d8b982df2 100644
--- a/source/common/frame.cpp
+++ b/source/common/frame.cpp
@@ -63,6 +63,7 @@ Frame::Frame()
     m_thetaPic = NULL;
     m_edgeBitPlane = NULL;
     m_edgeBitPic = NULL;
+    m_frameSegment = X265_AQ_NONE;
     m_isInsideWindow = 0;
 }

@@ -104,7 +105,7 @@ bool Frame::create(x265_param *param, float*
quantOffsets)
         CHECKED_MALLOC_ZERO(m_classifyCount, uint32_t, size);
     }

-    if (param->rc.aqMode == X265_AQ_EDGE || (param->rc.zonefileCount &&
param->rc.aqMode != 0))
+    if (param->rc.aqMode == X265_AQ_EDGE || param->rc.frameSegment ||
(param->rc.zonefileCount && param->rc.aqMode != 0))
     {
         uint32_t numCuInWidth = (param->sourceWidth + param->maxCUSize -
1) / param->maxCUSize;
         uint32_t numCuInHeight = (param->sourceHeight + param->maxCUSize -
1) / param->maxCUSize;
diff --git a/source/common/frame.h b/source/common/frame.h
index ac1185e81..485b0883d 100644
--- a/source/common/frame.h
+++ b/source/common/frame.h
@@ -142,6 +142,10 @@ public:
     pixel*                 m_edgeBitPlane;
     pixel*                 m_edgeBitPic;

+    /* segment for each frame */
+    int                    m_frameSegment;
+
+
     int                    m_isInsideWindow;

     Frame();
diff --git a/source/common/lowres.cpp b/source/common/lowres.cpp
index 578981d64..5e7718e7e 100644
--- a/source/common/lowres.cpp
+++ b/source/common/lowres.cpp
@@ -190,6 +190,9 @@ bool Lowres::create(x265_param* param, PicYuv *origPic,
uint32_t qgSize)
         }
     }

+    if (param->rc.frameSegment)
+        lowresEdgePlane = X265_MALLOC(pixel, lumaStride * (lines +
(origPic->m_lumaMarginY * 2)));
+
     return true;

 fail:
@@ -235,6 +238,7 @@ void Lowres::destroy()
     X265_FREE(edgeInclined);
     X265_FREE(qpAqMotionOffset);
     X265_FREE(blockVariance);
+    X265_FREE(lowresEdgePlane);
     if (maxAQDepth > 0)
     {
         for (uint32_t d = 0; d < 4; d++)
diff --git a/source/common/lowres.h b/source/common/lowres.h
index 2a7497258..03d713edb 100644
--- a/source/common/lowres.h
+++ b/source/common/lowres.h
@@ -44,6 +44,9 @@ struct ReferencePlanes
     pixel*   fpelLowerResPlane[3];
     pixel*   lowerResPlane[4];

+    /* Edge Plane in Lowres */
+    pixel*   lowresEdgePlane;
+
     bool     isWeighted;
     bool     isLowres;
     bool     isHMELowres;
diff --git a/source/common/param.cpp b/source/common/param.cpp
index ed6973d9c..fa16a364c 100755
--- a/source/common/param.cpp
+++ b/source/common/param.cpp
@@ -298,6 +298,7 @@ void x265_param_default(x265_param* param)
     param->rc.bEnableConstVbv = 0;
     param->bResetZoneConfig = 1;
     param->reconfigWindowSize = 0;
+    param->rc.frameSegment = 0;
     param->decoderVbvMaxRate = 0;
     param->bliveVBV2pass = 0;

@@ -1250,6 +1251,7 @@ int x265_param_parse(x265_param* p, const char* name,
const char* value)
         OPT("multi-pass-opt-analysis") p->analysisMultiPassRefine =
atobool(value);
         OPT("multi-pass-opt-distortion") p->analysisMultiPassDistortion =
atobool(value);
         OPT("aq-motion") p->bAQMotion = atobool(value);
+        OPT("sbrc") p->rc.frameSegment = atobool(value);
         OPT("dynamic-rd") p->dynamicRd = atof(value);
         OPT("analysis-reuse-level")
         {
@@ -2007,9 +2009,11 @@ void x265_print_params(x265_param* param)
              param->maxNumReferences, (param->limitReferences &
X265_REF_LIMIT_CU) ? "on" : "off",
              (param->limitReferences & X265_REF_LIMIT_DEPTH) ? "on" :
"off");

-    if (param->rc.aqMode)
+    if (param->rc.aqMode && !param->rc.frameSegment)
         x265_log(param, X265_LOG_INFO, "AQ: mode / str / qg-size / cu-tree
 : %d / %0.1f / %d / %d\n", param->rc.aqMode,
                  param->rc.aqStrength, param->rc.qgSize, param->rc.cuTree);
+ else if (param->rc.frameSegment)
+        x265_log(param, X265_LOG_INFO, "AQ: mode / str / qg-size / cu-tree
 : auto / %0.1f / %d / %d\n", param->rc.aqStrength, param->rc.qgSize,
param->rc.cuTree);

     if (param->bLossless)
         x265_log(param, X265_LOG_INFO, "Rate Control
 : Lossless\n");
@@ -2323,6 +2327,7 @@ char *x265_param2string(x265_param* p, int padx, int
pady)
     s += sprintf(s, " hist-threshold=%.2f", p->edgeTransitionThreshold);
     BOOL(p->bOptCUDeltaQP, "opt-cu-delta-qp");
     BOOL(p->bAQMotion, "aq-motion");
+    BOOL(p->rc.frameSegment, "sbrc");
     BOOL(p->bEmitHDR10SEI, "hdr10");
     BOOL(p->bHDR10Opt, "hdr10-opt");
     BOOL(p->bDhdr10opt, "dhdr10-opt");
@@ -2617,6 +2622,7 @@ void x265_copy_params(x265_param* dst, x265_param*
src)
     dst->rc.bEnableConstVbv = src->rc.bEnableConstVbv;
     dst->rc.hevcAq = src->rc.hevcAq;
     dst->rc.qpAdaptationRange = src->rc.qpAdaptationRange;
+    dst->rc.frameSegment = src->rc.frameSegment;

     dst->vui.aspectRatioIdc = src->vui.aspectRatioIdc;
     dst->vui.sarWidth = src->vui.sarWidth;
diff --git a/source/encoder/slicetype.cpp b/source/encoder/slicetype.cpp
index 1222a8511..5de1ad956 100644
--- a/source/encoder/slicetype.cpp
+++ b/source/encoder/slicetype.cpp
@@ -473,9 +473,9 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame
*curFrame, x265_param* param)
     if (!(param->rc.bStatRead && param->rc.cuTree &&
IS_REFERENCED(curFrame)))
     {
         /* Calculate Qp offset for each 16x16 or 8x8 block in the frame */
-        if (param->rc.aqMode == X265_AQ_NONE || param->rc.aqStrength == 0)
+        if (curFrame->m_frameSegment == X265_AQ_NONE ||
param->rc.aqStrength == 0)
         {
-            if (param->rc.aqMode && param->rc.aqStrength == 0)
+            if (curFrame->m_frameSegment && param->rc.aqStrength == 0)
             {
                 if (quantOffsets)
                 {
@@ -516,17 +516,17 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame
*curFrame, x265_param* param)
                 double bias_strength = 0.f;
                 double strength = 0.f;

-                if (param->rc.aqMode == X265_AQ_EDGE)
+                if (curFrame->m_frameSegment == X265_AQ_EDGE )
                     edgeFilter(curFrame, param);

-                if (param->rc.aqMode == X265_AQ_EDGE &&
!param->bHistBasedSceneCut && param->recursionSkipMode == EDGE_BASED_RSKIP)
+                if (curFrame->m_frameSegment == X265_AQ_EDGE  &&
!param->bHistBasedSceneCut && param->recursionSkipMode == EDGE_BASED_RSKIP)
                 {
                     pixel* src = curFrame->m_edgePic +
curFrame->m_fencPic->m_lumaMarginY * curFrame->m_fencPic->m_stride +
curFrame->m_fencPic->m_lumaMarginX;
                     primitives.planecopy_pp_shr(src,
curFrame->m_fencPic->m_stride, curFrame->m_edgeBitPic,
                         curFrame->m_fencPic->m_stride,
curFrame->m_fencPic->m_picWidth, curFrame->m_fencPic->m_picHeight,
SHIFT_TO_BITPLANE);
                 }

-                if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE ||
param->rc.aqMode == X265_AQ_AUTO_VARIANCE_BIASED || param->rc.aqMode ==
X265_AQ_EDGE)
+                if (curFrame->m_frameSegment == X265_AQ_AUTO_VARIANCE ||
curFrame->m_frameSegment == X265_AQ_AUTO_VARIANCE_BIASED ||
curFrame->m_frameSegment == X265_AQ_EDGE)
                 {
                     double bit_depth_correction = 1.f / (1 << (2 *
(X265_DEPTH - 8)));
                     for (int blockY = 0; blockY < maxRow; blockY +=
loopIncr)
@@ -535,7 +535,7 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame
*curFrame, x265_param* param)
                         {
                             uint32_t energy, edgeDensity, avgAngle;
                             energy = acEnergyCu(curFrame, blockX, blockY,
param->internalCsp, param->rc.qgSize);
-                            if (param->rc.aqMode == X265_AQ_EDGE)
+                            if (curFrame->m_frameSegment == X265_AQ_EDGE)
                             {
                                 edgeDensity = edgeDensityCu(curFrame,
avgAngle, blockX, blockY, param->rc.qgSize);
                                 if (edgeDensity)
@@ -575,17 +575,17 @@ void LookaheadTLD::calcAdaptiveQuantFrame(Frame
*curFrame, x265_param* param)
                 {
                     for (int blockX = 0; blockX < maxCol; blockX +=
loopIncr)
                     {
-                        if (param->rc.aqMode ==
X265_AQ_AUTO_VARIANCE_BIASED)
+                        if (curFrame->m_frameSegment ==
X265_AQ_AUTO_VARIANCE_BIASED)
                         {
                             qp_adj =
curFrame->m_lowres.qpCuTreeOffset[blockXY];
                             qp_adj = strength * (qp_adj - avg_adj) +
bias_strength * (1.f - modeTwoConst / (qp_adj * qp_adj));
                         }
-                        else if (param->rc.aqMode == X265_AQ_AUTO_VARIANCE)
+                        else if (curFrame->m_frameSegment ==
X265_AQ_AUTO_VARIANCE)
                         {
                             qp_adj =
curFrame->m_lowres.qpCuTreeOffset[blockXY];
                             qp_adj = strength * (qp_adj - avg_adj);
                         }
-                        else if (param->rc.aqMode == X265_AQ_EDGE)
+                        else if (curFrame->m_frameSegment == X265_AQ_EDGE)
                         {
                             inclinedEdge =
curFrame->m_lowres.edgeInclined[blockXY];
                             qp_adj =
curFrame->m_lowres.qpCuTreeOffset[blockXY];
@@ -1377,6 +1377,45 @@ void Lookahead::getEstimatedPictureCost(Frame
*curFrame)
     }
 }

+double computeBrightnessIntensity(pixel *inPlane, int width, int height,
intptr_t stride)
+{
+    pixel* rowStart = inPlane;
+    double count = 0;
+
+    for (int i = 0; i < height; i++)
+    {
+        for (int j = 0; j < width; j++)
+        {
+            if (rowStart[j] > BRIGHTNESS_THRESHOLD)
+                count++;
+        }
+        rowStart += stride;
+    }
+
+    /* Returns the brightness percentage of the input plane */
+    return (count / (width * height)) * 100;
+}
+
+double computeEdgeIntensity(pixel *inPlane, int width, int height,
intptr_t stride)
+{
+    pixel* rowStart = inPlane;
+    double count = 0;
+
+    for (int i = 0; i < height; i++)
+    {
+        for (int j = 0; j < width; j++)
+        {
+            if (rowStart[j] > 0)
+                count++;
+        }
+        rowStart += stride;
+    }
+
+    /* Returns the edge percentage of the input plane */
+    return (count / (width * height)) * 100;
+}
+
+
 void PreLookaheadGroup::processTasks(int workerThreadID)
 {
     if (workerThreadID < 0)
@@ -1391,6 +1430,36 @@ void PreLookaheadGroup::processTasks(int
workerThreadID)
         ProfileScopeEvent(prelookahead);
         m_lock.release();
         preFrame->m_lowres.init(preFrame->m_fencPic, preFrame->m_poc);
+
+        /* SBRC */
+        if (preFrame->m_param->rc.frameSegment)
+        {
+            int heightL = preFrame->m_lowres.lines;
+            int widthL = preFrame->m_lowres.width;
+            pixel *lumaPlane = preFrame->m_lowres.fpelPlane[0];
+            intptr_t stride = preFrame->m_lowres.lumaStride;
+            double brightnessIntensity = 0, edgeIntensity = 0;
+
+            /* Edge plane computation */
+            memset(preFrame->m_lowres.lowresEdgePlane, 0, stride *
(heightL + (preFrame->m_fencPic->m_lumaMarginY * 2)) * sizeof(pixel));
+            pixel* lowresEdgePic = preFrame->m_lowres.lowresEdgePlane +
preFrame->m_fencPic->m_lumaMarginY * stride +
preFrame->m_fencPic->m_lumaMarginX;
+            computeEdge(lowresEdgePic, lumaPlane, NULL, stride, heightL,
widthL, false);
+
+            /*Frame edge percentage computation */
+            edgeIntensity = computeEdgeIntensity(lowresEdgePic, widthL,
heightL, stride);
+
+            /* Frame Brightness percentage computation */
+            brightnessIntensity = computeBrightnessIntensity(lumaPlane,
widthL, heightL, stride);
+
+            /* AQ mode switch */
+            if (edgeIntensity < FRAME_EDGE_THRESHOLD)
+                preFrame->m_frameSegment = brightnessIntensity >
FRAME_BRIGHTNESS_THRESHOLD? X265_AQ_AUTO_VARIANCE :
X265_AQ_AUTO_VARIANCE_BIASED;
+            else
+                preFrame->m_frameSegment = brightnessIntensity >
FRAME_BRIGHTNESS_THRESHOLD? X265_AQ_EDGE : X265_AQ_EDGE_BIASED;
+        }
+        else
+            preFrame->m_frameSegment = preFrame->m_param->rc.aqMode;
+
         if (m_lookahead.m_bAdaptiveQuant)
             tld.calcAdaptiveQuantFrame(preFrame, m_lookahead.m_param);
         tld.lowresIntraEstimate(preFrame->m_lowres,
m_lookahead.m_param->rc.qgSize);
diff --git a/source/x265.h b/source/x265.h
index 5d242d653..787aa12fa 100644
--- a/source/x265.h
+++ b/source/x265.h
@@ -581,6 +581,7 @@ typedef enum
 #define X265_AQ_AUTO_VARIANCE        2
 #define X265_AQ_AUTO_VARIANCE_BIASED 3
 #define X265_AQ_EDGE                 4
+#define X265_AQ_EDGE_BIASED          1
 #define x265_ADAPT_RD_STRENGTH   4
 #define X265_REFINE_INTER_LEVELS 3
 /* NOTE! For this release only X265_CSP_I420 and X265_CSP_I444 are
supported */
@@ -1496,6 +1497,9 @@ typedef struct x265_param
         /* internally enable if tune grain is set */
         int      bEnableConstVbv;

+        /* enable SBRC mode for each sequence */
+        int      frameSegment;
+
         /* if only the focused frames would be re-encode or not */
         int       bEncFocusedFramesOnly;

diff --git a/source/x265cli.cpp b/source/x265cli.cpp
index f6ba0ab30..c004b7915 100755
--- a/source/x265cli.cpp
+++ b/source/x265cli.cpp
@@ -262,6 +262,7 @@ namespace X265_NS {
         H0("   --aq-strength <float>         Reduces blocking and blurring
in flat and textured areas (0 to 3.0). Default %.2f\n",
param->rc.aqStrength);
         H0("   --qp-adaptation-range <float> Delta QP range by QP
adaptation based on a psycho-visual model (1.0 to 6.0). Default %.2f\n",
param->rc.qpAdaptationRange);
         H0("   --[no-]aq-motion              Block level QP adaptation
based on the relative motion between the block and the frame. Default
%s\n", OPT(param->bAQMotion));
+        H1("   --[no-]sbrc                   Enables the segment based
rate control, using its scene statistics. Default %s\n",
OPT(param->rc.frameSegment));
         H0("   --qg-size <int>               Specifies the size of the
quantization group (64, 32, 16, 8). Default %d\n", param->rc.qgSize);
         H0("   --[no-]cutree                 Enable cutree for Adaptive
Quantization. Default %s\n", OPT(param->rc.cuTree));
         H0("   --[no-]rc-grain               Enable ratecontrol mode to
handle grains specifically. turned on with tune grain. Default %s\n",
OPT(param->rc.bEnableGrain));
diff --git a/source/x265cli.h b/source/x265cli.h
index 7072f7616..8640a5bbb 100644
--- a/source/x265cli.h
+++ b/source/x265cli.h
@@ -184,6 +184,8 @@ static const struct option long_options[] =
     { "qp",             required_argument, NULL, 'q' },
     { "aq-mode",        required_argument, NULL, 0 },
     { "aq-strength",    required_argument, NULL, 0 },
+    { "sbrc",                 no_argument, NULL, 0 },
+    { "no-sbrc",              no_argument, NULL, 0 },
     { "rc-grain",             no_argument, NULL, 0 },
     { "no-rc-grain",          no_argument, NULL, 0 },
     { "ipratio",        required_argument, NULL, 0 },
-- 
2.37.2.windows.2


-- 

Thanks & Regards
*Niranjan Kumar B*
Video Codec Engineer
Media & AI Analytics
+91 958 511 1449
<https://multicorewareinc.com/>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20221019/3f414055/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: sbrc.diff
Type: application/octet-stream
Size: 18193 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20221019/3f414055/attachment-0001.obj>


More information about the x265-devel mailing list