H.264 是魔法
微wx笑 2022-07-23【算法】 7 0关键字: H264 视频编码 算法
H.264 是一种视频压缩编解码器标准。它无处不在——互联网视频、蓝光、电话、安全摄像头、无人机,应有尽有。现在一切都使用 H.264。H.264 是一项了不起的技术。这是 30 多年
H.264 是一种视频压缩编解码器标准。它无处不在——互联网视频、蓝光、电话、安全摄像头、无人机,应有尽有。现在一切都使用 H.264。
H.264 是一项了不起的技术。这是 30 多年致力于一个目标的结果:减少传输全动态视频所需的带宽。
从技术上讲,这是非常有趣的。这篇文章将深入了解一些高层次的细节——我希望不会让你对这些错综复杂的东西感到厌烦。另请注意,此处解释的许多概念通常适用于视频压缩,而不仅仅是 H.264。
为什么还要压缩任何东西?
一个简单的未压缩视频文件将包含一组 2D 缓冲区,其中包含每帧的像素数据。所以它是一个 3D(2 个空间维度和 1 个时间)字节数组。每个像素需要 3 个字节来存储 - 三种原色(红色、绿色和蓝色)各一个字节。
1080p @ 60 Hz = 1920x1080x60x3 => ~ 370 MB/秒的原始数据。
这几乎是不可能处理的。一张 50GB 的蓝光光盘只能保存约 2 分钟。你不能把它快速移动到任何地方。甚至 SSD 也无法将其直接从 RAM 转储到磁盘 [^1]。
是的。我们需要压缩。
为什么是H.264压缩?
是的,我会回答这个问题。但首先让我给你看点东西。这是苹果主页:
我捕获了这个主页的屏幕并生成了两个文件:
苹果主页的PNG截图 1015KB
5 秒 60fps H.264 同一苹果主页 175KB视频
嗯。什么?这些文件大小看起来切换了。
不,他们是对的。300 帧长的 H.264 视频为 175KB。该视频的单帧 PNG 格式为 1015KB。
看起来我们存储的数据量是视频中数据量的 300 倍。但文件大小是五分之一。因此,H.264 的效率似乎是 PNG 的 1500 倍。
这怎么可能?好吧,有什么诀窍?
技巧非常多!H.264 使用了您能想到的所有技巧(以及您想不到的大量技巧)。让我们来看看重要的。
减轻重量
想象一下,您正在为街头赛车制造一辆汽车。你需要走得更快。你做的第一件事是什么?你减轻了一些体重。你的车重 3000 磅。你扔掉你不需要的东西。那些后座?噗。查查那些。那个低音炮?走了。没有适合你的音乐。空调?是的,放弃它。传播?是的。。不是。等待!我们会需要那个。
你删除了除了重要的东西之外的所有东西。
这种丢弃不需要节省空间的位的概念称为有损压缩。H.264 是一种有损编解码器——它会丢弃不太重要的位,而只保留重要的位。
PNG是一种无损编解码器。这意味着没有任何东西被丢弃。逐位,可以从 PNG 编码的图像中恢复原始源图像。
重要的位?算法如何知道我的帧中的哪些位是重要的?
修剪图像的明显方法很少。也许右上象限一直没用。因此,也许我们可以将这些像素归零并丢弃该象限。我们只会使用所需空间的 3/4。现在约 2200 磅。或者也许我们可以在框架边缘剪出一个粗边框,重要的东西无论如何都在中间。是的,你可以做到这些。但 H.264 不这样做。
H.264 实际上是做什么的?
H.264 与其他有损图像算法一样,会丢弃细节信息。这是与丢弃后的图像相比的原始特写镜头。
看看压缩后的 MacBook Pro 扬声器格栅上的孔是怎么看出来的?如果不放大,您甚至会注意到差异。右侧图像的重量是原始图像的7% - 我们甚至没有压缩传统意义上的图像。想象一下您的汽车仅重 200 磅!
7% 哇!你如何丢弃这样的详细信息?
为此,我们需要一个快速的数学课。
信息熵
现在我们要开始多汁的部分了!哈双关语!如果你上过信息论课,你可能会记得信息熵。信息熵是表示某些信息所需的位数。请注意,它不仅仅是某些数据集的大小。它是必须用于表示数据集中包含的所有信息的最小位数。
例如,如果您的数据集是单次抛硬币的结果,则需要 1 位熵。如果您记录了两次抛硬币,则需要 2 位。说得通?
假设你有一个奇怪的硬币——你已经扔了 10 次,每次都落在正面。你会如何向某人描述这些信息?你不会说HHHHHHHHH。你只会说“10 次投掷,全是正面”——砰!您刚刚压缩了一些数据!简单的。我为你节省了数小时的操心讲座。这显然过于简单化了,但是您已经将一些数据转换为相同信息的另一种更短的表示形式。您减少了数据冗余。这个数据集中的信息熵没有改变——你只是在表示之间进行了转换。这种类型的编码器称为熵编码器——它是一种通用的无损编码器,适用于任何类型的数据。
频域
现在您了解了信息熵,让我们继续讨论数据的转换。您可以用一些基本单位表示数据。如果使用二进制,则有 0 和 1。如果使用十六进制,则有 16 个字符。您可以轻松地在两个系统之间进行转换。它们本质上是等价的。到目前为止,一切都很好?好的!
现在,一些想象力!想象一下,您可以将任何随空间(或时间)变化的数据集(例如图像的亮度值)转换为不同的坐标空间。因此,假设我们有频率坐标,而不是 xy 坐标。freqX 和 freqY 现在是轴。这称为频域表示。还有另一个令人费解的数学定理 [^2] 表明您可以对任何数据执行此操作,并且只要 freqX 和 freqY 足够高,您就可以实现完美的无损转换。
好的,但是freqX 和freqY 是什么?
freqX 和 freqY 是其他一些基本单位集。就像我们从二进制切换到十六进制一样,我们有一个不同的基本单位,我们正在从熟悉的 XY 切换到 freqX 和 freqY。十六进制“A”看起来与二进制“1010”不同。两者的意思相同,但看起来不同。所以这是我们的图像在频域中的样子:
MacBook Pro 上的精细格栅在该图像的高频分量中具有很高的信息含量。细微变化的内容 = 高频成分。颜色和亮度的任何形式的渐变 - 例如渐变是该图像的低频分量。介于两者之间的任何东西都介于两者之间。如此精细的细节=高频率。柔和的渐变 = 低频率。说得通?
在频域表示中,低频分量靠近该图像的中心。高频分量朝向图像的边缘。
好的。有点道理。但是为什么要做这一切呢?
因为现在,您可以获取该频域图像,然后屏蔽边缘 - 丢弃包含高频成分信息的信息。现在,如果您转换回您的常规 xy 坐标,您会发现生成的图像看起来与原始图像相似,但丢失了一些精细的细节。但是现在,图像只占据了一小部分空间。通过控制蒙版的大小,您现在可以精确地调整您希望输出图像的详细程度。
这是主页中笔记本电脑的特写。除了现在,已经应用了一个圆形边框蒙版。
这些数字表示该图像的信息熵作为原始图像的一部分。即使是 2%,除非您处于此缩放级别,否则您不会注意到差异。2%!- 你的车现在重 60 磅!
所以你就是这样减肥的。有损压缩中的这个过程称为量化[^3]。
好的。印象深刻,我猜。你还有什么?
色度二次采样。
人/眼的大脑系统不太擅长解析颜色的更精细细节。它可以很容易地检测到亮度的微小变化,但不能检测颜色。所以必须有一些方法来丢弃颜色信息以减轻更多的重量。
在电视信号中,R+G+B 颜色数据被转换为 Y+Cb+Cr。Y 是亮度(基本上是黑白亮度),Cb 和 Cr 是色度(颜色)分量。RGB 和 YCbCr 在信息熵方面是等价的。
为什么不必要地复杂化?RGB 对你来说不够好?
回到我们有彩电之前,我们只有 Y 信号。当彩电刚开始出现时,工程师们必须想办法将 RGB 颜色与 Y 一起传输。他们明智地决定将颜色信息编码为 Cb 和 Cr,并与 Y 一起传输,而不是使用两个单独的数据流。 Y 信息。这样,BW 电视只会查看 Y 分量。此外,彩色电视将查看色度分量并在内部转换为 RGB。
但是看看诀窍:Y 分量以全分辨率编码。C 组件仅在四分之一分辨率下。由于眼睛/大脑在检测颜色变化方面很糟糕,你可以摆脱这个。通过这样做,您可以将总带宽减少一半,而视觉差异非常小。一半!您的汽车现在重 30 磅!
这种丢弃一些颜色信息的过程称为色度二次采样[^4]。虽然不是 H.264 特有的并且本身已经存在了几十年,但它几乎被普遍使用。
这些是有损压缩的大减重器。我们的帧现在很小——因为我们丢弃了大部分细节信息和一半颜色信息。
等待。而已?我们可以做更多的事情吗?
是的。减肥只是第一步。到目前为止,我们只关注单个帧内的空间域。现在是探索时间压缩的时候了——我们在其中查看一组跨时间的帧。
运动补偿
H.264 是一种运动补偿压缩标准。
运动补偿?现在怎么办?
想象一下,你正在观看一场网球比赛。相机固定在某个角度。唯一移动的是球来回移动。你将如何编码这些信息?你做你一直做的事,对吧?你有一个 3D 像素数组,空间二维和时间一维。正确的?
不。你为什么要?无论如何,大多数图像都是相同的。球场、网、人群,都是静止的。唯一真正的动作是球在移动。如果您可以在背景中只使用一张静态图像,然后只有一张球的动态图像怎么办?那不是节省很多空间吗?你明白我要去哪里了吗?得到它?看看我要去哪里?运动估计?
撇开蹩脚的玩笑不谈,这正是 H.264 所做的。H.264 将图像分割成宏块——通常是 16x16 像素块,它将用于运动估计。它编码一个静态图像 - 通常称为I 帧(帧内)。这是一个完整的帧 - 包含构建该帧所需的所有位。然后后续帧是P 帧(预测)或B 帧(双向预测)。P 帧是对来自前一帧的每个宏块的运动矢量进行编码的帧。所以解码器必须根据之前的帧来构建一个 P 帧。它从视频流中的最后一个 I 帧开始,然后遍历每个后续帧 - 将运动矢量增量相加,直到它到达当前帧。
B 帧更有趣,预测是双向发生的,既来自过去的帧,也来自未来的帧。所以你现在可以想象为什么苹果主页的视频压缩得这么好。因为它实际上只是三个 I 帧,其中宏块被平移。
假设您一直在 YouTube 上播放视频。你错过了对话的最后几秒钟,所以你向后擦了几秒钟。您是否注意到它不会立即从您刚刚选择的时间码开始播放。它会暂停片刻,然后播放。它已经缓冲了来自网络的那些帧,因为你刚刚播放了它,那为什么要暂停呢?
是的,这让我很生气。为什么这样做?
因为您已经要求解码器跳转到某个任意帧,所以解码器必须重做所有计算 - 从最近的 I 帧开始,并将运动矢量增量加到您所在的帧 - 这在计算上很昂贵,因此短暂的停顿。希望你现在不会那么恼火,知道它实际上是在努力工作,而不仅仅是为了惹恼你而坐在那里。
由于您只对运动矢量增量进行编码,因此该技术对于任何具有运动的视频都非常节省空间,但需要进行一些计算。
现在我们已经涵盖了空间和时间压缩!到目前为止,我们在 Quantization 中节省了大量空间。色度二次采样进一步将所需空间减半。最重要的是,我们有运动补偿,它只存储了我们在该视频中的约 300 帧的 3 个实际帧。
在我看来还不错。怎么办?
现在我们结束并敲定交易。我们使用传统的无损熵编码器。因为为什么不呢?让我们把它拍在那儿,以防万一。
熵编码器
在有损步骤之后,I 帧包含冗余信息。P 和 B 帧中每个宏块的运动矢量——它们的整组具有相同的值——因为当我们的测试视频中的图像平移时,几个宏块移动相同的量。
熵编码器将处理这种冗余。而且由于它是通用无损编码器,我们不必担心它会做出什么权衡。我们可以恢复所有进入的数据。
而且,我们完成了!它的核心是 H.264 等视频压缩编解码器的工作原理。这些就是它的伎俩。
好,很好!但我很想知道我们的车现在有多重。
原始视频是以 1232x1154 的奇数分辨率捕获的。如果我们在这里应用数学,我们得到:
5 秒 @ 60 fps = 1232x1154x60x3x5 => 1.2 GB
压缩视频 => 175 KB
如果我们对 3000 磅的汽车应用相同的比例,我们得到0.4 磅作为最终重量。6.5盎司!
是的。这是魔法!
显然,我在很大程度上过度简化了该领域数十年的深入研究。如果您想了解更多信息,维基百科页面非常具有描述性。
有意见吗?我是不是搞错了什么?不喜欢蹩脚的笑话吗?被咒骂得罪了?使用HackerNews或Reddit发表您的意见!
如果你想聊天,或者在Twitter或LinkedIn上联系我。
[^1] SSD 基准
[^2]奈奎斯特-香农采样定理
[^3][量化]( https://en.wikipedia.org/wiki/Quantization_(signal_processing)
[^4]色度二次采样
原文:https://sidbala.com/h-264-is-magic/
本文为转载文章,版权归原作者所有,不代表本站立场和观点。