Skip to content
Snippets Groups Projects
EncSampleAdaptiveOffset.cpp 279 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* The copyright in this software is being made available under the BSD
     * License, included below. This software may be subject to other third party
     * and contributor rights, including patent rights, and no such rights are
     * granted under this license.
     *
    
     * Copyright (c) 2010-2023, ITU/ISO/IEC
    
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *
     *  * Redistributions of source code must retain the above copyright notice,
     *    this list of conditions and the following disclaimer.
     *  * Redistributions in binary form must reproduce the above copyright notice,
     *    this list of conditions and the following disclaimer in the documentation
     *    and/or other materials provided with the distribution.
     *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
     *    be used to endorse or promote products derived from this software without
     *    specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
     * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     * THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /**
     \file     EncSampleAdaptiveOffset.cpp
     \brief       estimation part of sample adaptive offset class
     */
    #include "EncSampleAdaptiveOffset.h"
    
    #include "CommonLib/UnitTools.h"
    #include "CommonLib/dtrace_codingstruct.h"
    #include "CommonLib/dtrace_buffer.h"
    #include "CommonLib/CodingStructure.h"
    
    #if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #include "CommonLib/BilateralFilter.h"
    #endif
    
    
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <math.h>
    
    //! \ingroup EncoderLib
    //! \{
    
    
    #define SAOCtx(c) SubCtx( Ctx::Sao, c )
    
    
    Xiaoyu Xiu's avatar
    Xiaoyu Xiu committed
    #if JVET_W0066_CCSAO
    #include <algorithm>
    
    struct SetIdxCount
    {
      uint8_t  setIdx;
      uint16_t count;
    };
    
    struct CtbCost
    {
      int16_t pos;
      double  cost;
    };
    
    bool compareSetIdxCount(SetIdxCount a, SetIdxCount b) { return a.count > b.count; }
    
    bool compareCtbCost(CtbCost a, CtbCost b) { return a.cost < b.cost; }
    #endif
    
    
    //! rounding with IBDI
    inline double xRoundIbdi2(int bitDepth, double x)
    {
    #if FULL_NBIT
      return ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) -0.5)));
    #else
      if (DISTORTION_PRECISION_ADJUSTMENT(bitDepth) == 0)
        return ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) -0.5)));
      else
        return ((x) > 0) ? (int)(((int)(x) + (1 << (DISTORTION_PRECISION_ADJUSTMENT(bitDepth) - 1)))
                                 / (1 << DISTORTION_PRECISION_ADJUSTMENT(bitDepth)))
                         : ((int)(((int)(x) - (1 << (DISTORTION_PRECISION_ADJUSTMENT(bitDepth) - 1)))
                                  / (1 << DISTORTION_PRECISION_ADJUSTMENT(bitDepth))));
    #endif
    }
    
    inline double xRoundIbdi(int bitDepth, double x)
    {
      return (bitDepth > 8 ? xRoundIbdi2(bitDepth, (x)) : ((x)>=0 ? ((int)((x)+0.5)) : ((int)((x)-0.5)))) ;
    }
    
    
    EncSampleAdaptiveOffset::EncSampleAdaptiveOffset()
    {
      m_CABACEstimator = NULL;
    
    
      ::memset( m_saoDisabledRate, 0, sizeof( m_saoDisabledRate ) );
    
    }
    
    EncSampleAdaptiveOffset::~EncSampleAdaptiveOffset()
    {
    }
    
    void EncSampleAdaptiveOffset::createEncData(bool isPreDBFSamplesUsed, uint32_t numCTUsPic)
    {
      //statistics
      const uint32_t sizeInCtus = numCTUsPic;
      m_statData.resize( sizeInCtus );
      for(uint32_t i=0; i< sizeInCtus; i++)
      {
        m_statData[i] = new SAOStatData*[MAX_NUM_COMPONENT];
        for(uint32_t compIdx=0; compIdx < MAX_NUM_COMPONENT; compIdx++)
        {
          m_statData[i][compIdx] = new SAOStatData[NUM_SAO_NEW_TYPES];
        }
      }
      if(isPreDBFSamplesUsed)
      {
        m_preDBFstatData.resize( sizeInCtus );
        for(uint32_t i=0; i< sizeInCtus; i++)
        {
          m_preDBFstatData[i] = new SAOStatData*[MAX_NUM_COMPONENT];
          for(uint32_t compIdx=0; compIdx < MAX_NUM_COMPONENT; compIdx++)
          {
            m_preDBFstatData[i][compIdx] = new SAOStatData[NUM_SAO_NEW_TYPES];
          }
        }
    
      }
    
    
      for(int typeIdc=0; typeIdc < NUM_SAO_NEW_TYPES; typeIdc++)
      {
        m_skipLinesR[COMPONENT_Y ][typeIdc]= 5;
        m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 3;
    
        m_skipLinesB[COMPONENT_Y ][typeIdc]= 4;
        m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 2;
    
        if(isPreDBFSamplesUsed)
        {
          switch(typeIdc)
          {
          case SAO_TYPE_EO_0:
            {
              m_skipLinesR[COMPONENT_Y ][typeIdc]= 5;
              m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 3;
    
              m_skipLinesB[COMPONENT_Y ][typeIdc]= 3;
              m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 1;
            }
            break;
          case SAO_TYPE_EO_90:
            {
              m_skipLinesR[COMPONENT_Y ][typeIdc]= 4;
              m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 2;
    
              m_skipLinesB[COMPONENT_Y ][typeIdc]= 4;
              m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 2;
            }
            break;
          case SAO_TYPE_EO_135:
          case SAO_TYPE_EO_45:
            {
              m_skipLinesR[COMPONENT_Y ][typeIdc]= 5;
              m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 3;
    
              m_skipLinesB[COMPONENT_Y ][typeIdc]= 4;
              m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 2;
            }
            break;
          case SAO_TYPE_BO:
            {
              m_skipLinesR[COMPONENT_Y ][typeIdc]= 4;
              m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 2;
    
              m_skipLinesB[COMPONENT_Y ][typeIdc]= 3;
              m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 1;
            }
            break;
          default:
            {
              THROW("Not a supported type");
            }
          }
        }
      }
    
    Xiaoyu Xiu's avatar
    Xiaoyu Xiu committed
    
    #if JVET_W0066_CCSAO
      if (m_createdEnc)
      {
        return;
      }
      m_createdEnc = true;
    
      for (int i = 0; i < MAX_CCSAO_SET_NUM; i++)
      {
    
        m_ccSaoStatData    [i] = new CcSaoStatData[m_numCTUsInPic];
    
    #if JVET_Y0106_CCSAO_EDGE_CLASSIFIER
        m_ccSaoStatDataEdge[i] = new CcSaoStatData[m_numCTUsInPic];
    #endif
    
    Xiaoyu Xiu's avatar
    Xiaoyu Xiu committed
      }
    
    #if JVET_Y0106_CCSAO_EDGE_CLASSIFIER
    
    #if JVET_AE0151_CCSAO_HISTORY_OFFSETS_AND_EXT_EO
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      int numStatsEdge = m_numCTUsInPic * MAX_CCSAO_EDGE_DIR * MAX_CCSAO_EDGE_THR * MAX_CCSAO_BAND_IDC * MAX_NUM_COMPONENT * MAX_CCSAO_EDGE_IDC;
    
      m_ccSaoStatDataEdgePre = new CcSaoStatData[numStatsEdge];
    #else
    
      for (int comp = Y_C; comp < N_C; comp++)
      {
        m_ccSaoStatDataEdgeNew[comp] = new CcSaoStatData[m_numCTUsInPic * (CCSAO_EDGE_BAND_NUM_Y + CCSAO_EDGE_BAND_NUM_C)
                                                         * CCSAO_QUAN_NUM * CCSAO_EDGE_TYPE];
      }
    
    Xiaoyu Xiu's avatar
    Xiaoyu Xiu committed
      m_bestCcSaoControl = new uint8_t[m_numCTUsInPic];
      m_tempCcSaoControl = new uint8_t[m_numCTUsInPic];
      m_initCcSaoControl = new uint8_t[m_numCTUsInPic];
    
      for (int i = 0; i < MAX_CCSAO_SET_NUM; i++)
      {
        m_trainingDistortion[i] = new int64_t[m_numCTUsInPic];
      }
    #endif
    
    }
    
    void EncSampleAdaptiveOffset::destroyEncData()
    {
      for(uint32_t i=0; i< m_statData.size(); i++)
      {
        for(uint32_t compIdx=0; compIdx< MAX_NUM_COMPONENT; compIdx++)
        {
          delete[] m_statData[i][compIdx];
        }
        delete[] m_statData[i];
      }
      m_statData.clear();
    
    
      for(int i=0; i< m_preDBFstatData.size(); i++)
      {
        for(int compIdx=0; compIdx< MAX_NUM_COMPONENT; compIdx++)
        {
          delete[] m_preDBFstatData[i][compIdx];
        }
        delete[] m_preDBFstatData[i];
      }
      m_preDBFstatData.clear();
    
    Xiaoyu Xiu's avatar
    Xiaoyu Xiu committed
    
    #if JVET_W0066_CCSAO
      if (!m_createdEnc)
      {
        return;
      }
      m_createdEnc = false;
    
      for (int i = 0; i < MAX_CCSAO_SET_NUM; i++)
      {
    
        if (m_ccSaoStatData    [i]) { delete[] m_ccSaoStatData    [i]; m_ccSaoStatData    [i] = nullptr; }
    
    #if JVET_Y0106_CCSAO_EDGE_CLASSIFIER
    
        if (m_ccSaoStatDataEdge[i]) { delete[] m_ccSaoStatDataEdge[i]; m_ccSaoStatDataEdge[i] = nullptr; }
    
    Xiaoyu Xiu's avatar
    Xiaoyu Xiu committed
      }
    
    #if JVET_Y0106_CCSAO_EDGE_CLASSIFIER
    
    #if JVET_AE0151_CCSAO_HISTORY_OFFSETS_AND_EXT_EO
      if (m_ccSaoStatDataEdgePre) { delete[] m_ccSaoStatDataEdgePre; m_ccSaoStatDataEdgePre = nullptr; }
    #else
    
      for (int comp = Y_C; comp < N_C; comp++)
      {
        if (m_ccSaoStatDataEdgeNew[comp])
        {
          delete[] m_ccSaoStatDataEdgeNew[comp];
          m_ccSaoStatDataEdgeNew[comp] = nullptr;
        }
      }
    
    Xiaoyu Xiu's avatar
    Xiaoyu Xiu committed
    
      if (m_bestCcSaoControl) { delete[] m_bestCcSaoControl; m_bestCcSaoControl = nullptr; }
      if (m_tempCcSaoControl) { delete[] m_tempCcSaoControl; m_tempCcSaoControl = nullptr; }
      if (m_initCcSaoControl) { delete[] m_initCcSaoControl; m_initCcSaoControl = nullptr; }
    
      for (int i = 0; i < MAX_CCSAO_SET_NUM; i++)
      {
        if (m_trainingDistortion[i]) { delete[] m_trainingDistortion[i]; m_trainingDistortion[i] = nullptr; }
      }
    #endif
    
    }
    
    void EncSampleAdaptiveOffset::initCABACEstimator( CABACEncoder* cabacEncoder, CtxCache* ctxCache, Slice* pcSlice )
    {
      m_CABACEstimator = cabacEncoder->getCABACEstimator( pcSlice->getSPS() );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      m_ctxCache       = ctxCache;
    
      m_CABACEstimator->initCtxModels( *pcSlice );
      m_CABACEstimator->resetBits();
    }
    
    
    
    void EncSampleAdaptiveOffset::SAOProcess( CodingStructure& cs, bool* sliceEnabled, const double* lambdas,
    #if ENABLE_QPA
                                              const double lambdaChromaWeight,
    #endif
    
    Vadim Seregin's avatar
    Vadim Seregin committed
                                              const bool bTestSAODisableAtPictureLevel, const double saoEncodingRate, const double saoEncodingRateChroma, const bool isPreDBFSamplesUsed, bool isGreedyMergeEncoding
    
    #if JVET_V0094_BILATERAL_FILTER
    
                                              ,BIFCabacEst* bifCABACEstimator
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
                                             )
    
    #if ALF_SAO_TRUE_ORG && !JVET_V0094_BILATERAL_FILTER && !JVET_X0071_CHROMA_BILATERAL_FILTER
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      PelUnitBuf org = cs.getTrueOrgBuf();
    #else
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
    
      PelUnitBuf res = cs.getRecoBuf();
      PelUnitBuf src = m_tempBuf;
    
    #if !JVET_V0094_BILATERAL_FILTER && !JVET_X0071_CHROMA_BILATERAL_FILTER
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      // Moved until after the bilateral filter has been initialized
    
      memcpy(m_lambda, lambdas, sizeof(m_lambda));
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
    
    #if JVET_V0094_BILATERAL_FILTER
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      const PreCalcValues& pcv = *cs.pcv;
    
      BifParams& bifParams = cs.picture->getBifParam(COMPONENT_Y);
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      int width = cs.picture->lwidth();
      int height = cs.picture->lheight();
      int block_width = pcv.maxCUWidth;
      int block_height = pcv.maxCUHeight;
    
      int width_in_blocks = width / block_width + (width % block_width != 0);
      int height_in_blocks = height / block_height + (height % block_height != 0);
    
      bifParams.numBlocks = width_in_blocks * height_in_blocks;
      bifParams.ctuOn.resize(bifParams.numBlocks);
    
      std::fill(bifParams.ctuOn.begin(), bifParams.ctuOn.end(), 0);
    
      // Currently no RDO to figure out if we should turn CTUs on or off
      bifParams.frmOn = 1;
      bifParams.allCtuOn = 1;
    
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      if( bifParams.frmOn == 0 )
      {
        std::fill( bifParams.ctuOn.begin(), bifParams.ctuOn.end(), 0 );
      }
      else if( bifParams.allCtuOn )
      {
        std::fill( bifParams.ctuOn.begin(), bifParams.ctuOn.end(), 1 );
      }
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    
      //double MseNoFltFrame = 0;
      //double MseFltDefFrame = 0;
      //double MseFltDefSwitchFrame = 0;
      //int CtuIdx = 0;
    #endif
    
    #if JVET_X0071_CHROMA_BILATERAL_FILTER
    
      if(cs.pps->getUseChromaBIF())
      {
    
        const PreCalcValues& pcv = *cs.pcv;
    
        BifParams& bifParamsCb = cs.picture->getBifParam( COMPONENT_Cb );
        BifParams& bifParamsCr = cs.picture->getBifParam( COMPONENT_Cr );
    
        int width = cs.picture->lwidth();
        int height = cs.picture->lheight();
    
        int blockWidth = pcv.maxCUWidth;
        int blockHeight = pcv.maxCUHeight;
    
        int widthInBlocks = width / blockWidth + (width % blockWidth != 0);
        int heightInBlocks = height / blockHeight + (height % blockHeight != 0);
    
        bifParamsCb.numBlocks = widthInBlocks * heightInBlocks;
        bifParamsCr.numBlocks = widthInBlocks * heightInBlocks;
    
        bifParamsCb.ctuOn.resize( bifParamsCb.numBlocks);
        bifParamsCr.ctuOn.resize( bifParamsCr.numBlocks);
        std::fill( bifParamsCb.ctuOn.begin(), bifParamsCb.ctuOn.end(), 0);
        std::fill( bifParamsCr.ctuOn.begin(), bifParamsCr.ctuOn.end(), 0);
        bifParamsCb.frmOn = 0;
        bifParamsCr.frmOn = 0;
        bifParamsCb.allCtuOn = 0;
        bifParamsCr.allCtuOn = 0;
    
        if ( bifParamsCb.frmOn == 0)
    
          std::fill( bifParamsCb.ctuOn.begin(), bifParamsCb.ctuOn.end(), 0 );
    
        else if ( bifParamsCb.allCtuOn)
    
          std::fill( bifParamsCb.ctuOn.begin(), bifParamsCb.ctuOn.end(), 1 );
    
        if ( bifParamsCr.frmOn == 0)
    
          std::fill( bifParamsCr.ctuOn.begin(), bifParamsCr.ctuOn.end(), 0 );
    
        else if ( bifParamsCr.allCtuOn)
    
          std::fill( bifParamsCr.ctuOn.begin(), bifParamsCr.ctuOn.end(), 1 );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      
    
    #if JVET_V0094_BILATERAL_FILTER
    
    #if JVET_X0071_CHROMA_BILATERAL_FILTER
        BilateralFilter bilateralFilter;
    
        if(!cs.sps->getSAOEnabledFlag() && (cs.pps->getUseBIF() || cs.pps->getUseChromaBIF()))
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          bilateralFilter.create();
          if( cs.pps->getUseBIF() )
          {
    
            bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Y, cs, src, bifCABACEstimator ); // Filters from src to res
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          }
          if( cs.pps->getUseChromaBIF() )
          {
            //Cb
    
            bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cb, cs, src, bifCABACEstimator );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
            //Cr
    
            bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cr, cs, src, bifCABACEstimator );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          }
          bilateralFilter.destroy();
          return;
    
        }
        memcpy(m_lambda, lambdas, sizeof(m_lambda));
    #else
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      BilateralFilter bilateralFilter;
      
      // Special case when SAO = 0 and BIF = 1.
      // Just filter reconstruction and return.
      // No need to estimate SAO parameters.
      if(!cs.sps->getSAOEnabledFlag() && cs.pps->getUseBIF())
      {
        bilateralFilter.create();
    
        bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Y, cs, src, bifCABACEstimator); // Filters from src to res
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        bilateralFilter.destroy();
        return;
      }
      memcpy(m_lambda, lambdas, sizeof(m_lambda));
    
    #endif
    #else
    #if JVET_X0071_CHROMA_BILATERAL_FILTER
    
      BilateralFilter bilateralFilter;
      if(!cs.sps->getSAOEnabledFlag() && cs.pps->getUseChromaBIF())
      {
        bilateralFilter.create();
        //Cb
    
        bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cb, cs, src, bifCABACEstimator );
    
        bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cr, cs, src, bifCABACEstimator );
    
        bilateralFilter.destroy();
        return;
      }
      memcpy(m_lambda, lambdas, sizeof(m_lambda));
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
      
    
    #if JVET_V0094_BILATERAL_FILTER
    
    #if JVET_X0071_CHROMA_BILATERAL_FILTER
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      if( cs.pps->getUseBIF() || cs.pps->getUseChromaBIF() )
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        if( cs.pps->getUseBIF() )
    
          bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Y, cs, src, bifCABACEstimator ); // Filters from src to res'
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        if( cs.pps->getUseChromaBIF() )
    
          bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cb, cs, src, bifCABACEstimator );
    
          bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cr, cs, src, bifCABACEstimator );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        getStatistics( m_statData, org, src, res, cs );
    
        bilateralFilter.destroy();
      }
      else
      {
        getStatistics(m_statData, org, src, src, cs);
      }
    #else
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      //apply BILAT to res
      if(cs.pps->getUseBIF())
      {
        bilateralFilter.create();
    
        bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Y, cs, src, bifCABACEstimator); // Filters from src to res
    
        getStatistics(m_statData, org, src, res, cs);
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        bilateralFilter.destroy();
      }
      else
      {
    
        getStatistics(m_statData, org, src, src, cs);
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      }
    
    #endif
    #else
    #if JVET_X0071_CHROMA_BILATERAL_FILTER
    
      if(cs.pps->getUseChromaBIF())
    
        bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cb, cs, src, bifCABACEstimator );
    
        bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cr, cs, src, bifCABACEstimator );
    
        getStatistics(m_statData, org, src, res, cs);
        bilateralFilter.destroy();
      }
      else
      {
        getStatistics(m_statData, org, src, src, cs);
      }
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #else
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
    
    
      if(isPreDBFSamplesUsed)
      {
        addPreDBFStatistics(m_statData);
      }
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      
    
    #if JVET_V0094_BILATERAL_FILTER
    
    #if JVET_X0071_CHROMA_BILATERAL_FILTER
    
      if(cs.pps->getUseBIF() || cs.pps->getUseChromaBIF())
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      //undo BILAT on res
      if(cs.pps->getUseBIF())
        res.copyFrom(src);
    
    #endif
    #else
    #if JVET_X0071_CHROMA_BILATERAL_FILTER
    
      if(cs.pps->getUseChromaBIF())
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
      
    
      //slice on/off
      decidePicParams(*cs.slice, sliceEnabled, saoEncodingRate, saoEncodingRateChroma);
    
      //block on/off
      std::vector<SAOBlkParam> reconParams(cs.pcv->sizeInCtus);
    
      decideBlkParams( cs, sliceEnabled, m_statData, src, res, &reconParams[0], cs.picture->getSAO(), bTestSAODisableAtPictureLevel,
    #if ENABLE_QPA
                       lambdaChromaWeight,
    #endif
                       saoEncodingRate, saoEncodingRateChroma, isGreedyMergeEncoding );
    
    
      DTRACE_UPDATE(g_trace_ctx, (std::make_pair("poc", cs.slice->getPOC())));
      DTRACE_PIC_COMP(D_REC_CB_LUMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Y);
      DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cb);
      DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cr);
    
      DTRACE    ( g_trace_ctx, D_CRC, "SAO" );
      DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.getRecoBuf() );
    
    }
    
    
    void EncSampleAdaptiveOffset::getPreDBFStatistics(CodingStructure& cs)
    {
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #if ALF_SAO_TRUE_ORG
      PelUnitBuf org = cs.getTrueOrgBuf();
    #else
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    #endif
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      getStatistics(m_preDBFstatData, org,
    
    #if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
    
    Vadim Seregin's avatar
    Vadim Seregin committed
                    rec, rec,
    #else
                    rec,
    #endif
                    
                    cs, true);
    
    }
    
    void EncSampleAdaptiveOffset::addPreDBFStatistics(std::vector<SAOStatData**>& blkStats)
    {
      const uint32_t numCTUsPic = (uint32_t)blkStats.size();
      for(uint32_t n=0; n< numCTUsPic; n++)
      {
        for(uint32_t compIdx=0; compIdx < MAX_NUM_COMPONENT; compIdx++)
        {
          for(uint32_t typeIdc=0; typeIdc < NUM_SAO_NEW_TYPES; typeIdc++)
          {
            blkStats[n][compIdx][typeIdc] += m_preDBFstatData[n][compIdx][typeIdc];
          }
        }
      }
    }
    
    
    Vadim Seregin's avatar
    Vadim Seregin committed
    void EncSampleAdaptiveOffset::getStatistics(std::vector<SAOStatData**>& blkStats, PelUnitBuf& orgYuv, PelUnitBuf& srcYuv,
    
    #if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
    
    Vadim Seregin's avatar
    Vadim Seregin committed
                                                PelUnitBuf& bifYuv,
    #endif
                                                CodingStructure& cs, bool isCalculatePreDeblockSamples)
    
    {
      bool isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail;
    
      const PreCalcValues& pcv = *cs.pcv;
      const int numberOfComponents = getNumberValidComponents(pcv.chrFormat);
    
      size_t lineBufferSize = pcv.maxCUWidth + 1;
      if (m_signLineBuf1.size() != lineBufferSize)
      {
        m_signLineBuf1.resize(lineBufferSize);
        m_signLineBuf2.resize(lineBufferSize);
      }
    
      int ctuRsAddr = 0;
      for( uint32_t yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUHeight )
      {
        for( uint32_t xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUWidth )
        {
          const uint32_t width  = (xPos + pcv.maxCUWidth  > pcv.lumaWidth)  ? (pcv.lumaWidth - xPos)  : pcv.maxCUWidth;
          const uint32_t height = (yPos + pcv.maxCUHeight > pcv.lumaHeight) ? (pcv.lumaHeight - yPos) : pcv.maxCUHeight;
          const UnitArea area( cs.area.chromaFormat, Area(xPos , yPos, width, height) );
    
          deriveLoopFilterBoundaryAvailibility(cs, area.Y(), isLeftAvail, isAboveAvail, isAboveLeftAvail );
    
          //NOTE: The number of skipped lines during gathering CTU statistics depends on the slice boundary availabilities.
          //For simplicity, here only picture boundaries are considered.
    
          isRightAvail      = (xPos + pcv.maxCUWidth  < pcv.lumaWidth );
          isBelowAvail      = (yPos + pcv.maxCUHeight < pcv.lumaHeight);
          isAboveRightAvail = ((yPos > 0) && (isRightAvail));
    
    
          int numHorVirBndry = 0, numVerVirBndry = 0;
          int horVirBndryPos[] = { -1,-1,-1 };
          int verVirBndryPos[] = { -1,-1,-1 };
    
          int horVirBndryPosComp[] = { -1,-1,-1 };
          int verVirBndryPosComp[] = { -1,-1,-1 };
    
    Brian Heng's avatar
    Brian Heng committed
          bool isCtuCrossedByVirtualBoundaries = isCrossedByVirtualBoundaries(xPos, yPos, width, height, numHorVirBndry, numVerVirBndry, horVirBndryPos, verVirBndryPos, cs.picHeader );
    
          for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
          {
            const ComponentID compID = ComponentID(compIdx);
            const CompArea& compArea = area.block( compID );
    
            int  srcStride  = srcYuv.get(compID).stride;
            Pel* srcBlk     = srcYuv.get(compID).bufAt( compArea );
    
            int  orgStride  = orgYuv.get(compID).stride;
            Pel* orgBlk     = orgYuv.get(compID).bufAt( compArea );
    
    
    #if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
    
    Vadim Seregin's avatar
    Vadim Seregin committed
            int  bifStride  = bifYuv.get(compID).stride;
            Pel* bifBlk     = bifYuv.get(compID).bufAt( compArea );
    #endif
            
    
              horVirBndryPosComp[i] = (horVirBndryPos[i] >> ::getComponentScaleY(compID, area.chromaFormat)) - compArea.y;
    
              verVirBndryPosComp[i] = (verVirBndryPos[i] >> ::getComponentScaleX(compID, area.chromaFormat)) - compArea.x;
    
            getBlkStats(compID, cs.sps->getBitDepth(toChannelType(compID)), blkStats[ctuRsAddr][compID]
    
    Vadim Seregin's avatar
    Vadim Seregin committed
                      , srcBlk, orgBlk,
    
    #if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
    
    Vadim Seregin's avatar
    Vadim Seregin committed
                        bifBlk, bifStride,
    #endif
                        srcStride, orgStride, compArea.width, compArea.height
    
                      , isLeftAvail,  isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail
                      , isCalculatePreDeblockSamples
    
                      , isCtuCrossedByVirtualBoundaries, horVirBndryPosComp, verVirBndryPosComp, numHorVirBndry, numVerVirBndry
    
                      );
          }
          ctuRsAddr++;
        }
      }
    }
    
    void EncSampleAdaptiveOffset::decidePicParams(const Slice& slice, bool* sliceEnabled, const double saoEncodingRate, const double saoEncodingRateChroma)
    {
      if ( slice.getPendingRasInit() )
      { // reset
        for (int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++)
        {
          for (int tempLayer = 1; tempLayer < MAX_TLAYER; tempLayer++)
          {
            m_saoDisabledRate[compIdx][tempLayer] = 0.0;
          }
        }
      }
    
      const int picTempLayer = slice.getDepth();
    
      //decide sliceEnabled[compIdx]
      const int numberOfComponents = m_numberOfComponents;
      for (int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++)
      {
        sliceEnabled[compIdx] = false;
      }
    
      for (int compIdx = 0; compIdx < numberOfComponents; compIdx++)
      {
        // reset flags & counters
        sliceEnabled[compIdx] = true;
    
        if (saoEncodingRate>0.0)
        {
          if (saoEncodingRateChroma>0.0)
          {
            // decide slice-level on/off based on previous results
            if( (picTempLayer > 0)
              && (m_saoDisabledRate[compIdx][picTempLayer-1] > ((compIdx==COMPONENT_Y) ? saoEncodingRate : saoEncodingRateChroma)) )
            {
              sliceEnabled[compIdx] = false;
            }
          }
          else
          {
            // decide slice-level on/off based on previous results
            if( (picTempLayer > 0)
              && (m_saoDisabledRate[COMPONENT_Y][0] > saoEncodingRate) )
            {
              sliceEnabled[compIdx] = false;
            }
          }
        }
      }
    }
    
    int64_t EncSampleAdaptiveOffset::getDistortion(const int channelBitDepth, int typeIdc, int typeAuxInfo, int* invQuantOffset, SAOStatData& statData)
    {
      int64_t dist        = 0;
      int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(channelBitDepth);
    
      switch(typeIdc)
      {
        case SAO_TYPE_EO_0:
        case SAO_TYPE_EO_90:
        case SAO_TYPE_EO_135:
        case SAO_TYPE_EO_45:
          {
            for (int offsetIdx=0; offsetIdx<NUM_SAO_EO_CLASSES; offsetIdx++)
            {
              dist += estSaoDist( statData.count[offsetIdx], invQuantOffset[offsetIdx], statData.diff[offsetIdx], shift);
            }
          }
          break;
        case SAO_TYPE_BO:
          {
            for (int offsetIdx=typeAuxInfo; offsetIdx<typeAuxInfo+4; offsetIdx++)
            {
              int bandIdx = offsetIdx % NUM_SAO_BO_CLASSES ;
              dist += estSaoDist( statData.count[bandIdx], invQuantOffset[bandIdx], statData.diff[bandIdx], shift);
            }
          }
          break;
        default:
          {
            THROW("Not a supported type");
          }
      }
    
      return dist;
    }
    
    inline int64_t EncSampleAdaptiveOffset::estSaoDist(int64_t count, int64_t offset, int64_t diffSum, int shift)
    {
      return (( count*offset*offset-diffSum*offset*2 ) >> shift);
    }
    
    
    inline int EncSampleAdaptiveOffset::estIterOffset(int typeIdx, double lambda, int offsetInput, int64_t count, int64_t diffSum, int shift, int bitIncrease, int64_t& bestDist, double& bestCost, int offsetTh )
    {
      int iterOffset, tempOffset;
      int64_t tempDist, tempRate;
      double tempCost, tempMinCost;
      int offsetOutput = 0;
      iterOffset = offsetInput;
      // Assuming sending quantized value 0 results in zero offset and sending the value zero needs 1 bit. entropy coder can be used to measure the exact rate here.
      tempMinCost = lambda;
      while (iterOffset != 0)
      {
        // Calculate the bits required for signaling the offset
        tempRate = (typeIdx == SAO_TYPE_BO) ? (abs((int)iterOffset)+2) : (abs((int)iterOffset)+1);
        if (abs((int)iterOffset)==offsetTh) //inclusive
        {
          tempRate --;
        }
        // Do the dequantization before distortion calculation
        tempOffset  = iterOffset << bitIncrease;
        tempDist    = estSaoDist( count, tempOffset, diffSum, shift);
        tempCost    = ((double)tempDist + lambda * (double) tempRate);
        if(tempCost < tempMinCost)
        {
          tempMinCost = tempCost;
          offsetOutput = iterOffset;
          bestDist = tempDist;
          bestCost = tempCost;
        }
        iterOffset = (iterOffset > 0) ? (iterOffset-1):(iterOffset+1);
      }
      return offsetOutput;
    }
    
    void EncSampleAdaptiveOffset::deriveOffsets(ComponentID compIdx, const int channelBitDepth, int typeIdc, SAOStatData& statData, int* quantOffsets, int& typeAuxInfo)
    {
      int bitDepth = channelBitDepth;
      int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(bitDepth);
      int offsetTh = SampleAdaptiveOffset::getMaxOffsetQVal(channelBitDepth);  //inclusive
    
      ::memset(quantOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES);
    
      //derive initial offsets
      int numClasses = (typeIdc == SAO_TYPE_BO)?((int)NUM_SAO_BO_CLASSES):((int)NUM_SAO_EO_CLASSES);
      for(int classIdx=0; classIdx< numClasses; classIdx++)
      {
        if( (typeIdc != SAO_TYPE_BO) && (classIdx==SAO_CLASS_EO_PLAIN)  )
        {
          continue; //offset will be zero
        }
    
        if(statData.count[classIdx] == 0)
        {
          continue; //offset will be zero
        }
    
        quantOffsets[classIdx] =
          (int) xRoundIbdi(bitDepth, (double)(statData.diff[classIdx] << DISTORTION_PRECISION_ADJUSTMENT(bitDepth))
                                       / (double)(statData.count[classIdx] << m_offsetStepLog2[compIdx]));
        quantOffsets[classIdx] = Clip3(-offsetTh, offsetTh, quantOffsets[classIdx]);
      }
    
      // adjust offsets
      switch(typeIdc)
      {
        case SAO_TYPE_EO_0:
        case SAO_TYPE_EO_90:
        case SAO_TYPE_EO_135:
        case SAO_TYPE_EO_45:
          {
            int64_t classDist;
            double classCost;
            for(int classIdx=0; classIdx<NUM_SAO_EO_CLASSES; classIdx++)
            {
              if(classIdx==SAO_CLASS_EO_FULL_VALLEY && quantOffsets[classIdx] < 0)
              {
                quantOffsets[classIdx] =0;
              }
              if(classIdx==SAO_CLASS_EO_HALF_VALLEY && quantOffsets[classIdx] < 0)
              {
                quantOffsets[classIdx] =0;
              }
              if(classIdx==SAO_CLASS_EO_HALF_PEAK   && quantOffsets[classIdx] > 0)
              {
                quantOffsets[classIdx] =0;
              }
              if(classIdx==SAO_CLASS_EO_FULL_PEAK   && quantOffsets[classIdx] > 0)
              {
                quantOffsets[classIdx] =0;
              }
    
              if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
              {
                quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], classDist , classCost , offsetTh );
              }
            }
    
            typeAuxInfo =0;
          }
          break;
        case SAO_TYPE_BO:
          {
            int64_t  distBOClasses[NUM_SAO_BO_CLASSES];
            double costBOClasses[NUM_SAO_BO_CLASSES];
            ::memset(distBOClasses, 0, sizeof(int64_t)*NUM_SAO_BO_CLASSES);
            for(int classIdx=0; classIdx< NUM_SAO_BO_CLASSES; classIdx++)
            {
              costBOClasses[classIdx]= m_lambda[compIdx];
              if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
              {
                quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], distBOClasses[classIdx], costBOClasses[classIdx], offsetTh );
              }
            }
    
            //decide the starting band index
            double minCost = MAX_DOUBLE, cost;
            for(int band=0; band< NUM_SAO_BO_CLASSES- 4+ 1; band++)
            {
              cost  = costBOClasses[band  ];
              cost += costBOClasses[band+1];
              cost += costBOClasses[band+2];
              cost += costBOClasses[band+3];
    
              if(cost < minCost)
              {
                minCost = cost;
                typeAuxInfo = band;
              }
            }
            //clear those unused classes
            int clearQuantOffset[NUM_SAO_BO_CLASSES];
            ::memset(clearQuantOffset, 0, sizeof(int)*NUM_SAO_BO_CLASSES);
            for(int i=0; i< 4; i++)
            {
              int band = (typeAuxInfo+i)%NUM_SAO_BO_CLASSES;
              clearQuantOffset[band] = quantOffsets[band];
            }
            ::memcpy(quantOffsets, clearQuantOffset, sizeof(int)*NUM_SAO_BO_CLASSES);
          }
          break;
        default:
          {
            THROW("Not a supported type");
          }
    
      }
    
    
    }
    
    void EncSampleAdaptiveOffset::deriveModeNewRDO(const BitDepths &bitDepths, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES], bool* sliceEnabled, std::vector<SAOStatData**>& blkStats, SAOBlkParam& modeParam, double& modeNormCost )
    {
      double minCost, cost;
      uint64_t previousFracBits;
      const int numberOfComponents = m_numberOfComponents;
    
      int64_t dist[MAX_NUM_COMPONENT], modeDist[MAX_NUM_COMPONENT];
      SAOOffset testOffset[MAX_NUM_COMPONENT];
      int invQuantOffset[MAX_NUM_SAO_CLASSES];
      for(int comp=0; comp < MAX_NUM_COMPONENT; comp++)
      {
        modeDist[comp] = 0;
      }
    
      //pre-encode merge flags
      modeParam[COMPONENT_Y].modeIdc = SAO_MODE_OFF;
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      const TempCtx ctxStartBlk   ( m_ctxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
    
      m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), true );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      const TempCtx ctxStartLuma  ( m_ctxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
      TempCtx       ctxBestLuma   ( m_ctxCache );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      //------ luma --------//
      const ComponentID compIdx = COMPONENT_Y;
      //"off" case as initial cost
      modeParam[compIdx].modeIdc = SAO_MODE_OFF;
      m_CABACEstimator->resetBits();
      m_CABACEstimator->sao_offset_pars( modeParam[compIdx], compIdx, sliceEnabled[compIdx], bitDepths.recon[CHANNEL_TYPE_LUMA] );
      modeDist[compIdx] = 0;
      minCost = m_lambda[compIdx] * (FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits());
      ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() );
      if( sliceEnabled[compIdx] )
    
    Vadim Seregin's avatar
    Vadim Seregin committed
        for( int typeIdc = 0; typeIdc < NUM_SAO_NEW_TYPES; typeIdc++ )
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          testOffset[compIdx].modeIdc = SAO_MODE_NEW;
          testOffset[compIdx].typeIdc = typeIdc;
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          //derive coded offset
          deriveOffsets( compIdx, bitDepths.recon[CHANNEL_TYPE_LUMA], typeIdc, blkStats[ctuRsAddr][compIdx][typeIdc], testOffset[compIdx].offset, testOffset[compIdx].typeAuxInfo );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          //inversed quantized offsets
          invertQuantOffsets( compIdx, typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, testOffset[compIdx].offset );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          //get distortion
          dist[compIdx] = getDistortion( bitDepths.recon[CHANNEL_TYPE_LUMA], testOffset[compIdx].typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][compIdx][typeIdc] );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
          //get rate
          m_CABACEstimator->getCtx() = SAOCtx( ctxStartLuma );
          m_CABACEstimator->resetBits();
          m_CABACEstimator->sao_offset_pars( testOffset[compIdx], compIdx, sliceEnabled[compIdx], bitDepths.recon[CHANNEL_TYPE_LUMA] );
          double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits();
          cost = ( double ) dist[compIdx] + m_lambda[compIdx] * rate;
          if( cost < minCost )
          {
            minCost = cost;
            modeDist[compIdx] = dist[compIdx];
            modeParam[compIdx] = testOffset[compIdx];
            ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma );
    
    Vadim Seregin's avatar
    Vadim Seregin committed
      //"off" case as initial cost
    
      cost = 0;
      previousFracBits = 0;
      m_CABACEstimator->resetBits();
      for(uint32_t componentIndex = COMPONENT_Cb; componentIndex < numberOfComponents; componentIndex++)