图片压缩 · 2024年 9月 2日 1

视频压缩魔法——H.264

有人说,ph只会发涩涩;有人说,ph只会水贴;还有人说,ph只会用发涩涩来水贴。今天,我就要证明,他们说的确实是对的😥

但是我还是会发技术性的文章的(大概),代表作为原神抽卡机制详解(也不是很详)(顺便芙芙专武复刻池子太烂了也没抽恼)

所以今天我要来讲一个故事:

有一天,ph在搬运一个10小时长的meme视频的时候,挂着剪辑软件渲染成品,就出去吃饭了。在去饭堂的路上,我想了想那8个g的视频该有多少信息啊。然后按照我最天真无邪可爱纯真的算法,我在脑海里写到:

$$ \left ( 1920 × 1080 \right )_{像素} × 3_{字节每像素} × 60_{帧每秒} = 355.96\ MB/s $$

这样的话,一个10小时长的视频就得有……

$$ 355.96\ MB/s × \left ( 10 × 3600 \right )_{秒} = 12.22\ TB $$

擦了擦头上的冷汗,确认了自己看到的视频文件确确实实是8g大之后,我不禁想着,为什么视频压缩这么猛呢?吃完饭回来一搜,那就是我与H.264命运中的邂逅……

举个栗子:

这是苹果主页的一张png截图,大小为1015 KB:

而这是苹果主页的一个5秒钟长60fps的视频,大小为175 KB:

细心的读者可能想说:嗯?你这文件大小不对啊,是不是写反了。

没写反,300帧的H.264视频的大小 就是 1张png图片大小的 1/15。

是不是很魔法?那确实很魔法。

接下来,就让我来讲讲H.264的伟力的一角:去掉每一帧的高频信息。

帧?高频信息?图片哪来的频率?

非常好问题,使我的图片进行快速傅里叶变换。

好吧,说真的,还是直观一点理解好啦!

其实图片的频率和图片像素的变化快慢(也就是梯度)有关。变化越快,频率就越高。比如这张图片:

左边是原图,右边是把高频信息去掉的图片。

可以看到,在音响网格这些地方的图片频率就很高,所以在右图就模糊了,变成了它低频率的样子。

经常使用.jpg的大伙都知道,其实jpeg格式也有这么一步,而png作为无损压缩就不会扔掉任何信息。

而这种处理的背后正是利用了人类眼睛的弱点,我们的眼睛对高频的信息并不敏感,就像上面的对比图,要是不细看很难看出区别。

不过,这么处理到底能让文件大小小多少呢?

右图是左图7%的大小。

7%!

顺便给大家看看各种压缩程度的频域和图片的对比图:

当然,这么一算还是不够。那12.22 TB的大小乘了7%还是有876 GB。所以接下来就要请出H.264的下一个伟力:色度二次采样

色度?二次采样?色度又是什么东西?(似曾相识的问句结构……)

我亲爱的读者,我知道你很急,你先别急,由我给你(也不是很慢的)慢慢道来。

对着学校的大屏贴脸看的大伙都知道(或者视力比较好可以盯着自己电脑屏幕看),屏幕是由红绿蓝(RGB)三种颜色的光点组成的(well,有些学校的垃圾LED屏只有红色像素点)。图片也是如此,在前面计算每个像素的字节数时字节数为3就是因为RGB每个分量都用一个字节来存储(老家伙肯定听过24位真彩吧)。像素的RGB值就可以理解为像素的色度了。

那么二次采样又是什么意思?

这里又不得不说人类眼睛的另一个特点了,人类眼睛对亮度信息的敏感程度比对颜色信息的敏感程度要高很多很多。人眼中全部的感光细胞也就大约一亿三千七百万个,其中视杆细胞就占了95%(约1亿3千万),而视锥细胞仅有约7百万个。视杆细胞专门负责感应亮度,据说连一个光子的刺激都能感受到。而视锥细胞负责感应颜色。总结来说就是,人眼色色dame。

这么一说,聪明的读者应该猜到接下来我们在哪里下刀了吧。没错,我直接对着颜色信息猛砍!

把RGB转化为亮度(Y)+色度(Cb、Cr)的形式,然后把色度二次采样,也就是每个像素点和周围点取平均。这时候经常看涩涩的小伙伴都知道了,这不就是马赛克吗。

这样一来,亮度信息不变,色度信息变成了原来的1/4,总文件大小又少了将近一半!

可是876 GB 乘以 1/2 还是有438 GB大, 顶我4个恋活荒野大镖客了。

这就要我们H.264出第三次手了:运动补偿

这次词好理解了,但是大伙处于保持队形的考虑还是会问:

运动?补偿?补偿不是增加东西的意思吗?

就拿我那个10小时的meme视频来说吧。其实就是一个十几秒的视频循环了几千次,中间再套点小彩蛋而已。完全可以只存一次十几秒的视频然后告诉电脑重复这么多次嘛。

更一般的来说,对于和上一帧差不多一模一样的像素,我们完全可以把多余的表达省略掉。或者更好的是,比如一个踢足球的视频,我直接把背景存成一张静止的图片,然后把足球用ps扣出来让它单独运动,那得省多少信息呀。H.264就是这么处理的,它会把一帧分成一个宏像素(一般是16×16的大小),然后生成一个I帧(Intra frame),这种帧包含了完整的帧的信息(也就是完完整整一张图片),然后它之后的帧就只会生成P帧(predicted)或者B帧(bi-directionally predicted)。P帧存储了每一个宏像素的运动向量,所以要从最近的I帧开始每一帧都生成一个。而B帧包含了从过去到当前帧的运动向量,也包含了从未来帧到当前帧的运动向量(Time Machine?!这也是命运石之门的选择吗……(红莉栖啾啾啾))。

图太少了所以从运动补偿的百度百科偷了一张不明所以的图:

所以,其实上面那个苹果主页的视频只包含了3个I帧,这就是为什么大小这么小(小小的也很可爱)。

这也可以解释为什么在看视频的时候往回跳一点点在烂一点的电脑上会卡一下。在b站看视频还有可能是缓存被清理了(虽然也不是这个原因),但是在本地视频上也会有这个问题。原因就是因为往回退的时候,当前帧的信息要从最近的I帧开始,根据P帧和B帧一帧一帧的计算,才能得到这帧的信息。

说了这么多,你怎么还是没说为什么叫运动补偿?

哎呀,这,把视频的画面变化变成运动向量,也是一种补偿吧完全不是因为我编不下去了

不过可惜这一步很难量化成具体缩小了多少,变化快的视频就需要更多的I帧,效果也就不会那么好。

最后的最后,再用entropy encoder把冗余的信息去一下就好啦(这些地方的压缩方法就和.png一样了,就不细说了)(完全不是懒得写了)!

这就是把12.22 TB的视频变成8 GB的视频的秘密啦!变成了原始文件0.06%的大小呢……

拿实际物品来举例的话,就像把2吨重的车变成了1.2千克重的书包。

谢谢你,H.264!

参考资料:

1、https://sidbala.com/h-264-is-magic/

2、https://www.youtube.com/watch?v=pX2L_UAIoME

3、https://www.youtube.com/watch?v=r6Rp-uo6HmI