changeset 12550:5ac4c74c5418 draft

Add option to enable slice-based SAO filter.
author Pooja Venkatesan <pooja@multicorewareinc.com>
date Tue, 03 Sep 2019 14:25:44 +0530
parents a092e82e6acf
children c525b46b92bb
files doc/reST/cli.rst source/CMakeLists.txt source/common/param.cpp source/common/slice.h source/encoder/encoder.cpp source/encoder/entropy.cpp source/encoder/framefilter.cpp source/encoder/framefilter.h source/test/regression-tests.txt source/x265.h source/x265cli.h
diffstat 11 files changed, 96 insertions(+-), 15 deletions(-) [+]
line wrap: on
line diff
--- a/doc/reST/cli.rst	Thu Aug 01 22:55:21 2019 +0200
+++ b/doc/reST/cli.rst	Tue Sep 03 14:25:44 2019 +0530
@@ -1996,6 +1996,24 @@ Loop filters
 	on inter prediction mode, CTU spatial-domain correlations, and relations
 	between luma and chroma.
 	Default disabled
+	
+.. option:: --selective-sao <0..4>
+
+	Toggles SAO at slice level. Default 4.
+
+	+--------------+---------------------------------------+
+	|     Level    |              Description              |     
+	+==============+=======================================+
+	|      0       | Disable SAO for all slices            |
+	+--------------+---------------------------------------+
+	|      1       | Enable SAO only for I-slices          |
+	+--------------+---------------------------------------+
+	|      2       | Enable SAO for I-slices & P-slices    |                                  |
+	+--------------+---------------------------------------+
+	|      3       | Enable SAO for all reference slices   |
+	+--------------+---------------------------------------+
+	|      4       | Enable SAO for all slices             |
+	+--------------+---------------------------------------+
 
 VUI (Video Usability Information) options
 =========================================
--- a/source/CMakeLists.txt	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/CMakeLists.txt	Tue Sep 03 14:25:44 2019 +0530
@@ -29,7 +29,7 @@ option(NATIVE_BUILD "Target the build CP
 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 178)
+set(X265_BUILD 179)
 configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
                "${PROJECT_BINARY_DIR}/x265.def")
 configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
--- a/source/common/param.cpp	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/common/param.cpp	Tue Sep 03 14:25:44 2019 +0530
@@ -215,6 +215,7 @@ void x265_param_default(x265_param* para
     param->bEnableSAO = 1;
     param->bSaoNonDeblocked = 0;
     param->bLimitSAO = 0;
+    param->selectiveSAO = 4;
 
     /* Coding Quality */
     param->cbQpOffset = 0;
@@ -375,6 +376,7 @@ int x265_param_default_preset(x265_param
             param->subpelRefine = 0;
             param->searchMethod = X265_DIA_SEARCH;
             param->bEnableSAO = 0;
+            param->selectiveSAO = 0;
             param->bEnableSignHiding = 0;
             param->bEnableWeightedPred = 0;
             param->rdLevel = 2;
@@ -404,6 +406,7 @@ int x265_param_default_preset(x265_param
             param->rc.hevcAq = 0;
             param->rc.qgSize = 32;
             param->bEnableSAO = 0;
+            param->selectiveSAO = 0;
             param->bEnableFastIntra = 1;
         }
         else if (!strcmp(preset, "veryfast"))
@@ -551,6 +554,7 @@ int x265_param_default_preset(x265_param
         {
             param->bEnableLoopFilter = 0;
             param->bEnableSAO = 0;
+            param->selectiveSAO = 0;
             param->bEnableWeightedPred = 0;
             param->bEnableWeightedBiPred = 0;
             param->bIntraInBFrames = 0;
@@ -578,6 +582,7 @@ int x265_param_default_preset(x265_param
             param->psyRd = 4.0;
             param->psyRdoq = 10.0;
             param->bEnableSAO = 0;
+            param->selectiveSAO = 0;
             param->rc.bEnableConstVbv = 1;
         }
         else if (!strcmp(tune, "animation"))
@@ -1282,6 +1287,10 @@ int x265_param_parse(x265_param* p, cons
         OPT("svt-pred-struct") x265_log(p, X265_LOG_WARNING, "Option %s is SVT-HEVC Encoder specific; Disabling it here \n", name);
         OPT("svt-fps-in-vps") x265_log(p, X265_LOG_WARNING, "Option %s is SVT-HEVC Encoder specific; Disabling it here \n", name);
 #endif
+        OPT("selective-sao")
+        {
+            p->selectiveSAO = atoi(value);
+        }
         OPT("fades") p->bEnableFades = atobool(value);
         OPT("field") p->bField = atobool( value );
         OPT("cll") p->bEmitCLL = atobool(value);
@@ -1686,6 +1695,8 @@ int x265_check_params(x265_param* param)
         CHECK( (param->bFrameAdaptive==0), "Adaptive B-frame decision method should be closed for field feature.\n" );
         // to do
     }
+    CHECK(param->selectiveSAO < 0 || param->selectiveSAO > 4,
+        "Invalid SAO tune level. Value must be between 0 and 4 (inclusive)");
 #if !X86_64
     CHECK(param->searchMethod == X265_SEA && (param->sourceWidth > 840 || param->sourceHeight > 480),
         "SEA motion search does not support resolutions greater than 480p in 32 bit build");
@@ -1862,6 +1873,8 @@ void x265_print_params(x265_param* param
     }
     TOOLOPT(param->bSaoNonDeblocked, "sao-non-deblock");
     TOOLOPT(!param->bSaoNonDeblocked && param->bEnableSAO, "sao");
+    if (param->selectiveSAO != 4)
+        TOOLOPT(param->selectiveSAO, "selective-sao");
     TOOLOPT(param->rc.bStatWrite, "stats-write");
     TOOLOPT(param->rc.bStatRead,  "stats-read");
     TOOLOPT(param->bSingleSeiNal, "single-sei");
@@ -1971,6 +1984,7 @@ char *x265_param2string(x265_param* p, i
     BOOL(p->bEnableSAO, "sao");
     BOOL(p->bSaoNonDeblocked, "sao-non-deblock");
     s += sprintf(s, " rd=%d", p->rdLevel);
+    s += sprintf(s, "selective-sao=%d", p->selectiveSAO);
     BOOL(p->bEnableEarlySkip, "early-skip");
     BOOL(p->bEnableRecursionSkip, "rskip");
     BOOL(p->bEnableFastIntra, "fast-intra");
@@ -2420,6 +2434,7 @@ void x265_copy_params(x265_param* dst, x
     else dst->analysisLoad = NULL;
     dst->gopLookahead = src->gopLookahead;
     dst->radl = src->radl;
+    dst->selectiveSAO = src->selectiveSAO;
     dst->maxAUSizeFactor = src->maxAUSizeFactor;
     dst->bEmitIDRRecoverySEI = src->bEmitIDRRecoverySEI;
     dst->bDynamicRefine = src->bDynamicRefine;
--- a/source/common/slice.h	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/common/slice.h	Tue Sep 03 14:25:44 2019 +0530
@@ -356,6 +356,7 @@ public:
     bool        m_bCheckLDC;       // TODO: is this necessary?
     bool        m_sLFaseFlag;      // loop filter boundary flag
     bool        m_colFromL0Flag;   // collocated picture from List0 or List1 flag
+    int         m_bUseSao;
 
     int         m_iPPSQpMinus26;
     int         numRefIdxDefault[2];
--- a/source/encoder/encoder.cpp	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/encoder/encoder.cpp	Tue Sep 03 14:25:44 2019 +0530
@@ -1621,6 +1621,28 @@ int Encoder::encode(const x265_picture* 
             }
             /* determine references, setup RPS, etc */
             m_dpb->prepareEncode(frameEnc);
+            if (!!m_param->selectiveSAO)
+            {
+                Slice* slice = frameEnc->m_encData->m_slice;
+                slice->m_bUseSao = curEncoder->m_frameFilter.m_useSao = 1;
+                switch (m_param->selectiveSAO)
+                {
+                case 3: if (!IS_REFERENCED(frameEnc))
+                            slice->m_bUseSao = curEncoder->m_frameFilter.m_useSao = 0;
+                        break;
+                case 2: if (!!m_param->bframes && slice->m_sliceType == B_SLICE)
+                            slice->m_bUseSao = curEncoder->m_frameFilter.m_useSao = 0;
+                        break;
+                case 1: if (slice->m_sliceType != I_SLICE)
+                            slice->m_bUseSao = curEncoder->m_frameFilter.m_useSao = 0;
+                        break;
+                }
+            }
+            else
+            {
+                Slice* slice = frameEnc->m_encData->m_slice;
+                slice->m_bUseSao = curEncoder->m_frameFilter.m_useSao = 0;
+            }
 
             if (m_param->rc.rateControlMode != X265_RC_CQP)
                 m_lookahead->getEstimatedPictureCost(frameEnc);
@@ -2891,6 +2913,14 @@ void Encoder::configure(x265_param *p)
 
     }
 
+    if (p->selectiveSAO && !p->bEnableSAO)
+    {
+        p->bEnableSAO = 1;
+        x265_log(p, X265_LOG_WARNING, "SAO turned ON when selective-sao is ON\n");
+    }
+
+    if (!p->selectiveSAO && p->bEnableSAO)
+        p->selectiveSAO = 4;
 
     if (p->interlaceMode)
         x265_log(p, X265_LOG_WARNING, "Support for interlaced video is experimental\n");
--- a/source/encoder/entropy.cpp	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/encoder/entropy.cpp	Tue Sep 03 14:25:44 2019 +0530
@@ -641,12 +641,18 @@ void Entropy::codeSliceHeader(const Slic
             WRITE_FLAG(1, "slice_temporal_mvp_enable_flag");
     }
     const SAOParam *saoParam = encData.m_saoParam;
-    if (slice.m_sps->bUseSAO)
+    if (slice.m_bUseSao)
     {
         WRITE_FLAG(saoParam->bSaoFlag[0], "slice_sao_luma_flag");
         if (encData.m_param->internalCsp != X265_CSP_I400)
             WRITE_FLAG(saoParam->bSaoFlag[1], "slice_sao_chroma_flag");
     }
+    else if(encData.m_param->selectiveSAO)
+    {
+        WRITE_FLAG(0, "slice_sao_luma_flag");
+        if (encData.m_param->internalCsp != X265_CSP_I400)
+            WRITE_FLAG(0, "slice_sao_chroma_flag");
+    }
 
     // check if numRefIdx match the defaults (1, hard-coded in PPS). If not, override
     // TODO: this might be a place to optimize a few bits per slice, by using param->refs for L0 default
@@ -706,7 +712,7 @@ void Entropy::codeSliceHeader(const Slic
 
     if (encData.m_param->maxSlices <= 1)
     {
-        bool isSAOEnabled = slice.m_sps->bUseSAO ? saoParam->bSaoFlag[0] || saoParam->bSaoFlag[1] : false;
+        bool isSAOEnabled = slice.m_sps->bUseSAO && slice.m_bUseSao ? saoParam->bSaoFlag[0] || saoParam->bSaoFlag[1] : false;
         bool isDBFEnabled = !slice.m_pps->bPicDisableDeblockingFilter;
 
         if (isSAOEnabled || isDBFEnabled)
--- a/source/encoder/framefilter.cpp	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/encoder/framefilter.cpp	Tue Sep 03 14:25:44 2019 +0530
@@ -163,7 +163,7 @@ void FrameFilter::destroy()
 
     if (m_parallelFilter)
     {
-        if (m_param->bEnableSAO)
+        if (m_useSao)
         {
             for(int row = 0; row < m_numRows; row++)
                 m_parallelFilter[row].m_sao.destroy((row == 0 ? 1 : 0));
@@ -178,6 +178,7 @@ void FrameFilter::init(Encoder *top, Fra
 {
     m_param = frame->m_param;
     m_frameEncoder = frame;
+    m_useSao = 1;
     m_numRows = numRows;
     m_numCols = numCols;
     m_hChromaShift = CHROMA_H_SHIFT(m_param->internalCsp);
@@ -196,12 +197,12 @@ void FrameFilter::init(Encoder *top, Fra
 
     if (m_parallelFilter)
     {
-        if (m_param->bEnableSAO)
+        if (m_useSao)
         {
             for(int row = 0; row < numRows; row++)
             {
                 if (!m_parallelFilter[row].m_sao.create(m_param, (row == 0 ? 1 : 0)))
-                    m_param->bEnableSAO = 0;
+                    m_useSao = 0;
                 else
                 {
                     if (row != 0)
@@ -235,7 +236,7 @@ void FrameFilter::start(Frame *frame, En
     {
         for(int row = 0; row < m_numRows; row++)
         {
-            if (m_param->bEnableSAO)
+            if (m_useSao)
                 m_parallelFilter[row].m_sao.startSlice(frame, initState);
 
             m_parallelFilter[row].m_lastCol.set(0);
@@ -245,7 +246,7 @@ void FrameFilter::start(Frame *frame, En
         }
 
         // Reset SAO common statistics
-        if (m_param->bEnableSAO)
+        if (m_useSao)
             m_parallelFilter[0].m_sao.resetStats();
     }
 }
@@ -472,11 +473,11 @@ void FrameFilter::ParallelFilter::proces
                 deblockCTU(ctuPrev, cuGeoms[ctuGeomMap[cuAddr - 1]], Deblock::EDGE_HOR);
 
                 // When SAO Disable, setting column counter here
-                if (!m_frameFilter->m_param->bEnableSAO & !ctuPrev->m_bFirstRowInSlice)
+                if (!m_frameFilter->m_useSao & !ctuPrev->m_bFirstRowInSlice)
                     m_prevRow->processPostCu(col - 1);
             }
 
-            if (m_frameFilter->m_param->bEnableSAO)
+            if (m_frameFilter->m_useSao)
             {
                 // Save SAO bottom row reference pixels
                 copySaoAboveRef(ctuPrev, reconPic, cuAddr - 1, col - 1);
@@ -514,12 +515,12 @@ void FrameFilter::ParallelFilter::proces
             deblockCTU(ctuPrev, cuGeoms[ctuGeomMap[cuAddr]], Deblock::EDGE_HOR);
 
             // When SAO Disable, setting column counter here
-            if (!m_frameFilter->m_param->bEnableSAO & !ctuPrev->m_bFirstRowInSlice)
+            if (!m_frameFilter->m_useSao & !ctuPrev->m_bFirstRowInSlice)
                 m_prevRow->processPostCu(numCols - 1);
         }
 
         // TODO: move processPostCu() into processSaoUnitCu()
-        if (m_frameFilter->m_param->bEnableSAO)
+        if (m_frameFilter->m_useSao)
         {
             const CUData* ctu = m_encData->getPicCTU(m_rowAddr + numCols - 2);
 
@@ -570,7 +571,7 @@ void FrameFilter::processRow(int row)
     m_frameEncoder->m_cuStats.countLoopFilter++;
 #endif
 
-    if (!m_param->bEnableLoopFilter && !m_param->bEnableSAO)
+    if (!m_param->bEnableLoopFilter && !m_useSao)
     {
         processPostRow(row);
         return;
@@ -596,7 +597,7 @@ void FrameFilter::processRow(int row)
                 x265_log(m_param, X265_LOG_WARNING, "detected ParallelFilter race condition on last row\n");
 
             /* Apply SAO on last row of CUs, because we always apply SAO on row[X-1] */
-            if (m_param->bEnableSAO)
+            if (m_useSao)
             {
                 for(int col = 0; col < m_numCols; col++)
                 {
@@ -634,7 +635,7 @@ void FrameFilter::processRow(int row)
 
     if (numRowFinished == m_numRows)
     {
-        if (m_param->bEnableSAO)
+        if (m_useSao)
         {
             // Merge numNoSao into RootNode (Node0)
             for(int i = 1; i < m_numRows; i++)
--- a/source/encoder/framefilter.h	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/encoder/framefilter.h	Tue Sep 03 14:25:44 2019 +0530
@@ -46,6 +46,7 @@ public:
 
     x265_param*   m_param;
     Frame*        m_frame;
+    int           m_useSao;
     FrameEncoder* m_frameEncoder;
     int           m_hChromaShift;
     int           m_vChromaShift;
--- a/source/test/regression-tests.txt	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/test/regression-tests.txt	Tue Sep 03 14:25:44 2019 +0530
@@ -155,6 +155,7 @@ BasketballDrive_1920x1080_50.y4m, --pres
 big_buck_bunny_360p24.y4m, --bitrate 500 --fades
 720p50_parkrun_ter.y4m,--preset medium --bitrate 400 --hme
 ducks_take_off_420_1_720p50.y4m,--preset medium --aq-mode 4 --crf 22 --no-cutree
+ducks_take_off_420_1_720p50.y4m,--preset medium --selective-sao 4 --sao --crf 20
 
 # Main12 intraCost overflow bug test
 720p50_parkrun_ter.y4m,--preset medium
--- a/source/x265.h	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/x265.h	Tue Sep 03 14:25:44 2019 +0530
@@ -1223,6 +1223,12 @@ typedef struct x265_param
      * non-deblocked pixels are used entirely. Default is disabled */
     int       bSaoNonDeblocked;
 
+    /* Select tune rate in which SAO has to be applied.
+    1 - Filtering applied only on I-frames(I) [Light tune]
+    2 - No Filtering on B frames (I, P) [Medium tune]
+    3 - No Filtering on non-ref b frames  (I, P, B) [Strong tune] */
+    int       selectiveSAO;
+
     /*== Analysis tools ==*/
 
     /* A value between 1 and 6 (both inclusive) which determines the level of 
--- a/source/x265cli.h	Thu Aug 01 22:55:21 2019 +0200
+++ b/source/x265cli.h	Tue Sep 03 14:25:44 2019 +0530
@@ -200,6 +200,7 @@ static const struct option long_options[
     { "no-deblock",           no_argument, NULL, 0 },
     { "deblock",        required_argument, NULL, 0 },
     { "no-sao",               no_argument, NULL, 0 },
+    { "selective-sao",  required_argument, NULL, 0 },
     { "sao",                  no_argument, NULL, 0 },
     { "no-sao-non-deblock",   no_argument, NULL, 0 },
     { "sao-non-deblock",      no_argument, NULL, 0 },
@@ -589,6 +590,7 @@ static void showHelp(x265_param *param)
     H0("   --[no-]sao                    Enable Sample Adaptive Offset. Default %s\n", OPT(param->bEnableSAO));
     H1("   --[no-]sao-non-deblock        Use non-deblocked pixels, else right/bottom boundary areas skipped. Default %s\n", OPT(param->bSaoNonDeblocked));
     H0("   --[no-]limit-sao              Limit Sample Adaptive Offset types. Default %s\n", OPT(param->bLimitSAO));
+    H0("   --selective-sao <int>         Enable slice-level SAO filter. Default %d\n", param->selectiveSAO);
     H0("\nVUI options:\n");
     H0("   --sar <width:height|int>      Sample Aspect Ratio, the ratio of width to height of an individual pixel.\n");
     H0("                                 Choose from 0=undef, 1=1:1(\"square\"), 2=12:11, 3=10:11, 4=16:11,\n");