x265 中的变换操作

一 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值