视频标注
在 Potato 中用时间片段、帧分类、关键帧和对象跟踪来标注视频,并沿时间轴逐帧导航。
Potato 支持视频标注,涵盖时间分割、帧分类、关键帧标记和对象跟踪。
标注模式
你选的 mode 决定标注者产出什么,以及界面上出现哪些控件,所以要按研究所需的分析单位来选。
| 模式 | 描述 | 使用场景 |
|---|---|---|
segment | 标记时间范围 | 场景检测、说话人轮次 |
frame | 分类单个帧 | 帧级标注 |
keyframe | 标记重要时刻 | 高亮检测 |
tracking | 跨帧跟踪对象 | 对象跟踪 |
combined | 在一个界面中使用所有模式 | 复杂标注任务 |
基本配置
最简方案只给出标签,其余显示选项全部留在默认值,这样标注者进入的是片段时间轴,缩放、速度控制和逐帧步进都已可用:
annotation_schemes:
- name: "video_segments"
description: "Mark segments where the speaker changes"
annotation_type: "video_annotation"
labels:
- name: "Speaker A"
color: "#FF6B6B"
- name: "Speaker B"
color: "#4ECDC4"配置选项
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
name | string | 必填 | 标注的唯一标识符 |
description | string | 必填 | 显示给标注者的说明 |
annotation_type | string | 必填 | 必须为 "video_annotation" |
labels | list | 必填 | 可用的标注标签 |
mode | string | "segment" | 标注模式 |
min_segments | integer | 0 | 最少所需标注数 |
max_segments | integer | null | 最大允许标注数 |
timeline_height | integer | 70 | 时间轴高度(像素) |
overview_height | integer | 40 | 概览栏高度(像素) |
zoom_enabled | boolean | true | 启用时间轴缩放 |
playback_rate_control | boolean | true | 显示播放速度选择器 |
frame_stepping | boolean | true | 启用逐帧导航 |
show_timecode | boolean | true | 显示帧号和时间 |
video_fps | integer | 30 | 用于计算的视频帧率 |
source_field | string | "" | 条目包含多个视频时,本方案所标注的显示字段 |
标签配置
标签可以是简单字符串,也可以是带颜色和键盘快捷键的对象。key_value 把标签绑定到某个按键,这在片段标注中最有用,因为标注者的手一直放在播放控件上:
labels:
- name: "intro"
color: "#4ECDC4"
key_value: "1"
- name: "content"
color: "#3B82F6"
key_value: "2"
- name: "outro"
color: "#8B5CF6"
key_value: "3"按模式的示例
片段模式(默认)
标记带开始和结束时间的时间范围。缩放和播放速度这两个控件决定标注者能把边界放得多准,所以场景类任务会把两者都保持开启:
annotation_schemes:
- name: "scene_detection"
description: "Mark each scene in the video"
annotation_type: "video_annotation"
mode: "segment"
labels:
- name: "indoor"
color: "#3B82F6"
key_value: "1"
- name: "outdoor"
color: "#22C55E"
key_value: "2"
- name: "transition"
color: "#F59E0B"
key_value: "3"
zoom_enabled: true
playback_rate_control: true帧模式
对单个帧分类。逐帧步进和时间码读数让标注者能停在某一帧上,并记录下他们判断的是哪一帧,标签因此才可复现:
annotation_schemes:
- name: "frame_quality"
description: "Classify each frame's quality"
annotation_type: "video_annotation"
mode: "frame"
labels:
- name: "good"
color: "#22C55E"
- name: "blurry"
color: "#F59E0B"
- name: "occluded"
color: "#EF4444"
frame_stepping: true
show_timecode: true关键帧模式
标记重要时刻。关键帧是一个点而不是一段范围,所以整个任务的准确度都落在逐帧步进上:
annotation_schemes:
- name: "highlights"
description: "Mark key moments in the video"
annotation_type: "video_annotation"
mode: "keyframe"
labels:
- name: "goal"
color: "#22C55E"
- name: "foul"
color: "#EF4444"
- name: "highlight"
color: "#F59E0B"
frame_stepping: true跟踪模式
用边界框跨帧跟踪对象。把 video_fps 设成你自己文件的帧率,因为 Potato 用这个值把时间换算成帧号,数值不符会让每个框都偏移:
annotation_schemes:
- name: "object_tracking"
description: "Track the ball throughout the video"
annotation_type: "video_annotation"
mode: "tracking"
labels:
- name: "ball"
color: "#3B82F6"
- name: "player"
color: "#22C55E"
frame_stepping: true
video_fps: 30组合模式
组合模式把片段、帧标签、关键帧和跟踪放在一个界面里。它对标注者每段视频的要求最高,所以只在同一遍标注要产出多种结果时才用:
annotation_schemes:
- name: "full_pass"
description: "Full video annotation"
annotation_type: "video_annotation"
mode: "combined"
labels:
- name: "action"
color: "#3B82F6"
- name: "dialogue"
color: "#22C55E"
- name: "transition"
color: "#F59E0B"
zoom_enabled: true
playback_rate_control: true
frame_stepping: true
show_timecode: true视频显示
如果只需要播放视频而不做标注(例如配合其他标注类型使用),用 video 类型。它渲染一个播放器,不收集任何数据,标注者看完片段后回答任务中其他方案提出的问题:
annotation_schemes:
- name: "video_player"
description: "Watch the video clip"
annotation_type: "video"
video_path: "{{video_url}}"
controls: true
autoplay: false
loop: false
muted: false字幕与说明文字
如果视频带字幕,可以标注字幕,替代视频时间轴,或者与它并用。Potato 能读取 WebVTT、SubRip、TTML/DFXP、SubStation Alpha,以及 YouTube 的 json3 和 srv1/srv2/srv3 格式,并且是根据文件内容而不是扩展名来识别的。
yt-dlp --write-auto-subs --sub-langs en --sub-format vtt --skip-download <URL>{"id": "talk_01", "conversation": {"audio": "media/talk_01.mp3",
"transcript": "captions/talk_01.vtt"}}注意: 自动生成的字幕没有标点,也没有说话人标签,它的字幕块边界落在字幕窗口写满的地方,而不是句子结束的地方。不要在这种字幕上建立句子级的方案。替代做法见标注 YouTube 字幕。
完整的格式列表见文字稿格式。
键盘快捷键
Potato 根据方案生成快捷键列表,所以帧、片段、关键帧和分类相关的按键只会出现在用到它们的模式中。
| 按键 | 操作 |
|---|---|
Space | 播放/暂停 |
← / → | 前后跳转 5 秒 |
, / . | 上一帧/下一帧(frame_stepping 开启时) |
[ | 设置片段起点(片段模式和组合模式) |
] | 设置片段终点(片段模式和组合模式) |
Enter | 创建片段(片段模式和组合模式) |
K | 标记关键帧(关键帧模式和组合模式) |
C | 对当前帧分类(帧模式和组合模式) |
Del | 删除选中项 |
+ / - | 放大/缩小 |
0 | 适应视图 |
每个带 key_value 的标签会再加上自己的按键,所以在上面的示例中 1、2、3 用来选择标签。
数据格式
输入数据
数据文件中应包含视频路径或 URL:
[
{
"id": "video_1",
"video_url": "https://example.com/videos/sample1.mp4",
"description": "Meeting recording - identify speakers"
},
{
"id": "video_2",
"video_url": "/data/videos/sample2.mp4",
"description": "Activity video - label actions"
}
]输出格式
输出因模式而异:
片段模式:
{
"id": "video_1",
"annotations": {
"scene_detection": [
{
"start": 0.0,
"end": 5.2,
"start_frame": 0,
"end_frame": 156,
"label": "indoor"
}
]
}
}帧模式:
{
"id": "video_1",
"annotations": {
"frame_quality": [
{
"frame": 120,
"time": 4.0,
"label": "good"
}
]
}
}关键帧模式:
{
"id": "video_1",
"annotations": {
"highlights": [
{
"frame": 450,
"time": 15.0,
"label": "goal",
"note": "First goal of the match"
}
]
}
}跟踪模式:
{
"id": "video_1",
"annotations": {
"object_tracking": [
{
"id": "track_1",
"label": "ball",
"frames": [
{"frame": 0, "bbox": {"x": 100, "y": 200, "w": 30, "h": 30}},
{"frame": 1, "bbox": {"x": 105, "y": 198, "w": 30, "h": 30}}
]
}
]
}
}支持的视频格式
- MP4(推荐)
- WebM
- MOV
- OGG
视频对象跟踪与关键帧插值
v2.2.0 新增
跟踪模式会在关键帧之间做插值。标注者只在少数几帧上标出对象的位置,Potato 补上中间的帧,所以跟踪一个对象只需要几个框,而不是每帧一个框。把 video_fps 设成源文件的帧率,因为插值是按帧号计算的:
annotation_schemes:
- name: "tracking"
description: "Track objects with keyframe interpolation"
annotation_type: "video_annotation"
mode: "tracking"
labels:
- name: "person"
color: "#3B82F6"
- name: "vehicle"
color: "#22C55E"
frame_stepping: true
video_fps: 30工作流程
- 导航到某个关键帧,在对象周围画出边界框
- 向前跳过若干帧,重新放置边界框
- 系统自动为中间帧插值出位置
- 按需要检查并调整插值出来的位置
有效标注模式的完整集合为:segment、frame、keyframe、tracking 和 combined。
技术说明
- 使用 Peaks.js 做时间轴可视化
- 标准 HTML5 视频元素(无需额外的服务器依赖)
- 时间轴支持拖动选择来创建片段
最佳实践
- 使用压缩过的视频 - 大文件加载慢
- 设置合适的帧率 - 让
video_fps与视频实际帧率一致 - 保持逐帧步进开启 - 帧级标注依赖它
- 使用播放速度控制 - 慢放有助于精细的工作
- 给出清晰的片段定义 - 明确什么算片段边界
- 使用区分度高的颜色 - 让标签在视觉上容易分辨
- 考虑时间轴高度 - 复杂的分割任务可以调高