H.264编码协议

25 年 3 月 10 日 星期一
2012 字
11 分钟

H.264编码协议

某学生因学习压缩而压缩学习的抽象历程

introduce

为什么不写摘要?因为先打出来的字是 i

对于一段视频应该在我们电脑存储的空间远比实际空间要大得多,这得益于好的压缩算法,节约大量空间,在这其中H.264算是比较经典的了

(然而当我在网上搜索资料时,尤其是b站,把h.264放到音视频里面讲的实在太多,导致像我这样针对压缩算法的learner来说属实不利,所以这篇blog就出现了)(实际是我太懒了啥都没做,写点东西激励一下自己

H.264主要是针对视频中的时间冗余空间冗余进行压缩后文会稍微详细的介绍

method

首先我们将一段视频分成一帧一帧的,然后现在我们就只讨论其中的一帧,也就是无视其他的视频帧

空间冗余(帧内预测)

现在我们想象一副画面,一幅图,一个视频的一帧(以每秒30帧算),玩过cv的人都知道我们会把这副图分成一个个的小块(最小单位长度为1)进行研究,在H.264中我们先划分宏块,宏块就是其中16X16的区域。

现在我们再把注意力集中在宏块上,计算每一个宏块的像素值(也就是RGB:red ,green ,blue),在宏块上我们再次划分子块,子块的大小就不怎么讲究了,8X16,8X8,4X4...都行

这个时候进入帧内预测比较精彩的点了,对于一幅图像,相邻的两个像素的亮度和色度值是比较接近的(这里涉及到另一种表达,把像素值RGB换算成YUV,Y是亮度,U和V是两个维度的色度值),因此我们存储第一个像素的YUV,那第二个像素我们是不是可以参照第一个值存储更少的信息,节约码率。首先原始值成为X,并对他进行编码,在编码之前假设有一个参考像素X‘,这个参考像素和它附近的像素有关,根据这个X’,我推导出一个Xp,也就是,假设我真的像猜想那么做了,我的像素值应该是推导出来的,然后为了验证这个的正确性,我将X-Xp,得到了残差d,这个残差d就是推导和实际的差距,也就是说,我们只需要将d进行DCT变换+量化放入这个块需要的信息存储就好了

(如果看到这里你觉得你懂了,emmm打住,因为我还没懂)

X‘到底是怎么来的!因为附近的像素有多个,因此假设我们知道了某一个方向的像素值,至于这里为什么是某个方向,原因是我们进行预编码会遵循一个比较美的路径,比如从左上到右下。各种路径/预测路径就出现了,这里介绍最基础的,比如当前块的十六个像素值,完全由其上方块最后一行的那四个像素值决定,第一列所有的Xp值都等于A,第二排都等于B,这里就不多花篇幅来讲了(因为我困了QAQ)

https://blog.csdn.net/zhoutaopower/article/details/127338910各种预测模式在这里

然后对残差进行DCT变换和量化进一步化简(后文会提到)

这样对一个帧的处理也就差不多了,我们现在再来关注帧与帧的关系吧

时间冗余(帧间压缩)

假设摄像头每秒抓取30帧(上文提了一点),这30帧的数据大部分情况下都是相关联的。也有可能不止30帧的的数据,可能几十帧,上百帧的数据都是关联特别密切的。对于这些关联特别密切的帧,其实我们只需要保存一帧的数据,其它帧都可以通过这一帧再按某种规则预测出来,所以说视频数据在时间上的冗余是极多的

这个时候我们就要对一些关联性大帧分到一组去,算法就是:在相邻几幅图像画面中,一般有差别的像素只有10%以内的点,亮度差值变化不超过2%,而色度差值的变化只有1%以内,我们认为这样的图可以分到一组(这是copy的)。现在我们就只需要关注这单个组了,啊对了,这一组被称作GOP,这组帧中我们只保留第一帧的完整数据,称作I帧(对I帧是可以进行帧内预测来压缩的),其他的我们可以暂时称为P帧和B帧,之后再区分

P帧:参考I帧+差异生成,B帧:参考I和P帧才能生成

然后通过运动矢量和补偿,也就是帧间预测技术。

运动矢量是一个块到参考帧位置的向量,这样就能描述一个块大概的变化,然后得到残差后再用DCT,提高准确率

具体及逆行运动估计的算法其实比较笼统,比如使用全搜索算法等等,代码方面如果后续需要我再补,毕竟这只是idea

DCT

DCT,也就是离散正弦变换,将视频帧从空间域转换到频域,使得后续的量化和熵编码步骤能够针对主要频率成分进行更有效的压缩,经过DCT转换后,我们会得到一组系数矩阵,这些系数矩阵反映了原始数据在不同频率上的能量分布。低频系数通常包含图像的主要能量,而高频系数包含细节和噪声,学过信号的比较了解了

编码

上述帧间和帧内的技术对残差d都采用了DCT,实际上这样的压缩尤其是帧内其实是有损的(不过问题不大就是了),后续就涉及到无损压缩CABAC,或者是哈夫曼编码(这个可以找机会写点)还有CAVLC(上下文自适应的变长编码),和DCT结合起来可以这么用

python
import numpy as np
import scipy.fftpack

# 假设 data 是已经经过预处理(例如,减去均值)并进行DCT变换的数据
data = np.random.rand(8, 8)  # 生成一个8x8的随机矩阵作为示例

# 执行DCT变换
dct_data = scipy.fftpack.dct(scipy.fftpack.dct(data.T, norm='ortho').T, norm='ortho')

# 量化函数(简单的缩放,实际应用中会更复杂)
def quantize(dct_data, quality_factor):
    return np.round((dct_data / quality_factor))

# 量化DCT数据
quality_factor = 10
quantized_data = quantize(dct_data, quality_factor)

# 简单的霍夫曼编码实现(这里仅为示例,并非实际高效的霍夫曼编码实现)
def simple_huffman_encoding(block):
    # 对数据进行简单变换(不考虑实际霍夫曼编码的复杂过程)
    return ''.join(f'{abs(v)}{0 if v >= 0 else 1}' for v in block.flatten())

# 对量化后的数据进行简单的霍夫曼编码
huffman_encoded_data = simple_huffman_encoding(quantized_data)

# 输出处理后的数据
print("原始量化数据:")
print(quantized_data)
print("\n霍夫曼编码后的数据:")
print(huffman_encoded_data)

experiment

要不我直接用ffmpeg调库给你写写看?(要也得临时学QAQ)

conclusion

反正麻烦的东西我都跳过了,对技术兴趣也有限,如果我写的有什么问题,那也没事,不影响我用他的算法就好了

用到是帧内压缩算法运动估计与补偿算法DCT编码算法后面两个其实感觉像是套用,工业界这么干确实不错

流程就是

c
flowchart LR
    A[原始视频帧] --> B[帧内预测]
    B --> C[帧内DCT]
    A --> D[运动估计]
    D --> E[帧间预测]
    E --> F[帧间DCT]
    C --> G[量化]
    F --> G
    G --> H[熵编码]
    H --> I[压缩视频流]

就这样吧~晚安!

文章标题:H.264编码协议

文章作者:io-wy

文章链接:https://io-wy.github.io/posts/h264%E7%BC%96%E7%A0%81%E5%8D%8F%E8%AE%AE[复制]

最后修改时间:


商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,您可以自由地在任何媒体以任何形式复制和分发作品,也可以修改和创作,但是分发衍生作品时必须采用相同的许可协议。
本文采用CC BY-NC-SA 4.0进行许可。