本文只是简单的梳理,没有新的贡献,当需要用到变化量化时可根据本文迅速找到相关的文章。
关于变换,可以回顾下我原来的博文:http://blog.csdn.net/qq_21747841/article/details/73551511
当时对HEVC的理解远远不如现在,所以,再参考大神的博文:
http://blog.csdn.net/NB_vol_1/article/details/51190324
重新梳理变换的概念和函数。
变换原理以及公式
对于大部分图像来说,它们都有很多平坦区域和内容变换缓慢的区域,而且相邻像素点的相关性很强,通过变换,可以把这些相关性减少,同时把图像的能量在空间域的分散分布转换为在变换域的相对集中分布,这样就可以去除空间冗余了
HEVC中使用两种变换:DCT和DST。DST只处理帧内4x4模式的亮度块,其他的所有模式都使用DCT因此HEVC中,DST只有4x4的规格,而DCT有4x4、8x8、16x16、32x32等几种尺寸。
具体公式因为看不太懂,可以去看原博给的链接:http://blog.csdn.net/nb_vol_1/article/details/53288666以及我上面给的那个链接。中间的一些公式和计算这里就不放了,直接放代码实现,入口函数是encodeResAndCalcRdInterCU。
这个函数的作用是根据预测值,求出残差,然后进行TU的划分,然后进行变换、量化等操作以及RD代价的计算。在前面讲解预测的时候,这个函数已经分析过了,这里不再赘述。
而变换量化的主函数,TComTrQuant::transformNxN在最上面的链接也给出了,可以发现无论帧内预测还是帧间预测,它们最终都要使用同一个变换量化的主函数。
哈达玛变换在HEVC中的主要运用是在帧内预测的时候计算SAD。关于SAD请参考:http://blog.csdn.net/xiaoyi247/article/details/7913360
http://blog.sina.com.cn/s/blog_6d51159e0101qcxh.html
量化理论和公式
量化是指将信号的连续取值映射为有很多个离散幅值的过程。DCT之后,变换系数往往具有较大的动态范围,因此对变换系数进行量化可以有效减小信号取值空间,进而获得更好的压缩效果。
量化是视频中产生失真的根本原因。
量化器分为:标量量化器和矢量量化器两种,HEVC只使用标量量化器
HEVC中的量化也分成两种,一种是传统的量化,另一种是率失真优化量化(RDOQ)
更多量化方面的细节请参考http://blog.csdn.net/nb_vol_1/article/details/53288705
变换和量化的入口函数是TComTrQuant::transformNxN:
(1)首先判断是否确实需要变换量化
(2)如果使用了跳过变换的模式,那么调用xTransformSkip进行处理;否则调用xT进行变换处理
(3)调用xQuant函数进行量化
普通量化
Void TComTrQuant::xQuant( TComTU &rTu,
TCoeff * pSrc,
TCoeff * pDes,
#if ADAPTIVE_QP_SELECTION
TCoeff *pArlDes,
#endif
TCoeff &uiAbsSum,
const ComponentID compID,
const QpParam &cQP )
{
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();
TCoeff* piCoef = pSrc;// 原始系数
TCoeff* piQCoef = pDes;// 变换后的系数
#if ADAPTIVE_QP_SELECTION// 自适应量化系数选择
TCoeff* piArlCCoef = pArlDes;
#endif
const Bool useTransformSkip = pcCU->getTransformSkip(uiAbsPartIdx, compID);
Bool useRDOQ = useTransformSkip ? m_useRDOQTS : m_useRDOQ;
if ( useRDOQ && (isLuma(compID) || RDOQ_CHROMA) )
{
#if ADAPTIVE_QP_SELECTION
xRateDistOptQuant( rTu, piCoef, pDes, pArlDes, uiAbsSum, compID, cQP );//率失真优化量化
#else
xRateDistOptQuant( rTu, piCoef, pDes, uiAbsSum, compID, cQP );
#endif
}
else
{/*
** 传统的量化方式
** abs(x)=(abs(d) * MF + f_temp) >> (qbits + T_Shift)
** qbits = + floor(QP/6)
** MF = 2^qbits / Qstep
** f_temp = f << (qbits + T_Shift)
** 一般情况下,对于I帧,f=1/3,对于P、B帧,f=1/6
** x是量化后的系数
** d是量化之前的系数
*/
TUEntropyCodingParameters codingParameters;
getTUEntropyCodingParameters(codingParameters, rTu, compID);
const TCoeff entropyCodingMinimum = -(1 << g_maxTrDynamicRange[toChannelType(compID)]);
const TCoeff entropyCodingMaximum = (1 << g_maxTrDynamicRange[toChannelType(compID)]) - 1;
TCoeff deltaU[MAX_TU_SIZE * MAX_TU_SIZE];
const UInt uiLog2TrSize = rTu.GetEquivalentLog2TrSize(compID);
Int scalingListType = getScalingListType(pcCU->getPredictionMode(uiAbsPartIdx), compID);
assert(scalingListType < SCALING_LIST_NUM);
Int *piQuantCoeff = getQuantCoeff(scalingListType, cQP.rem, uiLog2TrSize-2);// 存放MF值的地方(MF可以通过查表得到),其中MF = 2^qbits / Qstep
const Bool enableScalingLists = getUseScalingList(uiWidth, uiHeight, (pcCU->getTransformSkip(uiAbsPartIdx, compID) != 0));
const Int defaultQuantisationCoefficient = g_quantScales[cQP.rem];
/* for 422 chroma blocks, the effective scaling applied during transformation is not a power of 2, hence it cannot be
* implemented as a bit-shift (the quantised result will be sqrt(2) * larger than required). Alternatively, adjust the
* uiLog2TrSize applied in iTransformShift, such that the result is 1/sqrt(2) the required result (i.e. smaller)
* Then a QP+3 (sqrt(2)) or QP-3 (1/sqrt(2)) method could be used to get the required result
*对于422色度块,变换期间应用的有效缩放不是2的幂,因此不能实现为位移(量化结果将大于所需的sqrt(2))
*或者,调整 uILog2TrSize应用于iTransformShift,使结果为1 / sqrt(2)所需结果(即较小)
*然后可以使用QP + 3(sqrt(2))或QP-3(1 / sqrt(2))方法 用于获得所需的结果
*/
// Represents scaling through forward transform
Int iTransformShift = getTransformShift(toChannelType(compID), uiLog2TrSize);// 即T_Shift
if (useTransformSkip && pcCU->getSlice()->getSPS()->getUseExtendedPrecision())
{
iTransformShift = std::max<Int>(0, iTransformShift);
}
const Int iQBits = QUANT_SHIFT + cQP.per + iTransformShift;//由于QpBDOffset由于Qp_per的增加而使得变换移位的减少得到平衡,所以任何内部位深度的QBits都可以正常工作
// QBits will be OK for any internal bit depth as the reduction in transform shift is balanced by an increase in Qp_per due to QpBDOffset
#if ADAPTIVE_QP_SELECTION
Int iQBitsC = MAX_INT;
Int iAddC = MAX_INT;
if (m_bUseAdaptQpSelect)
{
iQBitsC = iQBits - ARL_C_PRECISION;
iAddC = 1 << (iQBitsC-1);
}
#endif
const Int iAdd = (pcCU->getSlice()->getSliceType()==I_SLICE ? 171 : 85) << (iQBits-9);
const Int qBits8