H.264中的熵编码算法(主讲指数哥伦布编码)

2023-11-12

以下文章参考于殷文杰的博客。

https://yinwenjie.blog.csdn.net/article/details/52301584

1 熵编码基本概念

  • 1)“熵”这一概念原本来自于化学和热力学,用于度量能量退化的指标,即熵越高,物体或系统的做功能力越低。后来香农将这一概念引入到信息论中,用于表示消息的平均信息量。信源的熵通常可以表示信源所发出信息的不确定性,即越是随机的、前后不相关的信息,其熵越高。
  • 2)在信息论中,香农提出了信源编码定理。该定理说明了香农熵与信源符号概率之间的关系,说明信息的熵为信源无损编码后的平均码字长度的下限。任何的无损编码方法都不可能使编码后的平均码长小于香农熵,只能使其尽量接近。
  • 3)基于此,对信源进行熵编码的基本思想,是使其前后的码字之间尽量更加随机,尽量减小前后的相关性,更加接近其信源的香农熵。这样在表示同样的信息量时所用的数据长度更短。
  • 4)在实际使用中,常用的熵编码主要有变长编码和算术编码等方法。其中变长编码相对于算术编码较为简单,但平均压缩比可能略低。常见的变长编码方法有哈夫曼编码和香农-费诺编码等。

在这里插入图片描述

2 哈夫曼编码(变长编码(VLC))

参考我的以下文章。

https://blog.csdn.net/weixin_44517656/article/details/105227705

或者参考上面开头殷文杰写的。

3 H.264中的熵编码基本方法

在成功从NAL Unit中获取到语法元素的码流之后,接下来就是对语法元素的码流进行解析。但是我们FFmpeg解码得到的H264,都是经过预测、变换量化等步骤后得到的H.264语法元素,他们通过熵编码器压缩为符合标准的H.264码流。因此,为了还原各个语法元素,必须对码流使用熵编码的解码器进行解码。

在H.264的标准协议中,不同的语法元素指定了不同的熵编码方法。在协议文档中共指定了10种语法元素的描述符,这些描述符表达了码流解析为语法元素值的方法,其中包含了H.264标准所支持的所有熵编码方法:
以下均是熵编码的方法,共10种。

在这里插入图片描述

上面有许多熵编码,但是我们下面主要讲指数哥伦布编码,简称哥伦布编码。

3.1 指数哥伦布编码(变长编码(VLC))

3.1.1 指数哥伦布编码基本概念

  • 1)与上面介绍的哈夫曼编码一样,指数哥伦布编码同样属于变长编码(VLC)的一种。
    指数哥伦布编码同哈夫曼编码最显著的一点不同在于,哈弗曼编码构建完成后必须在传递的信息中加入码字和码元值的对应关系,也就是编码的码表,而指数哥伦布编码则不需要。
  • 2)如上表指出,常用的指数哥伦布编码通常可以分为四类。
    ue(v):无符号指数哥伦布编码。
    se(v):有符号指数哥伦布编码。
    me(v):映射指数哥伦布编码。
    te(v):截断指数哥伦布编码。
  • 3)其中无符号指数哥伦布编码ue(v)是其他编码方式的基础,其余几种方法基本可以由ue(v)推导得出。

3.1.2 无符号指数哥伦布编码ue(v)
其编码方法如下:
在这里插入图片描述
其中公式codeNum = 2^LeadingZeroBits - 1 + (xxx):

  • 1)codeNum代表求出无符号哥伦布编码的十进制数。
  • 2)LeadingZeroBits代表1前面的前缀0个数。注:无符号指数哥伦布编码由"前缀+1+后缀组成"。
  • 3)xxx代表后缀。

例如十进制数2时,带入公式:010(十进制)----->2^1-1+0=1(ue(v)),所以它在无符号指数哥伦布编码中的码元值为1。

并且需要注意,码元值的范围为:

2^LeadingZeroBits-1 ~  2^LeadingZeroBits-1 + 2^LeadingZeroBits-1 即:
2^LeadingZeroBits-12*(2^LeadingZeroBits-1)

例如LeadingZeroBits=5时,范围是31~62

其余三种指数哥伦布编码不是特别重要,了解下即可,并且均可以由无符号指数哥伦布编码得到。
最后提供十进制与无符号指数哥伦布编码的换算表:
在这里插入图片描述

3.1.3 有符号指数哥伦布编码
有符号的指数哥伦布编码值是通过无符号的指数哥伦布编码的值通过换算得到的,其语法元素描述符为se(v)。每一个无符号指数哥伦布编码的数值通过固定的换算关系转换为有符号的值,其换算关系为:n = (-1)^(k+1) * Ceil(k/2)。下表表示了有符号和无符号指数哥伦布编码之间的换算关系:
在这里插入图片描述

3.1.4 截断指数哥伦布编码
截断指数哥伦布编码的语法元素描述符为te(v)。当语法元素以te(v)解码时,首先需要判断的是语法元素的取值范围,假定为[0, x], x≥1。根据x的取值情况,语法元素根据下面不同情况进行解析:

  • 1)若x>1,解析方法同ue(v)相同。
  • 2)若x=1,语法元素值等同于下一位bit值的取反。

3.1.5 映射指数哥伦布编码
映射指数哥伦布编码的描述符为me(v),适用于预测模式为Intra_4x4, Intra_8x8或Inter的宏块的coded_block_pattern的编码。me(v)的映射方式并无指定的换算公式,通常由查表的方式进行。下表为H.264 spec文档的表9-4的一部分:
在这里插入图片描述

4 指数哥伦布编码同哈夫曼编码的比较

指数哥伦布编码与哈夫曼编码都遵循了同一规律,即针对不同的码元分配了bit位长度不同的码字,因此各自都属于变长编码的一种。然而二者仍然具有较大的差别,具体如:

  • 1)哈夫曼编码在编码过程中考虑了信源各个符号的概率分布特性,根据符号的概率分布进行编码,因此对于不同的信源,即使是相同的符号的哈夫曼编码的结果也是不同的。而指数哥伦布编码针对不同的信源采用的编码是统一的,因此无论是什么样的输入,输出的编码后的数据都是一致的。
  • 2)由于哈夫曼编码是针对信源特性进行的编码,因此在存储或传输编码后的数据之前必须在前面保存一份码表供解码段重建原始信息使用。而指数哥伦布编码不需要存储任何额外信息就可以进行解码。
  • 3)由于未考虑信源的实际特性,指数哥伦布编码的压缩比率通常比较低,对于有些信息甚至完全没有压缩效果,输出数据比原始数据更大,在这一点上哈夫曼编码作为“最优编码”在效率上更高。然而由于哈夫曼编码运算较指数哥伦布编码更为复杂,且必须保存码表信息增加了传输负荷,也对压缩比率造成了不利影响。
  • 4)实际上,对于视频压缩这样的需求而言,类似于哈夫曼编码所提供的压缩比率的优势远远不够,而且像H.264等编码标准都不会指望靠这样的方式来提高压缩比率。因此在实际的视频编码方法中使用的是指数哥伦布编码,但是只作为少数的辅助语法元素的编码以及多数语法元素的二值化方法。真正贡献了高压缩比还需要后面详述的CAVLC和CABAC等。

5 无符号指数哥伦布编码demo

个人已对代码添加详细注释。

#include <iostream>
#include <deque>
#include <assert.h>
using namespace std;

typedef unsigned char UINT8;

/*
	功能:获取某个字节的某位二进制
	参1:码流数据
	参2:第几个字节
	参3:第几位
*/
static int get_bit_at_position(UINT8 *buf, UINT8 &bytePotion, UINT8 &bitPosition)
{
	UINT8 mask = 0, val = 0;

	mask = 1 << (7 - bitPosition);//保证从第一位开始,例如bitPosition=0,1左移7位后,1000 0000
	val = ((buf[bytePotion] & mask) != 0);//!= 0用于确保val值为0-1.例若buf[bytePotion] & mask结果为00000100,不使用"!=0"的话结果为3
	++bitPosition;//指向下一位bit,实际上个人觉得自增放在调用函数执行更好
	if (bitPosition > 7)
	{
		bytePotion++;
		bitPosition = 0;
	}

	return val;
}

/*
	功能:获取哥伦布编码的十进制数
	参数:与上面意思一样
*/
static int get_uev_code_num(UINT8 *buf, UINT8 &bytePotion, UINT8 &bitPosition)
{
	assert(bitPosition < 8);//一个字节最多0-7共八个bit
	UINT8 val = 0, prefixZeroCount = 0;
	int prefix = 0, surfix = 0;

	//1 求出前缀所占的0,即长度,后面用于求后缀
	while (true)
	{
		val = get_bit_at_position(buf, bytePotion, bitPosition);
		if (val == 0)
		{
			prefixZeroCount++;
		}
		else
		{
			break;//遇到1必须退出,然后计算1后面的后缀,即prefix+1+surfix
		}
	}

	//2 先求出公式的前半部分
	prefix = (1 << prefixZeroCount) - 1;//获取2^prefixZeroCount-1,两者表达一样

	//3 再求出公式的后半部分,即surfix
	for (size_t i = 0; i < prefixZeroCount; i++)
	{
		val = get_bit_at_position(buf, bytePotion, bitPosition);//因为上面while是根据1跳出,所以第一个val必定是1后面的位,注:没有前缀不会进该for循环,因为prefixZeroCount为0
		surfix += val * (1 << (prefixZeroCount - 1 - i));//减1是因为求平方的原因,例如100,求第二个0,公式为0 * (1<<(prefixZeroCount-1)).  -i是因为每次for后,bitPosition会右移一位
	}

	prefix += surfix;

	return prefix;
}


int main(int argc, char* argv[])
{
	/*
	选以下六个数的原因:
		0xA6:1010 0110
		0x42:0100 0010
		0x98:1001 1000
		0xE2:1110 0010
		0x04:0000 0100
		0x8A:1000 1010
		由于哥伦布编码由前缀+1+后缀组成,并且前后缀长度相等,所以看上面:
		1)直接遇到1,所以前缀长度为0个0,后缀也同样(长度相等嘛),故为结果:1
		2)然后遇到0,再遇到1,所以前缀为一个0,同理后缀长度为1,所以结果为:010
		3)遇到0,然后遇到1,所以前缀为一个0,同理后缀长度为1,所以结果为:011
		其余同理。当我们最终列出所有结果时,即
		00100,00101,00110,00111,0001000,0001001,0001010
		
		根据哥伦布编码的公式(实际上是将编码的码流转成十进制):codeNum=2^PrefixZeroNum - 1 + surfix
		可以得出上面编码结果的十进制:
		1->2^0-1+0=0
		010->2^1-1+0=1
		011->2^1-1+1=2
		...
		0001010->2^3-1+2=9
		刚好这六个字节的哥伦布编码是十进制的0-9.这就是这6个字节的作用。
	*/

	
	/*总思路:从第一个字节的第一位开始算*/
	UINT8 strArray[6] = { 0xA6, 0x42, 0x98, 0xE2, 0x04, 0x8A };//哥伦布编码的0-9
	UINT8 bytePosition = 0, bitPosition = 0;//从第1个字节的第1位开始
	UINT8 dataLengthInBits = sizeof(strArray) * 8;//码流总位数

	int codeNum = 0;
	while ((bytePosition * 8 + bitPosition) < dataLengthInBits)
	{
		codeNum = get_uev_code_num(strArray, bytePosition, bitPosition);
		printf("ExpoColumb codeNum = %d\n", codeNum);
	}

	system("pause");

	return 0;
}

结果:
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

H.264中的熵编码算法(主讲指数哥伦布编码) 的相关文章

随机推荐

  • 详解KVM虚拟化原理

    详解KVM虚拟化原理 KVM架构 KVM Kernel based Virtual Machine 包含一个为处理器提供底层虚拟化 可加载的核心 模块kvm ko kvm intel ko或kvm amd ko 使用QEMU QEMU KV
  • sqlserver登录名和用户名的区别和联系-先存着-后续研究

    总括 登录名可以理解为进入整个大楼的钥匙 用户名可以理解为一个房间的钥匙 这里所说的大楼就是sql server服务器 而房间就是这个sql server服务器中的具体的库 要注意登录名是存在于master数据库的syslogins表中 用
  • 底部导航栏怎么写?

    底部导航栏需要怎么写 1 回忆一下 任何手机商城页面 底部导航栏都算固定在下面的 不管页面内容有多少 不管用户怎么滑动 底部导航栏始终在下面 2 点击到导航栏上的图标或者文字时 会跳转另一页面 3 点击导航栏上的图标或者文字时 所点的图标可
  • 爬取学校网站

    完整代码如下 可直接copy from bs4 import BeautifulSoup from bs4 import UnicodeDammit import urllib request import threading def im
  • 源码分析Hadoop FileInputFormat如何分片

    Hadoop采用的是分布式并行计算的模式来处理大数据 在处理时必然要对数据进行分片 将数据由大化小 将一个大的任务化为几个小的任务 这就是hadoop处理大数据的核心思想 这里要讨论的是hadoop对数据进行分片的方案 这里的分片是逻辑上的
  • 开发文档怎么编写_需求开发之软需编写技巧

    一 什么是软需 软需全称软件需求规格说明书 是产品 项目在研发过程中必不可少的一份过程文档 主要由产品 项目的需求人员负责编写 编写软需之前一般要先进行用户需求分析 二 软需的作用 软需的编写时间一般是安排在需求确定之后 代码编写之前 因为
  • python处理字节流形式的视频

    python处理内存中字节流形式的视频 在使用python的streamlit库处理上传的文件时碰到一个问题 文件上传后是以字节数组的形式存在内存中 我在后续需要使用cv2库逐帧操作上传的视频 这里就产生一个问题 cv2怎么读取到内存中字节
  • Android 12 应用兼容性适配指导

    一 兼容性调试工具 Android 11开始引入了新的工具 可针对Android新平台中的行为变更进行测试和调试 这些工具是兼容性框架的一部分 该框架使得开发者可通过开发者选项或adb命令单独打开和关闭各项变更 藉此 可在最新android
  • 腾讯gpu-manager

    基本原理 vCUDA通过劫持CUDA的显存申请和释放请求 为每个容器管理它的显存使用量 进而实现了显存隔离 唯一需要注意的是申请context并不通过malloc函数 因此无法知道进程在context使用了多少显存 因此vcuda每次都去向
  • cocos creator创建简单的动态网格

    如果初次尝试cocos的动态网格创建 一定会遇到非常多的问题 所以刚开始使用 最好用一个简单的东西来实现 逐步的复杂化 下面代码展示了一个最基础的三角面的创建 代码 private initDyMesh const pos new Floa
  • 记导入第三方库Alamofire的坑

    按照网上打的操作步骤导入之后 存在No Such Module Alamofire 解决办法是重新Build 但是根本没用 原因是版本问题 选择一个合适的版本即可 在readme 文件可看到对应的版本情况
  • 人体姿态估计--RMPE: Regional Multi-Person Pose Estimation

    RMPE Regional Multi Person Pose Estimation ICCV2017 Code is based Caffe and Torch https github com MVIG SJTU RMPE https
  • Spring Boot如何实现缓存的自动刷新

    Spring Boot如何实现缓存的自动刷新 在Web应用程序中 缓存是提高性能的重要手段之一 在Spring Boot应用程序中 我们可以使用Spring Cache来实现缓存功能 然而 当缓存的数据发生变化时 我们可能需要手动刷新缓存
  • html5 imports,html - HTML5 Imports not working - Stack Overflow

    The correct to do this is through server side pages includes or through JavaScript PHP example Welcome to my home page S
  • 在浏览器输入localhost:3000显示需要新应用打开此localhost原因

    今天做web应用开发时遇到在使用非谷歌浏览器时 输入localhost 3000 显示如下 显示需要新应用打开此localhost 实在是没办法显示出网页 经过反复尝试终于发现问题其实是现在使用非Chrome浏览器 在地址栏输入不带http
  • E: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系。

    ubuntu apt 安装软件的时候 经常有这种错误 是由于依赖关系无法满足而引起的 比如我在安装pangolin的时候 提示 下列软件包有未满足的依赖关系 libxkbcommon dev 依赖 libxkbcommon0 0 8 0 1
  • vm安装Ubuntu 本机navicat连接Ubuntu MySQL

    先下载 虚拟机软件 VMware Workstation Pro 我下的16版 自己找密钥 打开VMware Workstation Pro 左侧右键鼠标 新建虚拟机 如图 选择典型 下一步 稍后安装系统 下一步 选择系统 我选 Linux
  • MVC知识整理

    MVC基础知识整理 ASP NETMVC框架 这里以MVC5为例 涉及到知识有 Model View Controller的使用 Area和Global的理解 路由配置 数据传递的方式 AOP思想的体现 4大过滤器 各种Result Raz
  • 台式计算机关闭屏幕快捷键,多种电脑屏幕关闭方法推荐

    有时因为需要节约电脑电量 有时因为为避免同事窥屏 有时由于顾及后台运行任务进程诸如听歌 电脑磁盘碎片整理等多种原因 这些均促使我们需要关闭电脑屏幕 无论基于何种原因促使我们关闭电脑屏幕 总的来说其并不容易操作 与台式机设置专门显示屏关机按键
  • H.264中的熵编码算法(主讲指数哥伦布编码)

    以下文章参考于殷文杰的博客 https yinwenjie blog csdn net article details 52301584 1 熵编码基本概念 1 熵 这一概念原本来自于化学和热力学 用于度量能量退化的指标 即熵越高 物体或系