您的位置:首页 > 其它

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

2018-03-18 21:06 411 查看
先看后缀部分。
根据标准,语法元素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();
}与上述同样的步骤对其进行编码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  x265 HEVC 熵编码