H.266/VTM10.1 依赖性量化DQ
量化部分代码相对而言比较集中,就在DepQuant::quant函数中实现。这篇文章主要梳理一下TCQ网格量化在代码中的实现。并且梳理顺序按照量化过程来展示。
主要函数、变量名称及其功能
函数:xDecideAndUpdate()
TCQ的入口函数,他的实参分别为:
abs(tCoeff[scanInfo.rasterPos])
变换系数的绝对值
scanInfo
当前系数的扫描信息
(zeroOut && (scanInfo.posX >= effWidth || scanInfo.posY >= effHeight))
当前系数是否处于高频置零区域内的判断
defaultQuantisationCoefficient
缩放系数
xDecide()
针对单个系数,具体的量化实现他的实参为:
scanInfo.spt
当前系数在CG中的标志位,共有三个
absCoeff
变换系数绝对值
lastOffset
当前系数作为首个非零系数的RD代价
decisions
网格的连线
主要函数注释
下面是xDecideAndUpdata函数的开始`.
javastript
#if JVET_R0351_HIGH_BIT_DEPTH_SUPPORT_VS
void DepQuant::xDecideAndUpdate( const TCoeff absCoeff, const ScanInfo& scanInfo, bool zeroOut, TCoeff quantCoeff, int position )
#else
void DepQuant::xDecideAndUpdate( const TCoeff absCoeff, const ScanInfo& scanInfo, bool zeroOut, int quantCoeff, int position)
#endif
{
Decision* decisions = m_trellis[ scanInfo.scanIdx ]; //decisions中存储的三个量,abslevel代表某个可能的状态下的量化系数,rdcost为对应的rd代价,prevId代表与前项系数(scanpos)的状态转变。
std::swap( m_prevStates, m_currStates );//指针指向位置交换
xDecide(scanInfo.spt, absCoeff, lastOffset(scanInfo.scanIdx), decisions, zeroOut, quantCoeff,position,rem);//在完成一个系数的四个状态分别对应的level,失真distortion以及码率rate
if( scanInfo.scanIdx )
{
if( scanInfo.eosbb )//eosbb==1表示该系数为CG的最后一个系数
{
m_commonCtx.swap();//m_currstate4个数组代表着当前已经经过viterbi算法剪裁之后的四条最优路径
m_currStates[0].updateStateEOS( scanInfo, m_prevStates, m_skipStates, decisions[0] );//分别对四条路径的上下文信息进行更新(块级上下文)
m_currStates[1].updateStateEOS( scanInfo, m_prevStates, m_skipStates, decisions[1] );
m_currStates[2].updateStateEOS( scanInfo, m_prevStates, m_skipStates, decisions[2] );
m_currStates[3].updateStateEOS( scanInfo, m_prevStates, m_skipStates, decisions[3] );
::memcpy( decisions+4, decisions, 4*sizeof(Decision) );//将常规状态转换模式的连线方式转移到后4个全零状态上
}
else if( !zeroOut )
{
switch( scanInfo.nextNbInfoSbb.num )//依据邻近块的数量来选择case,其中num为邻近块的数量(邻近块上下文信息的概念请查阅JVET相关提案以及论文)对每一条最优路径m_currstates[n]的系数级上下文信息进行更新
//最主要的作用是更新了码率估计数组,对于下一个系数量化时,量化候选的码率查表计算有着重要影响
{
case 0:
m_currStates[0].updateState<0>( scanInfo, m_prevStates, decisions[0] );
m_currStates[1].updateState<0>( scanInfo, m_prevStates, decisions[1] );
m_currStates[2].updateState<0>( scanInfo, m_prevStates, decisions[2] );
m_currStates[3].updateState<0>( scanInfo, m_prevStates, decisions[3] );
break;
case 1:
m_currStates[0].updateState<1>( scanInfo, m_prevStates, decisions[0] );
m_currStates[1].updateState<1>( scanInfo, m_prevStates, decisions[1] );
m_currStates[2].updateState<1>( scanInfo, m_prevStates, decisions[2] );
m_currStates[3].updateState<1>( scanInfo, m_prevStates, decisions[3] );
break;
case 2:
m_currStates[0].updateState<2>( scanInfo, m_prevStates, decisions[0] );
m_currStates[1].updateState<2>( scanInfo, m_prevStates, decisions[1] );
m_currStates[2].updateState<2>( scanInfo, m_prevStates, decisions[2] );
m_currStates[3].updateState<2>( scanInfo, m_prevStates, decisions[3] );
break;
case 3:
m_currStates[0].updateState<3>( scanInfo, m_prevStates, decisions[0] );
m_currStates[1].updateState<3>( scanInfo, m_prevStates, decisions[1] );
m_currStates[2].updateState<3>( scanInfo, m_prevStates, decisions[2] );
m_currStates[3].updateState<3>( scanInfo, m_prevStates, decisions[3] );
break;
case 4:
m_currStates[0].updateState<4>( scanInfo, m_prevStates, decisions[0] );
m_currStates[1].updateState<4>( scanInfo, m_prevStates, decisions[1] );
m_currStates[2].updateState<4>( scanInfo, m_prevStates, decisions[2] );
m_currStates[3].updateState<4>( scanInfo, m_prevStates, decisions[3] );
break;
default:
m_currStates[0].updateState<5>( scanInfo, m_prevStates, decisions[0] );
m_currStates[1].updateState<5>( scanInfo, m_prevStates, decisions[1] );
m_currStates[2].updateState<5>( scanInfo, m_prevStates, decisions[2] );
m_currStates[3].updateState<5>( scanInfo, m_prevStates, decisions[3] );
}
}
if( scanInfo.spt == SCAN_SOCSBB )
{
std::swap( m_prevStates, m_skipStates );
}
}
}