对残差次数进行量化编码的函数,相比于HEVC,JEM中加入了二次变换,即在第一次主变换又进行第二次变换;此外,还加入了EMT,和DCT2是并列关系,根据相关索引进行选择;
Void TComTrQuant::transformNxN( TComTU & rTu,
const ComponentID compID,
Pel * pcResidual,//残差;
const UInt uiStride,
TCoeff * rpcCoeff,//残差经变换量化后的系数;
#if ADAPTIVE_QP_SELECTION
TCoeff * pcArlCoeff,
#endif
TCoeff & uiAbsSum,
const QpParam & cQP
#if VCEG_AZ08_KLT_COMMON
, Bool useKLT
#endif
)
{
const TComRectangle &rect = rTu.getRect(compID);
const UInt uiWidth = rect.width;
const UInt uiHeight = rect.height;
TComDataCU* pcCU = rTu.getCU();
const UInt uiAbsPartIdx = rTu.GetAbsPartIdxTU();
const UInt uiOrgTrDepth = rTu.GetTransformDepthRel();
uiAbsSum=0;//存放残差绝对值的和;
RDPCMMode rdpcmMode = RDPCM_OFF;
rdpcmNxN( rTu, compID, pcResidual, uiStride, cQP, rpcCoeff, uiAbsSum, rdpcmMode );
if (rdpcmMode == RDPCM_OFF)
{
uiAbsSum = 0;
//transform and quantise
if(pcCU->getCUTransquantBypass(uiAbsPartIdx))//旁路掉变换和量化
{
const Bool rotateResidual = rTu.isNonTransformedResidualRotated(compID);
const UInt uiSizeMinus1 = (uiWidth * uiHeight) - 1;
for (UInt y = 0, coefficientIndex = 0; y<uiHeight; y++)
{
for (UInt x = 0; x<uiWidth; x++, coefficientIndex++)
{
const Pel currentSample = pcResidual[(y * uiStride) + x];
rpcCoeff[rotateResidual ? (uiSizeMinus1 - coefficientIndex) : coefficientIndex] = currentSample;
uiAbsSum += TCoeff(abs(currentSample));
}
}
}
else//不旁路变换量化
{
#if DEBUG_TRANSFORM_AND_QUANTISE
std::cout << g_debugCounter << ": " << uiWidth << "x" << uiHeight << " channel " << compID << " TU at input to transform\n";
printBlock(pcResidual, uiWidth, uiHeight, uiStride);
#endif
#if JVET_C0024_QTBT
assert( (pcCU->getSlice()->getSPS()->getCTUSize() >= uiWidth) );
#else
assert( (pcCU->getSlice()->getSPS()->getMaxTrSize() >= uiWidth) );
#endif
if(pcCU->getTransformSkip(uiAbsPartIdx, compID) != 0)//如果跳过变换;
{
xTransformSkip( pcResidual, uiStride, m_plTempCoeff, rTu, compID );
}
else//不跳过变换
{
const Int channelBitDepth=pcCU->getSlice()->getSPS()->getBitDepth(toChannelType(compID));//10;
xT( channelBitDepth, rTu.useDST(compID), pcResidual, uiStride, m_plTempCoeff, uiWidth, uiHeight, pcCU->getSlice()->getSPS()->getMaxLog2TrDynamicRange(toChannelType(compID))
#if COM16_C806_EMT
, getEmtMode ( rTu, compID )
, getEmtTrIdx( rTu, compID )
#endif
#if VCEG_AZ08_KLT_COMMON
, useKLT && (compID == 0)
#endif
);//执行变换的主函数,也是第一次变换
}
#if JVET_C0024_QTBT
Char ucNsstIdx = pcCU->getROTIdx(toChannelType(compID), uiAbsPartIdx) ;//二次变换的索引
if (ucNsstIdx && uiWidth>=4 && uiHeight>=4
#if JVET_C0045_C0053_NO_NSST_FOR_TS
&& !pcCU->getTransformSkip(uiAbsPartIdx, compID)
#endif
)//ucNsstIdx在不等于0的前提下才做二次变换;
#else
if (pcCU->getROTIdx(uiAbsPartIdx)
#if JVET_C0045_C0053_NO_NSST_FOR_TS
&& !pcCU->getTransformSkip(uiAbsPartIdx, compID)
#endif
)
#endif
{
#if JVET_D0120_NSST_IMPROV
static Int NSST_MATRIX[64];
const Int iLog2SbSize = (uiWidth > 4 && uiHeight > 4) ? 3 : 2;
const Int iSbSize = (uiWidth > 4 && uiHeight > 4) ? 8 : 4;
const Int iSubGroupXMax = Clip3(1, 8, (Int)uiWidth ) >> iLog2SbSize;
const Int iSubGroupYMax = Clip3(1, 8, (Int)uiHeight) >> iLog2SbSize;
#else
static Int NSST_MATRIX[16];
Int iSubGroupXMax = Clip3 (1,16,(Int)( (uiWidth>>2)));
Int iSubGroupYMax = Clip3 (1,16,(Int)( (uiHeight>>2)));
#endif
Int iOffSetX = 0;
Int iOffSetY = 0;
Int y = 0;
Int* piCoeffTemp = m_plTempCoeff;
Int* piNsstTemp = NSST_MATRIX;
#if JVET_D0120_NSST_IMPROV
Int iString2CopySize = iSbSize*sizeof(Int);
#else
Int iString2CopySize = 4*sizeof(Int);
#endif
#if VCEG_AZ07_CTX_RESIDUALCODING
#if !JVET_C0024_QTBT
const UInt uiLog2BlockSize = g_aucConvertToBit[ uiWidth ] + 2;
#endif
#endif
const UInt uiScanIdx = pcCU->getCoefScanIdx(uiAbsPartIdx, uiWidth, uiHeight, compID);
#if JVET_C0024_QTBT
const UInt log2BlockWidth = g_aucConvertToBit[uiWidth] + MIN_CU_LOG2;
const UInt log2BlockHeight = g_aucConvertToBit[uiHeight] + MIN_CU_LOG2;
#else
const UInt log2BlockWidth = g_aucConvertToBit[uiWidth] + 2;
const UInt log2BlockHeight = g_aucConvertToBit[uiHeight] + 2;
#endif
#if VCEG_AZ07_CTX_RESIDUALCODING
#if JVET_D0120_NSST_IMPROV
#if JVET_C0024_QTBT
const UInt *scan = (log2BlockWidth>=3 && log2BlockHeight>=3) ? g_auiCoefTopLeftDiagScan8x8[log2BlockWidth-3] : g_scanOrder[ SCAN_GROUPED_4x4 ][ uiScanIdx ][ log2BlockWidth ][ log2BlockHeight ];
#else
const UInt *scan = uiLog2BlockSize>3 ? g_auiCoefTopLeftDiagScan8x8[log2BlockWidth-4] : g_scanOrder[ SCAN_GROUPED_4x4 ][ uiScanIdx ][ log2BlockWidth ][ log2BlockHeight ];
#endif
UInt uiIntraMode = pcCU->getIntraDir( toChannelType(compID), uiAbsPartIdx);//得到帧内预测模式;
if( compID != COMPONENT_Y )
{
#if JVET_E0062_MULTI_DMS && COM16_C806_LMCHROMA
if( uiIntraMode == LM_CHROMA_IDX )
{
uiIntraMode = PLANAR_IDX;
}
#if JVET_E0077_ENHANCED_LM
if (IsLMMode(uiIntraMode))
{
uiIntraMode = PLANAR_IDX;
}
#endif
}
assert( uiIntraMode<NUM_INTRA_MODE-1 );
const Int iNsstCandNum = ( uiIntraMode<=DC_IDX ) ? 3 : 4;//二次变换候选数量,DC和planar和CCLM模式的候选数量为3,其他为4;
#if JVET_C0024_QTBT
if( iNsstCandNum > ucNsstIdx )
#else
if( iNsstCandNum > pcCU->getROTIdx(uiAbsPartIdx) )
#endif
{
#if JVET_D0120_NSST_IMPROV
#if JVET_C0024_QTBT
const Int * permut = iSbSize>4 ? g_nsstHyGTPermut8x8[g_NsstLut[uiIntraMode]][ucNsstIdx - 1] : g_nsstHyGTPermut4x4[g_NsstLut[uiIntraMode]][ucNsstIdx - 1];
#else
const Int * permut = iSbSize>4 ? g_nsstHyGTPermut8x8[g_NsstLut[uiIntraMode]][pcCU->getROTIdx(uiAbsPartIdx) - 1] : g_nsstHyGTPermut4x4[g_NsstLut[uiIntraMode]][pcCU->getROTIdx(uiAbsPartIdx) - 1];
#endif
#endif
for (Int iSubGroupX = 0; iSubGroupX<iSubGroupXMax; iSubGroupX++)
{
for (Int iSubGroupY = 0; iSubGroupY<iSubGroupYMax; iSubGroupY++)
{
#if JVET_D0120_NSST_IMPROV
iOffSetX = iSbSize*iSubGroupX;
iOffSetY = iSbSize*iSubGroupY*uiWidth;
#else
iOffSetX = 4*iSubGroupX;
iOffSetY = 4*iSubGroupY*uiWidth;
#endif
piNsstTemp = NSST_MATRIX;
piCoeffTemp = m_plTempCoeff+iOffSetX+iOffSetY;
#if JVET_D0120_NSST_IMPROV
for( y = 0; y < iSbSize; y++ )
#else
for( y = 0; y < 4; y++ )
#endif
{
if( uiIntraMode>DIA_IDX )//垂直方向,即将CoeffTemp转置后复制给NsstTemp;
{
#if JVET_D0120_NSST_IMPROV
if( iSbSize==4 )
{
piNsstTemp[ 0] = piCoeffTemp[0]; piNsstTemp[ 4] = piCoeffTemp[1];
piNsstTemp[ 8] = piCoeffTemp[2]; piNsstTemp[12] = piCoeffTemp[3];
}
else if( iSbSize==8 )
{
piNsstTemp[ 0] = piCoeffTemp[0]; piNsstTemp[ 8] = piCoeffTemp[1];
piNsstTemp[16] = piCoeffTemp[2]; piNsstTemp[24] = piCoeffTemp[3];
piNsstTemp[32] = piCoeffTemp[4]; piNsstTemp[40] = piCoeffTemp[5];
piNsstTemp[48] = piCoeffTemp[6]; piNsstTemp[56] = piCoeffTemp[7];
}
#else
piNsstTemp[ 0] = piCoeffTemp[0]; piNsstTemp[ 4] = piCoeffTemp[1];
piNsstTemp[ 8] = piCoeffTemp[2]; piNsstTemp[12] = piCoeffTemp[3];
#endif
piNsstTemp ++;
}
else//uiIntraMode<=DIA_IDX
{
::memcpy(piNsstTemp, piCoeffTemp, iString2CopySize); //水平方向直接复制;
#if JVET_D0120_NSST_IMPROV
piNsstTemp += iSbSize;
#else
piNsstTemp +=4;
#endif
}
piCoeffTemp +=uiWidth;
}
#if JVET_C0024_QTBT
#if JVET_D0120_NSST_IMPROV//NSST是JEM中新加的技术,有两种
if ( iSbSize>4 )
{
FwdNsst8x8( NSST_MATRIX, g_NsstLut[uiIntraMode], ucNsstIdx-1 );//大于等于8x8的TU,使用8x8的NSST
}
else
{
FwdNsst4x4( NSST_MATRIX, g_NsstLut[uiIntraMode], ucNsstIdx-1 );//4x4的TU,使用4x4的NSST
}
piNsstTemp = NSST_MATRIX;
piCoeffTemp = m_plTempCoeff+iOffSetX+iOffSetY;
#if JVET_D0120_NSST_IMPROV
for( y = 0; y < iSbSize*iSbSize; y++ )
#else
for( y = 0; y < 16; y++ )
#endif
{
#if JVET_D0120_NSST_IMPROV
piCoeffTemp[scan[y]] = piNsstTemp[permut[y]];
#else
piCoeffTemp[scan[y]] = piNsstTemp[y];
#endif
}
}
}
}
}
#endif
#if VCEG_AZ08_KLT_COMMON
if (useKLT && (compID == 0))//如果是亮度分量且使用KLT
{
TUEntropyCodingParameters codingParameters;
getTUEntropyCodingParameters(codingParameters, rTu, compID);
const UInt *scan = codingParameters.scan;
reOrderCoeff(m_plTempCoeff, scan, uiWidth, uiHeight);//对系数进行重排
}
#endif
xQuant( rTu, m_plTempCoeff, rpcCoeff,
#if ADAPTIVE_QP_SELECTION
pcArlCoeff,
#endif
uiAbsSum, compID, cQP
);//进行量化的主函数
#if DEBUG_TRANSFORM_AND_QUANTISE
std::cout << g_debugCounter << ": " << uiWidth << "x" << uiHeight << " channel " << compID << " TU at output of quantiser\n";
printBlock(rpcCoeff, uiWidth, uiHeight, uiWidth);
#endif
}
}
//set the CBF
#if JVET_C0024_QTBT
assert(uiOrgTrDepth==0);
pcCU->setCbfPartRange((uiAbsSum > 0) ? 1 : 0, compID, uiAbsPartIdx, 0);
#else
pcCU->setCbfPartRange((((uiAbsSum > 0) ? 1 : 0) << uiOrgTrDepth), compID, uiAbsPartIdx, rTu.GetAbsPartIdxNumParts(compID));
#endif
}