Skip to content
Snippets Groups Projects
WeightPrediction.cpp 14.2 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-2018, 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     WeightPrediction.h
        \brief    weighting prediction class (header)
    */
    
    // Include files
    #include "CommonDef.h"
    #include "Unit.h"
    #include "InterpolationFilter.h"
    #include "WeightPrediction.h"
    #include "CodingStructure.h"
    
    
    static inline Pel weightBidir( int w0, Pel P0, int w1, Pel P1, int round, int shift, int offset, const ClpRng& clpRng)
    {
      return ClipPel( ( (w0*(P0 + IF_INTERNAL_OFFS) + w1*(P1 + IF_INTERNAL_OFFS) + round + (offset << (shift-1))) >> shift ), clpRng );
    }
    
    static inline Pel weightUnidir( int w0, Pel P0, int round, int shift, int offset, const ClpRng& clpRng)
    {
      return ClipPel( ( (w0*(P0 + IF_INTERNAL_OFFS) + round) >> shift ) + offset, clpRng );
    }
    
    static inline Pel noWeightUnidir( Pel P0, int round, int shift, int offset, const ClpRng& clpRng)
    {
      return ClipPel( ( ((P0 + IF_INTERNAL_OFFS) + round) >> shift ) + offset, clpRng );
    }
    
    static inline Pel noWeightOffsetUnidir( Pel P0, int round, int shift, const ClpRng& clpRng)
    {
      return ClipPel( ( ((P0 + IF_INTERNAL_OFFS) + round) >> shift ), clpRng );
    }
    
    
    // ====================================================================================================================
    // Class definition
    // ====================================================================================================================
    
    WeightPrediction::WeightPrediction()
    {
    }
    
    
    
    void  WeightPrediction::getWpScaling(const Slice                *pcSlice,
                                         const int                  &iRefIdx0,
                                         const int                  &iRefIdx1,
                                               WPScalingParam      *&wp0,
                                               WPScalingParam      *&wp1,
                                         const ComponentID           maxNumComp)
    {
      CHECK(iRefIdx0 < 0 && iRefIdx1 < 0, "Both picture reference list indizes smaller than '0'");
    
      const bool wpBiPred        = pcSlice->getPPS()->getWPBiPred();
      const bool bBiPred         = (iRefIdx0 >= 0 && iRefIdx1 >= 0);
      const bool bUniPred        = !bBiPred;
    
      if (bUniPred || wpBiPred)
      {
        // explicit --------------------
        if (iRefIdx0 >= 0)
        {
          pcSlice->getWpScaling(REF_PIC_LIST_0, iRefIdx0, wp0);
        }
        if (iRefIdx1 >= 0)
        {
          pcSlice->getWpScaling(REF_PIC_LIST_1, iRefIdx1, wp1);
        }
      }
      else
      {
        THROW( "Unsupported WP configuration" );
      }
    
      if (iRefIdx0 < 0)
      {
        wp0 = NULL;
      }
      if (iRefIdx1 < 0)
      {
        wp1 = NULL;
      }
    
      const uint32_t numValidComponent = getNumberValidComponents(pcSlice->getSPS()->getChromaFormatIdc());
      const bool bUseHighPrecisionPredictionWeighting = pcSlice->getSPS()->getSpsRangeExtension().getHighPrecisionOffsetsEnabledFlag();
    
      if (bBiPred)
      {
        // Bi-predictive case
        for (int yuv = 0; yuv < numValidComponent && yuv <= maxNumComp; yuv++)
        {
          const int bitDepth = pcSlice->getSPS()->getBitDepth(toChannelType(ComponentID(yuv)));
          const int offsetScalingFactor = bUseHighPrecisionPredictionWeighting ? 1 : (1 << (bitDepth - 8));
    
          wp0[yuv].w = wp0[yuv].iWeight;
          wp1[yuv].w = wp1[yuv].iWeight;
          wp0[yuv].o = wp0[yuv].iOffset * offsetScalingFactor;
          wp1[yuv].o = wp1[yuv].iOffset * offsetScalingFactor;
          wp0[yuv].offset = wp0[yuv].o + wp1[yuv].o;
          wp0[yuv].shift = wp0[yuv].uiLog2WeightDenom + 1;
          wp0[yuv].round = (1 << wp0[yuv].uiLog2WeightDenom);
          wp1[yuv].offset = wp0[yuv].offset;
          wp1[yuv].shift = wp0[yuv].shift;
          wp1[yuv].round = wp0[yuv].round;
        }
      }
      else
      {
        // UniPred
        WPScalingParam *const pwp = (iRefIdx0 >= 0) ? wp0 : wp1;
    
        for (int yuv = 0; yuv < numValidComponent && yuv <= maxNumComp; yuv++)
        {
          const int bitDepth            = pcSlice->getSPS()->getBitDepth(toChannelType(ComponentID(yuv)));
          const int offsetScalingFactor = bUseHighPrecisionPredictionWeighting ? 1 : (1 << (bitDepth - 8));
    
          pwp[yuv].w      = pwp[yuv].iWeight;
          pwp[yuv].offset = pwp[yuv].iOffset * offsetScalingFactor;
          pwp[yuv].shift  = pwp[yuv].uiLog2WeightDenom;
          pwp[yuv].round  = (pwp[yuv].uiLog2WeightDenom >= 1) ? (1 << (pwp[yuv].uiLog2WeightDenom - 1)) : (0);
        }
      }
    }
    
    void WeightPrediction::addWeightBi(const CPelUnitBuf          &pcYuvSrc0,
                                       const CPelUnitBuf          &pcYuvSrc1,
                                       const ClpRngs              &clpRngs,
                                       const WPScalingParam *const wp0,
                                       const WPScalingParam *const wp1,
                                             PelUnitBuf           &rpcYuvDst,
                                       const bool                  bRoundLuma /*= true*/,
                                       const ComponentID           maxNumComp)
    {
      const bool enableRounding[MAX_NUM_COMPONENT] = { bRoundLuma, true, true };
    
      const uint32_t numValidComponent = (const uint32_t)pcYuvSrc0.bufs.size();
    
      for (int componentIndex = 0; componentIndex < numValidComponent && componentIndex <= maxNumComp; componentIndex++)
      {
        const ComponentID compID = ComponentID(componentIndex);
    
        const Pel* pSrc0 = pcYuvSrc0.bufs[compID].buf;
        const Pel* pSrc1 = pcYuvSrc1.bufs[compID].buf;
              Pel* pDst  = rpcYuvDst.bufs[compID].buf;
    
        // Luma : --------------------------------------------
        const ClpRng& clpRng = clpRngs.comp[compID];
        const int  w0       = wp0[compID].w;
        const int  offset   = wp0[compID].offset;
        const int  clipBD   = clpRng.bd;
        const int  shiftNum = std::max<int>(2, (IF_INTERNAL_PREC - clipBD));
        const int  shift    = wp0[compID].shift + shiftNum;
        const int  round    = (enableRounding[compID] && (shift > 0)) ? (1 << (shift - 1)) : 0;
        const int  w1       = wp1[compID].w;
        const int  iHeight  = rpcYuvDst.bufs[compID].height;
        const int  iWidth   = rpcYuvDst.bufs[compID].width;
    
        const uint32_t iSrc0Stride = pcYuvSrc0.bufs[compID].stride;
        const uint32_t iSrc1Stride = pcYuvSrc1.bufs[compID].stride;
        const uint32_t iDstStride =  rpcYuvDst.bufs[compID].stride;
    
        for (int y = iHeight - 1; y >= 0; y--)
        {
          // do it in batches of 4 (partial unroll)
          int x = iWidth - 1;
    
          for (; x >= 3; )
          {
            pDst[x] = weightBidir(w0, pSrc0[x], w1, pSrc1[x], round, shift, offset, clpRng ); x--;
            pDst[x] = weightBidir(w0, pSrc0[x], w1, pSrc1[x], round, shift, offset, clpRng ); x--;
            pDst[x] = weightBidir(w0, pSrc0[x], w1, pSrc1[x], round, shift, offset, clpRng ); x--;
            pDst[x] = weightBidir(w0, pSrc0[x], w1, pSrc1[x], round, shift, offset, clpRng ); x--;
          }
          for (; x >= 0; x--)
          {
            pDst[x] = weightBidir(w0, pSrc0[x], w1, pSrc1[x], round, shift, offset, clpRng );
          }
    
          pSrc0 += iSrc0Stride;
          pSrc1 += iSrc1Stride;
          pDst += iDstStride;
        } // y loop
      } // compID loop
    }
    
    void  WeightPrediction::addWeightUni(const CPelUnitBuf          &pcYuvSrc0,
                                         const ClpRngs              &clpRngs,
                                         const WPScalingParam *const wp0,
                                               PelUnitBuf           &rpcYuvDst,
                                         const ComponentID           maxNumComp)
    {
      const uint32_t numValidComponent = (const uint32_t)pcYuvSrc0.bufs.size();
    
      for (int componentIndex = 0; componentIndex < numValidComponent && componentIndex <= maxNumComp; componentIndex++)
      {
        const ComponentID compID = ComponentID(componentIndex);
    
        const Pel* pSrc0 = pcYuvSrc0.bufs[compID].buf;
              Pel* pDst  = rpcYuvDst.bufs[compID].buf;
    
        // Luma : --------------------------------------------
        const ClpRng& clpRng    = clpRngs.comp[compID];
        const int  w0           = wp0[compID].w;
        const int  offset       = wp0[compID].offset;
        const int  clipBD       = clpRng.bd;
        const int  shiftNum     = std::max<int>(2, (IF_INTERNAL_PREC - clipBD));
        const int  shift        = wp0[compID].shift + shiftNum;
        const uint32_t iSrc0Stride  = pcYuvSrc0.bufs[compID].stride;
        const uint32_t iDstStride   = rpcYuvDst.bufs[compID].stride;
        const int  iHeight      = rpcYuvDst.bufs[compID].height;
        const int  iWidth       = rpcYuvDst.bufs[compID].width;
    
        if (w0 != 1 << wp0[compID].shift)
        {
          const int  round = (shift > 0) ? (1 << (shift - 1)) : 0;
          for (int y = iHeight - 1; y >= 0; y--)
          {
            int x = iWidth - 1;
            for (; x >= 3; )
            {
              pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng); x--;
              pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng); x--;
              pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng); x--;
              pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng); x--;
            }
            for (; x >= 0; x--)
            {
              pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng);
            }
            pSrc0 += iSrc0Stride;
            pDst += iDstStride;
          }
        }
        else
        {
          const int  round = (shiftNum > 0) ? (1 << (shiftNum - 1)) : 0;
          if (offset == 0)
          {
            for (int y = iHeight - 1; y >= 0; y--)
            {
              int x = iWidth - 1;
              for (; x >= 3; )
              {
                pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng); x--;
                pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng); x--;
                pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng); x--;
                pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng); x--;
              }
              for (; x >= 0; x--)
              {
                pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng);
              }
              pSrc0 += iSrc0Stride;
              pDst += iDstStride;
            }
          }
          else
          {
            for (int y = iHeight - 1; y >= 0; y--)
            {
              int x = iWidth - 1;
              for (; x >= 3; )
              {
                pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng); x--;
                pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng); x--;
                pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng); x--;
                pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng); x--;
              }
              for (; x >= 0; x--)
              {
                pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng);
              }
              pSrc0 += iSrc0Stride;
              pDst += iDstStride;
            }
          }
        }
      }
    }
    
    void  WeightPrediction::xWeightedPredictionUni(const PredictionUnit       &pu,
                                                   const CPelUnitBuf          &pcYuvSrc,
                                                   const RefPicList           &eRefPicList,
                                                         PelUnitBuf           &pcYuvPred,
                                                   const int                   iRefIdx_input/* = -1*/,
                                                   const ComponentID           maxNumComp)
    {
      WPScalingParam  *pwp, *pwpTmp;
    
      int iRefIdx = iRefIdx_input;
      if (iRefIdx < 0)
      {
        iRefIdx = pu.refIdx[eRefPicList];
      }
    
      CHECK(iRefIdx < 0, "Negative reference picture list index");
    
      if (eRefPicList == REF_PIC_LIST_0)
      {
        getWpScaling(pu.cs->slice, iRefIdx, -1, pwp, pwpTmp, maxNumComp);
      }
      else
      {
        getWpScaling(pu.cs->slice, -1, iRefIdx, pwpTmp, pwp, maxNumComp);
      }
      addWeightUni(pcYuvSrc, pu.cu->slice->clpRngs(), pwp, pcYuvPred, maxNumComp);
    }
    
    void  WeightPrediction::xWeightedPredictionBi(const PredictionUnit       &pu,
                                                  const CPelUnitBuf          &pcYuvSrc0,
                                                  const CPelUnitBuf          &pcYuvSrc1,
                                                        PelUnitBuf           &rpcYuvDst,
                                                  const ComponentID           maxNumComp)
    {
      const int iRefIdx0 = pu.refIdx[0];
      const int iRefIdx1 = pu.refIdx[1];
      WPScalingParam  *pwp0;
      WPScalingParam  *pwp1;
    
      CHECK( !pu.cs->pps->getWPBiPred(), "Weighted Bi-prediction disabled" );
    
      getWpScaling(pu.cu->slice, iRefIdx0, iRefIdx1, pwp0, pwp1, maxNumComp);
    
      if (iRefIdx0 >= 0 && iRefIdx1 >= 0)
      {
        addWeightBi(pcYuvSrc0, pcYuvSrc1, pu.cu->slice->clpRngs(), pwp0, pwp1, rpcYuvDst, true, maxNumComp);
      }
      else if (iRefIdx0 >= 0 && iRefIdx1 < 0)
      {
        addWeightUni(pcYuvSrc0, pu.cu->slice->clpRngs(), pwp0, rpcYuvDst, maxNumComp);
      }
      else if (iRefIdx0 < 0 && iRefIdx1 >= 0)
      {
        addWeightUni(pcYuvSrc1, pu.cu->slice->clpRngs(), pwp1, rpcYuvDst, maxNumComp);
      }
      else
      {
        THROW( "Both reference picture list indizes are negative" );
      }
    }