05 - 复现指南:MotionScape — 无人机视角的未来视频生成评测基准¶
预计阅读:16 分钟 | 前置知识:Python 与 ffmpeg 使用、光流基本概念
MotionScape 是真实拍摄的无人机第一人称视频基准,用途是评测世界模型与未来视频生成——不是训练用的数据集。它给 228 段固定长度的片段、每段配语义标注与运动强度分级,再用一套统一协议去量各种生成模型的输出质量。
本指南的可复现边界 - 能跑通的:仓库克隆、源视频重建、运动分层、语义标注。这几步只要有 Python、ffmpeg 和网络就能做,不需要 GPU。 - 跑不动的:基线推理与 FVD。五个基线里最小的是 Cosmos-Predict2.5-2B,最大的是 MAGI-1-24B,加一个独立的 TensorFlow 环境算 I3D-FVD。8 GB 显存的机器到运动分层为止。 - 一个绕不开的限制:原作者不分发原视频(README 原文:"The original audiovisual content is not redistributed")。你拿到的是元数据与标注,视频要按 manifest 里的 URL 自己去抓。源被下架,对应样本就重建不出来。
目录¶
1. 基准概述¶
- GitHub:Thelegendzz/MotionScape(8 stars)
- 数据集(元数据与标注):HF
thelegendzz/MotionScape· Zenodo DOI 10.5281/zenodo.21954342 - 论文:arXiv:2604.07991
- 定位(README 原句):"a real-world first-person UAV-view benchmark for evaluating world models and future video generation under different conditioning settings and visual-motion intensities"
1.1 关键数字¶
| 属性 | 数值 | 出处 |
|---|---|---|
| 片段数 | 228 | 论文 Table 1 / manifest |
| 每段帧数 | 275(固定,无例外) | manifest 全部 228 项的 frame_count |
| 总帧数 | 62,700 | 228 × 275 |
| 总时长 | 约 34.9 分钟 | 论文 |
| 每段时长 | 约 9.2 秒 | 论文(275 帧 @ 29.97 FPS) |
| 统一帧率 | 30000/1001 ≈ 29.97 FPS | manifest benchmark_specification |
| 分段 | 观测前缀 1–200 帧 + 未来目标 201–275 帧 | 同上 |
| 前缀/目标总帧数 | 45,600 / 17,100 | 论文 |
| 源视频 | 67 个,平均每源取 3.4 段 | 论文 |
| 源分辨率 | ≥ 1080p;manifest 里 170/228 是 3840×2160 | 论文 Table 1 + manifest |
| 位姿真值 | 无 | 论文与发布内容 |
| 运动分级 | Low / Medium / High = 75 / 75 / 78 | 论文 + dynamicity_buckets.json |
1.2 它是「评测」不是「训练」¶
这个区分决定了整份指南的用法。228 段、34.9 分钟的体量不足以训练一个世界模型,它的角色是考卷:每段都切好了「看 200 帧、预测后 75 帧」,条件可以是文本、图像或视频,评测时用同一套协议跑所有模型。
它也不给位姿。没有相机轨迹、没有 GPS/IMU 真值,能评的是生成得像不像、动力学对不对,不能评定位精度——这一点在设计下游任务时会先撞上。
2. 仓库与数据获取¶
2.1 仓库结构¶
真实顶层(每个目录都有用):
MotionScape/
├── Dockerfile
├── LICENSE # MIT(evaluate_video_metrics.py 保留 NVIDIA 的 Apache-2.0)
├── README.md
├── requirements.txt
├── reconstruction/ # 源视频下载 + 基准重建
│ ├── download_sources.py
│ └── reconstruct_benchmark.py
├── motion_stratification/ # 光流运动分层
│ ├── compute_motion_strata.py
│ └── motion_scoring.py
├── annotation/ # 语义标注生成
│ └── generate_semantic_annotations.py
├── evaluation/ # 最终评测实现
│ ├── evaluate_video_metrics.py
│ ├── i3d_fvd.py
│ ├── regroup_metrics_by_motion.py
│ ├── requirements-frame-metrics.txt
│ └── requirements-i3d-fvd.txt
├── inference/ # 五个基线的适配器 + 权重下载器
│ ├── cosmos/ cogvideox/ wan/ longcat/ magi/
│ ├── docker/ # 统一基线容器
│ ├── download_model_weights.py
│ └── requirements-download.txt
└── prompts/ # 语义标注与四套生成提示词
├── semantic_annotation.txt
├── text2world.txt image2world.txt
├── video2world_task_level.txt video2world_clip_level.txt
└── clip_assisted_inspection.txt
2.2 环境准备¶
git clone https://github.com/Thelegendzz/MotionScape.git
cd MotionScape
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
README 推荐 Python 3.10 或更新。系统侧还需要 ffmpeg 与 ffprobe 两个可执行文件——重建脚本靠它们做时间重采样与抽帧,缺了会在第一步就报错。下载侧固定 yt-dlp==2026.03.17(manifest 里记录的就是这个版本)。
2.3 拿到的是标注,不是视频¶
Hugging Face 与 Zenodo 上发布的是元数据与标注:source_manifest.json、annotations/、dynamicity_buckets.json。原视频不分发。
所以要还原基准,路径只有一条:
# 第 1 步:按 manifest 抓源视频(yt-dlp 抓 YouTube,保留 manifest 里的文件名)
python reconstruction/download_sources.py \
--manifest /path/to/MotionScape/source_manifest.json \
--output-dir /path/to/MotionScape_raw
下载:仓库里没有
scripts/download_dataset.sh;huggingface-cli download --repo-type dataset Thelegendzz/MotionScape下到的也只是标注,没有视频——发布方明确不分发视听内容,版权与可用性仍归源平台与版权方。
download_sources.py 按 manifest 记录的分辨率、标称帧率、编码去选流,并且会在记录的流不可用时直接报错,而不是悄悄换一个编码。可选参数里比较有用的是 --proxy、--cookies-from-browser、--limit、--dry-run。已存在的源文件不会被覆盖。
3. 数据格式详解¶
3.1 source_manifest.json¶
顶层四个键:source_acquisition / benchmark_specification / items / summary。
{
"source_acquisition": {
"tool": "yt-dlp",
"format_selector": "bv[height=2160]+ba/bv+ba",
"merge_output_format": "mp4",
"extractor_args": "youtube:player_client=android",
"yt_dlp_version": "2026.03.17"
},
"benchmark_specification": { "fps": "30000/1001", "num_frames": 275,
"observed_frames": 200, "future_frames": 75 },
"summary": { "source_videos": 67, "benchmark_samples": 228 },
"items": [ /* 228 项 */ ]
}
items[] 每项七个字段:sample_id、video、video_index、segment_index、url、start_s、frame_count、resolution、fps、codec。
实测分布(228 项逐条统计,可复算):
| 字段 | 分布 |
|---|---|
frame_count |
275 × 228(无例外) |
codec |
vp9 169 · h264 39 · av1 20 |
resolution |
3840×2160 170 · 1920×1080 43 · 2560×1440 12 · 3840×1634 2 · 7680×3268 1 |
fps(源) |
30/1 92 · 30000/1001 50 · 50/1 40 · 60/1 34 · 60000/1001 12 |
这张表解释了两件事。一是编码不只有 H.264:H.264 只占 39/228,主力是 VP9。二是源帧率很杂(30 / 29.97 / 50 / 60 / 59.94),统一到 29.97 是重建阶段做的重采样,不是源本身就整齐。
start_s 是每段在源视频时间轴上的定点(例如 44.0、15.047),不是随机取。
3.2 annotations/¶
每个样本一个扁平 JSON,只有四个字段:
{ "sample_id": "...", "weather": "Overcast", "environment": "Urban parking structure",
"caption": "..." }
weather 与 environment 是自由文本,不是枚举——写下游代码时不要按固定类别去 match。caption 描述的是相机视角的运动,提示词里特意用 "the camera viewpoint" 或 "the onboard camera",不暗示能看到无人机本体。
3.3 dynamicity_buckets.json¶
给每个样本的运动分数与 Low / Medium / High 归属。这个分数是在缩放后的图像空间里量的,是视觉动态性/视角运动强度的代理量,不是物理相机速度——README 专门写了这一句,引用数字时别把它当速度用。
3.4 重建产物¶
reconstruct_benchmark.py 为每个 sample_id 产出四份东西:
MotionScape_reconstructed/
├── full_frames/<sample_id>/ # 275 帧图像序列
├── full_videos/<sample_id>.mp4 # 275 帧整段
├── observed_prefix_videos/<sample_id>.mp4 # 1–200 帧(条件输入)
└── last_75_videos/<sample_id>.mp4 # 201–275 帧(预测目标 / GT)
最后一步会校验帧数正是 275 / 200 / 75,对不上就报错。评测时 --gt-dir 指的就是 last_75_videos。
4. 重建与运动分层¶
4.1 重建¶
python reconstruction/reconstruct_benchmark.py \
--input-json /path/to/MotionScape/source_manifest.json \
--video-root /path/to/MotionScape_raw \
--output-dir /path/to/MotionScape_reconstructed
对每个 sample_id,脚本依次做七件事:
- 定位到源时间轴上的
start_s; - 时间重采样到 30000/1001 FPS;
- 取前 275 个连续输出帧;
- 写出 275 帧图像序列与整段视频;
- 写出 1–200 帧的观测前缀视频;
- 写出 201–275 帧的未来目标视频;
- 校验 275 / 200 / 75 三个帧数。
两个容易踩的点:公开的扁平 manifest 模式下 end_s 不作为抽取边界——边界就是「起点 + 275 帧」;已完整的样本默认跳过,要覆盖必须显式加 --overwrite。
4.2 运动分层:把每一步的参数都对一遍¶
论文的最终协议只用未来目标段 201–275 帧来算运动分数(因为分层要刻画的是「未来有多难预测」)。整条链路的参数都是可核的:
| 环节 | 设置 |
|---|---|
| 源帧率 | 30000/1001 |
| 时间降采样 | 每 3 帧取 1(得 25 帧、24 个相邻帧对) |
| 缩放 | 854×480,area 插值 |
| 光流 | 灰度 Farnebäck 稠密光流,OpenCV 参数 (0.5, 3, 15, 3, 5, 1.2, 0) |
| 帧对统计量 | 光流幅值的空间 75 分位 |
| 片段分数 | 24 个帧对统计量的均值 |
| 分档 | 全局 33 / 66 分位切成 Low / Medium / High |
python -m motion_stratification.compute_motion_strata \
--frames-root /path/to/MotionScape_reconstructed/full_frames \
--manifest-json /path/to/MotionScape/source_manifest.json \
--output-json /path/to/dynamicity_buckets_detailed.json
最终 228 段的分档是 Low 75 / Medium 75 / High 78。
一个反直觉的地方:降采样到「每 3 帧取 1」后,等效帧率只有约 9.99 FPS。分层的分辨率与帧率都比评测时低得多——这是刻意的,运动强度只需要一个相对排序,不需要精细的流场。
5. 语义标注与生成提示词¶
5.1 标注怎么生成¶
标注由 prompts/semantic_annotation.txt 驱动,经 OpenAI 兼容的 API 生成:
export ANNOTATION_API_KEY='...'
python annotation/generate_semantic_annotations.py \
--input-path /path/to/MotionScape_reconstructed/full_frames \
--mode prediction \
--dynamicity-json /path/to/MotionScape/dynamicity_buckets.json \
--prompt-file prompts/semantic_annotation.txt \
--output-dir /path/to/new_annotations \
--response-json
两个必须照做的细节:时序不能乱——条件帧 196–200 先发,随后才是抽样的未来帧 201–275;温度固定 0.1。196–200 这五帧只作时序上下文,提示词要求模型只描述 201–275。
5.2 四套生成提示词¶
| 文件 | 条件 |
|---|---|
prompts/text2world.txt |
纯文本 |
prompts/image2world.txt |
单帧图像 |
prompts/video2world_task_level.txt |
只看固定任务提示词 + 观测视频,不读任何逐样本标注 |
prompts/video2world_clip_level.txt |
weather + environment + caption 按此顺序拼接 |
clip_assisted_inspection.txt 记的是预处理阶段的负面提示词(字幕文字、标题/logo/片头、转场特效、第三人称无人机画面四类),用来在选片阶段剔掉非目标片段,不喂给生成模型。
评测里还有一个口径要分清:CLIPSim 只用 caption,因为它是评测输入而不是生成提示词。
6. 基线推理与评测¶
6.1 五个基线¶
| 基线 | 入口 |
|---|---|
| Cosmos-Predict2.5-2B | inference/cosmos/ 的补丁与资产 |
| CogVideoX1.5-5B / I2V | inference/cogvideox/t2v.py · i2v.py |
| Wan2.2 T2V / I2V(A14B) | inference/wan/t2v.py · i2v.py |
| LongCat-Video | inference/longcat/v2w.py |
| MAGI-1-24B | inference/magi/v2w.py |
各模型用的条件帧数与原生帧率都不一样,论文的评测按各自原生时序配置跑:
| 模型 / 任务 | 条件帧数 | 原生 FPS | 输出帧数 |
|---|---|---|---|
| Cosmos-Predict2.5-2B(T2W / I2W / V2W) | 0 / 1 / 5 | 30 | 93 / 92 / 88 |
| CogVideoX1.5-5B(T2W / I2W) | 0 / 1 | 16 | 41 |
| Wan2.2-A14B(T2W / I2W) | 0 / 1 | 16 | 40 |
| LongCat-Video(V2W) | 13 | 15 | 40 |
| MAGI-1-24B(V2W) | 32 | 16 | 40 |
权重要自己下(仓库不分发第三方权重):
python -m venv .venv-download && source .venv-download/bin/activate
pip install -r inference/requirements-download.txt
python inference/download_model_weights.py --output-root /data/MotionScape_models --model all --dry-run
Cosmos 权重是 gated 的,得先在 Hugging Face 上接受 NVIDIA 许可再登录。
那条
huggingface-hub==0.36.0的钉版本只属于下载环境,不要拿它去替换任何基线的推理环境——README 专门警告过。
6.2 指标与预处理¶
最终评测器是 evaluation/evaluate_video_metrics.py:
python evaluation/evaluate_video_metrics.py \
--gt-dir /path/to/MotionScape_reconstructed/last_75_videos \
--pred-dir /path/to/model_outputs \
--dynamicity-buckets-json /path/to/MotionScape/dynamicity_buckets.json \
--clipsim-caption-dir /path/to/MotionScape/annotations \
--clipsim-caption-field caption \
--clipsim-model openai/clip-vit-base-patch32 \
--output-json /path/to/metrics_per_clip.json \
--output-dynamicity-json /path/to/metrics_by_dynamicity.json \
--frame-alignment timestamp --frame-metric-size 704x1280 \
--fvd-mode Video2World --model-name MODEL_NAME \
--i3d-python .venv-i3d/bin/python
六项指标的口径不是随便取的,每一条都会影响可比性:
- PSNR / SSIM / LPIPS / Warping Error 在各模型原生帧率上做时间戳对齐后按片段算;GT 与预测的 RGB 帧各自独立缩放到 1280×704;PSNR/SSIM/Warping Error 用
[0,1]区间,LPIPS 用[-1,1]。 - Warping Error 先用相邻 GT 帧估计光流,把前一张预测帧 warp 过来,再对有效像素取平均。
- CLIPSim 固定取最多 75 个连续可用预测帧。运动分层只用于聚合,不改动 CLIPSim 的帧选择。
- FVD 是分布级的:在每个运动档内,把所有片段的 I3D 嵌入合起来比一次,得到 Low / Medium / High 各一个 FVD——不是片段级 FVD 的平均。FVD 在 GT/预测对的公共时长上均匀采 75 个时间戳,转成 224×224 与
[-1,1],每段抽一个 DeepMind I3D Mean 嵌入。
FVD 需要独立的 TensorFlow 环境(官方 TF-Hub I3D 实现与主环境冲突):
6.3 环境别互相污染¶
| 环境 | PyTorch | Transformers | Diffusers |
|---|---|---|---|
| Cosmos-Predict2.5-2B | 2.7.1+cu128 | 4.57.1 | 0.35.2 |
| CogVideoX1.5-5B / I2V | 2.8.0+cu128 | 5.13.1 | 0.39.0 |
| Wan2.2 T2V / I2V | 2.8.0+cu128 | 4.51.3 | 0.39.0 |
| LongCat-Video | 2.8.0+cu128 | 4.41.0 | 0.35.1 |
| 帧指标 / CLIPSim | 2.8.0+cu128 | 5.13.1 | N/A |
MAGI 自带一整套独立运行时(Python 3.10.20、PyTorch 2.11.0+cu130、FlashAttention/FlashInfer、CUDA 13.0/Blackwell),README 明确要求不要与上面任何一个环境合并。统一的基线容器说明在 inference/docker/README.md。
对 8 GB 显存:这五个基线一个都放不下。本篇能在这类机器上跑完的只有 §2–§4(重建 + 分层),语义标注走 API 不需要本地 GPU。生成质量部分只能引论文数字,不能自己出数。
7. 常见问题与解决方案¶
Q1: 重建时某些样本的源视频抓不下来¶
源被下架、换区、或变成会员可见都会导致 yt-dlp 失败。先 --dry-run 看清单,再用 --cookies-from-browser 或 --proxy 重试,--limit 可以先小批量试通。发布方不分发视频,这个风险无法绕过——抓不到的样本就重建不出来,评测时要按实际重建成功的样本数报告。
Q2: 下载器报「记录的流不可用」¶
这是设计如此:download_sources.py 按 manifest 的 resolution / fps / codec 精确选流,找不到就报错,不会静默换一个编码。因为编码变了会让重建结果的画质基线漂移。要放宽只能改 manifest 或接受缺样本,不要绕过去改脚本。
Q3: ffmpeg: command not found¶
重建依赖系统级的 ffmpeg 与 ffprobe,pip 装的 Python 包不能替代:
Q4: FVD 装不上 / 与主环境冲突¶
官方 TF-Hub I3D 实现自带一套 TensorFlow 依赖,必须单独建环境(evaluation/requirements-i3d-fvd.txt),并用 --i3d-python 把那个解释器路径传给评测脚本。官方权重不是 HF checkpoint,而是从 TF-Hub 解析 deepmind/i3d-kinetics-400/1,评测器会校验固定的聚合 SHA-256。
Q5: 想用 8 GB 显存跑一个基线¶
跑不动。最小的是 Cosmos-Predict2.5-2B(且显存需求远大于参数量本身,还要扛 93 帧的时间维),最大的是 MAGI-24B。可行的替代是:用你自己的小模型生成预测视频,只要帧数对得上、能落到 --pred-dir,就能接进这套评测协议——这也是把 MotionScape 当基准用的意义:协议与模型解耦。
Q6: CLIPSim 用哪段文本?¶
只用 caption,不用 weather + environment。生成侧的 clip-level Video2World 提示词要拼 weather + environment + caption,但 CLIPSim 是评测输入,口径就窄一档。两者别混。
参考资源¶
- MotionScape GitHub: https://github.com/Thelegendzz/MotionScape
- 数据集(HF): https://huggingface.co/datasets/thelegendzz/MotionScape
- Zenodo 记录: https://doi.org/10.5281/zenodo.21954342
- 论文: https://arxiv.org/abs/2604.07991
- yt-dlp: https://github.com/yt-dlp/yt-dlp
- TensorFlow Hub I3D (Kinetics-400): https://tfhub.dev/deepmind/i3d-kinetics-400/1
延伸阅读¶
思考题¶
-
定位判断:228 段、34.9 分钟、无位姿真值。如果给你一个自研的无人机世界模型,MotionScape 能用来做什么、不能用来做什么?分别说出理由。
-
分层的分辨率:运动分层把帧缩到 854×480、每 3 帧取 1,等效约 9.99 FPS。评测时的图像处理却是 1280×704、各模型原生帧率。为什么两处的分辨率差这么多还能用同一套分层结果?
-
步骤归属:仓库里没有
scripts/preprocess.py。按上面给出的真实仓库结构,降低分辨率这个需求应该落在哪一步、由谁负责? -
FVD 的口径:为什么 FVD 要在每个运动档内把片段嵌入合起来比一次,而不是算每个片段的 FVD 再平均?这两种算法在什么情况下会给出一致的结论?
-
条件帧的差异:LongCat 用 13 个条件帧、MAGI 用 32 个,Cosmos 的 V2W 只用 5 个。如果想让三者在同一条件下比,需要改动什么?改了之后还算不算「各自原生配置的报告」?