【转载】视频压缩的原理-发展-应用,手把手教你学习codec(三)

第五步 - 熵编码 在我们量化数据(图像块/切片/帧)之后,我们仍然可以以无损的方式来压缩它。有许多方法(算法)可用来压缩数据。我们将简单体验其中几个,你可以阅读这本很棒的书去深入理解:Understanding Compression: Data Compression for Modern De

第五步 - 熵编码

在我们量化数据(图像块/切片/帧)之后,我们仍然可以以无损的方式来压缩它。有许多方法(算法)可用来压缩数据。我们将简单体验其中几个,你可以阅读这本很棒的书去深入理解:Understanding Compression: Data Compression for Modern Developers

VLC 编码:

让我们假设我们有一个符号流:a, e, rt,它们的概率(从0到1)由下表所示。

a

e

r

t

概率

0.3

0.3

0.2

0.2

我们可以分配不同的二进制码,(最好是)小的码给最可能(出现的字符),大些的码给最少可能(出现的字符)。

a

e

r

t

概率

0.3

0.3

0.2

0.2

二进制码

0

10

110

1110

让我们压缩 eat 流,假设我们为每个字符花费 8 bit,在没有做任何压缩时我们将花费 24 bit。但是在这种情况下,我们使用各自的代码来替换每个字符,我们就能节省空间。

第一步是编码字符 e10,第二个字符是 a,追加(不是数学加法)后是 [10][0],最后是第三个字符 t,最终组成已压缩的比特流 [10][0][1110]1001110,这只需 7 bit(比原来的空间少 3.4 倍)。

请注意每个代码必须是唯一的前缀码,Huffman 能帮你找到这些数字。虽然它有一些问题,但是视频编解码器仍然提供该方法,它也是很多应用程序的压缩算法。

编码器和解码器都必须知道这个(包含编码的)字符表,因此,你也需要传送这个表。

算术编码

让我们假设我们有一个符号流:a, e, r, st,它们的概率由下表所示。

a

e

r

s

t

概率

0.3

0.3

0.15

0.05

0.2

考虑到这个表,我们可以构建一个区间,区间包含了所有可能的字符,字符按出现概率排序。

让我们编码 eat 流,我们选择第一个字符 e 位于 0.3 到 0.6 (但不包括 0.6)的子区间,我们选择这个子区间,按照之前同等的比例再次分割。

让我们继续编码我们的流 eat,现在使第二个 a 字符位于 0.3 到 0.39 的区间里,接着再次用同样的方法编码最后的字符 t,得到最后的子区间 0.354 到 0.372

我们只需从最后的子区间 0.354 到 0.372 里选择一个数,让我们选择 0.36,不过我们可以选择这个子区间里的任何数。仅靠这个数,我们将可以恢复原始流 eat。就像我们在区间的区间里画了一根线来编码我们的流。

反向过程(又名解码)一样简单,用数字 0.36 和我们原始区间,我们可以进行同样的操作,不过现在是使用这个数字来还原被编码的流。

在第一个区间,我们发现数字落入了一个子区间,因此,这个子区间是我们的第一个字符,现在我们再次切分这个子区间,像之前一样做同样的过程。我们会注意到 0.36 落入了 a 的区间,然后我们重复这一过程直到得到最后一个字符 t(形成我们原始编码过的流 eat)。

编码器和解码器都必须知道字符概率表,因此,你也需要传送这个表。

非常巧妙,不是吗?人们能想出这样的解决方案实在是太聪明了,一些视频编解码器使用这项技术(或至少提供这一选择)。

关于无损压缩量化比特流的办法,这篇文章无疑缺少了很多细节、原因、权衡等等。作为一个开发者你应该学习更多。刚入门视频编码的人可以尝试使用不同的熵编码算法,如ANS

自己动手:CABAC vs CAVLC
你可以生成两个流,一个使用 CABAC,另一个使用 CAVLC,并比较生成每一个的时间以及最终的大小。

第六步 - 比特流格式

完成所有这些步之后,我们需要将压缩过的帧和内容打包进去。需要明确告知解码器编码定义,如颜色深度,颜色空间,分辨率,预测信息(运动向量,帧内预测方向),档次*,级别*,帧率,帧类型,帧号等等更多信息。

* 译注:原文为 profile 和 level,没有通用的译名

我们将简单地学习 H.264 比特流。第一步是生成一个小的 H.264* 比特流,可以使用本 repo 和 ffmpeg 来做。

./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264

* ffmpeg 默认将所有参数添加为 SEI NAL,很快我们会定义什么是 NAL。

这个命令会使用下面的图片作为帧,生成一个具有单个帧,64x64 和颜色空间为 yuv420 的原始 h264 比特流。

H.264 比特流

AVC (H.264) 标准规定信息将在宏帧(网络概念上的)内传输,称为 NAL(网络抽象层)。NAL 的主要目标是提供“网络友好”的视频呈现方式,该标准必须适用于电视(基于流),互联网(基于数据包)等。

同步标记用来定义 NAL 单元的边界。每个同步标记的值固定为 0x00 0x00 0x01 ,最开头的标记例外,它的值是 0x00 0x00 0x00 0x01 。如果我们在生成的 h264 比特流上运行 hexdump,我们可以在文件的开头识别至少三个 NAL。

Comment