第3章 帧内预测:35种方向与模式详解
帧内预测,说白了就是用已经编好的像素去猜还没编的像素。H.265 比 H.264 多了不少花样,从 9 种模式直接干到了 35 种。嗯,这 35 种不是随便加的,每一条方向都有它的物理意义。
我刚开始接触 HEVC 时也觉得 35 种太多了,后来调过几个序列才发现——方向越细,纹理越复杂的块越省码率。你想想看,一个 45° 的斜条纹,用 H.264 的粗略方向去猜,残差能大一圈。
3.1 帧内预测的整体流程
先看一张整体图,把 35 种模式怎么来的、怎么选的说清楚。
流程不复杂,但每一步都有坑。我后面会一个一个说。
3.2 35 种预测模式的分类
H.265 的帧内模式分三大类:
| 类别 | 模式编号 | 数量 | 说明 |
|---|---|---|---|
| Planar | 0 | 1 | 平面插值,适合平滑渐变区域 |
| DC | 1 | 1 | 均值填充,适合平坦区域 |
| 角度预测 | 2–34 | 33 | 覆盖 -135° 到 +135° 方向 |
3.3 33 种角度预测方向
角度模式从 2 到 34,覆盖了从近乎水平到近乎垂直的各个方向。具体对应关系如下:
| 模式编号 | 角度偏移 | 物理方向 | 典型适用场景 |
|---|---|---|---|
| 2–9 | 近水平 | 右上 → 左下 | 水平纹理、横条纹 |
| 10–17 | 斜上 | 左上 → 右下 | 斜向纹理、45° 边缘 |
| 18–25 | 近垂直 | 上 → 下 | 垂直纹理、竖条纹 |
| 26–34 | 陡斜 | 右上 → 左下(陡) | 陡峭边缘、锐角纹理 |
每个角度模式对应一个 角度偏移量,范围从 -32 到 +32(以 1/32 像素精度为单位)。这个偏移量决定了参考像素的插值位置。
3.4 角度预测的插值实现
角度预测不是简单复制参考像素,而是要做 分数像素插值。H.265 用 1/32 像素精度,比 H.264 的 1/4 像素细得多。
插值公式如下:
// 角度偏移量: intraPredAngle (范围 -32 ~ 32)
// 参考像素: ref[]
// 预测位置: (x, y)
// 计算投影位置
int offset = (y * intraPredAngle) >> 5; // 等效于除以 32
int idx = x + offset;
int fact = (y * intraPredAngle) & 31; // 取小数部分
// 双线性插值
if (fact == 0) {
pred[x][y] = ref[idx + 1]; // 整数位置,直接取
} else {
pred[x][y] = ((32 - fact) * ref[idx + 1] + fact * ref[idx + 2] + 16) >> 5;
}
这段代码我调过很多次。有个细节容易忽略:offset 可能是负数,意味着要往反方向取参考像素。边界处理稍不注意就会数组越界。
>> 5 没问题,但 intraPredAngle 如果是负数,右移在 C 标准里是 implementation-defined。后来我统一改成 ((y * intraPredAngle + 16) >> 5) 才稳定。嗯,跨平台编码一定要小心符号右移。
3.5 DC 模式
DC 模式最简单,也最容易被低估。它的预测值就是参考像素的均值:
// 对于 N×N 块
int sum = 0;
for (int i = 0; i < N; i++) {
sum += refTop[i]; // 上方参考像素
sum += refLeft[i]; // 左侧参考像素
}
int dcVal = (sum + N) >> (log2(N) + 1); // 均值,四舍五入
// 所有像素用同一个值
for (int y = 0; y < N; y++)
for (int x = 0; x < N; x++)
pred[x][y] = dcVal;
DC 模式在平坦区域表现极好,码率几乎为零。但遇到渐变或纹理,残差会很大。我建议编码器在 RMD 阶段给 DC 一个「保底名额」,别让它被过早淘汰。
3.6 Planar 模式
Planar 模式是 H.265 新增的,H.264 没有。它用水平和垂直两个方向的线性插值做平均,能很好地拟合平滑渐变。
// 水平插值
int horVal = (N - 1 - x) * refLeft[y] + (x + 1) * refTopRight;
// 垂直插值
int verVal = (N - 1 - y) * refTop[x] + (y + 1) * refBottomLeft;
// 最终预测值
pred[x][y] = (horVal + verVal + N) >> (log2(N) + 1);
注意 refTopRight 和 refBottomLeft 分别是右上角和左下角的参考像素。这两个值决定了渐变的方向和幅度。
3.7 帧内编码优化策略
35 种模式全算一遍,计算量太大。实际编码器必须做优化。我常用的策略有这几个:
- RMD 粗选:用 SATD(Hadamard 变换后的绝对差和)快速排序,只保留 Top 3–8 个模式。
- MPM 机制:从相邻 CU 的模式中继承 3 个最可能模式,加入候选列表。MPM 命中率通常超过 60%。
- 模式分组跳过:如果 DC 和 Planar 的 SATD 已经很小,跳过大部分角度模式。
- CU 尺寸自适应:大 CU(32×32、64×64)角度模式可以少算几个,小 CU(4×4、8×8)必须算全。
| 优化手段 | 节省时间 | 码率损失 | 推荐场景 |
|---|---|---|---|
| RMD 保留 Top 3 | ~40% | < 0.5% | 实时编码 |
| MPM 优先 | ~20% | < 0.2% | 所有场景 |
| 大 CU 角度裁剪 | ~15% | ~1% | 低延迟应用 |
| 全模式 + RD 精算 | 0% | 0% | 离线编码、最高质量 |
3.8 避坑指南
最后说几个我实际项目中遇到的坑:
- 参考像素不可用:图像边界或 slice 边界处,参考像素可能不存在。处理方式是填充最近的可用像素,或者用 DC 值代替。
- 角度模式索引越界:当
offset为负且绝对值很大时,idx可能小于 0。一定要做 clamp。 - Planar 模式的参考像素顺序:H.265 标准里
refTopRight和refBottomLeft的获取方式有严格定义,别搞反了。 - DC 模式的边界滤波:对于 4×4 块,DC 预测值在边界处要做平滑滤波,否则会有块效应。
嗯,帧内预测这部分内容不少,但核心就三件事:方向选对、插值算准、候选剪够。把这三点吃透了,H.265 的帧内编码你就掌握了七八成。