Label Studio VideoVector 标签实战:基于关键帧插值的视频矢量标注完整指南
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
VideoVector是 Label Studio 中面向视频帧的矢量标注标签,它将图像领域的矢量(多边形/折线/骨架)能力扩展到视频,并以关键帧 + 线性插值的方式让标注结果随时间轴自动跟随目标。本文以 videovector.md 为主干,结合仓库前端源码(web/libs/editor下的标签定义、区域模型与绘制工具)与 SAM2 视频教程,完整讲解标签配置、交互操作、关键帧原理、参数含义与导出结果格式,帮助你快速搭建可复制的视频矢量标注方案。
一、VideoVector 标签概述
VideoVector为视频帧带来矢量标注能力,通常与<Video/>(承载视频对象)和<Labels/>(提供类别标签)组合使用。它支持可闭合路径(closable)和骨架模式(skeleton),并通过基于关键帧的插值在视频帧之间平滑过渡。
- 适用数据类型:
video - 可用范围:
VideoVector与VideoVectorLabels两个标签当前仅在Label Studio Enterprise(含自托管版)与 Starter Cloud中可用。
在配置层面,VideoVector是一个独立于Labels的矢量绘制控制标签,因此可以单独控制矢量样式(颜色、粗细、点大小);而 VideoVectorLabels 则是VideoVector与Labels的合并标签,把“绘制矢量”和“选择类别”合并进一个标签,适合配置更简洁的场景。
二、结合 SAM2 实现视频分割与目标追踪
VideoVector最有价值的用法是与Segment Anything Model 2(SAM2)配合,用于视频分割与目标追踪工作流:
- 先在关键帧上手工放置一个矢量(或由 SAM2 交互式提示生成);
- 由 SAM2 后端跨视频帧跟踪并传播目标对象;
- 标注者只需修正模型漂移的帧,无需逐帧手动画矢量。
这种“关键帧引导 + 模型传播 + 人工修正”的组合,正是这两个标签最擅长的场景。
- 逐步配置教程见 SAM2 with Videos ML backend 教程(仓库中的
segment_anything_2_video.md包含从源码启动 ML 后端、设置LABEL_STUDIO_URL/LABEL_STUDIO_API_KEY环境变量、将http://localhost:9090接入项目Settings -> Machine Learning -> Add Model的完整流程,以及推荐的基础标注配置)。 - 需要注意:SAM2 视频后端存在已知限制——仅支持 GPU 服务器、目前只支持单个目标的跟踪等,配置前请阅读教程中的 “Known limitations” 章节。
值得补充的是,源码层面已经为“SAM2 交互式后端 + 矢量控制”预留了规则:skeleton.ts 中的isVectorSkeletonEnabled()表明——当控制标签绑定 SAM2 交互式 ML 后端时(hasInteractiveBackend === true),即使配置了skeleton="true"也会被忽略,因为 SAM2 始终产出闭合的 mask,骨架模式与交互式后端不兼容(对应源码注释 BROS-1434)。也就是说,配置skeleton属性的行为在不同后端环境下会有所不同。
三、关键帧(Keyframe)机制原理
关键帧是VideoVector的核心数据模型:
- 当你在某一帧上标注矢量时,Label Studio 将该帧保存为关键帧;
- 之后当你移动点、添加点或闭合路径时,会在对应帧上创建新的关键帧;
- Label Studio 会在关键帧之间对每个顶点的位置做线性插值(包含贝塞尔控制点),因此播放视频时矢量会跟随目标运动。
源码印证了这一机制。VideoVectorRegion.jsx(第 14–38 行)实现了核心插值逻辑:
interpolateVertex(prev, next, r):对单个顶点做线性插值,x = prev.x + (next.x - prev.x) * r;interpolateVertices(prevKeyframe, nextKeyframe, frame):按顶点 ID 匹配两个关键帧间的顶点,插值比例r = (frame - prevKeyframe.frame) / (nextKeyframe.frame - prevKeyframe.frame);后一关键帧中不存在的顶点保持原样。
同一文件中的getShape(frame)视图则负责在播放/跳帧时解析当前帧应显示的几何形状:命中关键帧直接返回其顶点;位于两个关键帧之间时向前查找插值;对于仅作为生命周期端点的空关键帧(enabled: false且无顶点),会就近取前/后最近的含形状关键帧,保证画布与时间轴显示一致(BROS-1513)。
四、路径与点的基本操作
| 操作 | 说明 |
|---|---|
| 添加点 | 在空白处单击。 |
| 在路径线段上添加点 | 按住Shift单击两点之间的线段。 |
| 结束或退出路径 | 按Esc,或在最后添加的点上双击。 |
| 移动点 | 单击点并拖动即可重新定位。 |
| 删除点 | 按住Alt(macOS 为Option)并单击已有顶点。 |
这些交互在绘制工具层有对应实现:VideoVector.js(工具) 中定义了双击判定阈值(300ms 内、像素距离 5px 内视为双击)以及完成区域后的单击防误触窗口(400ms,BROS-1411),保证“双击闭合/退出”与“单击选中”不会互相干扰;同时通过editingPointGesture标记区分“调整已有顶点”与“新增顶点”的手势,避免在选中开放路径时误追加顶点(BROS-1413)。
五、高级功能:闭合路径与骨架模式
5.1 闭合路径(Closed paths)
通过closable="true"参数允许创建多边形等闭合形状:
| 操作 | 说明 |
|---|---|
| 闭合路径 | 在最后一个点上双击,系统自动在首点与末点之间补上一条线段。 |
| 断开闭合路径 | 按住Alt(macOS 为Option)单击闭合路径上的某条线段,将其重新打开;再单击某个点即可删除该点。 |
5.2 骨架模式(Skeleton)
通过skeleton="true"参数开启骨架矢量(用于人体/物体关键点骨架等分支路径场景):
- 开启后,新添加的点连接到当前活动点(active point),而不是最后添加的点;
- 这使得路径可以从任意已有顶点分支,形成骨架结构。
仓库中 skeleton.test.ts 与端到端用例 vector_points.cy.ts(如“添加分支后用一次 Esc 取消选中已恢复的骨架矢量”)均覆盖了骨架模式的分支绘制与退出行为。
六、标签参数详解
下表来自 includes/tags/videovector.md,与 VideoVector.js 中TagAttrs模型的定义(含默认值)一致:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name | string | — | 元素名称(必填) |
toName | string | — | 要控制的元素名称(视频对象,必填) |
opacity | number | 0.2 | 矢量的不透明度 |
fillColor | string | #f48a42 | 矢量填充颜色,支持十六进制或 HTML 颜色名 |
strokeColor | string | #f48a42 | 描边颜色(十六进制) |
strokeWidth | number | 2 | 描边宽度 |
pointSize | small|medium|large | small | 矢量控制点(手柄)的大小 |
pointStyle | rectangle|circle | circle | 点的样式 |
closable | boolean | false | 是否允许闭合形状 |
skeleton | boolean | false | 是否启用骨架模式(允许分支路径) |
minPoints | number|none | none | 允许的最小点数 |
maxPoints | number|none | none | 允许的最大点数 |
snap | pixel|none | none | 是否将矢量吸附到图像像素 |
pointSizeEnabled | number | 5 | 形状被选中时点的像素大小 |
pointSizeDisabled | number | 3 | 形状未选中时点的像素大小 |
源码细节:fillColor、strokeColor在 VideoVector.js 中通过customTypes.color校验,opacity通过customTypes.range()约束,minPoints/maxPoints使用customTypes.positiveInteger(空值null即表示不限制)。
七、标注配置示例
7.1 视频矢量标注(VideoVector + Labels 组合)
<View> <Header>Label the video:</Header> <Video name="video" value="$video" /> <VideoVector name="vector" toName="video" /> <Labels name="videoLabels" toName="video"> <Label value="Road" background="#944BFF"/> <Label value="Boundary" background="#98C84E"/> </Labels> </View>7.2 带闭合路径的标签矢量标注(VideoVectorLabels 合并标签)
<View> <Video name="video" value="$video" /> <VideoVectorLabels name="labels" toName="video" closable="true"> <Label value="Road" /> <Label value="Boundary" /> </VideoVectorLabels> </View>VideoVectorLabels的参数与VideoVector基本一致,并额外提供标签选择相关参数:choice(single|multiple,默认single)、maxUsages(单个标签在任务中的最大使用次数)、showInline(标签是否同行显示,默认true),且默认strokeWidth为1,fillColor/strokeColor无默认值。详见 videovectorlabels.md 与其参数插页。
7.3 视频格式建议
由于帧号(frame)是矢量插值的坐标基准,视频的时长与帧率识别准确性至关重要。建议使用 MP4 容器 + H.264 (AVC) 视频编码 + AAC 音频,并转换为恒定帧率(建议 30fps),确保浏览器播放、帧数统计和标注对齐无误。仓库 video.md 给出了可直接套用的 FFmpeg 转换命令:
# 提取视频流精确时长(秒) DUR=$(ffprobe -v error -select_streams v:0 -show_entries stream=duration -of default=nokey=1:noprint_wrappers=1 input.mp4) # 重新编码为目标格式(H.264 + AAC,恒定 30fps) ffmpeg -i input_video.mp4 -c:v libx264 -profile:v high -level 4.0 -pix_fmt yuv420p -r 30 -c:a aac -b:a 128k -to $DUR output_video.mp4同时建议用ffprobe -v error -show_format -show_streams -print_format json input.mp4检查视频参数;音频流与视频流时长不一致也会导致总帧数异常。
八、结果导出格式(Result parameters)
VideoVector(与VideoVectorLabels)的标注结果以VideoVectorRegionResult类型序列化输出:
- Kind:global typedef
- Returns:
VideoVectorRegionResult— Label Studio 格式的序列化视频矢量区域数据
| 字段 | 类型 | 说明 |
|---|---|---|
original_width | number | 原始视频帧宽度(px) |
original_height | number | 原始视频帧高度(px) |
image_rotation | number | 视频帧旋转角度(度) |
value | Object | 标注值主体 |
value.sequence | Array.<Object> | 关键帧数组;关键帧之间的位置由插值得到 |
value.sequence[].frame | number | 该关键帧所作用的帧号 |
value.sequence[].enabled | boolean | 自该关键帧起矢量是否可见 |
value.sequence[].closed | boolean | 该关键帧上矢量是闭合(多边形)还是开放(折线) |
value.sequence[].vertices | Array.<Object> | 顶点对象数组,包含坐标、贝塞尔曲线信息与顶点间关系 |
value.labels | Array.<string> | 分配给该矢量的标签名数组(配合<Labels>或VideoVectorLabels时) |
示例 JSON 导出
{ "original_width": 1920, "original_height": 1280, "image_rotation": 0, "value": { "sequence": [ { "frame": 1, "enabled": true, "closed": false, "vertices": [ { "id": "point-1", "x": 25.0, "y": 30.0, "prevPointId": null, "isBezier": false }, { "id": "point-2", "x": 75.0, "y": 70.0, "prevPointId": "point-1", "isBezier": false } ] }, { "frame": 30, "enabled": true, "closed": false, "vertices": [ { "id": "point-1", "x": 40.0, "y": 45.0, "prevPointId": null, "isBezier": false }, { "id": "point-2", "x": 80.0, "y": 60.0, "prevPointId": "point-1", "isBezier": false } ] } ], "labels": ["Road"] } }解读要点:
frame: 1与frame: 30是两个关键帧,第 1~30 帧之间顶点的显示位置由线性插值计算(与 VideoVectorRegion.jsx 中的interpolateVertices逻辑一一对应);prevPointId维护顶点的顺序关系,构成路径拓扑;isBezier: false表示当前为直线顶点(贝塞尔控制点在true时生效并同样参与插值);- 坐标
x/y以百分比(0–100)形式存储在 sequence 中,视图层负责百分比到像素的换算(源码注释明确说明 “stores coordinates as percentages (0-100) in a keyframesequence”,由VideoVectorShape负责转换)。
九、延伸阅读与源码索引
- 标签定义与默认值:VideoVector.js、VideoVectorLabels.jsx
- 区域模型与插值实现:VideoVectorRegion.jsx
- 绘制工具与手势判定:VideoVector.js(工具)
- 骨架模式与 SAM2 交互后端的兼容规则:skeleton.ts
- 相关标签文档:videovectorlabels.md、video.md
- SAM2 视频后端接入教程:segment_anything_2_video.md
掌握了上述标签配置、关键帧插值模型与导出格式,你即可在支持的环境中搭建“人工标注关键帧 + SAM2 传播 + 导出插值序列”的完整视频矢量标注流水线,并基于value.sequence结构无缝对接下游模型训练或后处理逻辑。
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考