H264编解码过程简述
帧内编码详细过程
第1步:划分宏块和子块
H.264 的帧内预测不是在固定的 16x16 宏块级别进行的,它允许更灵活的划分以获得更高的预测精度。
- 1.对于亮度分量:
•一个 16x16 的宏块可以按 Intra_16x16 模式进行预测。
•也可以进一步划分为 16 个 4x4 的子块,每个子块独立进行预测(Intra_4x4)。
•在 High Profile 中,还引入了 8x8 的划分(Intra_8x8)。 - 2.对于色度分量:
•通常整个 8x8 的色度块(对应一个 16x16 的亮度块)使用 Intra_8x8 模式预测,其过程与亮度 Intra_16x16 类似。
编码器会为每个块尝试所有可能的预测模式,并采用率失真优化(RDO, Rate-Distortion Optimization) 来计算哪种“划分方式 + 预测模式”的组合能在码率和失真之间达到最佳平衡。
第2步:帧内预测
这是帧内编码的灵魂。预测器会利用当前块左边和上边已编码并重建的像素(记为 A到 M)来填充当前块中的每一个像素。
亮度预测(Luma):
- Intra_4x4(9种预测模式):适用于细节丰富的区域。
- 模式0 (Vertical):A, B, C, D的值向下垂直复制。pred[x, y] = p[x, -1]
- 模式1 (Horizontal):I, J, K, L的值向右水平复制。pred[x, y] = p[-1, y]
- 模式2 (DC):所有预测像素是 (A+B+C+D+I+J+K+L+4)/8的平均值
- 模式3-8 (Diagonal):沿不同方向(如 45° 左下、右下等)进行插值预测。例如,模式3(Diagonal Down-Left):pred[x,y] = (p[x+y, -1] + p[x+y+1, -1] + 1) >> 1
- Intra_16x16(4种预测模式):适用于平坦区域。
- 模式0 (Vertical):同 4x4。
- 模式1 (Horizontal):同 4x4。
- 模式2 (DC):同 4x4,但平均值的计算范围更大。
- 模式3 (Plane):利用上方和左方的像素拟合出一个线性平面 pred[x,y] = Clip1((a * (x - 7) + b * (y - 7) + 16) >> 5),非常适合亮度平滑渐变的区域。
色度预测(Chroma):
- 与亮度 Intra_16x16 类似,有 4 种模式:DC、Horizontal、Vertical、Plane。过程完全一样,只是块大小是 8x8。
第3步:计算残差并变换
- 1.计算残差(Residual):将当前块的原始像素值减去上一步得到的预测像素值,得到一个残差块 residual = original - prediction。残差块的数据能量(方差)通常远小于原始块,因此更容易被压缩。
- 2.变换(Transform):对残差块进行整数离散余弦变换(Integer DCT),将空间域的残差信号转换到频域。
a.•其本质是一种近似于 DCT 的变换,但所有操作都是整数运算,避免了浮点精度问题,且正变换和逆变换完全匹配,没有编解码漂移。
b.目的:将能量集中到少数低频系数上,高频系数大多接近于0,为后续的量化做准备。
c.块大小:对于 4x4 残差块,使用 4x4 整数 DCT;对于 16x16 预测产生的整个 16x16 残差块,会先将其分割成 16个 4x4 块,然后对每个 4x4 块进行 DCT(这是一种二次变换,能进一步提高压缩率)。
第4步:量化
- 1.量化(Quantization):这是信息丢失的主要环节,也是压缩率的主要来源。量化器用一个大步长(Quantization Step,由 QP 值决定)去除以变换后的频域系数,然后取整。
a.Q_coeff = round(DCT_coeff / Q_step)
b.QP 值越大,量化步长越大,更多的系数被归零,压缩率越高,但图像失真也越大。
c.QP 值越小,量化步长越小,保留的细节越多,图像质量越高,但压缩率越低。 - 2.H.264 的量化过程还结合了一个预定义的量化矩阵,可以对不同频率的系数使用不同的量化步长,例如对人眼更敏感的低频系数细量化(小步长),对不敏感的高频系数粗量化(大步长)。
第5步:重排序与熵编码
-
1.重排序(Reordering):
•量化后的系数块(例如 4x4)会按照一个之字形(Zig-Zag)扫描顺序重新排列成一维数组。
•目的:将非零系数(通常是低频系数)集中到数组前端,将为零的系数(通常是高频系数)甩到数组末尾,从而产生长串的“0”,便于后续的游程编码。 -
2.熵编码(Entropy Coding):这是最后一步,也是无损压缩步骤。
a.H.264 主要使用两种熵编码方案:•CAVLC(上下文自适应的可变长编码):用于 Baseline 和 Extended Profile。它根据周围已编码块的非零系数个数(上下文),为当前块选择不同的码表,对游程长度和非零系数值进行高效编码。
•CABAC(上下文自适应的二进制算术编码):用于 Main 及更高 Profile。它是更高级的算术编码,同样利用上下文信息,其压缩效率比 CAVLC 高出 10%~15%,但计算复杂度也更高。
-
3.熵编码器不仅编码残差系数,还编码所有的宏块头信息(如宏块类型、预测模式、QP值、运动矢量等)。
第6步:重建帧(为后续帧提供参考)
编码器必须模拟解码器的行为,以确保后续帧的预测是基于编码器已拥有的信息,避免漂移误差。
- 1.反量化与反变换:将量化后的系数进行反量化和反变换,得到解码后的残差块。这个残差块因为经历了有损量化,与原始残差块已有差异。
- 2.重建块:将解码后的残差块与第2步的预测块相加:reconstructed_block = prediction + decoded_residual。
- 3.去块效应滤波:虽然去块滤波通常被认为是帧间循环的一部分,但它对帧内编码同样重要。它会平滑块与块之间的边界,消除因分块编码产生的明显块状瑕疵,显著提升主观视觉质量。滤波后的重建帧被放入帧缓存中,作为后续帧(如果是I帧,则是后续P/B帧)进行帧间预测的参考帧。
总结与关键点
- •核心是预测:帧内压缩的效率极大程度上依赖于预测的准确性。预测越准,残差越小,越容易压缩。
- •灵活的块划分:4x4, 8x8, 16x16 的多种选择让编码器能自适应图像内容。
- •率失真优化(RDO):编码器在选择“预测模式”和“划分方式”时,并非简单地选择残差最小的,而是会计算 J = D + λR这个代价函数,寻找码率(R) 和失真(D) 之间的最优解。
- •重建环路:编码器内置了一个本地解码器,确保编解码双方的状态完全同步。
整个过程完美体现了视频编码的经典思路:预测 -> 残差 -> 变换 -> 量化 -> 熵编码,而帧内预测的魅力就在于其纯粹利用空间相关性的各种精巧预测方向。
帧内预测残差大能否直接DCT后编码
帧内编码时,帧内预测步骤得到的残差太大,如果不预测,直接16x16 进行DCT变换后编码呢
这是一个非常出色且切中要害的问题。答案是:技术上完全可以,但这几乎总是更差的选择,会导致压缩效率显著下降。
H.264 标准本身就包含了这种“不预测”的模式,但它是以一种更聪明的方式被集成在预测框架内的。下面我们来详细解释为什么直接进行 DCT 是低效的。
1. “不预测”在 H.264 中对应的模式
你所说的“直接16x16进行DCT变换”,在 H.264 的帧内预测框架中,实际上最接近 Intra_16x16 的 DC 预测模式。
- •DC 预测模式做了什么? 它计算当前宏块上方和左侧相邻像素的平均值,然后用这个平均值填充整个 16x16 的预测块。
- •如果图像纹理复杂,这个“平均值”和原始像素的差异会非常大,导致第一步计算出的残差确实很大,看起来似乎和“不预测”没区别。
关键点在于:即使在这个“最差”的预测模式下,编码器依然完成了一个至关重要的步骤——去除空间冗余。
2. 为什么“预测”这一步不可省略?(核心原因)
即使预测后残差很大,预测过程也从根本上改变了数据的统计特性,使其变得更容易被后续的变换和熵编码压缩。
原因一:去除空间冗余,改变数据统计分布
- •原始像素块:像素值代表的是绝对的亮度或色度信息(例如,150, 152, 149, 155…)。这些值通常在0到255之间均匀分布,具有较高的熵值,难以压缩。
- •预测残差块:像素值代表的是“真实值”与“预测值”的差异(例如,+2, -5, +1, -3…)。这些差异值(残差)通常集中在0附近,服从拉普拉斯分布。
这种分布的改变对压缩至关重要: - •变换(DCT):DCT 善于将能量集中到低频分量。对于集中在0附近的残差信号,经过DCT后,能量会更加集中,高频系数几乎都为0。
- •熵编码(CAVLC/CABAC):熵编码器是为这种“数值小且集中在0附近”的统计模型量身定制的。它可以用非常短的码字来表示常见的0和小数值,而用较长的码字表示罕见的大数值,从而实现高效压缩。
如果直接对原始像素做DCT,能量集中效果会差很多,会得到更多数值较大的高频系数,最终熵编码器需要为这些大数值分配更多的比特,导致压缩率下降。
原因二:DC 值单独处理
即使在最“差”的 DC 预测模式下,它也做了一件有意义的事:它将整个块的直流(DC)分量从交流(AC)分量中分离了出来。
- •预测块是一个平坦的、由平均值构成的块。原始块减去这个平均值后,得到的残差块的平均值理论上接近于0。
- •这意味着,残差块本身不包含直流分量,原始块的直流信息已经被提取到那个“平均值”中了。
- •在编码时,DC 系数和 AC 系数是分开处理和编码的。DC 系数通常被单独预测和编码,效率更高。
直接对原始块做DCT,DC分量和AC分量混杂在一起,无法享受到这种优化。
原因三:编码器的智能选择
一个优秀的编码器(如 x264, x265)不会傻傻地只用一种模式。它会使用率失真优化(RDO) 来尝试所有可能的预测模式(包括各种角度的 Intra_4x4, Intra_16x16 和 DC 模式)。
- •RDO 计算:对于每一种模式,它都会计算一个代价:代价 = 失真度(如SSE) + λ * 所需比特数。
- •模式决策:编码器会选择总代价最低的模式。如果图像某区域纹理极度复杂,没有任何一种方向性能提供好的预测,那么 DC 模式(即你所说的“类似不预测”的模式)就会因为其“所需比特数”最少而胜出。
也就是说,DC 模式是编码器在“预测不准”时的一种优雅降级方案,它依然在预测的框架内,并享受框架带来的好处,而不是彻底抛弃预测。
帧间编码详细过程(以一個 P 帧的宏块为例)
第1步:划分宏块与子块
和帧内编码一样,帧间编码也支持灵活的块划分,这是 H.264 高效率的关键之一。
一个 16x16 的宏块可以按以下方式划分:
- •16x16, 16x8, 8x16, 8x8:这些是基本的划分方式。
- •如果选择 8x8 划分(称为子宏块),每个 8x8 块还可以进一步划分为:8x8, 8x4, 4x8, 4x4。
编码器会使用率失真优化(RDO) 尝试所有可能的划分组合,最终选择编码代价(码率 + 失真)最小的那种。小块划分更适合处理复杂、不规则的运动,但需要额外比特来编码更多的运动矢量;大块划分适合平坦或运动一致的区域,更节省比特。
第2步:运动估计
这是帧间编码中最复杂、计算量最大的环节。
- 目标:为当前块(或子块)在指定的参考帧(通常是前一帧或前几帧)中,找到一个最匹配的、尺寸相同的参考块。
- 匹配准则:通常使用绝对差和(SAD) 或平方差和(SSD) 作为匹配程度的度量标准。SAD 计算更简单,最常用。SAD = Σ |当前像素值 - 参考像素值|
- 搜索算法:
- 全搜索:在搜索窗口内逐像素遍历,一定能找到最优解,但计算量极大,实际应用中不常用。
- 快速算法:如菱形搜索(Diamond Search)、六边形搜索(Hexagon-Based Search) 等。这些算法通过特定的搜索模式大幅减少搜索点数,以极小的性能损失换取计算量的大幅下降。
- 输出:运动估计的结果是两个信息:
- 1运动矢量:一个二维向量 (MVx, MVy),表示参考块相对于当前块位置的位移。
- 2参考帧索引:指明这个参考块来自于哪个参考帧(在有多帧参考的情况下)。
第3步:运动补偿
- 过程:根据上一步得到的运动矢量 (MVx, MVy)和参考帧索引,直接从参考帧中取出对应的像素块,这个块就是预测块。
- 亚像素精度:为了获得更精确的预测,H.264 支持 1/2 像素 和 1/4 像素 精度的运动矢量。
- •首先通过6抽头滤波器对参考帧的整像素点进行插值,得到半像素位置的值。
- •然后通过线性滤波器对半像素点进行插值,得到1/4像素位置的值。
- •亚像素精度极大地提高了预测的准确性,特别是对于缓慢、平滑的运动,能显著减小残差。
第4步:计算残差并变换
1.计算残差:残差块 = 原始块 - 预测块。由于运动补偿预测通常非常准确,这个残差块的能量非常小,数据高度集中在0附近。
2.变换:对残差块进行整数离散余弦变换。和帧内编码一样,通常使用 4x4 或 8x8 的 DCT 变换,将残差从空间域变换到频域,进一步集中能量。
第5步:量化与熵编码
1.1.量化:使用量化参数(QP)对变换系数进行有损量化。这一步是压缩和质量损失的主要来源。
2.2.熵编码:使用 CAVLC 或 CABAC 对量化后的系数、运动矢量、参考帧索引、宏块类型等所有信息进行无损压缩编码。
- a.运动矢量的编码:运动矢量本身具有很高的空间相关性(相邻块的运动通常相似)。因此,H.264 对运动矢量采用预测编码。它先利用相邻块的运动矢量预测出当前块的 MVP,然后对运动矢量差(MVD = MV - MVP) 进行熵编码,而不是直接编码 MV 本身,这大大节省了比特。
第6步:重建与去块滤波(解码环路)
这是保证编码器与解码器同步的关键步骤,绝不能省略。
1.1.反量化与反变换:对量化后的系数进行反量化和反变换,得到解码后的残差块。这个残差块是有失真的。
2.2.重建块:重建块 = 预测块 + 解码后的残差块。注意,这里相加的“预测块”必须和解码端将来能生成的预测块完全一致(即使用相同的运动矢量和参考帧)。
3.3.去块效应滤波:对重建块的边界进行自适应滤波,消除因分块编码产生的块状瑕疵。滤波后的帧才会被放入参考帧缓冲区,用于后续帧的运动补偿预测。这一步至关重要,因为它提高了参考帧的质量,从而改善了后续帧的预测效果,形成良性循环。
I帧切换P帧的实现原理
H264编码时,新的一帧画面,如何决定使用帧内编码还是帧间编码呢?? 我并没有设置GOP 参数。 如果当前画面和前一个画面变化很大,用什么参数作为判断依据,然后将编码为 I帧的。
核心机制:场景切换检测
当你说“没有设置GOP参数”时,通常意味着你使用了编码器的默认配置。大多数现代编码器(如 x264, x265)的默认模式是自动动态 GOP。在这种模式下,I 帧的放置不再遵循一个固定的间隔(如每 250 帧一个 I 帧),而是由编码器智能地根据画面内容来决定。
这个智能决策的过程就叫做场景切换检测。其目标就是识别出你所说的“当前画面和前一个画面变化很大”的情况。
判断依据和参数
编码器判断“变化很大”并决定编码为 I 帧的依据是一个综合性的度量,主要基于以下几个参数和计算:
1. 核心参数:亮度分量的绝对差和 / 平方差和
这是最直接、最主要的判断依据。编码器会快速地将当前待编码帧(当前帧)与最近的参考帧(通常是前一帧)进行比较。
- •SAD (Sum of Absolute Differences):计算两个帧对应像素亮度值绝对差的总和。•SAD = Σ |Curr_pixel - Ref_pixel|
- •SSD (Sum of Squared Differences):计算平方差的总和,对大差异的惩罚更大。•SSD = Σ (Curr_pixel - Ref_pixel)²
如何工作:
编码器会计算整个帧或宏块的 SAD/SSD。如果这个总值超过一个内部自适应阈值,编码器就认为发生了场景切换。
- •为什么是亮度? 因为人眼对亮度信息最敏感,亮度分量携带了图像的大部分信息,是判断内容变化最有效的依据。
2. 率失真代价(Rate-Distortion Cost)
这是更高级、更精确的判断依据。编码器会进行一个快速的“预分析”或“试编码”:
- •尝试用帧间(P帧)模式:为当前帧做一次快速但粗略的运动估计和补偿,计算如果把它编成 P 帧所需要的总代价 J_inter。这个代价 J_inter包含了预测残差的大小(失真 D)和编码运动矢量、残差等所需比特数(码率 R)的综合考量(J = D + λR)。
- •尝试用帧内(I帧)模式:估算如果把它编成 I 帧所需要的总代价 J_intra。
决策:
如果 J_intra甚至小于 J_inter,或者两者接近,说明帧间编码的优势很小,此时使用帧内编码更划算。这种情况几乎总是发生在场景切换时,因为运动补偿完全失效,残差极大,导致 J_inter变得非常高。
3. 编码器的内部状态和历史信息
编码器还会考虑其他因素:
- •距离上一个 I 帧的时间:即使没有强烈的场景切换,编码器也不会无限期地不插入 I 帧。它会设置一个最大 GOP 长度(例如默认 250 帧或 10 秒)作为安全上限,防止错误传播时间过长和方便随机访问。即使画面没大变,到了这个上限也会强制插入一个 I 帧。
- •码率控制状态:如果编码器正处于一个高复杂度场景,码率控制算法可能会建议插入一个 I 帧来“重置”状态,以长期控制码率。