x265对语法元素cu_qp_delta_abs的编码(后缀及符号位部分)

先看后缀部分。

根据标准,语法元素cu_qp_delta_abs的后缀值suffixVal = cu_qp_delta_abs − 5。

void Entropy::codeDeltaQP(const CUData& cu, uint32_t absPartIdx)
{
    int dqp = cu.m_qp[absPartIdx] - cu.getRefQP(absPartIdx);

    int qpBdOffsetY = QP_BD_OFFSET;

    dqp = (dqp + 78 + qpBdOffsetY + (qpBdOffsetY / 2)) % (52 + qpBdOffsetY) - 26 - (qpBdOffsetY / 2);

    uint32_t absDQp = (uint32_t)((dqp > 0) ? dqp  : (-dqp));//语法元素cu_qp_delta_abs的值
    uint32_t TUValue = X265_MIN((int)absDQp, CU_DQP_TU_CMAX);//语法元素的前缀值
    writeUnaryMaxSymbol(TUValue, &m_contextState[OFF_DELTA_QP_CTX], 1, CU_DQP_TU_CMAX);//使用截断一元码(TU)二元化方法将前缀转换为二元码,在对每一位比特进行常规编码。
    if (absDQp >= CU_DQP_TU_CMAX)
        writeEpExGolomb(absDQp - CU_DQP_TU_CMAX, CU_DQP_EG_k);使用EGK二元化方法将后缀转换为二元码,在对每一位比特进行旁路编码。

    if (absDQp > 0)
    {
        uint32_t sign = (dqp > 0 ? 0 : 1);
        encodeBinEP(sign);对符号进行旁路编码
    }
}

codeDeltaQP函数中判断当absDQp>=CU_DQP_TU_CMAX(5)时也就是存在后缀值时对其进行编码。

标准中确定使用零阶指数哥伦布编码对其进行二值化。该过程在writeEpGolomb函数中完成,传入的参数第一个就是后缀值,第二个参数值CU_DQP_EG_k(0)表示指数哥伦布编码的阶数。

void Entropy::writeEpExGolomb(uint32_t symbol, uint32_t count)
{
    uint32_t bins = 0;
    int numBins = 0;

    while (symbol >= (uint32_t)(1 << count))
    {
        bins = 2 * bins + 1;
        numBins++;
        symbol -= 1 << count;
        count++;
    }

    bins = 2 * bins + 0;
    numBins++;

    bins = (bins << count) | symbol;
    numBins += count;

    X265_CHECK(numBins <= 32, "numBins too large\n");
    encodeBinsEP(bins, numBins);
}

函数中变量bins代表二值化后的二进制串,numBins就表示二进制串的长度。首先讲解下无符号数的零阶指数哥伦布编码的原理:分为前缀码和后缀码,计算M=log2(V+1),且向下取整,表示前缀码中‘1’的个数,最后加上‘0’得到前缀码,计算INFO=V+1-2的M次,它的二进制串就是后缀码。

简单点表示就是前缀码最后一个‘1’为分界线,前面有n个零,且满足 V-2的n-1次+...+2的0次<2的n次,  而V-2的n-1次+...+2的0次 得到的结果的二进制串就是后缀码。x265中也是利用这一点,while循环中就确定了前缀码中‘1’的个数,并且symbol已经被减去了2的幂次求和,输出分界‘1’,然后将bins左移count(n)位,填入symbol,完成二值化。

将二进制串bins和长度numBins传入encodeBinEP函数实现旁路编码。

void Entropy::encodeBinsEP(uint32_t binValues, int numBins)
{
    if (!m_bitIf)
    {
        m_fracBits += 32768 * numBins;
        return;
    }

    while (numBins > 8)
    {
        numBins -= 8;
        uint32_t pattern = binValues >> numBins;
        m_low <<= 8;
        m_low += m_range * pattern;
        binValues -= pattern << numBins;
        m_bitsLeft += 8;

        if (m_bitsLeft >= 0)
            writeOut();
    }

    m_low <<= numBins;
    m_low += m_range * binValues;
    m_bitsLeft += numBins;

    if (m_bitsLeft >= 0)
        writeOut();
}

第一个if语句是通过估计的方法实现编码,这里就不管了。

函数中通过while循环逐个传入字节,应该是为了控制码流,这里不进行解释。先看旁路编码的流程图

                                        

循环中以8位为一个单位进行算术编码,首先编码高8位,流程图中需要逐位完成的上半部分,函数中直接一步完成,也就是直接将L左移8位,然后L=L*R,两者的结果是相同的。调整完变量后,由writeOut函数根据L来输出码流。while循环后的部分步骤相同,只是numBins<8。

最后是对符号位的编码,非常简单,encodeBinEP(sign),对其进行旁路编码          

void Entropy::encodeBinEP(uint32_t binValue)
{
    if (!m_bitIf)
    {
        m_fracBits += 32768;
        return;
    }
    m_low <<= 1;
    if (binValue)
        m_low += m_range;
    m_bitsLeft++;

    if (m_bitsLeft >= 0)
        writeOut();
}
与上述同样的步骤对其进行编码。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值