一 transformSkip模式,直接拷贝到量化后的内存块中
二 帧内4x4块,用dst变换
三 常规块用dct变换
四 进行RDOQ 量化操作
五 常规量化SDH操作
uint32_t Quant::transformNxN(const CUData& cu,
const pixel* fenc,
uint32_t fencStride,
const int16_t* residual,
uint32_t resiStride,
coeff_t* coeff,
uint32_t log2TrSize,
TextType ttype,
uint32_t absPartIdx,
bool useTransformSkip)
/*
cu 转换CU对象
fenc 原始帧图像
fencStride 原始帧图像块的步长
residual 残差数据
resiStride 残差数据步长
coeff 存储残差经过变换、量化后的系数
log2TrSize TU尺寸一般是4,8,16,32
ttype 数据通道类型,亮度/色度
absPartIdx CU地址
useTransformSkip 是否使用变换跳过模式
*/
{
const uint32_t sizeIdx = log2TrSize - 2;
int numSig = 0;
if (cu.m_tqBypass[0]) //如果使用变换/量化的bypass模式,
//也就是跳过变换/量化步骤,直接降残差拷贝到变换系数内存块
{
S265_CHECK(log2TrSize >= 2 && log2TrSize <= 5, "Block size mistake!\n");
numSig = primitives.cu[sizeIdx].copy_cnt(coeff, residual, resiStride);
//上面这句话就是直接拷贝
}
else
{
bool isLuma = ttype == TEXT_LUMA;
bool usePsy = m_psyRdoqScale && isLuma && !useTransformSkip;
int transformShift = MAX_TR_DYNAMIC_RANGE - S265_DEPTH - log2TrSize; //表示通过正向变换进行缩放
// Represents scaling through forward transform
S265_CHECK((cu.m_slice->m_sps->quadtreeTULog2MaxSize >= log2TrSize), "transform size too large\n");
if (useTransformSkip) //应用跳过变换模式,则只需要将残差进行相应的位移,无须进行其他操作
{
#if S265_DEPTH <= 10 //如果是10bit以下,
S265_CHECK(transformShift >= 0, "invalid transformShift\n");
primitives.cu[sizeIdx].cpy2Dto1D_shl(m_resiDctCoeff, residual, resiStride, transformShift);
//拷贝残差,将二维的残差,拷贝到一维
//将二维因子拷贝为一维因子
#else
if (transformShift >= 0)
primitives.cu[sizeIdx].cpy2Dto1D_shl(m_resiDctCoeff, residual, resiStride, transformShift);
else
primitives.cu[sizeIdx].cpy2Dto1D_shr(m_resiDctCoeff, residual, resiStride, -transformShift);
#endif
}
else //非transform skip 其它的就是要变换的模式
{
bool isIntra = cu.isIntra(absPartIdx);
if (!sizeIdx && isLuma && isIntra) //如果变换块是4x4 ,sizeIdx = 0, 而且是亮度块,而且是intra块,就用dst 4x4变换
primitives.dst4x4(residual, m_resiDctCoeff, resiStride);
else
primitives.cu[sizeIdx].dct(residual, m_resiDctCoeff, resiStride); //其它的用dct变换, 结果存储在m_resiDctCoeff 中
/* NOTE: if RDOQ is disabled globally, psy-rdoq is also disabled, so
* there is no risk of performing this DCT unnecessarily */
if (usePsy)
{
int trSize = 1 << log2TrSize;
/* perform DCT on source pixels for psy-rdoq */
primitives.cu[sizeIdx].copy_ps(m_fencShortBuf, trSize, fenc, fencStride);
primitives.cu[sizeIdx].dct(m_fencShortBuf, m_fencDctCoeff, trSize);
}
if (m_nr && m_nr->offset)
{
/* denoise is not applied to intra residual, so DST can be ignored */
int cat = sizeIdx + 4 * !isLuma + 8 * !isIntra;
int numCoeff = 1 << (log2TrSize * 2);
primitives.denoiseDct(m_resiDctCoeff, m_nr->residualSum[cat], m_nr->offset[cat], numCoeff);
m_nr->count[cat]++;
}
}
if (m_rdoqLevel) //如果rdoq级别大于0, 菜进行RDOQ量化,否则使用常规的均匀量化
numSig = (this->*rdoQuant_func[log2TrSize - 2])(cu, coeff, ttype, absPartIdx, usePsy);
else //常规均匀量化
{
int deltaU[32 * 32]; //用于存储量化误差矩阵,在常规量化中,deltaU只用于进行符号位隐藏的操作。
int scalingListType = (cu.isIntra(absPartIdx) ? 0 : 3) + ttype;
//根据预测模式和亮度/色度分量得到 前向量化表的类型,用于选择不同的前向量化表
int rem = m_qpParam[ttype].rem; //qscale余数部分
int per = m_qpParam[ttype].per; //qscale整数部分
const int32_t* quantCoeff = m_scalingList->m_quantCoef[log2TrSize - 2][scalingListType][rem]; //用整数部分寻址 量化因子
//根据当前TU尺寸,前向量化类型和QP余数部分,选择对应的量化表
int qbits = QUANT_SHIFT + per + transformShift;
//量化右移位置,由3部分组成:1 量化打来的位数增加, 2 Qp/6部分带来的位数增加 3 前向变换带阿里的位数增加
int add = (cu.m_slice->m_sliceType == I_SLICE ? 171 : 85) << (qbits - 9);
//量化后右移可能会带来低位上的损失,这里对右移可能带阿里的损失进行补偿,
//HEVC规定I_SLICE补偿1/3,其他类型slice补偿1/6
//结合量化公式I_SLICE中的add实际相当于,add >> qbits = (171 << (qbits-9)) >> qbits = 171>>9 = 171/512 = 1/3
//非I_SLICE中add实际相当于,add >> qbits = (85 << (qbits - 9)>>qbits = 85>>9 = 85/512 = 1/6)
int numCoeff = 1 << (log2TrSize * 2); //当前变换块中包含得系数个数
numSig = primitives.quant(m_resiDctCoeff, quantCoeff, deltaU, coeff, qbits, add, numCoeff);
//进行常规量化,参考C语言函数quant_c,返回值为量化后的非零系数个数
if (numSig >= 2 && cu.m_slice->m_pps->bSignHideEnabled)
//假设非零系数个数 > 2 ,并且使能符号位隐藏,则进行符号位隐藏操作。
{
TUEntropyCodingParameters codeParams;
cu.getTUEntropyCodingParameters(codeParams, absPartIdx, log2TrSize, isLuma);
numSig = signBitHidingHDQ(coeff, deltaU, numSig, codeParams, log2TrSize);
}
}
}
if (!numSig && b_noSkipLastTuInRow && ttype == TEXT_CHROMA_V )
{
CUData *nextRowFirstCTU = cu.m_encData->getPicCTU(cu.m_cuAddr + 1);
int nextCuCbf = 0;
nextCuCbf = nextRowFirstCTU->getQtRootCbf(0);
if (nextCuCbf)
return numSig; //next row first cu will code dqp
else
{
int curQp = cu.m_qp[absPartIdx];
int curRefQp = cu.getRefQP(0);
int32_t nexCuQP = nextRowFirstCTU->m_qp[0];
assert(curQp == nexCuQP);
if (curQp != curRefQp && !cu.getCbf(absPartIdx, TEXT_LUMA, 0) && !cu.getCbf(absPartIdx, TEXT_CHROMA_U, 0))
{
coeff[0] = 1;
numSig = 1;
}
};
}
return numSig;
}