算术编码是一种常用的变长编码方法,和Huffman编码类似,也是对出现概率大的符号赋予短码,出现概率小的符号赋予长码,但算术编码不是简单的将每个信源符号映射成一个码字,而是对整个输入序列分配一个码字,所以平均意义上可以为每个信源符号分配长度小于1的码字。

算术编码的原理参考:熵编码:算术编码

H.266/VVC中采用的是基于上下文的自适应二进制算术编码(Context-based Adaptive Binary Arithmetic Coding,CABAC)。CABAC将二进制算术编码和上下文模型结合起来,其主要特点为:

(1)采用二进制算术编码,将所有语法元素转化为二进制串,消除了乘法运算操作,降低了计算复杂度,提高了编码效率

(2)建立上下文模型,充分利用了符号间的相关性,根据已编码的符号自适应的进行模型更新,进一步提高了编码效率。

VVC并不是对所有语法元素使用算术编码的方法,而是对于不同的语法元素使用不同的编码方法。如对比特流高层特性的语法元素,本身信息量较小,采用定长编码;对于比特流中比例较大的残差系数等信息,使用CABAC编码。

CABAC主要经过以下三个步骤:

  1. 二进制化
  2. 上下文建模
  3. 二进制算术编码

CABAC首先对输入的非二进制语法元素进行二进制化处理,将其唯一地转换为二进制串。如果输入的本身就是二进制语法元素,则可以跳过二进制化步骤。二进制化之后,进入二进制算术编码阶段。二进制算术编码过程中分为常规编码模式和旁路编码模式。在常规编码模式中,首先根据先前编码的语法元素为其选择一个上下文模型,接着,将上下文模型和二进制值一起送到常规编码器中进行编码,并将结果输出到码流中,同时,根据当前编码的二进制值更新上下文模型。在旁路编码模式中,无需对概率模型进行更新,将二进制值0和1看作等概分布,使用各占1/2的固定概率进行编码。

1、二进制化

CABAC仅对二进制符号0或1进行编码,因此对于非二进制符号需要将其转换为二进制串。VVC中常用的二进制化方法包括截断莱码(truncated Rice (TR) )、截断二进制码(truncated binary (TB) )、k阶指数哥伦布码(the k-th order Exp-Golomb (EGk) binarization )和定长码( fixed-length (FL))。
H.266/VVC中的二进制化参考:H.266/VVC熵编码之二进制化

2、二进制算术编码

算术编码流程:将输入的语法元素经过二进制化转换为一个或者多个bin值,根据bin值对应的ctxId选择上下文模型,并根据当前的bin值选择上下文模型,编码完后,再根据bin值更新上下文模型(更新区间起始点和区间宽度)。

参数介绍:

  • m_low:区间起始点(区间左端点)
  • m_range:区间宽度
  • ctxId:模型对应的索引号
  • lps:小概率符号
  • mps:大概率符号
  • LPS:小概率符号对应的区间宽度
  • MPS:大概率符号对应的区间宽度
  • m_state[0],m_state[1]:模型状态概率,H.266 中采用多概率模型。
  • m_rate:用于更新模型状态概率

CABAC的算术编码的编码区间宽度m_range的取值为(),通常区间左侧子区间为mps对应的子区间,右侧子区间为lps对应的子区间。

CABAC的常规编码流程:

  1. 确定区间起始点m_low和区间宽度m_range
  2. 根据ctxId选择相应的上下文模型,再根据m_range和模型对应的状态概率m_state确定LPS,更新m_range=m_range-LPS
  3. 根据模型状态概率确定mps,再判断待编码的bin值是mps还是lps
  4. 更新m_low和m_range,如果bin=lps,则m_Low=m_Low+m_Range,m_range=LPS;若bin=mps,则m_Low=m_Low,m_Range=m_Range
  5. 根据bin值和m_rate更新上下文模型状态概率
  6. 如果新的编码区间宽度m_range超过了()的范围,需要对m_range进行重归一化,即对m_low和m_range同时左移一定位数,并根据需要输出一定比特。

VVC解码端的常规编码流程如下图所示:

(其中pStateidx1和pStateIdx2指代m_state[0],m_state[1],ivlCurrRange指代m_range,valMps指代mps,ivlLpsRange指代LPS,ivlOffset指代m_low)

VVC解码端的重归一化流程如下图所示:

VTM中代码实现:

template <class BinProbModel>
void TBinEncoder<BinProbModel>::encodeBin( unsigned bin, unsigned ctxId ) //常规编码
{BinCounter::addCtx( ctxId );BinProbModel& rcProbModel = m_Ctx[ctxId]; //获得相应上下文模型uint32_t      LPS         = rcProbModel.getLPS( m_Range ); //获得LPS的区间长度DTRACE( g_trace_ctx, D_CABAC, "%d" " %d " "%d" "  " "[%d:%d]" "  " "%2d(MPS=%d)"  "  " "  -  " "%d" "\n", DTRACE_GET_COUNTER( g_trace_ctx, D_CABAC ), ctxId, m_Range, m_Range - LPS, LPS, ( unsigned int ) ( rcProbModel.state() ), bin == rcProbModel.mps(), bin );m_Range   -=  LPS; //更新 range = range - LPS(即MPS)if( bin != rcProbModel.mps() ) //如果当前符号是lps{int numBits   = rcProbModel.getRenormBitsLPS( LPS ); // 通过查表获取需要左移的位数(用于重归一化)m_bitsLeft   -= numBits;m_Low        += m_Range; //移动m_lowm_Low         = m_Low << numBits;m_Range       = LPS   << numBits;if( m_bitsLeft < 12 ) // m_bitsLeft 表示存储(m_Low)的寄存器中还剩余多少 bit{writeOut();}}else //如果当前符号是mps,则不需要更新m_low和m_range{if( m_Range < 256 ) // 如果更新后的区间宽度小于256,则需要重归一化{int numBits   = rcProbModel.getRenormBitsRange( m_Range ); // 获取需要左移的位数:1m_bitsLeft   -= numBits;m_Low       <<= numBits; // 输出比特m_Range     <<= numBits; // 扩大区间宽度if( m_bitsLeft < 12 ){writeOut();}}}rcProbModel.update( bin ); //更新模型概率状态BinEncoderBase::m_BinStore.addBin( bin, ctxId );
}

其中,getLPS函数用来得到lps的区间宽度,mps函数用来计算大概率符号值

  uint8_t getLPS(unsigned range) const{uint16_t q = state();if (q & 0x80) //如果 q > 128q = q ^ 0xff; //q按位取反后小于127 return ((q >> 2) * (range >> 5) >> 1) + 4; // 返回LPS的区间长度}uint8_t state() const { return (m_state[0] + m_state[1]) >> 8; } // 表示P(1)的概率 pStateuint8_t mps() const { return state() >> 7; } // valMps 返回MPS的值0 or 1

上下文模型的初始化与更新

在算术编码开始前,需要初始化上下文模型,上下文模型的初始化是为了利用每个模型的 initValue 得到一个初始的模型概率状态(m_state[0], m_state[1]),并在模型中加入了一个窗口参数(WindowSize),由此得到了一个控制上下文模型更新的参数 m_rate。

void BinProbModel_Std::init( int qp, int initId )
{int slope = (initId >> 3) - 4;int offset = ((initId & 7) * 18) + 1;int inistate = ((slope   * (qp - 16)) >> 1) + offset;int state_clip = inistate < 1 ? 1 : inistate > 127 ? 127 : inistate;const int p1 = (state_clip << 8);m_state[0]   = p1 & MASK_0;m_state[1]   = p1 & MASK_1;
}void setLog2WindowSize(uint8_t log2WindowSize) //根据WindowSize设置pstate的更新速率{int rate0 = 2 + ((log2WindowSize >> 2) & 3);int rate1 = 3 + rate0 + (log2WindowSize & 3);m_rate    = 16 * rate0 + rate1;CHECK(rate1 > 9, "Second window size is too large!");}

上下文模型的更新主要是根据当前编码bin值和更新速率m_rate更新m_state[0],m_state[1]。

update函数用来更新模型状态概率

  void update(unsigned bin) // 上下文状态概率更新过程{int rate0 = m_rate >> 4;int rate1 = m_rate & 15;m_state[0] -= (m_state[0] >> rate0) & MASK_0;m_state[1] -= (m_state[1] >> rate1) & MASK_1;if (bin){m_state[0] += (0x7fffu >> rate0) & MASK_0;m_state[1] += (0x7fffu >> rate1) & MASK_1;}}

3、旁路编码

旁路编码假定符号0或1各占1/2的概率进行编码,其中 0 区间在前,1 区间在后,同时不需要对概率模型进行更新。

为了使区间划分更加简单,不采用直接对区间长度二等分的方法,而是采用保持编码区间长度不变方法使区间下限 m_low加倍的方法实现区间划分,可以直接通过将 m_low左移 1 位实现,这样既达到了同样的效果又省去了在重归一化过程中同时对 m_range 和 m_low 进行的加倍操作。

VVC解码端的旁路编码流程如下:

VTM代码实现:

void BinEncoderBase::encodeBinEP( unsigned bin )
{DTRACE( g_trace_ctx, D_CABAC, "%d" "  " "%d" "  EP=%d \n", DTRACE_GET_COUNTER( g_trace_ctx, D_CABAC ), m_Range, bin );BinCounter::addEP();// 这里,由于m_range范围为0-510,旁路编码0和1各占一半,因此每编码一次bin需要进行一次重归一化// 左移位数为 1,并输出 1 个比特。这里为了更简便,将对 m_Low 的移位操作提前,并保持m_Range 不变。m_Low <<= 1;if( bin )// 如果编码的符号是1的话(旁路编码中,0的区间在前,1的区间在后,因此需要移动m_low){m_Low += m_Range;}m_bitsLeft--;if( m_bitsLeft < 12 ){writeOut();}
}

其中常规编码和旁路编码的最后,当 m_bitsLeft<12 都会调用writeOut函数进行比特输出
VTM代码具体如下:

void BinEncoderBase::writeOut()
{unsigned leadByte = m_Low >> ( 24 - m_bitsLeft ); // 将m_low的高8位存在LeadByte中m_bitsLeft       += 8;m_Low            &= 0xffffffffu >> m_bitsLeft;if( leadByte == 0xff ){m_numBufferedBytes++;}else{if( m_numBufferedBytes > 0 ){unsigned carry  = leadByte >> 8;unsigned byte   = m_bufferedByte + carry; // 将上次缓存的8位bit输出m_bufferedByte  = leadByte & 0xff;// 将本次m_low的高8位存入缓存器中m_Bitstream->write( byte, 8 ); //输出byte            = ( 0xff + carry ) & 0xff;while( m_numBufferedBytes > 1 ){m_Bitstream->write( byte, 8 );m_numBufferedBytes--;}}else{m_numBufferedBytes  = 1;m_bufferedByte      = leadByte;}}
}

H.266/VVC技术学习:算术编码相关推荐

  1. H.266/VVC技术学习之帧内模式编码

    在HEVC中,支持33种角度模式.DC模式和Planar模式,为了减少编码比特,使用长度为3的最可能模式列表.在VVC中,引入了ISP模式.MRL模式.MIP模式等,帧内模式编码时需要先对这些模式的f ...

  2. H.266/VVC技术学习:色度联合编码(JCCR)技术

    VVC支持色度联合编码(joint coding of chroma residual ,JCCR)模式. 色度联合编码模式的使用(激活)由TU级标志tu_joint_cbcr_residual_fl ...

  3. H.266/VVC技术学习:熵编码

    在VVC中,与HEVC中的设计相比,CABAC包含以下主要变化: Core CABAC engine 分离变换块和变换跳过块的残差编码结构. 变换系数的上下文建模 一.Core CABAC engin ...

  4. H.266/VVC技术学习之环路滤波:去块滤波(Deblock)技术

    一.方块效应 目前主流的视频编码标准都是基于分块的混合编码机制,其处理过程是针对每个块单独进行处理的,因此由于编码模式的差异以及量化误差的原因,会导致相邻块重建像素不连续的现象.对于一个两侧强相关性的 ...

  5. H.266/VVC技术学习:帧内预测之PDPC技术

    1.PDPC介绍 为了补偿以上较简单帧内预测模式在利用空间冗余度方面的不足,VVC 中引入了一种根据当前样本的位置及帧内预测模式的角度自适应选取反方向角度上的参考样本信息作为新的一个相对互补性的帧内预 ...

  6. H.266/VVC技术学习54:划分

    文章目录 1 图片被划分为CTU 2 图片被划分为SubPicture.Slice.Tile 2.1 Tile.SLice.SubPicture的概念 2.2 光栅扫描分区模式 2.3 矩形分区模式 ...

  7. H.266/VVC代码学习:帧内预测之角度预测函数(predIntraAng、xPredIntraAng)

    predIntraAng函数 VTM中,帧内预测的角度预测的入口函数为predIntraAng函数,该函数主要是用于进行传统的帧内预测(Planar.DC.角度预测),然后对Planar和DC模式使用 ...

  8. H.266/VVC代码学习20:角度预测入口 / 特殊模式的PDPC技术(predIntraAng)

    1.predIntraAng函数 predIntraAng是帧内0~66这67种预测的入口.其中可细分为: 模式0:PLANAR模式 模式1:DC模式 模式2~66:角度模式 此函数在亮度预测和色度预 ...

  9. H.266/VVC代码学习21:帧内角度预测的实现 / 近对角模式的PDPC(xPredIntraAng)

    xPredIntraAng函数的作用是对任意大小的块和任意模式,如何将参考像素的值根据其模式的角度填充进每一个像素. 下图是basketball drill的一个16*16的块,其预测模式为10(偏斜 ...

最新文章

  1. matlab bp神经网络
  2. 连续投影算法_出货量第一,专利300+,极米科技如何成为“投影一哥”?
  3. 汇编语言典型例子详解_从架构到 RTOS 详解 DSP 和 MCU 的区别和联系
  4. 阿里开源Canal--①简介
  5. camel.js_Camel 2.11 –没有Spring的Camel Web应用程序
  6. Android LBS系列05 位置策略(一)
  7. 安全专家教你如何利用Uber系统漏洞无限制的免费乘坐?
  8. 写录音机时遇到点问题
  9. 盈通785G显卡超频/开核教程
  10. 一条龙教程:Matlab下使用yalmip(工具箱)+cplex(求解器)
  11. 汉语言文学如何利用计算机思维,计算机在应用于汉语言文学时产生的优势与局限.PDF...
  12. 计算机中取消打印任务,取消打印任务的具体方法步骤
  13. 讲座报名|美团自动配送技术:感知预测与规划控制
  14. 天翼阅读倾情回馈——《海盗鬼皮书》限时免费畅读
  15. 【Monkey Run】Excel编程 VBA
  16. 语音识别中的WFST和语言模型
  17. 暗黑破坏神资源 - 紫冰整理
  18. 【例3.10】简单计算器
  19. 计算机显示器的三原色是,显示器参数看不懂?看完你就明白啦!
  20. JAVA基础学习-复习day11

热门文章

  1. 基于Java的截图工具
  2. codeforces C2. Pokémon Army (hard version)(模拟)
  3. Vue-routers(步骤)
  4. 计算机专业研究生核心能力培养(1)——论文阅读与积累
  5. DMM数据管理能力成熟度模型简介
  6. excel work
  7. Springboot网站第三方登录——QQ登录
  8. 了解cuda和显卡等基本概念
  9. MATLAB绘制雷达图并导出矢量图到Visio编辑(论文用图)
  10. 龙芯Fedora21平台制作feodra21-loongson-app docker镜像