[x265] [PATCH] Add Segment based Ratecontrol(SBRC) feature
Mahesh Pittala
mahesh at multicorewareinc.com
Fri Oct 21 09:28:55 UTC 2022
Pushed to master branch
On Wed, Oct 19, 2022 at 12:31 PM Niranjan Bala <
niranjan at multicorewareinc.com> wrote:
> 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/>
> _______________________________________________
> x265-devel mailing list
> x265-devel at videolan.org
> https://mailman.videolan.org/listinfo/x265-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20221021/4de33643/attachment-0001.html>
More information about the x265-devel
mailing list