4、H265帧间预测:运动估计、运动补偿、Merge模式、AMVP技术

帧间预测,说白了就是利用视频相邻帧之间的冗余信息来压缩数据。你想想看,一个物体在连续几帧里,位置可能变了,但长相没变。我们只需要告诉解码器:「这个块上一帧在那个位置,现在挪到这里了」,就能省下大量比特。

H265的帧间预测比H264复杂了不少,但核心思路没变:运动估计找参考块,运动补偿重建残差。不过H265多了Merge和AMVP这两个「聪明」的模式,让编码效率又上了一个台阶。

4.1 运动估计与运动补偿

运动估计(Motion Estimation, ME)是编码器最耗时的环节之一。我早期优化编码器时,发现ME能占到整个编码时间的60%以上。说白了,就是在参考帧里找一个最匹配的块。

H265支持的运动矢量精度最高到1/4像素。整像素搜索完,还要做亚像素插值。插值滤波器用的是DCT-IF(离散余弦变换插值滤波器),比H264的Wiener滤波器精度更高。

运动估计的核心流程:

  1. 确定搜索范围(通常为[-64, 64]像素)
  2. 整像素搜索(菱形搜索、六边形搜索等)
  3. 亚像素精化(1/2像素 → 1/4像素)
  4. 计算率失真代价(J = D + λ·R)

运动补偿(Motion Compensation, MC)就是根据运动矢量,从参考帧里把预测块「抠」出来。然后和当前块做差,得到残差块。残差再经过变换、量化、熵编码,就写进码流了。

这里有个细节:H265的预测块(PB)大小可以是64x64、32x32、16x16、8x8、4x4。但4x4只在特殊情况下用,我一般建议至少用8x8,否则码率开销太大。

4.2 Merge模式

Merge模式是H265的一大亮点。它的思路很简单:我不传运动矢量,只传一个索引。解码器根据索引,从候选列表里拿一个运动矢量直接用。

为什么会这样设计?因为相邻块的运动往往高度相关。一个宏块和它左边的块,大概率是同一个物体的运动。那何必重复传呢?

Merge模式的候选列表最多5个,按以下顺序构建:

候选序号 来源 说明
0 空域左方(A0, A1) 当前块左侧的相邻块
1 空域上方(B0, B1, B2) 当前块上方的相邻块
2 时域同位(Col) 参考帧中相同位置的块
3 组合候选 前两个候选的加权组合
4 零矢量 (0, 0) 兜底

我个人的经验:在低码率场景下(比如视频会议),Merge模式的使用率能到70%以上。因为画面运动小,相邻块的运动矢量几乎一样。这时候传一个索引比传一个完整的运动矢量省太多比特了。

Merge模式还有一个变种叫Skip模式。Skip模式不仅不传运动矢量,连残差都不传。解码器直接用预测块当重建块。这种模式在静止背景或缓慢运动的区域特别高效。

4.3 AMVP技术

AMVP(Advanced Motion Vector Prediction)是H265的另一个关键技术。它和Merge模式有点像,但有个本质区别:AMVP只预测运动矢量,不直接使用。编码器会传一个MVD(运动矢量差值),告诉解码器「预测值和我实际用的值差多少」。

AMVP的候选列表只有2个候选,比Merge少。构建方式如下:

  1. 空域候选:从左侧和上方各选一个
  2. 时域候选:从参考帧的同位块获取
  3. 如果候选不足:用零矢量补齐

编码器会从这2个候选里选一个率失真代价最小的,然后计算MVD。解码器拿到MVD后,加上预测值,就得到实际运动矢量。

我曾经踩过的坑:AMVP的时域候选需要做缩放。因为参考帧和当前帧的时间距离可能不同。如果不做缩放,运动矢量会偏差很大。HM参考软件里有个函数叫getScaledMV,就是干这个的。我第一次写的时候忘了调用,结果编码出来的视频全是马赛克。

4.4 Merge vs AMVP 对比

特性 Merge模式 AMVP
传输内容 候选索引(merge_idx) 候选索引 + MVD
候选数量 最多5个 2个
运动矢量精度 直接使用候选MV 预测MV + MVD
适用场景 运动简单、相邻块相似 运动复杂、需要精细调整
码率开销 低(只传索引) 中(索引 + 差值)

实际编码时,编码器会为每个预测单元(PU)做一次模式决策:是用Merge、Skip还是AMVP?这个决策基于率失真优化(RDO)。说白了,就是算一笔账:用哪种模式,失真最小、码率最低。

4.5 帧间预测整体流程

我把帧间预测的决策流程画了一张图,方便你理解:

H265 帧间预测模式决策流程 当前PU开始 运动估计(整像素 + 亚像素) 搜索参考帧,计算率失真代价 Skip模式 不传MV,不传残差 Merge模式 传merge_idx,不传MVD AMVP模式 传候选索引 + MVD 率失真优化(RDO)决策 比较三种模式的 J = D + λ·R 运动补偿 生成预测块,计算残差 输出到变换量化 实际编码器会遍历所有模式 选择率失真代价最小的

4.6 实际编码中的注意事项

讲几个我在项目中遇到的坑:

  • 参考帧管理:H265支持最多16个参考帧(H264只有16个?不对,H264也是16个,但H265的DPB管理更复杂)。我建议B帧用2个参考帧就够了,P帧用4个。再多对压缩率提升微乎其微,反而增加内存带宽。
  • 运动搜索范围:不是越大越好。1080p视频,搜索范围±64像素基本够用。4K视频可以扩大到±128。我见过有人设±256,编码时间翻倍,码率只降了0.5%。
  • Merge候选列表裁剪:HM参考软件里有个去重逻辑。如果两个候选的运动矢量相同,只保留一个。这个逻辑一定要实现,否则候选列表里全是重复的,浪费索引比特。

一个小技巧:在FFmpeg里用libx265编码时,可以通过-x265-params调整帧间预测参数。比如-x265-params "merange=64:subme=5"可以控制搜索范围和亚像素精度。我个人习惯在实时编码场景用subme=5,离线编码用subme=7。

帧间预测是H265压缩率的核心来源。Merge和AMVP这两个技术,说白了就是「用更少的比特传运动信息」。理解它们,你就能明白为什么H265比H264能省50%的码率。