CAVLC算法解析

2023-10-28 19:58
文章标签 算法 解析 cavlc

本文主要是介绍CAVLC算法解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

CAVLC算法解析

writeSyntaxElement_NumCoeffTrailingOnes里有lentab[3][4][17] codtab[3][4][17]

编码非零系数数目 (NumCoeff)和拖尾系数数目(TrailingOnes)

writeSyntaxElement_NumCoeffTrailingOnesChromaDC里有lentab[4][5] codtab[4][5]

与上面的函数功能一样, 只是这儿特别针对Chroma DC(色度直流系数)

writeSyntaxElement_TotalZeros里有lentab[TOTRUN_NUM(15)][16] codtab[TOTRUN_NUM][16]

编码最后一个非零系数前零的数目(TotalZeros)

writeSyntaxElement_TotalZerosChromaDC里有lentab[3][4] codtab[3][4]

特别针对色度直流系数ChromaDC

writeSyntaxElement_Run里有lentab[TOTRUN_NUM][16] codtab[TOTRUN_NUM][16]

编码非零系数前零的个数: write VLC for Run Before Next Coefficient, VLC0

writeSyntaxElement_Level_VLC1, writeSyntaxElement_Level_VLCN

编码除拖尾系数之外的非零系数的幅值

 

intwriteSyntaxElement_TotalZeros(SyntaxElement *se, DataPartition *this_dataPart);

intwriteSyntaxElement_TotalZerosChromaDC(SyntaxElement *se, DataPartition *this_dataPart);

intwriteSyntaxElement_Run(SyntaxElement *se, DataPartition *this_dataPart);

intwriteSyntaxElement_NumCoeffTrailingOnes(SyntaxElement *se, DataPartition *this_dataPart);

intwriteSyntaxElement_NumCoeffTrailingOnesChromaDC(SyntaxElement *se, DataPartition *this_dataPart);

intwriteSyntaxElement_Level_VLC1(SyntaxElement *se, DataPartition *this_dataPart);

intwriteSyntaxElement_Level_VLCN(SyntaxElement *se, intvlc, DataPartition *this_dataPart);

 

基于内容的自适应变长编码[CAVLC]

基于内容自适应的变长编码方式用于编码zigzag顺序扫描的4x4和2x2残差变换系数块。

1、             编码系数个数和零序列(coeff_token):

coeff_token = <TotalCoeff, TrailingOnes>;

TotalCoeff = 编码非零系数总数; [0 , 16]

TrailingOnes = 特殊处理的+/-1个数; [0 , 3]

2、           编码每个TrailingOne的符号:

倒序,从高频开始向前编码TrailingOne符号,每个符号一位,0为正,1为负,最多为3个。

3、           编码余下非零系数的幅值:

编码顺序:倒序,从高频开始向前编码直到DC系数,每个幅值码字level[i]包含一个幅值前缀level_prefix和一个幅值后缀level_suffix。

level[i]: Level为实际系数的幅值。但有个例外:

当TrailingOnes<3时,那么被编码的第一个非T1 幅值肯定不是+/-1(否则将算作T1)。这个幅值如果为负就加1(如果为正就减1),例如+/-2 被映射成+/-1, +/-3映射成+/-2进行编码,这样可以用较短的变长码.

levelCode:

         如果level[i]为正, levelCode = (Level[i] << 1) - 2;

如果 level[i]为负, levelCode = -(Level[i] << 1) - 1;

计算level_prefix:

编码时:

level_prefix = levelCode / (1 << suffixLength);

根据level_prefix查标准表9-6得到码字

解码时:

从比特流的当前位置开始读取,计算为0的leading bits数量。0的长度即为level_prefix值,对应关系可察看标准文档中表9-6。

计算suffixLength:

suffixLength为0-6 比特,其长度是自适应变换的。

suffixLength 增长过程:

1、             初始化suffixLength = 0 ; 如果有超过10个非零系数,并且少于3个TailingOnes,这时suffixLength = 1;

2、           编码最高频的非零系数;

3、           如果这个系数的幅值大于一定的阈值,则增长suffixLength

当前suffixLength

增加suffixLength的系数阈值

12 

24 

48 

N/A 

更新suffixLength函数如下:

if(suffixLength == 0)

++suffixLength;

else if(level[i] > (3<<suffixLength-1) && suffixLength < 6)

++suffixLength;

计算levelSuffixSize: (后缀是长度为levelSuffixSize的无符号整数)

除了以下两种情况levelSuffixSize等于suffixLength:

1、             level_prefix == 14 && suffixLength == 0 时, levelSuffixSize = 4;

2、           level_prefix >= 15 时,levelSuffixSize = level_prefix – 3;

4、             编码最后一个非零系数前零的个数

使用VLC编码最高频非零系数前所有零的个数

total_zeros:既为最高非零系数前所有零的个数;编码表见标准表9-7;表9-8;表9-9

5、             编码每个零游程

zerosLeft: 当前系数之前所有的零的个数

run_before: 紧接当前系数前的零个数

CAVLC算法的具体过程(参考Sunrise的总结)

编码过程:

假设有一个4*4数据块

{

0, 3, -1, 0,

0, -1, 1, 0,

1, 0, 0, 0,

0, 0, 0, 0

}

数据重排列:0,3,0,1,-1,-1,0,1,0......

1) 初始值设定:

非零系数的数目(TotalCoeffs) = 5;

拖尾系数的数目(TrailingOnes)= 3;

最后一个非零系数前零的数目(Total_zeros) = 3;

变量NC=1;

(说明:NC值的确定:色度的直流系数NC=-1;其他系数类型NC值是根据当前块左边4*4块的非零系数数目(NA)当前块上面4*4块的非零系数数目(NB)求得的,见毕厚杰书P1206.10)

suffixLength = 0;

i = TotalCoeffs = 5;

2) 编码coeff_token:

查标准(BS ISO/IEC 14496-10:2003)Table 9-5,可得:

If (TotalCoeffs == 5 && TrailingOnes == 3 && 0 <= NC < 2)

coeff_token = 0000 100;

Code = 0000 100;

3) 编码所有TrailingOnes的符号:

逆序编码,三个拖尾系数的符号依次是+(0),-(1),-(1);

:

TrailingOne sign[i--] = 0;

TrailingOne sign[i--] = 1;

TrailingOne sign[i--] = 1;

Code = 0000 1000 11;

4) 编码除了拖尾系数以外非零系数幅值Levels:

过程如下:

(1)将有符号的Level[ i ]转换成无符号的levelCode;

如果Level[ i ]是正的,levelCode = (Level[ i ]<<1) – 2;

如果Level[ i ]是负的,levelCode = - (Level[ i ]<<1) – 1;

(2)计算level_prefix:level_prefix = levelCode / (1<<suffixLength);

查表9-6可得所对应的bit string;

(3)计算level_suffix:level_suffix = levelCode % (1<<suffixLength);

(4)根据suffixLength的值来确定后缀的长度;

(5)suffixLength updata:

If ( suffixLength == 0 )

suffixLength++;

else if ( levelCode > (3<<suffixLength-1) && suffixLength <6)

suffixLength++;

回到例子中,依然按照逆序,Level[i--] = 1;(此时i = 1)

levelCode = 0;level_prefix = 0;

查表9-6,可得level_prefix = 0时对应的bit string = 1;

因为suffixLength初始化为0,故该Level没有后缀;

因为suffixLength = 0,suffixLength++;

Code = 0000 1000 111;

编码下一个Level:Level[0] = 3;

levelCode = 4;level_prefix = 2;查表得bit string = 001;

level_suffix = 0;suffixLength = 1;故码流为0010;

Code = 0000 1000 1110 010;

i = 0,编码Level结束。

5)编码最后一个非零系数前零的数目(TotalZeros):

查表9-7,TotalCoeffs = 5,total_zero = 3,bit string = 111;

Code = 0000 1000 1110 0101 11;

6) 对每个非零系数前零的个数(RunBefore)进行编码:

i = TotalCoeffs = 5;ZerosLeft = Total_zeros = 3;查表9-10:

依然按照逆序编码

ZerosLeft =3, run_before = 1 run_before[4]=10;

ZerosLeft =2, run_before = 0 run_before[3]=1;

ZerosLeft =2, run_before = 0 run_before[2]=1;

ZerosLeft =2, run_before = 1 run_before[1]=01;

ZerosLeft =1, run_before = 1 run_before[0]不需要码流来表示

Code = 0000 1000 1110 0101 1110 1101;

编码完毕。

 

CAVLC中的前缀和后缀——264小生

H264中的CAVLC在对TrailingOne之后的非零系数编码时,使用了前缀(prefix)和后缀(suffix)的概念,并且用suffixlength来表示后缀的长度。具体编码过程是这样的:

,suffixlength初始化为0(如果超过10个非零系数,而且少于3TrailingOnes,suffixlength初始化为1)

,将有符号的level(系数的值),转化为无符号数,有三种情况:

(1)如果Level是正的,levelCode = (Level<<1) – 2

(2)如果Level是负的,levelCode = -(Level<<1) – 1

(3)如果TrailingOnes小于3,那么第一个非TrailingOnes的非零系数必不为 +1-1,为了节省编码比特,将其幅值减1。也就是如果level为正,level=level-1;否则,level=level+1。然后再按(1)

(2)level转化为levelCodeT264,此步骤是通过直接对levelcode2完成的,levelcode=levelcode-2,这和通过对level幅值减1得到的levelcode是一样的。

,得到level_prefixlevel_suffix

先说明一下,在解码端,levelcode是这样被解码的:

levelcode=(level_prefix<<suffixlength)+level_suffix

知道这一点很重要。从解码levelcode的公式中可以看出,编码端的level_prefix就是将levelcode右移suffixlength位得到的,level_suffix直接就是levelcode的低suffixlength位。比如levelcode =13,也就是二进制'1101',而且如果此时的suffixlength=1,那么level_prefix=13>>1='110',level_suffix直接就是levelcode的最低位'1';再比如levelcode=13='1101', suffixlength=3,这时level_prefix=13>>3='1', level_suffix 就是 levelcode的低3'101'。根据level_prefix 的值查表得到输出的码字。

这里还有三种特殊情况,在解码端:

(1)如果此时发现suffixlength=0,level_prefix=14,那么认为level_suffix4(而不是0)

(2)如果此时发现level_prefix=15,那么认为level_suffix12位的。

(3)如果此时发现suffixlength=0,level_prefix=15,那么经过使用公式levelcode=

(level_prefix<<suffixlength)+level_suffix得到的levelcode还需要再加上15

这篇关于CAVLC算法解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/295584

相关文章

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

使用Python绘制3D堆叠条形图全解析

《使用Python绘制3D堆叠条形图全解析》在数据可视化的工具箱里,3D图表总能带来眼前一亮的效果,本文就来和大家聊聊如何使用Python实现绘制3D堆叠条形图,感兴趣的小伙伴可以了解下... 目录为什么选择 3D 堆叠条形图代码实现:从数据到 3D 世界的搭建核心代码逐行解析细节优化应用场景:3D 堆叠图

深度解析Python装饰器常见用法与进阶技巧

《深度解析Python装饰器常见用法与进阶技巧》Python装饰器(Decorator)是提升代码可读性与复用性的强大工具,本文将深入解析Python装饰器的原理,常见用法,进阶技巧与最佳实践,希望可... 目录装饰器的基本原理函数装饰器的常见用法带参数的装饰器类装饰器与方法装饰器装饰器的嵌套与组合进阶技巧

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

深度解析Spring Boot拦截器Interceptor与过滤器Filter的区别与实战指南

《深度解析SpringBoot拦截器Interceptor与过滤器Filter的区别与实战指南》本文深度解析SpringBoot中拦截器与过滤器的区别,涵盖执行顺序、依赖关系、异常处理等核心差异,并... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实