HEVC所使用的熵编码方法为CABAC(基于上下文的自适应二进制算术编码),大致分为三个步骤:
1、二值化
2、上下文建模(模型初始化、更新)
3、二进制算术编码
对该语法元素的编码是在codeDeltaQP函数中完成的。
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);对符号进行旁路编码
}
}
一、二值化
根据HEVC的标准,cu_qp_delta_abs语法元素的前缀部分的值为:prefixVal=Min(cu_qp_delta_abs,5)。并且对prefixVal的编码使用截断莱斯二进制化方法(TR),TR方法的两个参数cMax=5、cRiceParam = 0。x265中将cu_DQP_TU_CMAX(cMAX)和TUValue(prefixVal)两个参数传递给writeUnaryMaxSymbol函数并在其中完成二值化,该函数也同时对其进行编码,这之后会讲。
void Entropy::writeUnaryMaxSymbol(uint32_t symbol, uint8_t* scmModel, int offset, uint32_t maxSymbol)
{
X265_CHECK(maxSymbol > 0, "maxSymbol too small\n");
encodeBin(symbol ? 1 : 0, scmModel[0]);
if (!symbol)
return;
bool bCodeLast = (maxSymbol > symbol);
while (--symbol)
encodeBin(1, scmModel[offset]);
if (bCodeLast)
encodeBin(0, scmModel[offset]);
}
该函数中symbol为语法元素值,maxSymbol为门限值cMAX,莱斯参数R(cRiceParam)=0,其他参数先不管。TR分为前缀码和后缀码,两者分开求取。
前缀值P=V>>R,所以P=symbol,第一种情况:若P小于值(cMax>>R)=maxSymbol,则前缀码由P个‘1’和一个‘0’组成,当symbol为0时,P等于0,所以前缀就只有一个0,而p不等于0时,前缀码的第一个值就为1,所以对第一位进行编码的encodeBin函数的第一个参数就可以确定,剩余的‘1’在while循环中编码完成,因为循环条件为(--symbol)所以会编码symbol-1个‘1’。到此也就完成了P个‘1’的编码,且symbol<maxSymbol,bCodeLast