HM signBitHiding代碼閱讀與學習

Void TComTrQuant::signBitHidingHDQ( const ComponentID compID, TCoeff* pQCoef, TCoeff* pCoef, TCoeff* deltaU, const TUEntropyCodingParameters &codingParameters )
{
  const UInt width     = codingParameters.widthInGroups  << MLS_CG_LOG2_WIDTH;
  const UInt height    = codingParameters.heightInGroups << MLS_CG_LOG2_HEIGHT;
  const UInt groupSize = 1 << MLS_CG_SIZE; //groupSize通常都是16吧

  const TCoeff entropyCodingMinimum = -(1 << g_maxTrDynamicRange[toChannelType(compID)]);
  const TCoeff entropyCodingMaximum =  (1 << g_maxTrDynamicRange[toChannelType(compID)]) - 1;

  Int lastCG = -1; // lastCG = -1代表着最後一個非零係數一直沒有找到
  Int absSum = 0 ;
  Int n ;

  for( Int subSet = (width*height-1) >> MLS_CG_SIZE; subSet >= 0; subSet-- )
  { //這個大循環控制着CG遍歷的順序,從最後一個CG向前遍歷
    Int  subPos = subSet << MLS_CG_SIZE; // subPos就是CG的實際起始位置點
    Int  firstNZPosInCG=groupSize , lastNZPosInCG=-1 ;
    absSum = 0 ;
    //codingParameters.scan取出的位置與CG scanType有關係,可以關注下scanType成員
    for(n = groupSize-1; n >= 0; --n )
    {
      if( pQCoef[ codingParameters.scan[ n + subPos ]] )
      {
        lastNZPosInCG = n;//從一個CG的最後向前,找最後一個非零係數
        break;
      }
    }

    for(n = 0; n <groupSize; n++ )
    {
      if( pQCoef[ codingParameters.scan[ n + subPos ]] )
      {
        firstNZPosInCG = n////從一個CG的最前向後,找第一個非零係數
        break;
      }
    }

    for(n = firstNZPosInCG; n <=lastNZPosInCG; n++ )
    { //把非零係數的值加起來!不管正負,都叫absSum
      absSum += Int(pQCoef[ codingParameters.scan[ n + subPos ]]); //NOTE: RExt - only one bit is actually required!
    }

    if(lastNZPosInCG>=0 && lastCG==-1)
    {
      lastCG = 1 ;//更新lastCG的標誌位
    }
    //SBH_THRESHOLD = 4 即當前CG第一個非零係數與最後一個非零係數的位置(指編碼順序的位置,非座標位置)相差大於等於4,則做signbitHiding,否則,當前CG就結束了。
    if( lastNZPosInCG-firstNZPosInCG>=SBH_THRESHOLD )
    {
      UInt signbit = (pQCoef[codingParameters.scan[subPos+firstNZPosInCG]]>0?0:1) ;//如果CG中量化後的第一個非零係數爲正,signbit = 0;否則爲1
      if( signbit!=(absSum&0x1) )  //compare signbit with sum_parity
      {//如果CG中量化後的第一個非零係數爲正,非零係數的和爲奇數 或 CG中量化後的第一個非零係數爲負,非零係數的和爲偶數。這時候,第一個係數會被hiding成負值,爲了得到正確的符號結果,要有一個其他的係數做加1或減1操作,具體是誰,要通過RDO;
        TCoeff curCost    = std::numeric_limits<TCoeff>::max();
        TCoeff minCostInc = std::numeric_limits<TCoeff>::max();
        Int minPos =-1, finalChange=0, curChange=0;

        for( n = (lastCG==1?lastNZPosInCG:groupSize-1) ; n >= 0; --n )
        {//對於所有的非零係數,
          UInt blkPos   = codingParameters.scan[ n+subPos ];
          if(pQCoef[ blkPos ] != 0 )
          {
            if(deltaU[blkPos]>0)
            {
              curCost = - deltaU[blkPos];
              curChange=1 ;
            }
            else
            {
              //curChange =-1;
              if(n==firstNZPosInCG && abs(pQCoef[blkPos])==1)
              {
                curCost = std::numeric_limits<TCoeff>::max();
              }
              else
              {
                curCost = deltaU[blkPos];
                curChange =-1;
              }
            }
          }
          else
          {
            if(n<firstNZPosInCG)
            {
              UInt thisSignBit = (pCoef[blkPos]>=0?0:1);
              if(thisSignBit != signbit )
              {
                curCost = std::numeric_limits<TCoeff>::max();
              }
              else
              {
                curCost = - (deltaU[blkPos])  ;
                curChange = 1 ;
              }
            }
            else
            {
              curCost = - (deltaU[blkPos])  ;
              curChange = 1 ;
            }
          }

          if( curCost<minCostInc)
          {
            minCostInc = curCost ;
            finalChange = curChange ;
            minPos = blkPos ;
          }
        } //CG loop
        // 以上for循環爲RDO抉擇最優finalChange符號和Position的過程。抉擇的依據是之前預存的deltaU[blkPos]。
        //deltaU[blkPos]可以理解爲當前量化結果做完+1 -1調整後,新加入的量化的誤差,這個誤差當然越小越好。+代表量化結果可以考慮向上做+1偏移;爲-則向下做偏移。由於不同係數量化的誤差不一,所以存在最優的量化調整的係數,使得既能實現+-1操作,cost的損失又最小。
        if(pQCoef[minPos] == entropyCodingMaximum || pQCoef[minPos] == entropyCodingMinimum)
        {
          finalChange = -1;
        }

        if(pCoef[minPos]>=0)
        {
          pQCoef[minPos] += finalChange ;
        }
        else
        {
          pQCoef[minPos] -= finalChange ;
        }
      } // Hide
    }
    if(lastCG==1)
    {
      lastCG=0 ;
    }
  } // TU loop

  return;
}

Sign Bit Hiding ,據說是變換系數編碼中的符號位隱藏,可以帶來coding gain。

通過對每個CG單元量化結果的絕對值和的奇偶性,對編碼順序中第一個非零係數的符號判斷,來實現符號位的隱藏,每個CG就可以節省1bit (第一個非零係數的符號位不用編碼了)。

HEVC文檔裏說,

The transform coefficient coding in HEVC includes an option to omit the coding ofone sign flag per TSB(就是CG, coefficient group) if a sufficient number of significant coefficients is present in theblock. The scheme is called sign data hiding. Since the sign flags can be consideredto be uncorrelated, these flags are coded in CABAC bypass mode at the cost of 1 bit(see Sect.10.2). Hence, sign data hiding saves one bit per TSB when applied. The use of sign data hiding is configured by a flag in the PPS. 

SBH是利用裏變換系數本身的不相關性,其編碼是用bypass模式編碼。使用標識爲在PPS裏。

當CG中的significant coefficient超過了3個(看代碼裏是編碼順序的 最後一個非零係數與第一個非零係數之間相距超過了3個)則第一個非零係數(掃描序號最小的那個)的符號位不用再編碼了,可以通過塊內所有係數的絕對值的和的奇偶性來確定。


coeff[0]的符號通過:

coeff[0] = (1 -  ((absSum) % 2)) *| coeff[0] |

也就是說:

當CG係數絕對值的和爲奇數,則coeff[0]就會被判斷成負值。

當CG係數絕對值的和爲偶數,則coeff[0]就會被判斷成正值。

如果實際的coeff[0]的符號和CG係數絕對值和的奇偶性不相符,則需要通過對其他的某一個係數做+1 -1操作,是absSum的奇偶性與coeff[0]的符號一致,但具體是哪個係數,做+1還是-1,則需要經過RDO的過程。

deltaU預存了每個係數與其相鄰量化結果之間的一定意義上的誤差(不知道是哪種意義,但是代表着量化誤差就跑不了了)。

也就是說,做了+ 1 或 - 1,誤差是有的,就看誰調完誤差最小,那麼就調誰了。

下圖的塊,絕對值和是49,而第一個係數13是正值,所以需要做調整,如把1->0

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章