x265对变换系数的编码(其余非零系数的位置及非零系数幅值编码部分)

该部分的代码较长,本文分段讲解。

    // code significance flag
    uint8_t * const baseCoeffGroupCtx = &m_contextState[OFF_SIG_CG_FLAG_CTX + (bIsLuma ? 0 : NUM_SIG_CG_FLAG_CTX)];
    uint8_t * const baseCtx = bIsLuma ? &m_contextState[OFF_SIG_FLAG_CTX] : &m_contextState[OFF_SIG_FLAG_CTX + NUM_SIG_FLAG_CTX_LUMA];
    uint32_t c1 = 1;
    int scanPosSigOff = scanPosLast - (lastScanSet << MLS_CG_SIZE) - 1;
    int absCoeff[1 << MLS_CG_SIZE];
    int numNonZero = 1;
    unsigned long lastNZPosInCG;
    unsigned long firstNZPosInCG;
    absCoeff[0] = int(abs(coeff[posLast]));

先看能表明实际意义的变量,baseCoeffGroupCtx和baseCtx变量是根据当前编码的是色度块还是亮度块确定的上下文模型的基地址,分别用于CSBF和sig_coeff_flag语法元素的编码,两者之间的差值是由标准决定的。

前面的文章已经解释过scanPosLast表示的扫描后最后一个非零系数的位置,lastScanSet是由scanPosLast>>MLS_CG_SIZE(4)得来的,表示最后一个非零系数所在的CG的位置,而此处又将lastScanSet<<MLS_CG_SIZE(4)表示该CG第一个系数的位置,scanPosLast减去该值得到最后一个非零系数在CG中的位置,之前是在TB中的位置,最后“-1”就表示前一个系数。因此scanPosSigOff就表示最后一个非零系数的前一个系数的在CG中的位置。注意下这里所描述的位置都是扫描后的,也就是按照扫描顺序确定的。

创建了长度为16的数组absCoeff,用于存储CG中系数幅值的绝对值。在最后一条语句中,posLast也是前面解释过的,表示扫描前最后一个非零系数的位置。根据该变量取出其幅值并存入absCoeff[0]中。

    for (int subSet = lastScanSet; subSet >= 0; subSet--)
    {
        const uint32_t subCoeffFlag = coeffFlag[subSet];
        uint32_t scanFlagMask = subCoeffFlag;
        int subPosBase = subSet << MLS_CG_SIZE;
        
        if (subSet == lastScanSet)
        {
            X265_CHECK(scanPosSigOff == scanPosLast - (lastScanSet << MLS_CG_SIZE) - 1, "scanPos mistake\n");
            scanFlagMask >>= 1;
        }

        // encode significant_coeffgroup_flag
        const int cgBlkPos = codingParameters.scanCG[subSet];
        const int cgPosY   = cgBlkPos >> (log2TrSize - MLS_CG_LOG2_SIZE);
        const int cgPosX   = cgBlkPos & ((1 << (log2TrSize - MLS_CG_LOG2_SIZE)) - 1);
        const uint64_t cgBlkPosMask = ((uint64_t)1 << cgBlkPos);

        if (subSet == lastScanSet || !subSet)
            sigCoeffGroupFlag64 |= cgBlkPosMask;
        else
        {
            uint32_t sigCoeffGroup = ((sigCoeffGroupFlag64 & cgBlkPosMask) != 0);
            uint32_t ctxSig = Quant::getSigCoeffGroupCtxInc(sigCoeffGroupFlag64, cgPosX, cgPosY, cgBlkPos, (trSize >> MLS_CG_LOG2_SIZE));
            encodeBin(sigCoeffGroup, baseCoeffGroupCtx[ctxSig]);
        }

这里开始就进入了对TB内所有含有非零系数的CG循环,该循环一直覆盖到函数的结尾,这也就表明对剩余的语法元素是逐个CG进行编码的,且其顺序就是扫描的顺序。

函数之前的部分已经将每个CG的sig_coeff_flag语法元素存储于coeffFlag数组中,数组中一个数的有效长度为16,分别表示每个系数的flag,提取出当前CG的参数并存储于变量subCoeffFlag和scanFlagMask中。subPosBase就表示当前CG第一个系数在TB中的位置。

if语句判断当前CG是否为最后一个非零系数所在的CG,若是,则右移一位,也就是跳过最后一个非零系数的sig_coeff_flag,因为sig_coeff_flag的存储顺序是在TB中靠前的在高位。且对最后一个非零系数的部分操作已在循环外完成,absCoeff[0]的确定和numNonZero = 1,这些和后续对赋值信息的编码有关。

接下来的部分就开始对当前CG的CSBF(coded_sub_block_flag)语法元素进行编码,先根据扫描顺序,反向推出扫描前当前CG在TB中的位置,log2TrSize - MLS_CG_LOG2_SIZE表示TB中CG块的规模,然后通过常规的操作(不解释了)得到当前CG块在TB中的XY坐标。

cgBlkPosMask=1 << cgBlkPos用二进制串中‘1’的位置来表示当前CG在TB中的位置。

if语句判断当前CG是否为最后一个非零系数所在的CG或者是否为第一个CG,sigCoeffGroupFlag64 |= cgBlkPosMask就是将sigCoeffGroupFlag64该二进制串对应的位置直接置‘1’,因为第一个CG必然存在DC系数(直流分量),另一个就不用说了,两者的CSBF必然为‘1’,不需要进行编码,但感觉这个或运算有点多余。

其他情况就需要编码CSBF语法元素。sigCoeffGroupFlag64 & cgBlkPosMask取出CSBF并存储于sigCoeffGroup变量中。

对CSBF语法元素的编码具有一定的规范,它的上下文模型由右侧和下侧的CG的CSBF值决定,所以这里就需要通过getSigCoeffGroupCtxInc函数的执行来获取当前CG的CSBF的上下文模型。

    /* Context derivation process of coeff_abs_significant_flag */
    static uint32_t getSigCoeffGroupCtxInc(uint64_t cgGroupMask, uint32_t cgPosX, uint32_t cgPosY, uint32_t cgBlkPos, uint32_t trSizeCG)
    {
        X265_CHECK(cgBlkPos < 64, "cgBlkPos is too large\n");
        // NOTE: unsafe shift operator, see NOTE in calcPatternSigCtx
        const uint32_t sigPos = (uint32_t)(cgGroupMask >> (cgBlkPos + 1)); // just need lowest 8-bits valid
        const uint32_t sigRight = ((int32_t)(cgPosX - (trSizeCG - 1)) >> 31) & sigPos;
        const uint32_t sigLower = ((int32_t)(cgPosY - (trSizeCG - 1)) >> 31) & (sigPos >> (trSizeCG - 1));

        return (sigRight | sigLower) & 1;
    }

sigPos=cgGroupMask >> (cgBlkPos + 1)表示直接去除位置在当前CG之前的CG的CSBF的值(包括自己的),将之后一个CG的CSBF值置于最后一位。

cgPosX - (trSizeCG - 1)) 表示若X在右边界,也就是右边没CG块了,则该式值为零,否则都为负数,然后右移31位,也就是用符号位填充该变量。cgPosY - (trSizeCG - 1)) >> 31也具有相同的含义。

(sigRight | sigLower) & 1由该式可以知道对于变量sigRight和sigLower,我们只需要最后一位,所以对这两个变量的计算中,我们也只需考虑最后一位。相与取出下一个CG的CSBF的值,X方向直接相与,但Y方向还需要右移trSizeCG-1(最大纵坐标),将Y方向的下一个CG的CSBF的值置于该变量的最后一位。

最后只要右侧或下侧的CG的CSBF值为‘1’函数就返回‘1’,否则返回‘0’。

确定了偏移量ctxSig,在基地址baseCoeffGroupCtx的基础上也就确定了当前CG的CSBF编码的上下文模型,然后对其进行常规编码。

        // encode significant_coeff_flag
        if (sigCoeffGroupFlag64 & cgBlkPosMask)
        {
            X265_CHECK((log2TrSize != 2) || (log2TrSize == 2 && subSet == 0), "log2TrSize and subSet mistake!\n");
            const int patternSigCtx = Quant::calcPatternSigCtx(sigCoeffGroupFlag64, cgPosX, cgPosY, cgBlkPos, (trSize >> MLS_CG_LOG2_SIZE));
            const uint32_t posOffset = (bIsLuma && subSet) ? 3 : 0;

开始编码sig_coeff_flag语法元素。

if语句判断当前CG的CSBF值是否为‘1’,也就是当前CG是否存在非零系数,该if语句所包括的代码完成了当前CG内所有sig_coeff_flag的编码。

这里需要解释下,sig_coeff_flag的上下文模型的选取与其他参数有一定的关系:

对于8×8、16×16和32×32的TB,sig_coeff_flag的上下文模型索引的选择不仅跟当前CG的下、右侧CG的CSBF取值有关,还与当前变换系数在CG中的位置有关,根据当前CG的下、右侧CG的CSBF的取值分成4种模式,在当前CG中,每一种模式不同的位置对应着不同的上下文索引。

对于4×4的TB,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值