H.266/VVC technology learning: Joint Chroma Coding (JCCR) technology

VVC supports joint coding of chroma residual (JCCR) mode.

The use (activation) of the chroma joint coding mode is indicated by the TU level flag tu_joint_cbcr_residual_flag , and the selected mode is implicitly indicated by the chroma CBFs. If one or both of the chrominance CBFs of one TU are equal to 1 (tu_cbf_cb or tu_cbf_cr), there is a flag tu_joint_cbcr_residual_flag .

In PPS and slice head, the chrominance QP offset value in the joint chrominance residual coding mode is different from the chrominance QP offset value in the conventional chrominance residual coding mode. These chrominance QP offset values ​​are used to derive The chrominance QP value of the block coded using the joint chrominance residual coding mode.

The JCCR mode has 3 sub-modes. When the corresponding joint chrominance coding mode (mode 2 in the table) is activated in the TU, when the TU is quantized and decoded, the chrominance QP offset is added to the applied luminance-derived chrominance QP (the applied luma-derived chroma QP). For other modes (mode 1 and mode 3 in the table), the derivation method of chrominance QPs is the same as that of conventional Cb or Cr blocks.

The reconstruction process of the chrominance residual (resCb and resCr) of the transmission transform block is shown in the table. When the JCCR mode is activated, a joint chrominance residual block (resJointC [x] [y] is passed, and then the residual block Cb (resCb) and residual are derived by considering various information (such as tu_cbf_cb tu_cbf_cr, CSign) Difference block Cr (resCr).

On the encoder side, the joint chrominance component is derived as follows. According to the mode (as shown in the table above), the encoder generates resJointC{1,2} as follows:

If mode = 2 (single residual Cb = C, Cr = CSign * C after reconstruction), then according to

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

Otherwise, if mode = 1 (the reconstructed single residual Cb = C, Cr = (CSign * C) / 2), then according to

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

Otherwise (mode = 3, the reconstructed single residual Cr = C, Cb = (CSign * C) / 2), according to

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

 

tu_cbf_cb

tu_cbf_cr

reconstruction of Cb and Cr residuals

mode

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

The above three joint chroma coding modes are only supported on the I-chip. In P and B films, only mode 2 is supported. Therefore, in P and B slices, the syntax element tu_joint_cbcr_residual_flag only exists when the chroma cbfs are both 1 (that is, tu_cbf_cb=1 and tu_cbf_cr=1).

The JCCR mode can be combined with the Transform Skip (TS) mode. In order to speed up the selection speed of the coding end, the choice of JCCR transform depends on whether the independent coding of Cb and Cr components selects DCT-2 or TS as the best transform, and whether there are non-zero coefficients in the independent chrominance coding. Specifically divided into the following two situations:

(1) When both Cb and Cr components use DCT-2 or when only one chrominance component (Cb or Cr) has a non-zero coefficient and the chrominance component selects DCT-2, the JCCR mode only checks DCT-2. Similarly, when both Cb and Cr components use TS, or when only one chroma component has a non-zero coefficient and the component selects TS, the JCCR mode only checks TS.

(2) When the selective transformations of Cb and Cr components are different, that is, when one chooses DCT-2 and the other chooses TS or vice versa, JCCR tests both DCT-2 and TS. In these cases, the RD check of the DCT-2 mode is performed before the TS mode, and if the coefficients of DCT-2 are all zero, the TS RD check is skipped.

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>;
  }
}

The invTransformCbCr function is as follows, the function is mainly to obtain the Cr (Cb) residual through the Cb (Cr) residual

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; }
    }
  }
}

The fwdTransformCbCr function is to find ResJointC through ResCb and ResCr

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);
}

 

Guess you like

Origin blog.csdn.net/BigDream123/article/details/106267476