【HEVC学习与研究】28、第一帧第一个宏块的SAO部分完整解析结果

经过了前面一段时间的研究,现在大致将这第一个宏块SAO由码流到语法元素值的解析过程完整整理一下。这里没有太多原理部分,更多的像是一篇流水账一样,聊作记录。

在代码中,我们首先查看一下解析完条带头数据后,当前NAL中带解析的码流。还是看我们一直使用的这个demo序列的编码结果,码流中正式用语解析条带数据的值如:

206 103 162 107 167 87 243 112 29 35 44 3 245 69 197 199 130 168 75 91 169 13 159 38 44 174 148 159 37 172 43 217 73 169 33 74 88 146 195 80 196 231 214 59 238 104 42 137 56 252 115 11 102 195 85 4 47 66 181 232 246 222 92 26 84 152 189 7 62 243 15 16 231 158 235 4 90 252 47 41 204 119 249 104 7 178 137 231 213 62 100 80 122 148 87 35 46 139 55 177 40 227 175 3 129 248 70 5 219 190 208 79 169 200 205 61 36 197 240 51 42 243 229 201 62 49 207 2 104 181 68 53 25 98 248 54 26 176 237 220 224 72 115 168 55 148 140 140 197 203 62 189 25 189 183 69 172 218 189 37 249 242 225 138 233 55 202 66 0 136 113 212 253 71 92 157 212 121 84 53 48 69 117 229 187 241 18 60 42 35 101 161 142 2 247 106 43 25 92 141 181 219 34 71 6 98 42 245 84 140 62 252 245 190 24 ......

以上是当前NAL中带解析的数据,包含了slice中每一个CTU的SAO信息和Coding Quardtree信息。具体实现步骤如:

1、slice数据的解析由TDecTop::xDecodeSlice()执行,该函数调用TDecGop::decompressSlice()实现具体的工作。在该函数中调用TDecSbac::resetEntropy()函数,在末尾的TDecBinCABAC::start()函数中,码流中的前两个字符206和103赋予m_uiValue作为一个UInt值的低两位,m_uiValue的值为52839作为初值;

2、TDecGop::decompressSlice()调用TDecSlice::decompressSlice(),解析SAO的部分在TDecSbac::parseSaoOneLcuInterleaving()中实现。该函数中的一个参数为SAOParam结构体指针pSaoParam,该结构体的定义为:

struct SAOParam
{
  Bool       bSaoFlag[2];
  SAOQTPart* psSaoPart[3];
  Int        iMaxSplitLevel;
  Bool         oneUnitFlag[3];
  SaoLcuParam* saoLcuParam[3];
  Int          numCuInHeight;
  Int          numCuInWidth;
  ~SAOParam();
};

在该结构中,TDecSbac::parseSaoOneLcuInterleaving()解析的内容赋予SAOParam类型saoLcuParam[3]数组,定义如下:

typedef struct _SaoLcuParam
{
  Bool       mergeUpFlag;
  Bool       mergeLeftFlag;
  Int        typeIdx;
  Int        subTypeIdx;                  ///< indicates EO class or BO band position
  Int        offset[4];
  Int        partIdx;
  Int        partIdxTmp;
  Int        length;
} SaoLcuParam;

TDecSbac::parseSaoOneLcuInterleaving()函数首先对pSaoParam参数进行初始化:

for (Int iCompIdx=0; iCompIdx<3; iCompIdx++)
{
	pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeUpFlag    = 0;
	pSaoParam->saoLcuParam[iCompIdx][iAddr].mergeLeftFlag  = 0;
	pSaoParam->saoLcuParam[iCompIdx][iAddr].subTypeIdx     = 0;
	pSaoParam->saoLcuParam[iCompIdx][iAddr].typeIdx        = -1;
	pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[0]     = 0;
	pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[1]     = 0;
	pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[2]     = 0;
	pSaoParam->saoLcuParam[iCompIdx][iAddr].offset[3]     = 0;
}

由于当前CTU是slice中的第一个CTU,不存在左侧和上方的元素,因此parseSaoMerge()函数将不被执行。随后在3次for循环中,调用方法为parseSaoOffset(&(pSaoParam->saoLcuParam[iCompIdx][iAddr]), iCompIdx)。

第一次循环:

调用parseSaoType(),解析结果pSaoParam->saoLcuParam[0][0].typeIdx = -1;pSaoParam->saoLcuParam[0][0].length
= 0。

第二次循环:

调用parseSaoType(),解析结果pSaoParam->saoLcuParam[1][0].typeIdx
= 4;pSaoParam->saoLcuParam[0][0].length = 4;

循环调用4次parseSaoMaxUvlc(),解析结果为:pSaoParam->saoLcuParam[1][0].offset[0]
= 0; pSaoParam->saoLcuParam[1][0].offset[1] = 3,解析过程中读取该段码流中第三个字符‘162’; pSaoParam->saoLcuParam[1][0].offset[2]
= 1; pSaoParam->saoLcuParam[1][0].offset[3] = 1; 

针对四个offset值中的非零者,解码下一个bit,若为非0值,则该offset取反。修正后, pSaoParam->saoLcuParam[1][0].offset[3]
= -1; 

调用parseSaoUflc(5,
uiSymbol )函数,该函数读取码流中第四个字符‘107’,解析结果pSaoParam->saoLcuParam[1][0].subTypeIdx = 13;

第三次循环:

由于compIdx为2,因此不再调用parseSaoType(),pSaoParam->saoLcuParam[2][0].typeIdx
= pSaoParam->saoLcuParam[1][0].typeIdx = 4;

循环调用4次parseSaoMaxUvlc(),解析结果为:pSaoParam->saoLcuParam[2][0].offset[0]
= 0; pSaoParam->saoLcuParam[2][0].offset[1] = 1; 

pSaoParam->saoLcuParam[2][0].offset[2]
= 3,解析过程中读取码流中下一个字符‘167’; pSaoParam->saoLcuParam[2][0].offset[3] = 2; 

针对四个offset值中的非零者,解码下一个bit,若为非0值,则该offset取反。修正后, pSaoParam->saoLcuParam[1][0].offset[1]
= -1; pSaoParam->saoLcuParam[2][0].offset[2] = 3,解析该bit时读取下一个字符‘87’;pSaoParam->saoLcuParam[2][0].offset[3]
= -2; 

调用parseSaoUflc(5,
uiSymbol )函数,解析结果pSaoParam->saoLcuParam[2][0].subTypeIdx = 10;

至此,TDecSbac::parseSaoOneLcuInterleaving()对第一个CTU的SAO部分解析完成。

时间: 2024-09-17 13:45:53

【HEVC学习与研究】28、第一帧第一个宏块的SAO部分完整解析结果的相关文章

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

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

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

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

【HEVC学习与研究】36、对帧内预测参考数据进行滤波处理

在帧内预测的过程中,获取临近的Prediction Unit的边缘数据作为当前PU的参考数据.数据获取完成后,并不一定会直接使用这些数据进行预测,而可能会先将这些预测数据进行一次滤波操作.帧内参考像素的滤波在标准文档的8.4.4.2.3节详述. 帧内参考像素的滤波使能标记由一个标志位filterFlag标识.该标志位的判定方法为: 1.如果当前预测模式为DC模式,或者帧内预测的PU为4×4大小时,filterFlag一律为0: 2.计算当前的Intra预测模式同"水平"和"垂

【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学习与研究】26、HEVC的算数编码实现

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

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

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

【HEVC学习与研究】30、解码Coding Unit数据

一个CU结构可以认为是编码过程中作为实际处理过程的一个单元,标准文档的7.3.8.5节说明了CU的语法结构,如下图: 这个结构中,第一个元素cu_transquant_bypass_flag表示是否跳过scaling和变换过程,如果该元素不存在则默认为0.在xDecodeCU函数中,解析该元素的代码如下: if (pcCU->getSlice()->getPPS()->getTransquantBypassEnableFlag()) { <span style="whit

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

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

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

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