【HEVC学习与研究】26、HEVC的算数编码实现

关于HEVC的前25篇博文全文发表在新浪博客,地址为:http://blog.sina.com.cn/s/articlelist_1376260467_0_1.html。从第26篇开始博客全文发在CSDN,新浪同步更新摘要和链接地址。

在第13篇博文中贴出了我们在调试代码时所采用的二进制码流的开头一部分数据,并根据这些数据进行了NAL Header解析、参数集合解析和条带头解析等信息的分析。今后的博文中如无特殊情况依然会采用这些数据作为学习材料。

通过对这段码流进行分析,我们可以看出,在对于一个slice进行解码的过程中,对slice header和slice data采取了不同的熵解码方法:slice header的语法元素采用了变长码(主要是指数哥伦布编码),而slice data的语法元素采用的是算术编码CABAC。由于对CABAC的基本概念不是非常熟悉,之前一段时间一直在回顾H.264中CABAC相关的知识,现在来看HEVC重的算术编码相对于H.264做了什么样的改变。

1、数据准备

同JM86中在每一个slice中为算术编码进行数据准备不同,HM 10.0 decoder在程序的一开始就为CABAC解码的过程在做准备。在main函数中的最开始会定义一个解码器应用的实例:

TAppDecTop cTAppDecTop; 

在TAppDecTop这个类的声明中可以看出,该类包含一个TDecTop类型的成员m_cTDecTop,这个对象将实现对编码码流的解码。在TDecTop这一层还将实现针对不同类型的NAL,如VPS, SPS, PPS, SEI和slice数据等类型的NAL进行分别处理等操作,在TDecTop::decode函数实现。

在TDecTop的声明中找到了一个TDecSbac类型的成员m_cSbacDecoder,根据TDecSbac.cpp开头的简介提示可知,这个类实现的就是上下文自适应的熵解码器类。并且在类的声明中可以得知,该类对码流中的多种不同语法元素定义了相应的解析方法,如parseMVPIdx(), parseSaoMaxUvlc()等。除了这些解析方法之外,TDecSbac中还定义了一个数组存放上下文模型:ContextModel
m_contextModels[MAX_NUM_CTX_MOD];一共512个元素。这些元素的类型ContextModel是专门用来定义上下文模型的类,其成员包括了预定义的LPS和MPS的状态索引m_aucNextStateMPS[ 128 ]、m_aucNextStateLPS[ 128 ]以及表示当前状态的变量m_ucState。在该类的实现函数中,上述两个数组被分别赋予相应的初始值。

2、SbacDecoder的初始化

SbacDecoder类的构造函数内容几乎就是空的,但是该构造函数同时调用了SbacDecoder类成员的构造函数对其进行了初始化,其中数量最多的就是多个ContextModel3DBuffer类型的成员变量分别用于不同类型的语法元素的上下文模型。从命名来看就可以得知这是一个保存上下文模型的三维缓存,其中的成员变量也包含一个指向ContextModel类的指针m_contextModel和三个维度变量m_sizeX、m_sizeXY和m_sizeXYZ。在ContextModel3DBuffer的构造函数里将指定的上下文模型的指针指向m_contextModels[]的某个成员。这些ContextModel3DBuffer类型的语法元素上下文模型都将在TDecSbac的构造函数调用时进行初始化。这一步将完成上下文模型的初始化操作。

3、其他的初始化流程

在主函数decmain.cpp中,调用cTAppDecTop.decode()函数;该函数调用xInitDecLib();该函数会调用m_cTDecTop.init()函数实现TDecTop的初始化;TDecTop::init()函数分别调用了initROM()和m_cGopDecoder、m_cSliceDecoder和m_cEntropyDecoder的init函数进行初始化。在TGopGop的声明中可以看到该类声明了TDecEntropy、TDecSbac等类型的指针。m_cGopDecoder.init()函数的作用就是将TGopGop的各种指针指向了TDecTop的各个实例成员。

4、解码过程

对条带头的解析在TDecEntropy::xDecodeSlice()中实现,在其中调用parseSliceHeader()函数。

码流的实际解析过程在TDecGop::decompressSlice()中实现。decompressSlice函数中,m_pcBinCABAC指向的就是TDecTop中定义的TDecSbac的实例m_cSbacDecoder;另外在对m_pcEntropyDecoder、m_pcSbacDecoder、m_pcSbacDecoders、m_pcBinCABACs完成设置之后,TDecGop::decompressSlice()调用了TDecSlice::decompressSlice()函数进行解码slice_segment_data部分。

slice_segment_data由多个coding_tree_unit( )组成,在色度或亮度sao flag设为true时,coding_tree_unit( )的第一个语法元素为sao(),解析过程由pcSbacDecoder->parseSaoOneLcuInterleaving()实现。该函数第一个实际调用的函数是TDecSbac::parseSaoTypeIdx(),其中进行cabac解码的函数为TDecBinCABAC::decodeBin()函数。

TDecBinCABAC::decodeBin()函数的实现可以参考标准文档的9.3.4.3.2部分。

TDecBinCABAC::decodeBin( UInt& ruiBin, ContextModel &rcCtxModel )
{
    //实现导出ivlLpsRange值的过程:通过选定的上下文模型获取当前状态,通过计算ivlCurrRange的值计算得到qRangeIdx(qRangeIdx =( ivlCurrRange >> 6 ) & 3);
  UInt uiLPS = TComCABACTables::sm_aucLPSTable[ rcCtxModel.getState() ][ ( m_uiRange >> 6 ) - 4 ];
  m_uiRange -= uiLPS; //m_uiRange(标准文档中描述为ivlCurrRange)初始化为510,并变更为ivlCurrRange − ivlLpsRange
  UInt scaledRange = m_uiRange << 7;//计算更新的ivlOffset,这个值是表示算数解码器引擎状态的变量之一,在解码引擎初始化的时候进行赋值

  if( m_uiValue < scaledRange )//根据ivlOffset与ivlCurrRange的对比判断当前属于MPS还是LPS
  {
    // MPS path
    ruiBin = rcCtxModel.getMps();//直接将valMps返回给输出值binVal
    rcCtxModel.updateMPS();//更新MPS的上下文索引,通过查询表m_aucNextStateMPS[]获得

    if ( scaledRange >= ( 256 << 7 ) )
    {
      return;
    }

    m_uiRange = scaledRange >> 6;
    m_uiValue += m_uiValue;

    if ( ++m_bitsNeeded == 0 )
    {
      m_bitsNeeded = -8;
      m_uiValue += m_pcTComBitstream->readByte();
    }
  }
  else
  {
    // LPS path
    Int numBits = TComCABACTables::sm_aucRenormTable[ uiLPS >> 3 ];
    m_uiValue = ( m_uiValue - scaledRange ) << numBits;
    m_uiRange = uiLPS << numBits;
    ruiBin = 1 - rcCtxModel.getMps();//输出值valMps设为1-valMps
    rcCtxModel.updateLPS();//更新LPS的上下文模型索引,通过查询表m_aucNextStateLPS[]获得。

    m_bitsNeeded += numBits;

    if ( m_bitsNeeded >= 0 )
    {
      m_uiValue += m_pcTComBitstream->readByte() << m_bitsNeeded;
      m_bitsNeeded -= 8;
    }
  }
}
时间: 2025-01-31 05:36:16

【HEVC学习与研究】26、HEVC的算数编码实现的相关文章

【HEVC学习与研究】27、CABAC解析语法元素SAO

解析完成条带头之后下一步的工作是解析条带数据slice_segment_data.slice_segment_data数据主要由一个个的Coding_Tree_Unit(CTU)组成.每一个CTU的结构如下所示: coding_tree_unit( ) { xCtb = ( CtbAddrInRs % PicWidthInCtbsY ) << CtbLog2SizeY yCtb = ( CtbAddrInRs / PicWidthInCtbsY ) << CtbLog2SizeY

【HEVC学习与研究】34、HEVC参考软件HM中Intra预测参考像素的获取与管理

继续上一个section所讨论的问题.在section 33中讨论了HEVC帧内预测的几种不同模式,代表这几种模式的函数xPredIntraPlanar.xPredIntraAng和xDCPredFiltering调用的位置位于Void TComPrediction::predIntraLumaAng()中,所以也可以说,在一个PU内,函数Void TComPrediction::predIntraLumaAng实现了亮度分量的帧内预测.该函数的实现方法如下: Void TComPredicti

【HEVC学习与研究】40、X265的下载和编译

[因工作需要,开始研究一下X265的基本使用方法.由于对HEVC的算法.概念的完全理解尚需时日,因此暂时只是考虑一下如何对x265进行下载.编译和测试方法,内部代码的实现未来再进行研究.] 1.安装cmake 在官网下载最新版本即可.截至本文的时间的最新版本为3.2.1版. 2.安装yasm和vld 也是在官网下载即可. 2.下载x265的源代码 下载地址:https://bitbucket.org/multicoreware/x265/downloads/ 下载解压后,内容如下所示: 3.生成

【HEVC学习与研究】39、HEVC帧内编码的原理和实现(上)

[前面N篇博文都讲了一些HEVC帧内预测的代码结构和简单的方法,但是尚未对整体的算法和实现做一个比较完整的描述.本篇借助参考文献<High Efficiency Video Coding (HEVC) -- Algorithms and Architectures>的相关章节的阅读笔记,对HEVC的帧内预测算法做一个比较完整的阐述.] [摘要]:HEVC的帧内预测的架构分为三个步骤:①构建参考像素数组:②生成预测像素:③后处理操作.HEVC标准将这三个步骤进行了精密设计,以求达到较高的编码效率

【HEVC学习与研究】31、HM编码器的基本结构

通过解码器代码的研究,已经对HEVC的编解码技术有了一个初步的认识.现在我们就对照着编码器的代码进一步理解HEVC视频编码算法的各个技术细节. 编码器在整个HM解决方案中的工程名为TAppEncoder,入口点函数位于encmain.cpp文件中: int main(int argc, char* argv[]) { TAppEncTop cTAppEncTop; // print information fprintf( stdout, "\n" ); fprintf( stdout

【HEVC学习与研究】38、HEVC编码过程中的块分割结构

[本文主要分为前后两部分,前半部分基本是Vivienne Sze.Madhukar BudagaviGary和J. Sullivan所编著的<High Efficiency Video Coding (HEVC) --Algorithms and Architectures>的第三章前半部分的笔记,后半部分是在HM-10.0中对Intra预测时块分割相应的代码研究.] 0.摘要 在基于块结构的混合编码框架中,每一帧图像被分割成多个像素结构的像素块(block),而一帧图像中多个像素块聚合成为一

【HEVC学习与研究】42、HEVC帧内编码的原理和实现(下)

4.编码帧内预测模式 大量增加可选择的预测模式可以提供更高的编码效率,同时要求编码预测模式有更加高效的方法降低更多模式带来的负担.与H.264采用一个最可能模式不同的是,对于亮度分量,三个最可能模式用于预测实际的帧内预测模式.同时,也考虑了三个最可能模式中的冗余信息,重复的模式会使用其他模式进行替换.对于亮度分量,HEVC采用了同亮度分量相同的预测模式.在编码亮度和色度帧内预测模式时,各个语法元素的设计也体现了亮度分量更多的最可能候选模式以及亮度分量的统计特性. (1)亮度帧内预测模式的预测 H

【HEVC学习与研究】37、HM编码器的基本结构2:帧内编码部分的代码骨架

第31篇博文大体介绍了HEVC参考代码HM10的编码器结构,但仅仅停留在compressCU()以上的层次,并未对具体编码的结构做深入解析.在此篇博文中我们依据对视频YUV序列的帧内编码的流程,重新梳理HM编码器的代码结构. 1.编码器的上层结构: 主要包括: 入口点函数main()[创建cTAppEncTop类,解析输入的配置函数,设定时间相关的参数] cTAppEncTop.encode()[对编码器所使用的几个对象进行初始化,分配YUV数据缓存,循环读取YUV文件] m_cTEncTop.

【HEVC学习与研究】45、HEVC的自适应采样点补偿SAO

1.SAO的意义 在图像中像素值剧烈变化的边界区,经过编码-解码重建之后通常会出现波纹状的失真现象,这种失真称作振铃效应.振铃效应产生的根本原因在于边界区高频分量在编码过程中丢失.为了减少高频分量的损失,同时不降低编码的效率,HEVC引入了SAO技术,从像素域入手对振铃区域进行补偿. 2.基本原理 SAO对重建图像在deblocking之后操作,操作对象为重建完成的一帧,以一个CTB为一个基本单位.SAO所采用的方式主要有三类:边界补偿(BO).边带补偿(EO)和参数融合(Merge). (1)