4、H265编码原理详解:四叉树划分、更灵活的帧内预测、运动估计融合技术、SAO滤波
好,咱们今天来聊聊H265。说实话,我刚从H264转到H265那会儿,第一感觉就是——这玩意儿怎么这么复杂?但用久了你会发现,它的每一个设计都是有道理的。H265能在同等画质下比H264省出将近一半的码率,靠的就是这些“硬核”技术。
我习惯把H265的核心改进归纳为四个方向:四叉树划分、更灵活的帧内预测、运动估计融合技术、SAO滤波。咱们一个一个来拆解。
4.1 四叉树划分:从“固定网格”到“自适应拼图”
先说说四叉树划分。H264用的是宏块,固定16x16大小。你想想看,一块平坦的天空和一片复杂的树林,都用16x16去处理,效率肯定不一样。
H265引入了编码树单元(CTU)的概念。默认大小是64x64,最大能到128x128。然后它会像切蛋糕一样,根据图像内容自适应地往下划分。
核心思想:平坦区域用大块,细节区域用小块。这样既能保证编码效率,又不损失细节。
具体怎么划分的?我画个简化的逻辑给你看:
// 伪代码:四叉树划分决策
function splitCTU(ctu, depth) {
if (depth >= MAX_DEPTH) return; // 最大深度,通常是4层
if (isHomogeneous(ctu)) {
encodeAsWhole(ctu); // 平坦,不划分
} else {
splitIntoFour(ctu); // 复杂,继续切
for each subCtu in ctu.children {
splitCTU(subCtu, depth + 1);
}
}
}
我在项目中遇到过一个问题:有个场景是监控画面,背景几乎不动,但偶尔有树叶晃动。如果用固定块,要么浪费码率,要么出现块效应。用了四叉树之后,背景用64x64的大块,树叶区域自动切到8x8甚至4x4,效果立竿见影。
我的经验:实际调优时,建议把CTU大小设为64x64。太大(128x128)对硬件缓存压力大,太小(32x32)又发挥不出优势。
4.2 更灵活的帧内预测:从9种到35种
H264的帧内预测有9种模式(4x4块)。H265直接干到了35种!
这35种包括:
- 33种角度预测:覆盖了从水平到垂直的各个方向
- DC模式:取平均值,适合平坦区域
- Planar模式:平滑渐变,适合纹理过渡区域
说白了,H265把预测方向做得更精细了。原来H264只有8个方向(加上DC是9个),现在有33个角度。你想想看,一个斜着的纹理,原来只能选个近似的方向,现在几乎能找到完全匹配的方向。
我刚开始做H265优化时,觉得35种模式全算一遍太费时间。后来发现,其实可以用粗选+细选的策略:
- 先算几个关键方向(比如水平、垂直、对角线)的代价
- 在代价最小的方向附近,再细搜几个相邻角度
- 最后和DC、Planar比一下,选最优
// 快速帧内模式选择
int bestMode = DC_MODE;
int bestCost = INT_MAX;
// 第一步:粗搜关键方向
int keyModes[] = {0, 1, 10, 18, 26}; // 水平、垂直、对角线等
for (int i = 0; i < 5; i++) {
int cost = calcModeCost(keyModes[i]);
if (cost < bestCost) { bestCost = cost; bestMode = keyModes[i]; }
}
// 第二步:在最佳方向附近细搜
for (int offset = -2; offset <= 2; offset++) {
int mode = bestMode + offset;
if (mode < 2 || mode > 34) continue;
int cost = calcModeCost(mode);
if (cost < bestCost) { bestCost = cost; bestMode = mode; }
}
注意:Planar模式在自然图像中经常被选中,千万别为了省时间把它跳过了。我曾经犯过这个错,结果画面出现明显的“阶梯感”。
4.3 运动估计融合技术:合并与跳过
运动估计这块,H265最大的改进是引入了Merge模式和Skip模式。
什么意思呢?H264里每个块都要单独传运动矢量。但你想,如果相邻两个块的运动方向几乎一样,干嘛要传两遍?
Merge模式就是干这个的:
- 从周围已编码块中选一个候选运动矢量
- 当前块直接“借用”这个矢量
- 只需要传一个索引,告诉解码器“我用的是左边那个块的运动”
Skip模式更狠:连残差都不传了。如果当前块和预测块几乎一模一样,直接告诉解码器“跳过,用预测的就行”。
我做过一个统计:在低码率场景下,Skip模式能占到总块数的40%以上。尤其是那些静止背景的视频会议画面,Skip模式简直就是码率救星。
| 模式 | 传输内容 | 适用场景 | 码率节省 |
|---|---|---|---|
| 普通Inter | 运动矢量 + 残差 | 运动复杂区域 | 基准 |
| Merge | 候选索引 | 运动一致区域 | 约30% |
| Skip | 候选索引(无残差) | 静止或极相似区域 | 约60% |
避坑指南:Merge模式的候选列表最多5个。我曾经遇到过候选块全是无效的情况(比如在图像边界),这时候要记得用零矢量兜底,否则编码器会崩。
4.4 SAO滤波:消除“振铃效应”的最后一道防线
最后说说SAO(样点自适应补偿)滤波。这是H265新增的,H264没有。
为什么要加这个?因为H265的压缩率更高,带来的副作用就是——振铃效应更明显。你注意过没有,有些视频在物体边缘会出现一圈一圈的波纹,像水波一样,那就是振铃。
SAO滤波分两种模式:
- 边界补偿(EO):根据像素和相邻像素的对比,调整像素值。主要处理边缘振铃。
- 边带补偿(BO):根据像素值所在的区间,统一加一个偏移量。主要处理平坦区域的条纹噪声。
我刚开始做SAO时,觉得这步可有可无。直到有一次,一个客户投诉说我们的编码器在红色背景上会出现“条纹”。查了半天,发现就是SAO没开BO模式。开了之后,问题立刻消失。
// SAO边界补偿示例(简化版)
// 假设当前像素为X,相邻像素为A和B
// 根据X与A、B的对比,决定补偿值
if (X < A && X < B) {
// 局部最小值,需要增加
X += offset[0];
} else if (X > A && X > B) {
// 局部最大值,需要减少
X -= offset[1];
} // 其他情况不处理
关键点:SAO是在去块滤波之后做的。顺序不能乱——先去块,再SAO。我见过有人搞反了顺序,结果画面反而更差了。
嗯,总结一下。H265的这四个技术,说白了就是:
- 四叉树让块大小自适应
- 帧内预测让方向更精细
- Merge/Skip让运动信息更省
- SAO让画面更干净
每个技术单独看都不复杂,但组合起来,效果就非常惊人。你在嵌入式平台上实现时,建议先从SAO和Merge入手,这两个对码率节省最明显,而且计算量相对可控。四叉树和帧内预测的优化可以放到后面慢慢调。
好,这一章就到这儿。下一章咱们聊聊H265在嵌入式平台上的实际移植和优化技巧。