版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
H.266/VVC中有两个transformNxN函数,它们的参数不同,如下:
void transformNxN ( TransformUnit& tu, const ComponentID& compID, const QpParam& cQP, std::vector<TrMode>* trModes, const int maxCand );
void transformNxN ( TransformUnit& tu, const ComponentID& compID, const QpParam& cQP, TCoeff& uiAbsSum, const Ctx& ctx, const bool loadTr = false );
1.transformNxN函数(1)
第一个transformNxN()函数主要是通过比较DCT-2变换和TransformSkip的SAD来决定修改trModes对应模式(DCT-2和TransformSkip)是否使用。
/*
通过比较DCT2的SAD和TransformSkip的SAD决定是否使用TransFormSkip,只有尺寸小于等于32才调用
trModes数组存有多核变换的索引MTSIdx和能否进行参与后续比较(int,bool),经过本函数后修改MTSIdx对应的bool值
*/
#if JVET_O0502_ISP_CLEANUP
void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const QpParam& cQP, std::vector<TrMode>* trModes, const int maxCand )
#else
void TrQuant::transformNxN( TransformUnit &tu, const ComponentID &compID, const QpParam &cQP, std::vector<TrMode>* trModes, const int maxCand, double* diagRatio, double* horVerRatio )
#endif
{
CodingStructure &cs = *tu.cs;
const CompArea &rect = tu.blocks[compID];
const uint32_t width = rect.width;
const uint32_t height = rect.height;
const CPelBuf resiBuf = cs.getResiBuf(rect);//残差系数
#if MAX_TB_SIZE_SIGNALLING
CHECK( cs.sps->getMaxTbSize() < width, "Unsupported transformation size" );
#else
CHECK( MAX_TB_SIZEY < width, "Unsupported transformation size" );
#endif
int pos = 0;//表示trCost的第二个值,应该表示MTSIdx
std::vector<TrCost> trCosts;//trCosts:第一个值为SAD的值,第二个值为
std::vector<TrMode>::iterator it = trModes->begin();//it有两个值,一个代表多核变换的模式号,一个是bool值表示是否参与后续比较
const double facBB[] = { 1.2, 1.3, 1.3, 1.4, 1.5 };//一个系数值,含义?
while( it != trModes->end() )
{
//遍历trModes,获得每一个多核变换模式对应的cost
tu.mtsIdx = it->first;//获取多核变换模式号
CoeffBuf tempCoeff( m_mtsCoeffs[tu.mtsIdx], rect );//获取MTS对应系数
if( tu.noResidual )//如果没有残差,跳过
{
int sumAbs = 0;//绝对误差和:SAD
trCosts.push_back( TrCost( sumAbs, pos++ ) );
it++;
continue;
}
if( isLuma(compID) && tu.mtsIdx == MTS_SKIP )//TransformSkip模式:变换跳过模式
{
xTransformSkip( tu, compID, resiBuf, tempCoeff.buf );
}
else//Transform模式:正常变换
{
xT( tu, compID, resiBuf, tempCoeff, width, height );
}
int sumAbs = 0;//绝对误差和:SAD
for( int pos = 0; pos < width*height; pos++ )
{
//这里的pos是局部变量,表示的是每一个块里面的每一个像素值,和外部pos意义不同
sumAbs += abs( tempCoeff.buf[pos] );
}
double scaleSAD=1.0;
if (isLuma(compID) && tu.mtsIdx==MTS_SKIP && ((g_aucLog2[width] + g_aucLog2[height]) & 1) == 1 )
{
scaleSAD=1.0/1.414213562; //补偿系数 compensate for not scaling transform skip coefficients by 1/sqrt(2)
}
trCosts.push_back( TrCost( int(sumAbs*scaleSAD), pos++ ) );
it++;
}
#if !JVET_O0502_ISP_CLEANUP
// it gets the distribution of the DCT-II coefficients energy, which will be useful to discard ISP tests
CoeffBuf coeffsDCT( m_mtsCoeffs[0], rect );
xGetCoeffEnergy( tu, compID, coeffsDCT, diagRatio, horVerRatio );
#endif
int numTests = 0;
std::vector<TrCost>::iterator itC = trCosts.begin();
const double fac = facBB[g_aucLog2[std::max(width, height)]-2];//根据不同块大小获取不同系数
const double thr = fac * trCosts.begin()->first;//第一个SAD,带系数的(系数thr用于简化,是一种快速算法),用于其他MTS模式比较时
const double thrTS = trCosts.begin()->first;//第一个SAD,用于TransformSkip时比较
while( itC != trCosts.end() )//遍历变换代价值
{
const bool testTr = itC->first <= ( itC->second == 1 ? thrTS : thr ) && numTests <= maxCand;
trModes->at( itC->second ).second = testTr;//修改MTS模式对应的bool值
numTests += testTr;
itC++;
}
}
2.transformNxN函数(2)
第二个transformNxN()函数主要是进行对残差块进行变换。
流程如下:
- RDPCM ???
- 若RDPCM模式不是关闭状态且当前块是亮度块,则设置mtsIdx = MTS_Skip,否则执行3
- 若RDPCM模式为关闭状态,检查是否为Trans-quant-Bypass模式,若是则跳过变换和量化,否则执行4
- 若当前块是亮度块且mtsIdx=MTS_Skip,则执行xTransformSkip()函数,否则执行xT()函数进行主变换
- 判断是否使用二次变换,若使用二次变换,则调用xFwdLfnst()函数
- 调用xQuant函数进行量化。
#if JVET_O0502_ISP_CLEANUP
void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const QpParam& cQP, TCoeff& uiAbsSum, const Ctx& ctx, const bool loadTr )
#else
void TrQuant::transformNxN( TransformUnit &tu, const ComponentID &compID, const QpParam &cQP, TCoeff &uiAbsSum, const Ctx &ctx, const bool loadTr, double* diagRatio, double* horVerRatio )
#endif
{
CodingStructure &cs = *tu.cs;
const SPS &sps = *cs.sps;
const CompArea &rect = tu.blocks[compID];
const uint32_t uiWidth = rect.width;
const uint32_t uiHeight = rect.height;
const CPelBuf resiBuf = cs.getResiBuf(rect);
CoeffBuf rpcCoeff = tu.getCoeffs(compID);
if( tu.noResidual )//如果没有残差,直接设置cbf
{
uiAbsSum = 0;//存放残差系数绝对值的和;
TU::setCbfAtDepth( tu, compID, tu.depth, uiAbsSum > 0 );
return;
}
RDPCMMode rdpcmMode = RDPCM_OFF;//设置RDPCM模式
rdpcmNxN(tu, compID, cQP, uiAbsSum, rdpcmMode);
if( tu.cu->bdpcmMode && isLuma(compID) )
{
tu.mtsIdx = MTS_SKIP;
}
if (rdpcmMode == RDPCM_OFF)
{
uiAbsSum = 0;
// transform and quantize
if (CU::isLosslessCoded(*tu.cu))//旁路掉变换和量化,即直接把残差系数赋值给rpcCoeff;
{
const bool rotateResidual = TU::isNonTransformedResidualRotated( tu, compID );//是否残差旋转
for( uint32_t y = 0; y < uiHeight; y++ )
{
for( uint32_t x = 0; x < uiWidth; x++ )
{
const Pel currentSample = resiBuf.at( x, y );
if( rotateResidual )//存在残差旋转
{
rpcCoeff.at( uiWidth - x - 1, uiHeight - y - 1 ) = currentSample;
}
else
{
rpcCoeff.at( x, y ) = currentSample;
}
uiAbsSum += TCoeff( abs( currentSample ) );//计算出SAD的和
}
}
}
else//没有被旁路,进行变换、量化等操作
{
#if MAX_TB_SIZE_SIGNALLING
CHECK( cs.sps->getMaxTbSize() < uiWidth, "Unsupported transformation size" );
#else
CHECK( MAX_TB_SIZEY < uiWidth, "Unsupported transformation size" );
#endif
CoeffBuf tempCoeff( loadTr ? m_mtsCoeffs[tu.mtsIdx] : m_plTempCoeff, rect );
DTRACE_PEL_BUF( D_RESIDUALS, resiBuf, tu, tu.cu->predMode, compID );
if( !loadTr )
{
if( isLuma(compID) && tu.mtsIdx == MTS_SKIP )
{
xTransformSkip( tu, compID, resiBuf, tempCoeff.buf );//变换跳过模式,如果跳过变换,将残差系数经过伸缩和移位后赋值给temCoeff.buff;
}
else
{
xT( tu, compID, resiBuf, tempCoeff, uiWidth, uiHeight );//正常变换模式,tempCoeff保存经过变换后的残差系数
}
}
#if !JVET_O0502_ISP_CLEANUP
//we do this only with the DCT-II coefficients
if( isLuma(compID) &&
!loadTr && tu.mtsIdx == MTS_DCT2_DCT2
)
{
//it gets the distribution of the coefficients energy, which will be useful to discard ISP tests
//它得到了系数能量的分布,这将有助于抛弃ISP测试
xGetCoeffEnergy( tu, compID, tempCoeff, diagRatio, horVerRatio );
}
#endif
if( sps.getUseLFNST() )//进行二次变换LFNST
{
xFwdLfnst( tu, compID, loadTr );
}
DTRACE_COEFF_BUF( D_TCOEFF, tempCoeff, tu, tu.cu->predMode, compID );
xQuant( tu, compID, tempCoeff, uiAbsSum, cQP, ctx );//量化
DTRACE_COEFF_BUF( D_TCOEFF, tu.getCoeffs( compID ), tu, tu.cu->predMode, compID );
}
}
// set coded block flag (CBF)
TU::setCbfAtDepth (tu, compID, tu.depth, uiAbsSum > 0);
}
其中xT函数可以参考https://blog.csdn.net/BigDream123/article/details/102748739
xFwdLfnst函数可以参考https://blog.csdn.net/BigDream123/article/details/102748968
xTransformSkip函数可以参考https://blog.csdn.net/BigDream123/article/details/102748968