H.264 帧内预测
帧内预测核心思想:消除空间冗余
相邻的像素之间通常存在着极高的相似性。帧内预测利用空间相关性来消除数据冗余,从而实现高效压缩。
其基本原理是:不直接编码每个像素的原始值,而是先根据其邻近像素(已经编码完成的左侧和上方像素)来预测它的值,然后只对原始值与预测值之间的差值(即残差)进行编码和传输。 由于这个差值通常很小(甚至是零),编码它所需的数据量远小于编码原始像素值所需的数据量。
帧内预测包含4种类型:
- 对于亮度分量:
    - 16x16:适用于大面积平坦区域。
- 4x4:适用于纹理细节丰富的区域。
- 8x8:仅在High Profile中支持。
 
- 对于亮度分量:
    - 在8x8的块上进行。
 
预测的关键是利用当前块左边和上边的、已经被解码和重建的像素作为参考,它保证了编码器和解码器可以使用完全相同的参考像素,生成完全一致的预测块。
      M I J K L A B C D E F G H
      A p p p p
      B p p p p
      C p p p p
      D p p p p
- p: 代表当前需要预测的4x4块中的像素。
- A, B, C, D: 代表当前块左侧的4个可用参考像素。
- I, J, K, L: 代表当前块上方的4个可用参考像素。
- M: 代表当前块左上角的参考像素。
- E, F, G, H: 代表右上方的参考像素。
注意:参考像素不是原始图像的像素,而是重建后的像素(反量化、反变换的像素)。防止编解码器之间的误差累积。
亮度分量
Intra_4x4 (9种)
Intra_4x4 预测适用于包含大量细节的区域。它有9种预测模式,其中8种是方向性预测,1种是直流(DC)预测。
      M I J K L  (上方参考像素)
      A x x x x  <- 待预测像素 p(i,j)
      B x x x x
      C x x x x
      D x x x x
      ^
      |
 (左侧参考像素)
- Vertical: 垂直预测。块中的每个像素都由其正上方的参考像素 I, J, K, L复制而来。- pred(i, j) = p(i, -1)
 
- Horizontal: 水平预测。块中的每个像素都由其左侧的参考像素 A, B, C, D复制而来。- pred(i, j) = p(-1, j)
 
- DC 直流预测。所有像素都等于上方和左侧8个参考像素(A,B,C,D,I,J,K,L)的平均值。适用于没有明显纹理方向的平坦区域。- pred(i, j) = (A+B+C+D+I+J+K+L+4) >> 3
 
- 角度预测
    - Mode 3 (Diagonal Down-Left): 右上对角线预测。像素值由上方和右上方的参考像素沿约-45度方向预测得到。
        - pred(i,j) = (p(i+j, -1) + 2*p(i+j+1, -1) + p(i+j+2, -1) + 2) >> 2
 
- Mode 4 (Diagonal Down-Right): 左下对角线预测。像素值由上方、左方和左上方的参考像素沿约+45度方向预测得到。
        - pred(i, j)的计算较为复杂,依赖于- p(i-j, -1),- p(-1, j-i)等。
 
- Mode 5 (Vertical-Right): 垂直向右预测。
- Mode 6 (Horizontal-Down): 水平向下预测。
- Mode 7 (Vertical-Left): 垂直向左预测。
- Mode 8 (Horizontal-Up): 水平向上预测。
 
- Mode 3 (Diagonal Down-Left): 右上对角线预测。像素值由上方和右上方的参考像素沿约-45度方向预测得到。
        
 这4种(Mode 5-8)都是对角线预测的微调,通过不同权重的加权平均来实现不同角度的预测。
Intra_16x16 (4种)
Intra_16x16 预测适用于大面积平坦区域,计算复杂度较低。它只有4种模式。
- Mode 0 (Vertical): 垂直预测。与4x4模式类似,每一列都由其上方的参考像素复制得到。
- Mode 1 (Horizontal): 水平预测。与4x4模式类似,每一行都由其左侧的参考像素复制得到。
- Mode 2 (DC): 直流预测。所有像素都等于上方16个和左侧16个参考像素的平均值。
- Mode 3 (Plane): 平面预测。这是一种更复杂的模式。它假设块内像素值是一个线性变化的平面。通过左侧、上方和左上角的参考像素计算出一个平面方程 H*x + V*y + C,然后用这个方程为块内每个像素生成预测值。这能很好地处理渐变光照的场景。
Intra_8x8 (9种)
这9种模式与Intra_4x4的模式非常相似,只是作用于8x8的块,并使用16个上方和8个左侧的相邻像素进行预测。这是对4x4模式的一种扩展,同样在High Profile中提供。
色度分量
色度分量(Cb 和 Cr)通常比亮度分量平坦,因此其预测模式也相对简单。通常对整个8x8的色度块进行一次预测,模式有4种,与 Intra_16x16 的亮度预测模式完全一样:
- Mode 0 (DC)
- Mode 1 (Horizontal)
- Mode 2 (Vertical)
- Mode 3 (Plane)
模式选择
解码器只需要根据码流中指定的模式进行预测即可。但对于编码器,它需要找到“最佳”的预测模式。这个过程被称为率失真优化 (Rate-Distortion Optimization, RDO)。
对于一个宏块(Macroblock),编码器的大致流程如下:
- 
    遍历所有可能的模式组合。例如,对于一个16x16的宏块,编码器会尝试: - 将其作为一个 Intra_16x16 块,并计算4种模式的代价。
- 将其分割成16个 Intra_4x4 块,并为每个4x4块在9种模式中寻找最佳模式。
- (如果支持) 将其分割成4个 Intra_8x8 块,并为每个8x8块在9种模式中寻找最佳模式。
 
- 
    计算每种模式的代价 (Cost)。代价不仅仅是预测误差的大小,它是一个综合了“失真”和“码率”的指标。 - 
        失真 (Distortion, D): 通常用SAD或 SATD来快速衡量预测残差的大小。 \[SAD = \sum_{i=0}^{N-1}\sum_{j=0}^{N-1} |\text{Original}(i,j) - \text{Predicted}(i,j)|\]
- 
        码率 (Rate, R): 指的是编码该模式的类型信息以及编码残差数据所需要的比特数。 
- 
        代价函数: \[J = D + \lambda \cdot R\]其中 λ 是一个拉格朗日乘子,用于平衡码率和失真。 
 
- 
        
- 
    选择代价最小的模式。编码器会选择使代价 J 最小的模式(或者模式组合)作为该宏块的最终编码方式。 
- 
    编码输出。将选择的最佳预测模式信息和经过变换、量化后的残差数据写入到码流中。 
MPM
对于Intra_4x4宏块,内部的16个4x4小块,每个都需要从9种预测模式中选择一种。如果对每个4x4块都用固定长度的码(例如4比特)来表示其模式,那么一个宏块仅模式信息就需要 16 * 4 = 64 比特,这个开销是相当大的。
图像内容通常具有空间相关性,这意味着一个块的最佳预测模式很有可能与其相邻块的模式相同或相似。MPM规则就是利用这种相关性来节省码率的。
其核心思想是:不直接编码模式的绝对值,而是先预测一个最可能模式(MPM),然后只编码一个标志位来表示实际模式是否等于MPM。
- 如果等于,用1个比特就完成了模式的传输,效率极高。
- 如果不等于,再花费额外的比特来传输具体是哪一个模式。
由于MPM的预测准确率很高,大部分情况下都能用1比特完成,大大降低了传输模式信息所需的总比特数。这是一种典型的上下文自适应编码思想。
MPM的计算规则
MPM的计算非常直接和高效。对于当前需要编码的块(无论是4x4还是8x8亮度块),我们看它的两个邻居:
- 块A: 左侧相邻的块
- 块B: 上方相邻的块
     +--------+
     |   B    |
+----+--------+
| A  | Current|
|    | Block  |
+----+--------+
设 modeA 是块A的预测模式,modeB 是块B的预测模式。
MPM的计算规则如下:
\[\text{MPM} = \min(\text{modeA}, \text{modeB})\]规则:MPM就是左边和上边两个块预测模式中较小的那一个。
例如:
- 如果块A的模式是1 (Horizontal),块B的模式是0 (Vertical),那么当前块的MPM就是 min(1, 0) = 0(Vertical)。
- 如果块A的模式是3 (Diag-Down-Left),块B的模式是4 (Diag-Down-Right),那么当前块的MPM就是 min(3, 4) = 3(Diag-Down-Left)。
码流中的编码方式
对于每个4x4或8x8块的模式信息,编码器会传输两个语法元素:
- prev_intra4x4_pred_mode_flag(或- prev_intra8x8_pred_mode_flag)
- rem_intra4x4_pred_mode(或- rem_intra8x8_pred_mode)
编码流程如下:
- 计算MPM:根据上述规则,编码器计算出当前块的MPM。
- 比较实际模式与MPM:
    - 如果实际的最佳模式等于MPM:
        - 编码器写入 prev_intra_pred_mode_flag = 1。
- 模式信息编码结束。总共只花了1个比特。
 
- 编码器写入 
- 如果实际的最佳模式不等于MPM:
        - 编码器写入 prev_intra_pred_mode_flag = 0。
- 然后,编码器需要传输 rem_intra_pred_mode。这个值的长度为3比特(因为总共9个模式,排除了1个MPM,剩下8种可能,2^3=8)。
 
- 编码器写入 
 
- 如果实际的最佳模式等于MPM:
        
rem_intra_pred_mode 的映射关系:
这个剩余模式的值并不是直接等于实际模式的ID。它需要一个简单的映射。编码规则如下:
rem_intra_pred_mode 的值,按升序依次代表那些不等于MPM的模式。
- if (actual_mode < MPM)- rem_intra_pred_mode = actual_mode
 
- else- rem_intra_pred_mode = actual_mode - 1
 
假设块A的模式是1,块B的模式是0。那么当前块的MPM = min(1, 0) = 0。
- 情况1:当前块的最佳模式正好是0 (Vertical)
    - 编码器:actual_mode (0) == MPM (0)
- 写入码流:prev_intra_pred_mode_flag = 1
- 解码器:
        - 读取 flag=1。
- 自己也计算出 MPM = min(modeA, modeB) = 0。
- 得知当前块模式就是MPM,即模式0。
 
- 读取 
 
- 编码器:
- 情况2:当前块的最佳模式是3 (Diag-Down-Left)
    - 编码器:actual_mode (3) != MPM (0)
- 写入码流:
        - prev_intra_pred_mode_flag = 0
- 计算rem_intra_pred_mode:因为actual_mode (3) > MPM (0),所以rem = 3 - 1 = 2。
- 将rem = 2编码为3比特(即二进制010)并写入码流。
 
- 解码器:
        - 读取 flag=0。
- 自己也计算出 MPM = min(modeA, modeB) = 0。
- 知道实际模式不是0,继续读取3比特的 rem_intra_pred_mode,得到值为2。
- 根据映射规则反推实际模式:rem (2) >= MPM (0)(这里用rem的值和MPM比较是错的,应该是rem对应的值比MPM大) ->actual_mode = rem + 1 = 2 + 1 = 3。或者更准确地说,将模式0排除后,模式1是第0个,模式2是第1个,模式3是第2个。所以rem=2对应模式3。
 
- 读取 
 
- 编码器:
边界和特殊情况处理
在实际实现中,必须处理一些边界情况:
- 邻块不可用:如果当前块位于一个Slice(片)的左边界或上边界,那么块A或块B可能不存在。
- 邻块是Inter模式:如果当前块是Intra模式,但其邻块是Inter模式(例如在一个P帧中),那么邻块也没有Intra预测模式可用。
H.264标准规定:
- 如果一个邻块(A或B)不可用,或不是Intra编码模式,那么该邻块的模式在计算MPM时被视为 Mode 2 (DC模式)。
- 如果A和B都不可用,那么它们都被视为Mode 2,因此 MPM = min(2, 2) = 2。DC模式被当作一种“中性”的默认预测。
色度分量的应用
MPM规则同样适用于色度(Chroma)的帧内预测。色度有4种预测模式(0:DC, 1:Horizontal, 2:Vertical, 3:Plane)。其MPM计算和编码流程与亮度完全相同,只是模式的总数和 rem 值的比特数不同(4种模式排除1个MPM,剩下3种,需要2比特来编码 rem)。