Lernen der H.266 / VVC-Technologie: Joint Chroma Coding (JCCR) -Technologie

VVC unterstützt die gemeinsame Codierung des Chroma Residual (JCCR) -Modus.

Die Verwendung (Aktivierung) des Chroma-Joint-Codierungsmodus wird durch das TU-Level-Flag tu_joint_cbcr_residual_flag angezeigt , und der ausgewählte Modus wird implizit durch die Chroma-CBFs angezeigt. Wenn einer oder beide der Chrominanz-CBFs einer TU gleich 1 sind (tu_cbf_cb oder tu_cbf_cr), gibt es ein Flag tu_joint_cbcr_residual_flag .

In PPS und Slice Head unterscheidet sich der Chrominanz-QP-Versatzwert im gemeinsamen Chrominanz-Restcodierungsmodus vom Chrominanz-QP-Versatzwert im herkömmlichen Chrominanz-Restcodierungsmodus. Diese Chrominanz-QP-Versatzwerte werden verwendet, um den Chrominanz-QP-Wert von abzuleiten der Block, der unter Verwendung des gemeinsamen Codierungsmodus für die gemeinsame Chrominanz codiert wurde.

Der JCCR-Modus verfügt über 3 Untermodi. Wenn der entsprechende gemeinsame Chrominanzcodierungsmodus (Modus 2 in der Tabelle) in der TU aktiviert ist, wenn die TU quantisiert und decodiert wird, wird der Chrominanz-QP-Offset zum angewendeten Luminanz-abgeleiteten Chrominanz-QP (dem angewendeten Luma-abgeleiteten Chroma-QP) addiert ). Für andere Modi (Modus 1 und Modus 3 in der Tabelle) ist die Ableitungsmethode von Chrominanz-QPs dieselbe wie die von herkömmlichen Cb- oder Cr-Blöcken.

Der Rekonstruktionsprozess des Chrominanzrests (resCb und resCr) des Übertragungstransformationsblocks ist in der Tabelle gezeigt. Wenn der JCCR-Modus aktiviert ist, wird ein gemeinsamer Chrominanz-Restblock (resJointC [x] [y] übergeben und dann unter Berücksichtigung verschiedener Informationen (wie tu_cbf_cb tu_cbf_cr, CSign) der Restblock Cb (resCb) und der Restdifferenzblock Cr (resCr).

Auf der Encoderseite wird die gemeinsame Chrominanzkomponente wie folgt abgeleitet. Entsprechend dem Modus (wie in der obigen Tabelle gezeigt) erzeugt der Codierer resJointC {1,2} wie folgt:

Wenn mode = 2 (einzelner Rest Cb = C, Cr = CSign * C nach der Rekonstruktion), dann gemäß

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

Andernfalls, wenn mode = 1 (der rekonstruierte einzelne Rest Cb = C, Cr = (CSign * C) / 2), dann gemäß

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

Ansonsten (Modus = 3, der rekonstruierte einzelne Rest Cr = C, Cb = (CSign * C) / 2) gemäß

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

 

tu_cbf_cb

tu_cbf_cr

Rekonstruktion von Cb- und Cr-Residuen

Modus

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

Die obigen drei gemeinsamen Chroma-Codierungsmodi werden nur auf dem I-Chip unterstützt. In P- und B-Filmen wird nur Modus 2 unterstützt. Daher existiert in P- und B-Slices das Syntaxelement tu_joint_cbcr_residual_flag nur, wenn die Chroma-cbfs beide 1 sind (dh tu_cbf_cb = 1 und tu_cbf_cr = 1).

Der JCCR-Modus kann mit dem Transform Skip (TS) -Modus kombiniert werden. Um die Auswahlgeschwindigkeit des Codierungsende zu beschleunigen, hängt die Auswahl der JCCR-Transformation davon ab, ob die unabhängige Codierung der Cb- und Cr-Komponenten DCT-2 oder TS als beste Transformation auswählt und ob es Koeffizienten ungleich Null gibt in der unabhängigen Chrominanzcodierung. Speziell in die folgenden zwei Situationen unterteilt:

(1) Wenn sowohl Cb- als auch Cr-Komponenten DCT-2 verwenden oder wenn nur eine Chrominanzkomponente (Cb oder Cr) einen Koeffizienten ungleich Null hat und die Chrominanzkomponente DCT-2 auswählt, prüft der JCCR-Modus nur DCT-2. In ähnlicher Weise prüft der JCCR-Modus nur TS, wenn sowohl Cb- als auch Cr-Komponenten TS verwenden oder wenn nur eine Chroma-Komponente einen Koeffizienten ungleich Null hat und die Komponente TS auswählt.

(2) Wenn die selektiven Transformationen von Cb- und Cr-Komponenten unterschiedlich sind, dh wenn einer DCT-2 und der andere TS wählt oder umgekehrt, testet JCCR sowohl DCT-2 als auch TS. In diesen Fällen wird die RD-Prüfung des DCT-2-Modus vor dem TS-Modus durchgeführt, und wenn die Koeffizienten von DCT-2 alle Null sind, wird die TS-RD-Prüfung übersprungen.

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

Die invTransformCbCr-Funktion lautet wie folgt: Die Funktion besteht hauptsächlich darin, den Cr (Cb) -Rest durch den Cb (Cr) -Rest zu erhalten

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

Die Funktion fwdTransformCbCr besteht darin, ResJointC über ResCb und ResCr zu finden

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

 

Ich denke du magst

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