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

第31篇博文大体介绍了HEVC参考代码HM10的编码器结构,但仅仅停留在compressCU()以上的层次,并未对具体编码的结构做深入解析。在此篇博文中我们依据对视频YUV序列的帧内编码的流程,重新梳理HM编码器的代码结构。

1、编码器的上层结构:

主要包括:

  1. 入口点函数main()【创建cTAppEncTop类,解析输入的配置函数,设定时间相关的参数】
  2. cTAppEncTop.encode()【对编码器所使用的几个对象进行初始化,分配YUV数据缓存,循环读取YUV文件】
  3. m_cTEncTop.encode(...)【调用m_cGOPEncoder.compressGOP()实现对一个GOP的实际编码】
  4. m_cGOPEncoder.compressGOP()【调用initGOP设置GOP的参数;调用m_pcSliceEncoder->initEncSlice(),利用SPS和PPS中的信息创建编码的slice对象;调用m_pcSliceEncoder->compressSlice
    ( pcPic )对一个slice进行编码。】
  5. m_pcSliceEncoder->compressSlice ( pcPic )【设置编码slice的参数,对slice的每一个cu进行处理】
  6. TEncCu::compressCU()【编码一个CU】

以上各个函数分别在前一个函数中调用,形成类似一个反向的call stack的结构。

2、对一个CU进行编码的过程:

TEncCu::compressCU()函数的实现如下:

Void TEncCu::compressCU( TComDataCU*& rpcCU )
{
  // initialize CU data
  m_ppcBestCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );
  m_ppcTempCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );

#if RATE_CONTROL_LAMBDA_DOMAIN
  m_addSADDepth      = 0;
  m_LCUPredictionSAD = 0;
  m_temporalSAD      = 0;
#endif

  // analysis of CU
  xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 );

#if ADAPTIVE_QP_SELECTION
  if( m_pcEncCfg->getUseAdaptQpSelect() )
  {
    if(rpcCU->getSlice()->getSliceType()!=I_SLICE) //IIII
    {
      xLcuCollectARLStats( rpcCU);
    }
  }
#endif
}

该函数的核心方法为xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 )。该函数实现了编码一个CU的多种功能,首先判断尝试进行帧间预测,然后尝试进行帧内预测,另外还包括了PCM模式的编码以及一个CU的进一步划分等。这里主要关心的是intra模式,实现的代码为:

if ( !bEarlySkip )
{
// speedup for inter frames
        if( rpcBestCU->getSlice()->getSliceType() == I_SLICE ||
        rpcBestCU->getCbf( 0, TEXT_LUMA     ) != 0   ||
        rpcBestCU->getCbf( 0, TEXT_CHROMA_U ) != 0   ||
        rpcBestCU->getCbf( 0, TEXT_CHROMA_V ) != 0     ) // avoid very complex intra if it is unlikely
        {
        xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_2Nx2N );
           rpcTempCU->initEstData( uiDepth, iQP );
            if( uiDepth == g_uiMaxCUDepth - g_uiAddCUDepth )
            {
                      if( rpcTempCU->getWidth(0) > ( 1 << rpcTempCU->getSlice()->getSPS()->getQuadtreeTULog2MinSize() ) )
               {
                xCheckRDCostIntra( rpcBestCU, rpcTempCU, SIZE_NxN   );
                rpcTempCU->initEstData( uiDepth, iQP );
                }
        }
}
}

该函数调用的xCheckRDCostIntra实现了帧内预测的具体过程:

Void TEncCu::xCheckRDCostIntra( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, PartSize eSize )
{
  UInt uiDepth = rpcTempCU->getDepth( 0 );

  rpcTempCU->setSkipFlagSubParts( false, 0, uiDepth );

  rpcTempCU->setPartSizeSubParts( eSize, 0, uiDepth );
  rpcTempCU->setPredModeSubParts( MODE_INTRA, 0, uiDepth );
  rpcTempCU->setCUTransquantBypassSubParts( m_pcEncCfg->getCUTransquantBypassFlagValue(), 0, uiDepth );

  Bool bSeparateLumaChroma = true; // choose estimation mode
  UInt uiPreCalcDistC      = 0;
  if( !bSeparateLumaChroma )
  {
    m_pcPredSearch->preestChromaPredMode( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth] );
  }
  m_pcPredSearch  ->estIntraPredQT      ( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth], m_ppcResiYuvTemp[uiDepth], m_ppcRecoYuvTemp[uiDepth], uiPreCalcDistC, bSeparateLumaChroma );

  m_ppcRecoYuvTemp[uiDepth]->copyToPicLuma(rpcTempCU->getPic()->getPicYuvRec(), rpcTempCU->getAddr(), rpcTempCU->getZorderIdxInCU() );

  m_pcPredSearch  ->estIntraPredChromaQT( rpcTempCU, m_ppcOrigYuv[uiDepth], m_ppcPredYuvTemp[uiDepth], m_ppcResiYuvTemp[uiDepth], m_ppcRecoYuvTemp[uiDepth], uiPreCalcDistC );

  m_pcEntropyCoder->resetBits();
  if ( rpcTempCU->getSlice()->getPPS()->getTransquantBypassEnableFlag())
  {
    m_pcEntropyCoder->encodeCUTransquantBypassFlag( rpcTempCU, 0,          true );
  }
  m_pcEntropyCoder->encodeSkipFlag ( rpcTempCU, 0,          true );
  m_pcEntropyCoder->encodePredMode( rpcTempCU, 0,          true );
  m_pcEntropyCoder->encodePartSize( rpcTempCU, 0, uiDepth, true );
  m_pcEntropyCoder->encodePredInfo( rpcTempCU, 0,          true );
  m_pcEntropyCoder->encodeIPCMInfo(rpcTempCU, 0, true );

  // Encode Coefficients
  Bool bCodeDQP = getdQPFlag();
  m_pcEntropyCoder->encodeCoeff( rpcTempCU, 0, uiDepth, rpcTempCU->getWidth (0), rpcTempCU->getHeight(0), bCodeDQP );
  setdQPFlag( bCodeDQP );

  if( m_bUseSBACRD ) m_pcRDGoOnSbacCoder->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]);

  rpcTempCU->getTotalBits() = m_pcEntropyCoder->getNumberOfWrittenBits();
  if(m_pcEncCfg->getUseSBACRD())
  {
    rpcTempCU->getTotalBins() = ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
  }
  rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );

  xCheckDQP( rpcTempCU );
  xCheckBestMode(rpcBestCU, rpcTempCU, uiDepth);
}

该函数调用了estIntraPredQT和estIntraPredChromaQT分别实现亮度分量和色度分量的帧内估计,二者大同小异。这里重点观察亮度分量。estIntraPredQT执行了遍历各种intra模式的操作,调用了pcCU->getPattern()->initAdiPattern、predIntraLumaAng等函数。详情可见编号第3336的博文。

时间: 2024-11-17 10:51:42

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

【HEVC学习与研究】35、帧内预测参考数据的获取和滤波处理

帧内预测的参考像素值的获取在标准文档的8.4.4.2.2中指明. 举例说明,当前demo中,我们用来单步调试的第一个CU为64×64像素大小,那么参考像素由两部分组成,一部分包含2×64+1=129个,另一部分包含2×64=128个像素.这两部分分别作为垂直和水平方向上的预测数据.在编码的过程中,根据预测数据是否可得,共分为两种情况: 第一种:所有的预测数据都不可得.最直观的情况就是一帧数据中的第一个CU,该CU左侧和上方的数据都不存在,如下图所示.此时所有的预测数据都会制定一个默认值,计算方法

【HEVC学习与研究】32、编码一个CU(帧内部分)1

在一个compressSlice()中,在compressCU函数中实现对一个CU的编码,其中主要进行了CU的初始化,以及实际的编码操作. Void TEncCu::compressCU( TComDataCU*& rpcCU ) { // initialize CU data m_ppcBestCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() ); m_ppcTempCU[0]->initCU( rpcCU->getP

【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学习与研究】34、HEVC参考软件HM中Intra预测参考像素的获取与管理

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

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

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

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

关于HEVC的前25篇博文全文发表在新浪博客,地址为:http://blog.sina.com.cn/s/articlelist_1376260467_0_1.html.从第26篇开始博客全文发在CSDN,新浪同步更新摘要和链接地址. 在第13篇博文中贴出了我们在调试代码时所采用的二进制码流的开头一部分数据,并根据这些数据进行了NAL Header解析.参数集合解析和条带头解析等信息的分析.今后的博文中如无特殊情况依然会采用这些数据作为学习材料. 通过对这段码流进行分析,我们可以看出,在对于一个

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

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

【HEVC学习与研究】43、HEVC变换编码的实现

[变换和量化是整个视频编码技术系列中理论性和研究性较强的一部分,本文暂时不去研究变换的原理.推导过程等,只是调试一下在参考代码中对预测残差进行变换的实现过程.变换的原理在未来会作为一个研究方向进行探讨.] 1.HM中Intra模式的主要实现逻辑 以Intra的亮度模式为例.主要实现代码实现于TEncSearch::estIntraPredQT方法中.TEncSearch::estIntraPredQT实现时,首先获取当前CU的分割子块的个数,并且对每个子块分别进行预测.变换量化操作(代码中称之为

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

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