VVC仿射运动补偿:从原理到实践,提升视频编码效率的关键技术
1. 从“平移”到“形变”:为什么VVC需要仿射运动补偿
在视频编码的世界里,运动补偿预测(Motion Compensation Prediction, MCP)是压缩效率的基石。它通过描述并利用相邻帧之间的像素运动(即运动矢量),来预测当前块的像素值,从而大幅减少需要编码的残差信息。在H.265/HEVC及其之前的编码标准中,运动补偿模型主要基于平移模型。简单来说,它假设一个编码块(比如一个16x16的块)内所有像素的运动方向和速度是完全一致的,就像一块完整的瓷砖被整体挪动了一个位置。
这个模型在大多数场景下工作得不错,但对于现实世界中更复杂的运动就显得力不从心了。想象一下一个旋转的风扇叶片、一个正在挥手的人、或者一个逐渐放大的镜头——这些场景中,物体上不同点的运动方向和速度是变化的。如果强行用一个统一的运动矢量来描述整个块,预测出的画面就会产生明显的“撕裂”或“模糊”,导致残差能量巨大,压缩效率急剧下降。
H.266/VVC(Versatile Video Coding)引入的仿射运动补偿预测,正是为了解决这个核心痛点。它不再将编码块视为一个刚体,而是允许块内的运动呈现一种更符合物理规律的“形变”。仿射模型能够描述包括平移、旋转、缩放和剪切在内的多种运动形式。这意味着,对于一个正在旋转的物体,编码器现在可以用一组参数精确地描述块内每个像素点因旋转而产生的不同位移,从而得到质量高得多的预测帧,残差自然就小多了。
我处理过不少包含复杂运动的素材,比如航拍的城市转弯镜头或者体育赛事中的慢动作回放。在HEVC下,这些片段要么码率飙升,要么在相同码率下出现令人不快的块状模糊。而一旦切换到支持仿射模式的编码方案(如VVC或某些优化后的编码器),同等主观质量下,码率能有肉眼可见的下降,这就是运动模型进化带来的直接收益。
2. 仿射运动模型的核心原理:从参数到像素位移
理解仿射运动补偿,关键在于搞懂“仿射变换”这个数学工具如何被应用到视频的像素预测上。我们不需要成为数学家,但必须清楚其映射关系。
一个二维仿射变换可以用一个2x3的矩阵来表示,它建立了当前帧(预测帧)中一个像素点坐标(x, y)与参考帧中对应像素点坐标(x‘, y’)之间的关系。在VVC中,主要定义了两种仿射模型,对应不同复杂度的运动:
2.1 4参数仿射模型(简化模型)
这个模型适用于描述平移、旋转和均匀缩放(各向同性缩放)的组合运动。它需要4个参数(a, b, c, d)。
其运动矢量场(Motion Vector Field, MVF)由以下公式定义:
v_x = a * x + b * y + e v_y = c * x + d * y + f其中(v_x, v_y)是位于(x, y)的像素相对于某个参考点的运动矢量。(e, f)代表该参考点本身的平移分量。
更直观地,VVC的实现通常基于控制点。对于4参数模型,它使用一个编码块左上角和右上角的两个控制点的运动矢量MV_0和MV_1来推导出所有参数。
- 假设块左上角
(0, 0)的运动矢量为MV_0 = (mv_0_x, mv_0_y)。 - 假设块右上角
(w, 0)的运动矢量为MV_1 = (mv_1_x, mv_1_y),其中w是块的宽度。
那么,块内任意一点(x, y)的运动矢量(v_x, v_y)可以通过线性插值得到:
v_x = mv_0_x + (mv_1_x - mv_0_x) * x / w + (mv_2_x - mv_0_x) * y / h v_y = mv_0_y + (mv_1_y - mv_0_y) * x / w + (mv_2_y - mv_0_y) * y / h对于4参数模型,我们假设不存在垂直方向的剪切,因此mv_2_x - mv_0_x和mv_2_y - mv_0_y相关的项实际上由模型约束为特定的关系(或视为0),最终简化为仅由MV_0和MV_1决定。实际上,标准中直接给出的推导方式是:
v_x = mv_0_x + (mv_1_x - mv_0_x) * (x / w) - (mv_1_y - mv_0_y) * (y / w) v_y = mv_0_y + (mv_1_y - mv_0_y) * (x / w) + (mv_1_x - mv_0_x) * (y / w)这个形式能更清楚地看出,(mv_1_x - mv_0_x)和(mv_1_y - mv_0_y)共同决定了缩放和旋转的参数。
2.2 6参数仿射模型(完整模型)
这个模型是完整的仿射变换,在4参数模型的基础上,增加了剪切变换的能力。它需要6个参数,由三个控制点的运动矢量来定义。
在VVC中,这三个控制点通常是块的左上角、右上角和左下角,对应的运动矢量分别为MV_0,MV_1,MV_2。
此时,块内任意一点(x, y)的运动矢量计算公式为:
v_x = mv_0_x + (mv_1_x - mv_0_x) * (x / w) + (mv_2_x - mv_0_x) * (y / h) v_y = mv_0_y + (mv_1_y - mv_0_y) * (x / w) + (mv_2_y - mv_0_y) * (y / h)这个公式非常直观:(mv_1_x - mv_0_x)/w代表了水平方向在x轴上的变化率(水平缩放/剪切),(mv_2_x - mv_0_x)/h代表了水平方向在y轴上的变化率(水平剪切);垂直方向同理。
一个关键的操作细节:由于像素坐标是整数,而计算出的(x/w)和(y/h)可能是小数,VVC采用了高精度的定点数运算来保证插值的准确性。通常,w和h会被归一化,并使用位移操作来高效实现除法。在实际解码或预测生成时,需要按照标准规定的精度(如1/16或1/64像素精度)来计算每个子块或像素的运动矢量。
注意:VVC通常不以单个像素为单位应用仿射运动矢量,而是将一个大块(如8x8或4x4)划分为更小的子块(如4x4),为每个子块的中心点计算一个运动矢量,然后这个子块内的所有像素都使用这个运动矢量进行平移运动补偿。这是一种在精度和复杂度之间的折中,称为“仿射子块运动补偿”。
3. 编码端的仿射运动估计:如何找到那组最优参数
对于解码端来说,只要收到几个控制点的运动矢量,按照公式计算即可。但对于编码器,最大的挑战在于:如何为当前编码块找到那一组最能准确预测其内容的仿射模型参数?这是一个高维、非线性的优化问题,计算复杂度远高于传统的平移运动估计。
主流编码器(如VTM,VVC的参考软件)一般采用多阶段、启发式的搜索策略来平衡精度和复杂度:
3.1 仿射模式的触发与候选列表构建
并非所有块都适合使用仿射模式。编码器会先进行快速判断,例如:
- 块大小:通常只对足够大的块(如面积大于等于64像素)才考虑仿射模式,因为小块的形变运动不明显,且参数开销相对较大。
- 时空邻域信息:检查当前块的时空相邻块(左边、上边、右上、左下等)是否采用了仿射模式。如果是,它们的控制点运动矢量(CPMV)是极佳的初始候选。
编码器会构建一个仿射Merge候选列表和仿射AMVP(高级运动矢量预测)候选列表。
- Merge模式:直接继承邻块的运动模型参数(包括控制点矢量)。如果邻块是仿射编码的,则将其模型参数作为候选;也可以由多个平移邻块的矢量组合推导出仿射参数。编码器只需传输一个候选索引,效率极高。
- AMVP模式:需要传输运动矢量差(MVD)。编码器同样先构建一个预测器列表(基于邻块推导),然后对每个控制点的运动矢量进行精细搜索,并将搜索得到的矢量与预测器之差(MVD)进行编码。
3.2 参数搜索与迭代优化
当从候选列表中获得一个初始的仿射模型参数后,编码器会进行迭代细化以提升精度。一个经典的方法是基于梯度的搜索,例如“仿射运动估计的迭代细化”。
- 误差表面建模:假设当前块使用一组仿射参数
P进行预测,产生的预测块与原始块的残差为SAD(P)(绝对误差和)。我们的目标是找到使SAD最小的P。 - 梯度计算:在初始点
P0,编码器可以估计SAD相对于每个仿射参数(如a, b, c, d, e, f)的梯度。这通常通过给每个参数施加一个微小扰动(如±1像素偏移),重新计算预测和SAD来近似。 - 参数更新:沿着梯度下降的方向,更新仿射参数。例如,
P_new = P_old - step * Gradient。步长step需要精心选择。 - 迭代与终止:重复步骤2和3,直到
SAD的变化小于某个阈值,或达到最大迭代次数。
由于对6个参数进行全精度搜索计算量巨大,实践中会采用多种加速策略:
- 分层搜索:先在低分辨率(下采样)的图像上进行粗搜索,锁定大致区域,再在全分辨率上细化。
- 单向预测优先:先尝试单向仿射预测(只用一个参考帧列表),如果效果很好,则可能跳过双向预测的复杂搜索。
- 提前终止:如果当前仿射模式的率失真代价(RD Cost)在早期就远高于最好的平移模式,则提前终止对该仿射模式的进一步搜索。
我的实操心得:在调优编码器参数时,仿射搜索的复杂度配置(如迭代次数、搜索范围、是否启用快速算法)对编码速度和压缩效率的平衡影响巨大。对于实时通讯场景,我通常会大幅限制仿射搜索的深度和范围,甚至只启用Merge模式而关闭AMVP模式的精细搜索。而对于点播存储场景,则可以放开限制,追求极致压缩率。另一个关键点是,仿射模式在具有明显旋转、缩放前景的视频中收益最大,在静态背景或简单平移的视频中开启它只会徒增编码时间。
4. 解码端与预测信号生成:从比特流到重建像素
解码端接收到的关于仿射模式的信息,远比我们想象的“精简”。它不需要接收6个浮点数参数。解码过程体现了现代视频编码标准的精巧设计。
4.1 比特流中的信息解析
解码器从比特流中解析出以下关键信息:
- 预测模式标志:
pred_mode_flag指示当前块是帧内、帧间(平移)还是帧间仿射预测。 - 仿射模式类型:如果是仿射预测,一个标志位指示是4参数还是6参数模型。
- Merge索引或MVD:
- 如果使用Affine Merge模式,解码器解析一个候选列表索引。根据这个索引,从已重建的邻块信息中推导出当前块的控制点运动矢量(CPMV)。解码器自身维护着与编码器完全相同的候选列表构建规则,因此能推导出完全相同的CPMV。
- 如果使用Affine AMVP模式,解码器需要解析: a. 一个预测器索引,用于从AMVP候选列表中获得每个控制点运动矢量的预测值(
mvp)。 b. 每个控制点对应的运动矢量差(MVD),这些MVD是经过熵编码(如CABAC)的数值。 然后,计算真实运动矢量:mv = mvp + mvd。
4.2 运动矢量场推导与子块划分
获得控制点运动矢量MV_0, MV_1(, MV_2)后,解码器需要为块内每个像素生成预测值。直接为每个像素计算一个仿射MV并取参考像素是不现实的(复杂度太高)。VVC的方案是:
- 划分子块:将当前编码块划分为多个小的子块(例如,对于亮度分量,最小子块尺寸为4x4)。每个子块将共享一个运动矢量。
- 计算子块MV:以每个子块的中心点坐标
(x_c, y_c)代入第2章中的仿射公式,计算出该子块的中心运动矢量。这个MV的精度会取整到标准规定的运动矢量精度(如1/16像素)。 - 存储子块MV:计算出的子块MV会被存储下来,用于后续块的时空预测,以及用于后续的解码端运动矢量细化(DMVR)或双向光流(BDOF)等工具。
4.3 预测像素生成
对于每个4x4的子块,解码器将其运动矢量视为一个传统的平移运动矢量。
- 运动补偿插值:根据这个平移MV,去到指定的参考帧中,使用VVC定义的8抽头插值滤波器(对于亮度分量)获取对应的预测像素块。这个过程和普通的平移运动补偿完全一样。
- 生成预测块:将所有子块的预测块拼接起来,就形成了整个当前编码块的仿射预测块。
这里有一个极易被忽略但至关重要的细节:参考像素的存取与边界处理。由于仿射运动会导致子块的运动矢量指向不同的方向,整个编码块所访问的参考帧区域可能是一个不规则的四边形,而不是一个规整的矩形。这要求解码器的运动补偿引擎必须具备高效访问非对齐、非连续内存的能力。此外,当运动矢量指向参考帧边界之外时,需要进行边界填充(Padding),VVC有明确的边界扩展规则。如果处理不当,会在物体边缘产生不可预测的像素值,影响解码正确性和后续帧的预测。
提示:在实现或调试解码器时,仿射预测模块的单元测试必须包含大量极端案例:大旋转角、大缩放比、运动矢量指向参考帧边界、以及不同块大小和子块划分的组合。我曾遇到过因为子块MV取整精度与标准不一致,导致在特定序列下解码画面出现细微错位的Bug,排查起来非常耗时。
5. 率失真优化与模式决策:编码器的权衡艺术
编码器最终是否为一个块选择仿射模式,是一个经典的率失真优化(RDO)问题。它需要在编码比特数(Rate)和重建失真(Distortion)之间找到最佳平衡点。
5.1 率失真代价计算
对于每一个待选的预测模式(包括各种尺寸的平移模式、仿射Merge、仿射AMVP等),编码器都会模拟编码过程,计算其率失真代价J:
J = D + λ * RD是失真度,通常用SSE(误差平方和)或SATD(Hadamard变换后的绝对误差和)来度量,代表预测块与原始块的差异。R是估计的编码该模式所需的总比特数。这包括:- 模式标志位(帧内/帧间/仿射)的比特。
- 参考帧索引的比特。
- 运动矢量预测器索引的比特。
- 运动矢量差(MVD)的比特(对于AMVP模式)。
- 以及后续残差变换系数编码的比特(虽然RDO中会粗略估计,但精确计算需要在模式决定后进行)。
λ是拉格朗日乘子,一个与量化参数(QP)正相关的关键参数。QP越大(压缩越狠),λ越大,编码器越倾向于选择节省比特的模式,即使失真稍大。
对于仿射模式,其R部分通常高于平移模式,因为它可能需要传输多个控制点的信息(尽管有预测)。因此,只有当它带来的D的减少足够大,足以抵消R的增加时,即J_affine < J_translation,编码器才会选择仿射模式。
5.2 仿射模式的竞争力分析
在复杂的模式决策舞台上,仿射模式需要与众多对手竞争:
- 大块平移模式:对于大块,一个简单的平移MV也可能有不错的效果,且码率开销极低。
- 小块合并:将当前块分割成多个更小的块,每个小块用平移模式,可能更灵活。
- 帧内模式:在纹理复杂或没有好的时间预测参考时,空间预测可能更优。
编码器的决策流程通常是层次化的:
- 快速粗筛:使用低复杂度度量(如SAD、梯度)快速评估平移、仿射Merge等少数几个候选,淘汰明显差的。
- 精细RDO:对通过初选的候选模式,进行更精确的代价计算,包括变换、量化、熵编码的粗略估计。
- 最终抉择:选择
J值最小的模式作为最终编码模式。
仿射模式的优势区间非常明显:在中高码率下,对于包含明显非平移运动的中大型块。在低码率下,λ很大,编码器极度“抠门”,仿射模式多出来的那点比特开销往往让它失去竞争力,此时宁愿用平移模式产生大一点的残差,然后用粗量化压掉。在超高分辨率(如4K/8K)视频中,物体在画面中的运动更可能包含透视变化,仿射模式的用武之地更大。
6. 实际应用中的挑战、调试与性能观测
将仿射运动补偿从理论标准落实到实际编码器(如VTM、x266)或解码器中,会遇到一系列工程挑战。
6.1 计算复杂度瓶颈
仿射运动估计是编码器计算复杂度的主要贡献者之一。一次6参数仿射搜索涉及多次运动补偿插值(每次插值都需要8抽头滤波,计算密集)。在VTM的默认配置下,仿射搜索可能占据整个编码时间的15%-30%。
优化策略:
- 硬件指令集优化:利用SIMD指令(如AVX2, AVX-512)并行化插值滤波器和SAD计算。这是提升速度最有效的手段。
- 搜索算法优化:如前所述,采用更智能的梯度下降法、更严格的提前终止条件。
- 精度权衡:在运动估计阶段使用低精度插值(如4抽头滤波器)进行快速评估,仅在最终确定模式后使用标准8抽头滤波器生成预测块。
6.2 与其它编码工具的交互
仿射模式不是孤立的,它与VVC的其它高级工具紧密交互,可能产生“1+1>2”或相互冲突的效果。
- 解码端运动矢量细化:仿射模式计算出的子块MV可以作为DMVR的初始矢量,在解码端进行二次精细化搜索,进一步提升预测精度。
- 双向光流:BDOF在生成双向预测的最终像素时,会利用块内的运动矢量场信息。仿射模式提供的更精确的每子块MV,能为BDOF提供更好的输入,提升其效果。
- 几何划分模式:GPM将块沿斜线分割,两部分分别用不同的运动矢量。对于分割后形状不规则的区域,仿射模型有时比平移模型能提供更好的预测。编码器需要在GPM和仿射模式之间进行RDO选择。
- 帧内块复制:IBC主要用于屏幕内容。仿射模式与IBC通常互斥,因为IBC假设块在当前帧内能找到完全匹配的平移块。
6.3 客观与主观质量评估
在测试仿射模式性能时,不能只看BD-rate(客观码率节省)。BD-rate显示,在随机接入配置下,仿射模式能为自然视频带来约1%-3%的码率节省(相对于关闭仿射)。但这个数字因序列特性差异巨大:
- 对于
BasketballDrive(快速全局运动)或BQTerrace(摄像机运动)这类序列,节省可能超过3%。 - 对于静态或简单谈话头部序列,节省可能接近于0,甚至因为模式决策开销略有负收益。
更重要的是主观质量。在快速旋转或缩放边缘,仿射模式能显著减少“锯齿”或“模糊”感,使运动更加平滑自然。这种主观提升在中等码率下尤为明显。评测时,需要重点关注这些运动复杂区域的视觉质量对比。
6.4 调试与问题排查
在开发或集成仿射功能时,典型的调试问题包括:
- 解码 mismatch:编码器和解码器重建画面不一致。这通常源于:
- 控制点MV的推导逻辑不一致(尤其是Merge候选列表的构建顺序和填充规则)。
- 子块MV的计算精度或取整方式与标准不符。
- 参考帧边界处理不一致。
- 编码效率低下:仿射模式使用率远低于预期。需要检查:
- 运动估计搜索范围是否足够大?仿射搜索的起点(候选)质量是否高?
- RDO计算中的λ值或比特率估计模型是否对仿射模式过于苛刻?
- 快速跳过算法是否过于激进,在早期就错误地跳过了潜在的优秀仿射候选?
我的经验是,建立一个针对仿射模式的可视化调试工具至关重要。例如,将最终采用仿射模式的块用特定颜色高亮,并叠加显示其控制点MV和子块MV箭头。这能直观地看出编码器在哪些区域、为何选择了仿射模式,是验证算法行为和进行性能分析的神器。
仿射运动补偿是VVC超越HEVC的关键技术之一,它将运动建模从“整体平移”推进到了“局部形变”的时代。理解其原理、实现细节以及与编码器其他模块的协同,对于深入掌握现代视频编码技术、进行高效的编码器开发或应用选型都至关重要。尽管它增加了编码复杂度,但在处理日益丰富的视频内容,尤其是高分辨率、高动态范围、高帧率视频时,其所带来的压缩效率提升是不可或缺的。在实际项目中,是否启用、如何配置仿射功能,需要根据具体的应用场景(实时、存储、画质优先、速度优先)做出精细的权衡。
