【x265编码器】章节8——变换量化模块

 系列文章目录

   HEVC视频编解码标准简介

【x264编码器】章节1——x264编码流程及基于x264的编码器demo

【x264编码器】章节2——x264的lookahead流程分析

【x264编码器】章节3——x264的码率控制

【x264编码器】章节4——x264的帧内预测流程

【x264编码器】章节5——x264的帧间预测流程

【x264编码器】章节6——x264的变换量化

【x265编码器】章节1——lookahead模块分析

【x265编码器】章节2——编码流程及基于x265的编码器demo

【x265编码器】章节3——帧内预测流程

【x265编码器】章节4——帧间预测流程

【x265编码器】章节5——x265帧间运动估计流程

【x265编码器】章节6——x265的码率控制

【x265编码器】章节7——滤波模块

【x265编码器】章节8——变换量化模块



前言

x265完整的流程框架如下:

一、变换量化模块

变换量化的类型总的来说,有以下几种情况:

1.useTransformSkip模式,此时会直接复制到量化内存中;

2.4x4帧内模式,使用dst4x4进行4x4的dst变换;

3.常规的dct变换;

4.如果开启了m_rdoqLevel,此时采用RDOQ的变换量化操作;

5.使用m_quantCoef进行常规量化操作;

对应代码分析如下:

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)
{   //首先,根据log2TrSize计算出变换的尺寸索引sizeIdx
    const uint32_t sizeIdx = log2TrSize - 2;
    //如果cu.m_tqBypass[0]为真,表示使用旁路量化(bypass),直接进行系数复制操作,并返回复制的系数个数。否则,继续执行后续的变换和量化操作
    if (cu.m_tqBypass[0])
    {
        X265_CHECK(log2TrSize >= 2 && log2TrSize <= 5, "Block size mistake!\n");
        return primitives.cu[sizeIdx].copy_cnt(coeff, residual, resiStride);
    }
    //判断是否为亮度分量(isLuma)和是否使用PsyRDOQ(usePsy)。PsyRDOQ是一种基于心理视觉模型的变换系数量化优化方法
    bool isLuma  = ttype == TEXT_LUMA;
    bool usePsy  = m_psyRdoqScale && isLuma && !useTransformSkip;
    int transformShift = MAX_TR_DYNAMIC_RANGE - X265_DEPTH - log2TrSize; // Represents scaling through forward transform 计算变换的位移量transformShift,用于表示通过正向变换进行的缩放
    //检查变换尺寸是否超出了最大允许的尺寸
    X265_CHECK((cu.m_slice->m_sps->quadtreeTULog2MaxSize >= log2TrSize), "transform size too large\n");
    if (useTransformSkip)
    {//如果使用变换跳过(Transform Skip),则根据transformShift的正负值调用不同的函数对残差进行复制和移位操作
#if X265_DEPTH <= 10
        X265_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
    {   //否则,根据不同的情况调用相应的变换函数对残差进行变换,如4x4变换、8x8变换或其他尺寸的变换
        bool isIntra = cu.isIntra(absPartIdx);

        if (!sizeIdx && isLuma && isIntra)
            primitives.dst4x4(residual, m_resiDctCoeff, resiStride);
        else
            primitives.cu[sizeIdx].dct(residual, m_resiDctCoeff, resiStride);

        /* NOTE: if RDOQ is disabled globally, psy-rdoq is also disabled, so
         * there is no risk of performing this DCT unnecessarily */
        if (usePsy)
        {   //如果启用了PsyRDOQ,则对源像素进行DCT变换,并将结果存储在m_fencDctCoeff中
            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)
        {//如果启用了噪声减弱(Denoising)功能,并且存在噪声减弱参数,则根据不同的情况调用相应的噪声减弱函数对变换系数进行处理
            /* 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]++;
        }
    }
    //如果启用了RDOQ(Rate-Distortion Optimization Quantization),则根据不同的变换尺寸调用相应的RDOQ函数对系数进行量化和优化
    if (m_rdoqLevel)
        return (this->*rdoQuant_func[log2TrSize - 2])(cu, coeff, ttype, absPartIdx, usePsy);
    else
    {   //否则,根据量化参数和变换系数的值进行量化操作,并返回非零系数的个数
        int deltaU[32 * 32];

        int scalingListType = (cu.isIntra(absPartIdx) ? 0 : 3) + ttype;
        int rem = m_qpParam[ttype].rem;
        int per = m_qpParam[ttype].per;
        const int32_t* quantCoeff = m_scalingList->m_quantCoef[log2TrSize - 2][scalingListType][rem];

        int qbits = QUANT_SHIFT + per + transformShift;
        int add = (cu.m_slice->m_sliceType == I_SLICE ? 171 : 85) << (qbits - 9);
        int numCoeff = 1 << (log2TrSize * 2);

        uint32_t numSig = primitives.quant(m_resiDctCoeff, quantCoeff, deltaU, coeff, qbits, add, numCoeff);
        //如果非零系数的个数大于等于2并且启用了符号隐藏(Sign Hide)功能,则对系数进行符号隐藏处理
        if (numSig >= 2 && cu.m_slice->m_pps->bSignHideEnabled)
        {
            TUEntropyCodingParameters codeParams;
            cu.getTUEntropyCodingParameters(codeParams, absPartIdx, log2TrSize, isLuma);
            return signBitHidingHDQ(coeff, deltaU, numSig, codeParams, log2TrSize);
        }
        else//返回非零系数的个数
            return numSig;
    }
}


点赞、收藏,会是我继续写作的动力!赠人玫瑰,手有余香。

`self.dist_func = dist_func` 是 Python 中类的一个方法,其作用是将输入的 `dist_func` 函数赋值给类的实例属性 `dist_func`。 例如,当我们定义一个类 `Point`,并且有一个方法 `set_dist_func`,其作用是将一个函数作为参数传入,并将该函数赋值给实例属性 `dist_func`,代码如下: ``` class Point: def __init__(self, x, y): self.x = x self.y = y self.dist_func = None def set_dist_func(self, dist_func): self.dist_func = dist_func ``` 在这个例子中,我们定义了一个 `Point` 类,该类有 `x` 和 `y` 两个实例属性,以及一个名为 `set_dist_func` 的方法,该方法将一个函数作为参数传入,并将其赋值给实例属性 `dist_func`。 例如,我们定义一个计算两个点之间欧几里得距离的函数 `euclidean_distance`,然后通过 `set_dist_func` 方法将该函数赋值给一个 `Point` 实例的 `dist_func` 属性,代码如下: ``` import math def euclidean_distance(p1, p2): return math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2) p1 = Point(0, 0) p2 = Point(3, 4) p1.set_dist_func(euclidean_distance) print(p1.dist_func(p1, p2)) # 输出 5.0 ``` 在这个例子中,我们创建了两个 `Point` 实例 `p1` 和 `p2`,其中 `p1` 的坐标为 `(0, 0)`,`p2` 的坐标为 `(3, 4)`。然后,我们将 `euclidean_distance` 函数通过 `set_dist_func` 方法赋值给 `p1` 实例的 `dist_func` 属性。最后,我们调用 `p1.dist_func(p1, p2)` 方法计算 `p1` 和 `p2` 之间的欧几里得距离,并输出结果 `5.0`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值