•  


Improve: Scenecut Aware Frame Quantizer Selection · lawchingman/x265@3d579f9 · GitHub
Skip to content

Commit

Permalink
Improve: Scenecut Aware Frame Quantizer Selection
Browse files Browse the repository at this point in the history
This patch does the following:
1)Reduce bits for frames before the scenecut
2)Refactor Scenecut Aware Frame Quantizer Selection
3)Add option "--qp-delta-nonref" to set offset for
non-referenced inter frames(optional).
4)Enables Scenecut Aware Frame Quantizer Selection
to run only with pass 2
  • Loading branch information
niranjan-mcw committed Jul 3, 2020
1 parent 9dba266 commit 3d579f9
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 68 deletions .
20 changes: 15 additions & 5 deletions doc/reST/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1948,10 +1948,11 @@ Quality, rate control and rate distortion options

.. option :: --scenecut-aware-qp , --no-scenecut-aware-qp

Enables a ratecontrol algorithm for reducing the bits spent on the inter-frames
within the :option: ` --scenecut-window ` after a scenecut by increasing their QP
without any deterioration in visual quality. It also increases the quality of
scenecut I-Frames by reducing their QP. Default disabled.
It reduces the bits spent on the inter-frames within the :option: ` --scenecut-window `
before and after a scenecut by increasing their QP in ratecontrol pass2 algorithm
without any deterioration in visual quality. If a scenecut falls within the window,
the QP of the inter-frames after this scenecut will not be modified.
:option: ` --scenecut-aware-qp ` works only with --pass 2. Default disabled.

.. option :: --scenecut-window < integer >

Expand All @@ -1961,12 +1962,21 @@ Quality, rate control and rate distortion options

** Range of values: ** 0 to 1000

.. option :: -- max- qp-delta < integer >
.. option :: --qp-delta -ref < double >

The offset by which QP is incremented for inter-frames
when :option: ` --scenecut-aware-qp ` is enabled. Default 5.

** Range of values: ** 0 to 10

.. option :: --qp-delta-nonref < double >

The offset by which QP is incremented for non-referenced
inter-frames when :option: ` --scenecut-aware-qp ` is enabled.
The offset is computed from :option: ` --qp-delta-ref ` when it
is not explicitly specified.

** Range of values: ** 0 to 10

Quantization Options
====================
Expand Down
2 changes: 1 addition & 1 deletion source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 194 )
set (X265_BUILD 195 )
configure_file ( " ${PROJECT_SOURCE_DIR} /x265.def.in"
" ${PROJECT_BINARY_DIR} /x265.def" )
configure_file ( " ${PROJECT_SOURCE_DIR} /x265_config.h.in"
Expand Down
1 change: 1 addition & 0 deletions source/common/frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Frame::Frame()
m_thetaPic = NULL ;
m_edgeBitPlane = NULL ;
m_edgeBitPic = NULL ;
m_isInsideWindow = 0 ;
}

bool Frame::create (x265_param *param, float * quantOffsets)
Expand Down
2 changes: 2 additions & 0 deletions source/common/frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ class Frame
pixel* m_edgeBitPlane;
pixel* m_edgeBitPic;

int m_isInsideWindow;

Frame ();

bool create (x265_param *param, float * quantOffsets);
Expand Down
22 changes: 16 additions & 6 deletions source/common/param.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ void x265_param_default(x265_param* param)
param-> bEnableFades = 0 ;
param-> bEnableSceneCutAwareQp = 0 ;
param-> scenecutWindow = 500 ;
param-> maxQpDelta = 5 ;
param-> refQpDelta = 5 ;
param-> nonRefQpDelta = param-> refQpDelta + (SLICE_TYPE_DELTA * param-> refQpDelta );

/* Intra Coding Tools */
param-> bEnableConstrainedIntra = 0 ;
Expand Down Expand Up @@ -1342,7 +1343,8 @@ int x265_param_parse(x265_param* p, const char* name, const char* value)
OPT ( " fades " ) p-> bEnableFades = atobool (value);
OPT ( " scenecut-aware-qp " ) p-> bEnableSceneCutAwareQp = atobool (value);
OPT ( " scenecut-window " ) p-> scenecutWindow = atoi (value);
OPT ( " max-qp-delta " ) p-> maxQpDelta = atoi (value);
OPT ( " qp-delta-ref " ) p-> refQpDelta = atoi (value);
OPT ( " qp-delta-nonref " ) p-> nonRefQpDelta = atoi (value);
OPT ( " field " ) p-> bField = atobool ( value );
OPT ( " cll " ) p-> bEmitCLL = atobool (value);
OPT ( " frame-dup " ) p-> bEnableFrameDuplication = atobool (value);
Expand Down Expand Up @@ -1768,10 +1770,17 @@ int x265_check_params(x265_param* param)
}
CHECK (param-> selectiveSAO < 0 || param-> selectiveSAO > 4 ,
" Invalid SAO tune level. Value must be between 0 and 4 (inclusive) " );
if (param-> bEnableSceneCutAwareQp && !param-> rc . bStatRead )
{
param-> bEnableSceneCutAwareQp = 0 ;
x265_log (param, X265_LOG_WARNING, " Disabling Scenecut Aware Frame Quantizer Selection since it works only in pass 2 \n " );
}
CHECK (param-> scenecutWindow < 0 || param-> scenecutWindow > 1000 ,
" Invalid scenecut Window duration. Value must be between 0 and 1000(inclusive) " );
CHECK (param-> maxQpDelta < 0 || param-> maxQpDelta > 10 ,
" Invalid maxQpDelta value. Value must be between 0 and 10 (inclusive) " );
CHECK (param-> refQpDelta < 0 || param-> refQpDelta > 10 ,
" Invalid refQpDelta value. Value must be between 0 and 10 (inclusive) " );
CHECK (param-> nonRefQpDelta < 0 || param-> nonRefQpDelta > 10 ,
" Invalid nonRefQpDelta value. Value must be between 0 and 10 (inclusive) " );
for ( int level = 0 ; level < 3 ; level++)
CHECK (param-> hmeRange [level] < 0 || param-> hmeRange [level] >= 32768 ,
" Search Range for HME levels must be between 0 and 32768 " );
Expand Down Expand Up @@ -2219,7 +2228,7 @@ char *x265_param2string(x265_param* p, int padx, int pady)
s += sprintf (s, " qp-adaptation-range=%.2f " , p-> rc . qpAdaptationRange );
BOOL (p-> bEnableSceneCutAwareQp , " scenecut-aware-qp " );
if (p-> bEnableSceneCutAwareQp )
s += sprintf (s, " scenecut-window=%d max- qp-delta =%d " , p-> scenecutWindow , p-> maxQpDelta );
s += sprintf (s, " scenecut-window=%d qp-delta-ref=%f qp-delta -nonref=%f " , p-> scenecutWindow , p-> refQpDelta , p-> nonRefQpDelta );
s += sprintf (s, " conformance-window-offsets right=%d bottom=%d " , p-> confWinRightOffset , p-> confWinBottomOffset );
s += sprintf (s, " decoder-max-rate=%d " , p-> decoderVbvMaxRate );
# undef BOOL
Expand Down Expand Up @@ -2571,7 +2580,8 @@ void x265_copy_params(x265_param* dst, x265_param* src)
dst-> bEnableFades = src-> bEnableFades ;
dst-> bEnableSceneCutAwareQp = src-> bEnableSceneCutAwareQp ;
dst-> scenecutWindow = src-> scenecutWindow ;
dst-> maxQpDelta = src-> maxQpDelta ;
dst-> refQpDelta = src-> refQpDelta ;
dst-> nonRefQpDelta = src-> nonRefQpDelta ;
dst-> bField = src-> bField ;

dst-> confWinRightOffset = src-> confWinRightOffset ;
Expand Down
37 changes: 35 additions & 2 deletions source/encoder/encoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,7 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out)
inFrame-> m_lowres . bScenecut = false ;
inFrame-> m_lowres . satdCost = ( int64_t )- 1 ;
inFrame-> m_lowresInit = false ;
inFrame-> m_isInsideWindow = 0 ;
}

/* Copy input picture into a Frame and PicYuv, send to lookahead */
Expand All @@ -1808,6 +1809,23 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out)
if (m_param-> radl && m_param-> keyframeMax != m_param-> keyframeMin )
inFrame-> m_lowres . m_bIsHardScenecut = isHardSC;
}

if (m_param-> bEnableSceneCutAwareQp && m_param-> rc . bStatRead )
{
RateControlEntry * rcEntry = NULL ;
rcEntry = &(m_rateControl-> m_rce2Pass [inFrame-> m_poc ]);
if (rcEntry-> scenecut )
{
int backwardWindow = X265_MIN ( int ((p-> fpsNum / p-> fpsDenom ) / 10 ), p-> lookaheadDepth );
for ( int i = 1 ; i <= backwardWindow; i++)
{
int frameNum = inFrame-> m_poc - i;
Frame * frame = m_lookahead-> m_inputQueue . getPOC (frameNum);
if (frame)
frame-> m_isInsideWindow = BACKWARD_WINDOW;
}
}
}
if (m_param-> bHistBasedSceneCut && m_param-> analysisSave )
{
memcpy (inFrame-> m_analysisData . edgeHist , m_curEdgeHist, EDGE_BINS * sizeof ( int32_t ));
Expand Down Expand Up @@ -2224,8 +2242,23 @@ int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out)
frameEnc = m_lookahead-> getDecidedPicture ();
if (frameEnc && !pass && (!m_param-> chunkEnd || (m_encodedFrameNum < m_param-> chunkEnd )))
{
if (m_param-> bEnableSceneCutAwareQp && frameEnc-> m_lowres . bScenecut )
m_rateControl-> m_lastScenecut = frameEnc-> m_poc ;
if (m_param-> bEnableSceneCutAwareQp && m_param-> rc . bStatRead )
{
RateControlEntry * rcEntry;
rcEntry = &(m_rateControl-> m_rce2Pass [frameEnc-> m_poc ]);

if (rcEntry-> scenecut )
{
if (m_rateControl-> m_lastScenecut == - 1 )
m_rateControl-> m_lastScenecut = frameEnc-> m_poc ;
else
{
int maxWindowSize = int ((m_param-> scenecutWindow / 1000.0 ) * (m_param-> fpsNum / m_param-> fpsDenom ) + 0.5 );
if (frameEnc-> m_poc > (m_rateControl-> m_lastScenecut + maxWindowSize))
m_rateControl-> m_lastScenecut = frameEnc-> m_poc ;
}
}
}

if (m_param-> analysisMultiPassRefine || m_param-> analysisMultiPassDistortion )
{
Expand Down
126 changes: 82 additions & 44 deletions source/encoder/ratecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ bool RateControl::init(const SPS& sps)
double totalQpAq = 0 ;
for ( int i = 0 ; i < m_numEntries; i++)
{
RateControlEntry *rce;
RateControlEntry *rce , *rcePocOrder ;
int frameNumber;
int encodeOrder;
char picType;
Expand All @@ -597,13 +597,16 @@ bool RateControl::init(const SPS& sps)
return false ;
}
rce = &m_rce2Pass[encodeOrder];
rcePocOrder = &m_rce2Pass[frameNumber];
m_encOrder[frameNumber] = encodeOrder;
if (!m_param-> bMultiPassOptRPS )
{
e += sscanf (p, " in:%*d out:%*d type:%c q:%lf q-aq:%lf q-noVbv:%lf q-Rceq:%lf tex:%d mv:%d misc:%d icu:%lf pcu:%lf scu:%lf " ,
int scenecut = 0 ;
e += sscanf (p, " in:%*d out:%*d type:%c q:%lf q-aq:%lf q-noVbv:%lf q-Rceq:%lf tex:%d mv:%d misc:%d icu:%lf pcu:%lf scu:%lf sc:%d " ,
&picType, &qpRc, &qpAq, &qNoVbv, &qRceq, &rce-> coeffBits ,
&rce-> mvBits , &rce-> miscBits , &rce-> iCuCount , &rce-> pCuCount ,
&rce-> skipCuCount );
&rce-> skipCuCount , &scenecut);
rcePocOrder-> scenecut = scenecut != 0 ;
}
else
{
Expand Down Expand Up @@ -1311,7 +1314,8 @@ int RateControl::rateControlStart(Frame* curFrame, RateControlEntry* rce, Encode
copyRceData (rce, &m_rce2Pass[ index ]);
}
rce-> isActive = true ;
rce-> scenecut = false ;
if (!m_param-> rc . bStatRead )
rce-> scenecut = false ;
rce-> isFadeEnd = curFrame-> m_lowres . bIsFadeEnd ;
bool isRefFrameScenecut = m_sliceType!= I_SLICE && m_curSlice-> m_refFrameList [ 0 ][ 0 ]-> m_lowres . bScenecut ;
m_isFirstMiniGop = m_sliceType == I_SLICE ? true : m_isFirstMiniGop;
Expand Down Expand Up @@ -1856,11 +1860,12 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
{
double lqmin = m_lmin[m_sliceType];
double lqmax = m_lmax[m_sliceType];
qScale = scenecutAwareQp (curFrame, qScale);
qScale = scenecutAwareMasking (curFrame, qScale);
qScale = x265_clip3 (lqmin, lqmax, qScale);
q = x265_qScale2qp (qScale);
rce-> qpNoVbv = q;
}

if (m_isVbv)
{
lmin = m_lastQScaleFor[P_SLICE] / m_lstep;
Expand Down Expand Up @@ -1971,6 +1976,16 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
m_avgPFrameQp = (m_avgPFrameQp + rce-> qpNoVbv ) / 2 ;
}

/* Scenecut Aware QP offsets */
if (m_param-> bEnableSceneCutAwareQp )
{
double qmin = m_lmin[m_sliceType];
double qmax = m_lmax[m_sliceType];
q = scenecutAwareMasking (curFrame, q);
q = x265_clip3 (qmin, qmax, q);
rce-> qpNoVbv = x265_qScale2qp (q);
}

if (m_isVbv)
{
/* Do not overflow vbv */
Expand Down Expand Up @@ -2120,13 +2135,12 @@ double RateControl::rateEstimateQscale(Frame* curFrame, RateControlEntry *rce)
{
double qmin = m_lmin[m_sliceType];
double qmax = m_lmax[m_sliceType];
q = scenecutAwareQp (curFrame, q);
q = scenecutAwareMasking (curFrame, q);
q = x265_clip3 (qmin, qmax, q);
rce-> qpNoVbv = x265_qScale2qp (q);
}
q = clipQscale (curFrame, rce, q);


if (m_2pass)
rce-> frameSizePlanned = qScale2bits (rce, q);
else
Expand Down Expand Up @@ -2964,7 +2978,7 @@ int RateControl::writeRateControlFrameStats(Frame* curFrame, RateControlEntry* r
if (!curEncData. m_param -> bMultiPassOptRPS )
{
if ( fprintf (m_statFileOut,
" in:%d out:%d type:%c q:%.2f q-aq:%.2f q-noVbv:%.2f q-Rceq:%.2f tex:%d mv:%d misc:%d icu:%.2f pcu:%.2f scu:%.2f ; \n " ,
" in:%d out:%d type:%c q:%.2f q-aq:%.2f q-noVbv:%.2f q-Rceq:%.2f tex:%d mv:%d misc:%d icu:%.2f pcu:%.2f scu:%.2f sc:%d ; \n " ,
rce-> poc , rce-> encodeOrder ,
cType, curEncData. m_avgQpRc , curEncData. m_avgQpAq ,
rce-> qpNoVbv , rce-> qRceq ,
Expand All @@ -2973,7 +2987,8 @@ int RateControl::writeRateControlFrameStats(Frame* curFrame, RateControlEntry* r
curFrame-> m_encData -> m_frameStats . miscBits ,
curFrame-> m_encData -> m_frameStats . percent8x8Intra * m_ncu,
curFrame-> m_encData -> m_frameStats . percent8x8Inter * m_ncu,
curFrame-> m_encData -> m_frameStats . percent8x8Skip * m_ncu) < 0 )
curFrame-> m_encData -> m_frameStats . percent8x8Skip * m_ncu,
curFrame-> m_lowres . bScenecut ) < 0 )
goto writeFailure;
}
else
Expand Down Expand Up @@ -3150,52 +3165,75 @@ void RateControl::splitbUsed(char bused[], RateControlEntry *rce)
}
}

double RateControl:: scenecutAwareQp (Frame* curFrame, double q)
double RateControl:: scenecutAwareMasking (Frame* curFrame, double q)
{
double qp = x265_qScale2qp (q);
uint32_t maxWindowSize = uint32_t ((m_param-> scenecutWindow / 1000.0 ) * (m_param-> fpsNum / m_param-> fpsDenom ) + 0.5 );
uint32_t windowSize = maxWindowSize / 3 ;
int lastScenecut = m_top-> m_rateControl -> m_lastScenecut ;
int lastIFrame = m_top-> m_rateControl -> m_lastScenecutAwareIFrame ;
double maxQpDelta = double (m_param-> maxQpDelta );
double iSliceDelta = double ( I_SLICE_DELTA );
double sliceTypeDelta = SLICE_TYPE_DELTA * maxQpDelta ;
double window2Delta = WINDOW2_DELTA * maxQpDelta ;
double window3Delta = WINDOW3_DELTA * maxQpDelta ;
double refQpDelta = double (m_param-> refQpDelta );
double nonRefQpDelta = double ( m_param-> nonRefQpDelta );
double sliceTypeDelta = SLICE_TYPE_DELTA * refQpDelta ;
double window2Delta = WINDOW2_DELTA * refQpDelta ;
double window3Delta = WINDOW3_DELTA * refQpDelta ;

bool isFrameInsideWindow = curFrame-> m_poc > lastScenecut && curFrame-> m_poc <= (lastScenecut + int (maxWindowSize));

if (isFrameInsideWindow && IS_X265_TYPE_I (curFrame-> m_lowres . sliceType ))
{
m_top-> m_rateControl -> m_lastScenecutAwareIFrame = curFrame-> m_poc ;
}
else if (isFrameInsideWindow && (curFrame-> m_lowres . sliceType == X265_TYPE_P))
if (curFrame-> m_poc > lastScenecut && curFrame-> m_poc <= (lastScenecut + int (maxWindowSize)))
curFrame-> m_isInsideWindow = FORWARD_WINDOW;
if (curFrame-> m_isInsideWindow == FORWARD_WINDOW)
{
if (!(lastIFrame > lastScenecut && lastIFrame <= (lastScenecut + int (maxWindowSize))
&& curFrame-> m_poc > lastIFrame))
if ( IS_X265_TYPE_I (curFrame-> m_lowres . sliceType ) || curFrame-> m_lowres . bScenecut )
{
qp += maxQpDelta - sliceTypeDelta;
if (((curFrame-> m_poc ) > (lastScenecut + int (windowSize))) && ((curFrame-> m_poc ) <= (lastScenecut + 2 * int (windowSize))))
qp -= window2Delta;
else if (curFrame-> m_poc > lastScenecut + 2 * int (windowSize))
qp -= window3Delta;
m_top-> m_rateControl -> m_lastScenecutAwareIFrame = curFrame-> m_poc ;
}
}
else if (isFrameInsideWindow && IS_X265_TYPE_B (curFrame-> m_lowres . sliceType ))
{
if (!(lastIFrame > lastScenecut && lastIFrame <= (lastScenecut + int (maxWindowSize))
&& curFrame-> m_poc > lastIFrame))
else if (curFrame-> m_lowres . sliceType == X265_TYPE_P)
{
if (!(lastIFrame > lastScenecut && lastIFrame <= (lastScenecut + int (maxWindowSize))
&& curFrame-> m_poc >= lastIFrame))
{
qp += refQpDelta - sliceTypeDelta;
if (((curFrame-> m_poc ) > (lastScenecut + int (windowSize))) && ((curFrame-> m_poc ) <= (lastScenecut + 2 * int (windowSize))))
qp -= window2Delta;
else if (curFrame-> m_poc > lastScenecut + 2 * int (windowSize))
qp -= window3Delta;
}
}
else if (curFrame-> m_lowres . sliceType == X265_TYPE_BREF)
{
qp += maxQpDelta;
if (curFrame-> m_lowres . sliceType == X265_TYPE_B)
qp += sliceTypeDelta;
if (((curFrame-> m_poc ) > (lastScenecut + int (windowSize))) && ((curFrame-> m_poc ) <= (lastScenecut + 2 * int (windowSize))))
qp -= window2Delta;
else if (curFrame-> m_poc > lastScenecut + 2 * int (windowSize))
qp -= window3Delta;
if (!(lastIFrame > lastScenecut && lastIFrame <= (lastScenecut + int (maxWindowSize))
&& curFrame-> m_poc >= lastIFrame))
{
qp += refQpDelta;
if (((curFrame-> m_poc ) > (lastScenecut + int (windowSize))) && ((curFrame-> m_poc ) <= (lastScenecut + 2 * int (windowSize))))
qp -= window2Delta;
else if (curFrame-> m_poc > lastScenecut + 2 * int (windowSize))
qp -= window3Delta;
}
}
else if (curFrame-> m_lowres . sliceType == X265_TYPE_B)
{
if (!(lastIFrame > lastScenecut && lastIFrame <= (lastScenecut + int (maxWindowSize))
&& curFrame-> m_poc >= lastIFrame))
{
qp += nonRefQpDelta;
if (((curFrame-> m_poc ) > (lastScenecut + int (windowSize))) && ((curFrame-> m_poc ) <= (lastScenecut + 2 * int (windowSize))))
qp -= window2Delta;
else if (curFrame-> m_poc > lastScenecut + 2 * int (windowSize))
qp -= window3Delta;
}
}
}
if ( IS_X265_TYPE_I (curFrame-> m_lowres . sliceType ) && curFrame-> m_lowres . bScenecut )
qp = qp - iSliceDelta;
return x265_qp2qScale (qp);
else if (curFrame-> m_isInsideWindow == BACKWARD_WINDOW)
{
refQpDelta -= window3Delta;
nonRefQpDelta -= window3Delta;
if (curFrame-> m_lowres . sliceType == X265_TYPE_P)
qp += refQpDelta - sliceTypeDelta;
else if (curFrame-> m_lowres . sliceType == X265_TYPE_BREF)
qp += refQpDelta;
else if (curFrame-> m_lowres . sliceType == X265_TYPE_B)
qp += nonRefQpDelta;
}

return x265_qp2qScale (qp);
}

0 comments on commit 3d579f9

Please sign in to comment.
- "漢字路" 한글한자자동변환 서비스는 교육부 고전문헌국역지원사업의 지원으로 구축되었습니다.
- "漢字路" 한글한자자동변환 서비스는 전통문화연구회 "울산대학교한국어처리연구실 옥철영(IT융합전공)교수팀"에서 개발한 한글한자자동변환기를 바탕하여 지속적으로 공동 연구 개발하고 있는 서비스입니다.
- 현재 고유명사(인명, 지명등)을 비롯한 여러 변환오류가 있으며 이를 해결하고자 많은 연구 개발을 진행하고자 하고 있습니다. 이를 인지하시고 다른 곳에서 인용시 한자 변환 결과를 한번 더 검토하시고 사용해 주시기 바랍니다.
- 변환오류 및 건의,문의사항은 juntong@juntong.or.kr로 메일로 보내주시면 감사하겠습니다. .
Copyright ⓒ 2020 By '전통문화연구회(傳統文化硏究會)' All Rights reserved.
 한국   대만   중국   일본