1. 为什么单样本视频分割值得反复琢磨
第一次读到 OSVOS 这个工作的时候,我正被一个视频后期的小需求折磨得够呛:手头有一批产品展示短片,需要在每一帧里把主体抠出来换背景。传统做法是逐帧抠图,或者用绿幕,但素材本身没有绿幕条件,逐帧抠图几百帧下来人直接废掉。当时试过一些基于光流或者帧间差分的方法,主体一动就散架,遇到遮挡更是惨不忍睹。后来看到 OSVOS 的思路,第一反应是"原来还能这么干"——只在第一帧给一个标注,后面整段视频自动跟着分割,这几乎就是视频抠图场景里最理想的交互方式。
OSVOS 全称 One-Shot Video Object Segmentation,直译就是"单样本视频目标分割"。它的核心承诺非常直白:给定一段视频,你只需要在第一帧把目标物体的掩码画出来,模型就能把整段视频里这个物体的逐帧掩码全部推出来。这里的"One-Shot"不是指推理一次就完事,而是指每个新目标只需要一个标注样本,不需要针对这个目标重新训练一个专用模型,也不需要额外的大量标注数据。这个设定直接击中了视频分割领域最痛的痛点:标注成本。
视频分割的标注有多贵,做过的人心里都有数。一张图像的分割掩码已经够费劲了,视频还要逐帧标,而且帧与帧之间目标在动、在变形、在被遮挡,标注一致性极难保证。如果每来一个新视频、一个新目标都要重新标几百帧再训练,那这套流程根本没法落地。OSVOS 的价值就在于把这个成本压到了"一帧标注"这个量级,剩下的交给模型在离线阶段学到的通用物体先验。
这篇文章我打算按自己复现和折腾这个工作的实际路径来写,不搞那种论文翻译式的流水账。我会先讲清楚它到底解决了什么问题、和同期方法比取舍在哪,然后把网络结构、训练策略、推理流程这些关键环节拆开讲,中间穿插我自己踩过的坑和调参心得,最后聊聊它在今天还有什么参考价值、能怎么改。适合已经有一点深度学习基础、想做视频分割或者视频编辑相关项目的同学,也适合单纯想搞懂"单样本分割"这套范式的人。
2. OSVOS 到底在解决什么问题,以及它和同期方法的取舍
2.1 视频目标分割的两条技术路线
在 OSVOS 出现之前,视频目标分割大致分成两个阵营。第一个阵营是半监督视频目标分割,也叫 one-shot 设定,第一帧给掩码,后面靠时序传播。这类方法的代表思路是用光流把前一帧的分割结果 warping 到当前帧,再结合外观模型做修正。优点是快、不需要训练,缺点是误差会累积,一旦某一帧飘了,后面基本救不回来,遇到快速运动或者遮挡就直接崩。
第二个阵营是无监督视频目标分割,不给任何标注,靠运动显著性、物体性先验自动找出"显著的前景"。这类方法不需要人工介入,但问题是它不知道你要哪个物体,视频里出现多个运动物体时就抓瞎,而且对静态目标几乎无能为力。
OSVOS 走的是第一条路线,但做了一个关键改变:它不再依赖手工设计的传播规则,而是把"从单帧标注泛化到整段视频"这件事交给一个在大量数据上预训练过的深度网络。换句话说,它把半监督分割从"在线传播"变成了"离线学先验 + 在线微调"。
2.2 为什么是"单样本"而不是"少样本"
这里有个容易被忽略的设计选择。同期也有工作做 few-shot 分割,给几帧标注。OSVOS 坚持只给第一帧,理由是:在真实交互场景里,让用户多标一帧的边际成本远高于模型性能的那点提升。用户的心理预期是"我点一下,你全给我搞定",而不是"我标五帧,你帮我搞定剩下的"。这个产品思维其实比技术本身更值得学。
当然代价也是有的。只给一帧,模型对目标外观变化的建模能力就受限,尤其是目标在视频里发生剧烈形变、旋转、光照突变的时候,单帧先验不够用。OSVOS 的应对方式是在离线阶段用大量视频训练一个"通用分割能力",让网络见过足够多的物体和形变模式,在线阶段只需要微调适应这个特定目标。
2.3 和 MaskTrack、OnAVOS 这些后续工作的关系
OSVOS 之后很快出现了几个改进版本。MaskTrack 的思路是引入光流作为额外输入通道,让网络同时看到 RGB 和光流,显式建模帧间运动。OnAVOS 则是在线更新,把推理过程中置信度高的帧也加入训练集,让模型边跑边学。这些改进本质上都是在补 OSVOS 的短板:单帧先验不够、在线适应不足。
但 OSVOS 的骨架地位没变,它确立的"离线预训练 + 首帧微调 + 逐帧推理"三段式流程,基本成了后面所有 one-shot 分割方法的默认范式。理解了这个骨架,再看后续工作就很容易定位它们各自在补哪块。
3. 网络结构与训练策略的拆解
3.1 主干网络的选择逻辑
OSVOS 用的是 VGG-16 作为主干,这在今天看有点老,但放在当时是合理选择。VGG 结构规整、特征层级清晰,浅层保留空间细节、深层提供语义信息,对分割这种需要"既知道是什么又知道在哪"的任务很友好。它把 VGG 的全连接层去掉,保留卷积部分作为编码器,然后在后面接一个解码器把特征图上采样回原图尺寸,输出逐像素的二分类概率。
这里有个细节值得说:OSVOS 的解码器设计得比较轻,主要是几层上采样加卷积,没有用复杂的 skip connection 堆叠。原因是它不追求极致的边界精度,而是追求在单帧标注下泛化到整段视频的稳定性。过于复杂的解码器容易过拟合到第一帧的细节,反而在后续帧上表现更差。这个取舍思路我在自己改网络时验证过,确实如此。
3.2 三段式训练流程
OSVOS 的训练分三步,每一步的目的都不一样,这是它最核心的设计。
第一步是在图像分割数据集上预训练。用类似 DAVIS 或者 PASCAL 这种带掩码的图像数据集,训练网络学会"给定一张图,把前景物体分出来"。这一步学的是通用物体性先验,让网络知道什么是物体、边界大概长什么样。
第二步是在视频数据集上训练。用带标注的视频序列,让网络学会处理帧间变化。这一步很关键,因为图像和视频的分布差异很大,视频里有运动模糊、有遮挡、有外观漂移,只在图像上训练的网络直接上视频会水土不服。
第三步是在测试视频的第一帧上微调。给定一个新视频,用第一帧的标注对网络做少量迭代的微调,让网络适应这个特定目标的外观。这一步是"one-shot"的关键,也是整个流程里最需要小心处理的地方。
3.3 微调阶段的学习率与迭代次数
微调这一步的调参非常讲究。学习率太大,网络会迅速过拟合到第一帧,后面帧全崩;学习率太小,适应不充分,第一帧的先验没吃进去。我实测下来,微调阶段的学习率要比预训练阶段低一到两个数量级,迭代次数控制在几百次以内,通常一两百次就够。
还有一个技巧是冻结部分层。浅层特征(边缘、纹理)是通用的,不需要动;深层特征(语义)才需要适应新目标。所以微调时通常冻结前几层,只更新后面的层。这样既省时间又防止过拟合。这个策略在数据量极少的情况下几乎是标配。
提示:微调阶段一定要用第一帧的标注做监督,但不要用第一帧做验证。因为第一帧是训练数据,在上面评估毫无意义。真正的评估要看后续帧,而后续帧没有标注,所以实际部署时只能靠肉眼抽检。
4. 推理流程与在线传播的工程细节
4.1 逐帧独立推理还是带时序传播
OSVOS 的原始推理是逐帧独立的:把每一帧单独送进网络,输出该帧的分割掩码,帧与帧之间不传递信息。这个设计初看很反直觉,因为视频明明有时序连续性,为什么不利用?
原因有两个。第一,逐帧独立推理不会累积误差,每一帧都是独立判断,某一帧错了不会污染后面。第二,实现简单,可以并行处理,工程上友好。代价是丢失了时序平滑性,输出的掩码在帧间可能会有抖动。
后续的 MaskTrack 就是针对这一点改进的,把前一帧的分割结果作为额外输入,让网络参考上一帧的判断。但这也引入了误差累积的风险。所以这是个典型的取舍:要鲁棒性还是要平滑性。
4.2 后处理:条件随机场与引导滤波
原始输出的掩码边界通常比较毛糙,因为网络是在下采样的特征图上做预测再上采样的,边界精度天然受限。OSVOS 用了条件随机场(CRF)做后处理,把网络输出的概率图和原图的颜色信息结合起来,优化边界。
CRF 的原理简单说就是:如果两个相邻像素颜色很接近,它们更可能属于同一类;如果颜色差异大,更可能在边界两侧。用这个约束去修正网络的软输出,边界会贴合得更准。不过 CRF 计算量不小,对长视频来说是个负担。工程上如果对实时性有要求,可以用引导滤波替代,效果差一点但快很多。
4.3 处理目标消失与重现
这是实际部署时一定会遇到的问题:目标在中间某几帧走出画面,然后又走回来。逐帧独立推理在这种情况下反而有优势,因为目标不在画面里时,网络输出应该是全背景,目标回来时再重新分出来。但如果用了时序传播,目标消失那几帧的传播就会出错,回来时可能接不上。
我的处理经验是:加一个简单的前景置信度判断。如果某一帧网络输出的前景区域面积突然变得极小或者极大,就认为这一帧可能异常,标记出来人工检查或者用前后帧做插值修正。这个 trick 很土但很管用。
5. 复现过程中踩过的坑与调参心得
5.1 数据预处理里的归一化陷阱
我第一次复现的时候,训练 loss 一直不降,排查了半天发现是归一化的问题。图像预训练用的数据集和视频数据集的像素分布不一样,如果直接用同一套均值和方差做归一化,视频那一步的训练会很不稳定。正确做法是分别统计两个数据集的均值和方差,或者干脆用更鲁棒的归一化方式。
还有一个坑是掩码的编码。分割掩码通常是 0/1 二值图,但有些数据集用的是 0/255,有些用调色板索引。读进来如果不统一,训练时 loss 会直接爆炸。这个错误很低级但很常见,建议在读数据的地方加一个断言,检查掩码的取值分布。
5.2 微调时 batch size 与迭代次数的权衡
微调阶段只有一个样本(第一帧),batch size 只能是 1。这时候梯度噪声很大,训练曲线会抖得厉害。我的做法是用多次前向累积梯度,相当于把 batch size 虚拟地放大,梯度更稳。具体就是跑几次前向反向再更新一次参数,累积次数根据显存来定,通常 4 到 8 次。
迭代次数也不是越多越好。我做过对比实验,微调 100 次和 500 次在后续帧上的表现差异很小,但 500 次明显更容易过拟合第一帧。所以早停在这里很重要,可以每 50 次在几个后续帧上肉眼看一下效果,一旦发现边界开始变得"只贴合第一帧的形状"就停。
5.3 长视频的显存与速度优化
处理长视频时,逐帧推理虽然可以并行,但显存是个瓶颈。我的优化手段有几个:一是降低推理分辨率,把输入缩到网络能接受的最小尺寸,输出后再上采样回原尺寸,边界精度损失有限但速度提升明显;二是用半精度推理,现代显卡上基本无损;三是跳帧推理加插值,如果视频帧率很高、目标运动不快,可以每隔几帧推理一次,中间用光流或者线性插值补上。
注意:跳帧推理在目标快速运动时会出问题,插值出来的掩码会糊。所以这个方法只适合运动平缓的场景,用之前先抽几帧看看目标运动幅度。
6. 从今天的视角看 OSVOS 的局限与改造空间
6.1 主干网络换成现代架构
VGG-16 放在今天确实老了,参数量大、特征表达能力也不如后来的网络。把主干换成 ResNet 或者更现代的 Transformer 架构,理论上能提升性能。但要注意,换主干不是简单替换就行,解码器的设计、skip connection 的接法、微调时冻结哪些层,都要重新调。我试过换成 ResNet-50,性能有提升但训练更不稳定,需要更细致的学习率调度。
6.2 引入时序信息的正确姿势
前面说过 OSVOS 是逐帧独立的,丢失了时序信息。要补这块,最直接的是把前一帧的分割结果作为额外通道输入。但这样会引入误差累积,需要配合置信度机制:只有当上一帧的置信度足够高时,才把它作为参考输入,否则退化成逐帧独立。这个门控机制是关键,没有它时序信息反而会帮倒忙。
6.3 在视频编辑场景的落地改造
如果目标是视频编辑而不是学术评测,那评价标准就变了。学术上看的是 J&F 指标,工程上看的是用户改几次能满意。这时候可以做一些针对性的改造:比如允许用户在任意帧追加标注,而不只是第一帧;比如提供一个"修正笔刷",让用户直接涂抹错误区域,模型在线更新。这些交互设计比单纯提升指标更能提升实际体验。
我自己做过一个简化版的交互式分割工具,核心就是 OSVOS 的骨架加上一个"任意帧修正"的机制。用户在第一帧标完,跑一遍,看到哪帧不对就停下来涂两笔,模型用这几笔做一次快速微调再继续。实测下来,用户平均只需要修正两三次就能拿到可用的整段分割,比纯自动的结果好很多。
7. 一些零散但实用的经验
关于评估,学术数据集上的指标和实际观感经常对不上。J&F 指标高的模型,肉眼看起来边界可能还是毛毛的。所以不要只看指标,一定要抽帧看实际效果,尤其是目标边界复杂、有细长结构(比如头发、树枝)的时候。
关于数据,OSVOS 这类方法对训练数据的多样性很敏感。如果预训练数据里都是某种类型的物体,遇到没见过的类别就会明显变差。所以如果要做特定领域的应用,最好在预训练阶段就混入该领域的数据,哪怕量不大也有帮助。
关于部署,如果要做成产品,推理速度往往比精度更重要。用户等三秒和等三十秒的体验天差地别。这时候可以接受一定的精度损失来换速度,比如降分辨率、跳帧、用更轻的主干。先跑通再优化,不要一上来就追求 SOTA。
最后说个心态上的事。OSVOS 这种 one-shot 方法,本质上是在"通用先验"和"特定适应"之间找平衡。第一帧标注提供的信息量其实很少,模型能做的泛化是有上限的。遇到特别难的目标,不要指望调参能解决,该加标注就加标注,该上交互就上交互。工具是为人服务的,不是反过来。