HEVC中的CAVLC

xiaoxiao2021-02-28  108

HEVC中的CAVLC

CAVLC 基于上下文的自适应变长编码

    首先,HEVC的熵编码使用了两种算术编码:CABAC和CAVLC。CAVLC主要用于编码SEI、参数集、片头等,剩下的所有数据和语法元素均使用CABAC来编码。

    HEVC标准文档中使用到的一些描述符(描述符也表示操作方法):     1、ae(v) 使用cabac     2、b(8) 读进连续的8 bit     3、f(n) 读进连续的n bit     4、u(n) 读进连续的n bit,解码为无符号整数     5、se(v) 有符号指数哥伦布编码     6、ue(n) 无符号哥伦布指数编码

HEVC中与CAVLC有关的类

    HEVC中与CAVLC有关的类:SyntaxElementWriter、SEIWriterTEncCavlc

    1、SyntaxElementWriter语法元素写入者,定义了CAVLC的几种基本算法     2、SEIWriter,SEI写入者,由于SEI使用CAVLC来编码,因此SEIWriter继承自SyntaxElementWriter     3、TEncCavlc,CAVLC编码器,主要用于编码参数集以及slice头部等,继承自SyntaxElementWriter

HEVC中CAVLC的几种基本的算法

    CAVLC只是一个统称,它包含了多个算法:     1、零阶无符号哥伦布指数编码     2、零阶有符号指数哥伦布编码     3、不编码直接写入若干比特     4、不编码直接写入一个比特

class SyntaxElementWriter { protected: TComBitIf* m_pcBitIf; SyntaxElementWriter() :m_pcBitIf(NULL) {}; virtual ~SyntaxElementWriter() {}; // 设置比特流 Void setBitstream ( TComBitIf* p ) { m_pcBitIf = p; } Void xWriteCode ( UInt uiCode, UInt uiLength ); // 不编码直接写入若干比特 Void xWriteUvlc ( UInt uiCode ); // 零阶无符号哥伦布指数编码 Void xWriteSvlc ( Int iCode ); // 零阶有符号指数哥伦布编码 Void xWriteFlag ( UInt uiCode ); // 不编码直接写入一个比特 UInt xConvertToUInt ( Int iValue ) { return ( iValue <= 0) ? -iValue<<1 : (iValue<<1)-1; } };

指数哥伦布编码的理论

    指数哥伦布编码由前缀和后缀两部分构成,前缀和后缀都依赖于指数哥伦布码的阶数k。假设指数哥伦布码是N,阶数为k,下面是它的编码步骤:     (1)把N转换为二进制数,去掉最低的k个比特位,然后加上1     (2)计算留下的比特数,把这个数减去1,这就是需要增加的前缀0的个数     (3)把步骤(1)中去掉的k个比特位补回比特串的尾部。

零阶指数哥伦布编码

    根据上面的定义,可以得到零阶指数哥伦布编码的计算方法,假设输入值是N:     1、把N转换成二进制,假设转换后是bins,计算bins += 1     2、计算bins的比特数,假设是M,那么在bins的前面添加上M-1个0就得到最终的结果     或者     1、计算N += 1     2、把N转换成二进制串bins,计算二进制串的长度,假设是M,那么在bins的前面添加M-1个0     假设输入值N对应的二进制串的长度是len,那么零阶哥伦布码的长度是2*len-1     哥伦布码golomb = 前缀prefix + 后缀suffix     前缀prefix : len - 1个0     后缀suffix : (N+1)对应的二进制串

零阶指数哥伦布编码的实现

/* ** 无符号指数哥伦布编码 */ Void SyntaxElementWriter::xWriteUvlc ( UInt uiCode ) { UInt uiLength = 1; // 哥伦布码的长度 UInt uiTemp = ++uiCode; // 执行uiTemp = uiCode + 1 assert ( uiTemp ); while( 1 != uiTemp ) // 假设uiTemp(即uiCode + 1)对应的二进制长度是len,那么哥伦布码的长度 = 2 * len - 1 { uiTemp >>= 1; uiLength += 2; } m_pcBitIf->write( 0, uiLength >> 1); // 写入前缀: len - 1个0 m_pcBitIf->write( uiCode, (uiLength+1) >> 1); // 写入后缀:uiTemp对应的二进制 }     上面算法的步骤:     1、计算N+=1

    2、计算N对应的二进制串bins的比特数,假设是len

    3、把前缀(M-1个0)写入比特流中

    4、把后缀(N对应的二进制串)写入比特流流中

把零阶指数哥伦布码写入比特流中

    写入比特流中的流程:

    1、TComOutputBitstream包含两个部分:缓冲区和比特流     2、比特流就是已经处理完成的数据,存放在一个vector中     3、缓冲区暂存还没有写入比特流中的数据,TComOutputBitstream使用一个uchar类型的数据(8 bit)作为缓存区,因为很多时候写入的数据长度只有若干比特,不能直接写入比特流中,需要等到缓冲区满(达到8bit),才写入     3、m_num_held_bits表示缓冲区中已有的比特数     4、num_total_bits表示写入数据之后,缓冲区中总的比特数(可能会溢出,后面会解决这个问题)     5、next_num_held_bits有两种意思:         (1)如果缓冲区没有溢出,那么它表示缓冲区中总的比特数(已有+新增)         (2)如果缓冲区溢出,那么它表示数据占用完缓冲区后还需要的比特数,只能存放数据的一部分,剩下那部分需要等缓冲区的数据写入比特流之后再存放     6、next_held_bits是格式化之后的数据         (1)如果缓冲区不溢出,那它表示将要写入缓冲区中的数据         (2)如果缓冲区溢出,那它表示将一部分数据写入缓冲区之后,剩下的那部分数据     7、判断num_total_bits是否大于8,即判断新增数据之后,缓冲区是否会溢出     8、如果缓冲区不溢出,那么把数据写入缓冲区中,然后返回     9、如果缓冲区溢出,那么先写一部分数据到缓冲区中,然后把缓冲区写入比特流中,清空缓冲区,继续把剩余的数据写入缓冲区中

Void TComOutputBitstream::write ( UInt uiBits, UInt uiNumberOfBits ) { assert( uiNumberOfBits <= 32 ); assert( uiNumberOfBits == 32 || (uiBits & (~0 << uiNumberOfBits)) == 0 ); // m_num_held_bits缓存区中已经持有的比特数 // num_total_bits表示写入uiBits之后,缓存区中的总比特数 UInt num_total_bits = uiNumberOfBits + m_num_held_bits; // next_num_held_bits表示缓存区中经使用的比特数 UInt next_num_held_bits = num_total_bits % 8; // 把数据执行位移操作之后,存放进一个临时变量中, UChar next_held_bits = uiBits << (8 - next_num_held_bits); // 判断当前持有的比特数是否大于8,如果大于8,表示缓冲区已经满了,需要先写入比特流中 if (!(num_total_bits >> 3)) { // 把数据写入缓冲区的尾部 m_held_bits |= next_held_bits; m_num_held_bits = next_num_held_bits; return; } /* topword serves to justify held_bits to align with the msb of uiBits */ // 把一部分数据写入 UInt topword = (uiNumberOfBits - next_num_held_bits) & ~((1 << 3) -1); UInt write_bits = (m_held_bits << topword) | (uiBits >> next_num_held_bits); // 判断num_total_bits的长度:32,24,16,8 switch (num_total_bits >> 3) { case 4: m_fifo->push_back(write_bits >> 24); case 3: m_fifo->push_back(write_bits >> 16); case 2: m_fifo->push_back(write_bits >> 8); case 1: m_fifo->push_back(write_bits); } m_held_bits = next_held_bits; m_num_held_bits = next_num_held_bits; }

转载请注明原文地址: https://www.6miu.com/read-33396.html

最新回复(0)