🎬 第八章 音视频编解码技术与流程
📄 一句话概括

运动估计是【找】——在参考帧里搜出最像的块,算出运动矢量;运动补偿是【搬】——用这个矢量把参考块搬过来。这是编码器最耗时的一步。

📝 本节和前面几节的关系

8.3 一路讲完了帧内的技术,这一节回头补上帧间编码里最关键的一环。

一、估计与补偿:一找一搬

这两个词经常被混着说,实际分工很清楚: 运动估计(ME)——在参考帧里搜索,找到与当前块最像的那块,结果是一个运动矢量; 运动补偿(MC)——用这个矢量把参考块搬过来当作预测值,再算残差; 编码器做 ME + MC,解码器只做 MC——矢量从码流里读,不用找。 这一条不对称,就是【编码慢、解码快】最集中的体现。

二、怎么判断【像不像】

搜索要有标准,用的几乎都是同一个指标——绝对差之和(SAD): 把两个块逐像素相减、取绝对值、再全部加起来; SAD 越小越像——它算起来简单,没有乘法,硬件很友好; 一个 16×16 的块,每比一个候选点就是 256 次减法与累加。 所以搜索的代价 = 候选点数 × 256——这就是下一段要算的那笔账。

三、全搜索的代价有多大

先看最笨但最准的做法——把搜索窗口内的每个位置都试一遍: ±16 的窗口 → 33×33 = 1089 个候选点 → 单宏块 27.9 万次操作; 整帧 8160 个宏块 → 约 22.7 亿次操作,而这只是压一帧; ±64 的窗口 → 候选点涨到 16641,整帧操作约 347.6 亿次。 按 30 帧/秒算,全搜索的运算量会到每秒千亿次量级——所以真正的编码器绝不用全搜索。

四、快速搜索:够好就停

实际编码器用的是各种【快速搜索】,思路都是先用便宜的方法逼近: 用上一步的矢量当起点——相邻块的运动通常相似,从预测值附近开始找,命中率很高; 菱形/六边形模板——每一步只试模板上的几个点,走几步就收敛; 提前终止——SAD 已经够小就停,不再往下搜。 这样把几千个候选点砍到几十个,代价是偶尔错过真正的全局最优。 压缩比与速度的取舍,主要就发生在这里。

五、搜索范围不是越大越好

一个很自然的想法是【窗口开大点总没错】,事实并非如此: 窗口越大,找到更优匹配的概率上升,但代价是运算量按平方增长; 而且运动矢量本身也要写进码流——矢量越大、越不规则,编码它花的位数越多; 对大多数内容,真正的运动范围其实有限(除非是快速横摇镜头); 所以工程上会给搜索范围设一个上限,并用【矢量预测】把要编码的差值压小。 大范围搜索的收益,往往还抵不上它带来的矢量开销。

六、动手 Demo:运动估计的搜索量有多可怕

运动估计就是【在参考帧里找最像的那一块】。先看最笨的全搜索。

mb = (1920 // 16) * (1088 // 16)
for r in (16, 32, 64):
    pts = (2 * r + 1) ** 2
    ops = pts * 256
    print("±%-3d 候选 %5d 个,整帧 %.1f 亿次"
          % (r, pts, ops * mb / 1e8))

实跑输出:

--------------------------------------------------------------------
运动估计的搜索量有多可怕
--------------------------------------------------------------------

运动估计就是【在参考帧里找最像的那一块】。先看最笨的全搜索。

  搜索范围     候选点     单宏块操作     整帧操作
  --------------------------------------------------
  ±16          1089       278784         22.7 亿次
  ±32          4225       1081600        88.3 亿次
  ±64          16641      4260096        347.6 亿次

  【单宏块操作】= 候选点数 × 256(每个候选点要算 16×16 个像素的差)。
  整帧 = 单宏块 × 8160 个宏块。**±16 就已经 22.7 亿次运算,只压一帧。**

  所以真正的编码器**绝不用全搜索**,而是:
  先用**上一步的矢量**当起点,再用**菱形/六边形模板**快速逼近,**够好就停**。
  这就把几千个候选点砍到几十个——**压缩比与速度的取舍,主要发生在这里。**

整帧 = 单宏块 × 8160 个宏块——±16 就已经 22.7 亿次运算,只压一帧。 所以真正的编码器绝不用全搜索,而是用上一步的矢量当起点+菱形模板快速逼近。

30 秒实验

  1. 在 FFmpeg 里用 -me_method 与 -me_range 两个参数压同一段视频,对比耗时与大小。
  2. 把 -me_range 从 16 调到 64,看文件大小是不是反而涨了——想想为什么。

常见误区

  1. 以为解码端也要做运动估计。解码端只做运动补偿,矢量直接读。
  2. 以为搜索范围越大压得越好。矢量本身的编码开销会吃掉收益。
  3. 以为全搜索是最实用的方案。运算量到每秒千亿次量级,工程上不可行。
自测三问(点开对答案) 1. 运动估计与运动补偿的分工是什么? —— 前者搜索出运动矢量,后者用矢量搬参考块。 2. 解码端要做哪一步? —— 只做运动补偿——矢量直接从码流读出。 3. 快速搜索为什么能做到那么快? —— 用上一步矢量当起点、模板步行搜索、够好就提前终止。

相关笔记