H.266 / VVCテクノロジーの学習:Joint Chroma Coding(JCCR)テクノロジー

VVCは、彩度残差(JCCR)モードの共同コーディングをサポートします。

クロマジョイントコーディングモードの使用(アクティブ化)は、TUレベルフラグtu_joint_cbcr_residual_flagによって示さ、選択されたモードは、クロマCBFによって暗黙的に示されます。1つのTUのクロミナンスCBFの一方または両方が1(tu_cbf_cbまたはtu_cbf_cr)に等しい場合、フラグtu_joint_cbcr_residual_flagがあります。

PPSとスライスヘッドでは、ジョイントクロミナンス残差コーディングモードのクロミナンスQPオフセット値は、従来のクロミナンス残差コーディングモードのクロミナンスQPオフセット値とは異なります。これらのクロミナンスQPオフセット値は、のクロミナンスQP値を導出するために使用されます。ジョイントクロミナンス残差コーディングモードを使用してコーディングされたブロック。

JCCRモードには3つのサブモードがあります。対応するジョイントクロミナンスコーディングモード(表のモード2)がTUでアクティブ化されている場合、TUが量子化およびデコードされると、クロミナンスQPオフセットが適用された輝度由来のクロミナンスQP(適用された輝度由来のクロマQP)に追加されます。 )。他のモード(表のモード1およびモード3)の場合、クロミナンスQPの導出方法は、従来のCbまたはCrブロックの導出方法と同じです。

透過変換ブロックのクロミナンス残差(resCbおよびresCr)の再構成プロセスを表に示します。JCCRモードがアクティブになると、ジョイントクロミナンス残差ブロック(resJointC [x] [y]が渡され、さまざまな情報(tu_cbf_cb tu_cbf_cr、CSignなど)を考慮して、残差ブロックCb(resCb)および残差差分ブロックCrが渡されます。 (resCr)。

エンコーダ側では、ジョイントクロミナンス成分は次のように導出されます。モード(上記の表に示されている)に従って、エンコーダーは次のようにresJointC {1,2}を生成します。

モード= 2(単一の残差Cb = C、再構成後のCr = CSign * C)の場合、

   -resJointC [x] [y] =(resCb [x] [y] + CSign * resCr [x] [y])/ 2。

それ以外の場合、モード= 1(再構成された単一の残差Cb = C、Cr =(CSign * C)/ 2)の場合、

   -resJointC [x] [y] =(4 * resCb [x] [y] + 2 * CSign * resCr [x] [y])/ 5。

それ以外の場合(モード= 3、再構成された単一の残差Cr = C、Cb =(CSign * C)/ 2)、

   -resJointC [x] [y] =(4 * resCr [x] [y] + 2 * CSign * resCb [x] [y])/ 5。

 

tu_cbf_cb

tu_cbf_cr

CbおよびCr残差の再構築

モード

1

0

resCb [x] [y] = resJointC [x] [y]
resCr [x] [y] =(CSign * resJointC [x] [y])>> 1

1

1

1

resCb [x] [y] = resJointC [x] [y]
resCr [x] [y] = CSign * resJointC [x] [y]

2

0

1

resCb [x] [y] =(CSign * resJointC [x] [y])>> 1
resCr [x] [y] = resJointC [x] [y]

3

上記の3つのジョイントクロマコーディングモードは、Iチップでのみサポートされています。PおよびBフィルムでは、モード2のみがサポートされています。したがって、PスライスとBスライスでは、構文要素tu_joint_cbcr_residual_flagは、彩度cbfsが両方とも1(つまり、tu_cbf_cb = 1とtu_cbf_cr = 1)の場合にのみ存在します。

JCCRモードは、変換スキップ(TS)モードと組み合わせることができます。エンコード側の選択速度を高速化するために、JCCR変換の選択は、CbおよびCrコンポーネントの独立したエンコードがDCT-2またはTSを最良の変換として選択するかどうか、およびゼロ以外の係数があるかどうかによって異なります。独立したクロミナンスエンコーディングで。具体的には、次の2つの状況に分けられます。

(1)Cb成分とCr成分の両方がDCT-2を使用する場合、または1つのクロミナンス成分(CbまたはCr)のみがゼロ以外の係数を持ち、クロミナンス成分がDCT-2を選択する場合、JCCRモードはDCT-2のみをチェックします。同様に、CbコンポーネントとCrコンポーネントの両方がTSを使用する場合、または1つの彩度コンポーネントのみがゼロ以外の係数を持ち、コンポーネントがTSを選択する場合、JCCRモードはTSのみをチェックします。

(2)Cb成分とCr成分の選択的変換が異なる場合、つまり、一方がDCT-2を選択し、もう一方がTSを選択する場合、またはその逆の場合、JCCRはDCT-2とTSの両方をテストします。このような場合、DCT-2モードのRDチェックはTSモードの前に実行され、DCT-2の係数がすべてゼロの場合、TSRDチェックはスキップされます。

TrQuant::TrQuant() : m_quant( nullptr )
{
  // allocate temporary buffers

  {
    m_invICT      = m_invICTMem + maxAbsIctMode;
    m_invICT[ 0]  = invTransformCbCr< 0>;
    m_invICT[ 1]  = invTransformCbCr< 1>;
    m_invICT[-1]  = invTransformCbCr<-1>;
    m_invICT[ 2]  = invTransformCbCr< 2>;
    m_invICT[-2]  = invTransformCbCr<-2>;
    m_invICT[ 3]  = invTransformCbCr< 3>;
    m_invICT[-3]  = invTransformCbCr<-3>;
    m_fwdICT      = m_fwdICTMem + maxAbsIctMode;
    m_fwdICT[ 0]  = fwdTransformCbCr< 0>;
    m_fwdICT[ 1]  = fwdTransformCbCr< 1>;
    m_fwdICT[-1]  = fwdTransformCbCr<-1>;
    m_fwdICT[ 2]  = fwdTransformCbCr< 2>;
    m_fwdICT[-2]  = fwdTransformCbCr<-2>;
    m_fwdICT[ 3]  = fwdTransformCbCr< 3>;
    m_fwdICT[-3]  = fwdTransformCbCr<-3>;
  }
}

invTransformCbCr関数は次のとおりです。この関数は、主にCb(Cr)残差からCr(Cb)残差を取得するためのものです。

template<int signedMode> void invTransformCbCr( PelBuf &resCb, PelBuf &resCr )
{
  Pel*  cb  = resCb.buf;
  Pel*  cr  = resCr.buf;
  for( SizeType y = 0; y < resCb.height; y++, cb += resCb.stride, cr += resCr.stride )
  {
    for( SizeType x = 0; x < resCb.width; x++ )
    {
      if      ( signedMode ==  1 )  { cr[x] =  cb[x] >> 1;  }
      else if ( signedMode == -1 )  { cr[x] = -cb[x] >> 1;  }
      else if ( signedMode ==  2 )  { cr[x] =  cb[x]; }
      else if ( signedMode == -2 )  { cr[x] = (cb[x] == -32768 && sizeof(Pel) == 2) ? 32767 : -cb[x]; }   // non-normative clipping to prevent 16-bit overflow
      else if ( signedMode ==  3 )  { cb[x] =  cr[x] >> 1; }
      else if ( signedMode == -3 )  { cb[x] = -cr[x] >> 1; }
    }
  }
}

fwdTransformCbCr関数は、ResCbおよびResCrを介してResJointCを検索することです。

template<int signedMode> std::pair<int64_t,int64_t> fwdTransformCbCr( const PelBuf &resCb, const PelBuf &resCr, PelBuf& resC1, PelBuf& resC2 )
{
  const Pel*  cb  = resCb.buf;
  const Pel*  cr  = resCr.buf;
  Pel*        c1  = resC1.buf;
  Pel*        c2  = resC2.buf;
  int64_t     d1  = 0;
  int64_t     d2  = 0;
  for( SizeType y = 0; y < resCb.height; y++, cb += resCb.stride, cr += resCr.stride, c1 += resC1.stride, c2 += resC2.stride )
  {
    for( SizeType x = 0; x < resCb.width; x++ )
    {
      int cbx = cb[x], crx = cr[x];
      if      ( signedMode ==  1 )
      {
        c1[x] = Pel( ( 4*cbx + 2*crx ) / 5 );
        d1   += square( cbx - c1[x] ) + square( crx - (c1[x]>>1) );
      }
      else if ( signedMode == -1 )
      {
        c1[x] = Pel( ( 4*cbx - 2*crx ) / 5 );
        d1   += square( cbx - c1[x] ) + square( crx - (-c1[x]>>1) );
      }
      else if ( signedMode ==  2 )
      {
        c1[x] = Pel( ( cbx + crx ) / 2 );
        d1   += square( cbx - c1[x] ) + square( crx - c1[x] );
      }
      else if ( signedMode == -2 )
      {
        c1[x] = Pel( ( cbx - crx ) / 2 );
        d1   += square( cbx - c1[x] ) + square( crx + c1[x] );
      }
      else if ( signedMode ==  3 )
      {
        c2[x] = Pel( ( 4*crx + 2*cbx ) / 5 );
        d1   += square( cbx - (c2[x]>>1) ) + square( crx - c2[x] );
      }
      else if ( signedMode == -3 )
      {
        c2[x] = Pel( ( 4*crx - 2*cbx ) / 5 );
        d1   += square( cbx - (-c2[x]>>1) ) + square( crx - c2[x] );
      }
      else
      {
        d1   += square( cbx );
        d2   += square( crx );
      }
    }
  }
  return std::make_pair(d1,d2);
}

 

おすすめ

転載: blog.csdn.net/BigDream123/article/details/106267476