File size: 7,914 Bytes
8056602
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
726a0ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0b24a02
 
 
 
5e75f99
 
0b24a02
 
8056602
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#!/usr/bin/env python3
"""
HF Space 一次性环境搭建:下载权重 + 生成 BEV 地图缓存
运行一次后文件持久保存在挂载存储上,之后不再需要重复运行。

用法:
    python scripts/setup_space.py          # 全部下载+生成
    python scripts/setup_space.py --check  # 只检查状态
"""

import argparse
import os
import subprocess
import sys
import time
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent

# ---- 下载清单 ----
DOWNLOAD_TASKS = [
    {
        "name": "t5",
        "repo": "google/t5-v1_1-xxl",
        "local": ROOT / "pretrained" / "t5-v1_1-xxl",
        "desc": "T5-XXL 文本编码器 (~40 GB)",
    },
    {
        "name": "ckpt",
        "repo": "flymin/MagicDriveDiT-stage3-40k-ft",
        "local": ROOT / "ckpts" / "MagicDriveDiT-stage3-40k-ft",
        "desc": "Stage-3 DiT 扩散模型 (~8 GB)",
    },
    {
        "name": "vae",
        "repo": "THUDM/CogVideoX-2b",
        "local": ROOT / "pretrained" / "CogVideoX-2b",
        "allow_patterns": ["vae/**"],
        "desc": "CogVideoX-2b VAE 编码/解码器 (~2 GB)",
    },
    {
        "name": "data_pkl",
        "repo": "flymin/MagicDriveDiT-nuScenes-metadata",
        "repo_type": "dataset",
        "local": ROOT / "data" / "nuscenes_mmdet3d-12Hz",
        "allow_patterns": [
            "nuscenes_mmdet3d-12Hz/nuscenes_interp_12Hz_infos_val_with_bid.pkl",
        ],
        "desc": "nuScenes 标注 PKL (~430 MB)",
    },
    {
        "name": "ghost_peek",
        "repo": "doradream/magicdrive-ghost-peek",
        "repo_type": "dataset",
        "local": ROOT / "data" / "nuscenes_mmdet3d-12Hz",
        "allow_patterns": [
            "ghost_peek_s8.pkl",
            "ghost_peek_s25.pkl",
        ],
        "desc": "鬼探头场景 PKL (~660 MB, 需先上传到 HF Dataset)",
    },
    {
        "name": "nuscenes_maps",
        "repo": "doradream/magicdrive-data",
        "repo_type": "dataset",
        "local": ROOT / "data" / "nuscenes" / "maps",
        "allow_patterns": ["expansion/*.json"],
        "desc": "nuScenes 官方地图文件 (~10 MB)",
    },
    {
        "name": "map_json",
        "repo": "flymin/MagicDriveDiT-nuScenes-metadata",
        "repo_type": "dataset",
        "local": ROOT / "data" / "nuscenes" / "interp_12Hz_trainval",
        "allow_patterns": ["nuscenes/interp_12Hz_trainval/*.json"],
        "desc": "nuScenes 地图 JSON (~50 MB)",
    },
]

# ---- 地图缓存生成配置 ----
MAP_CACHE_DIR = ROOT / "data" / "nuscenes_mmdet3d-12Hz" / "nuscenes_map_aux_12Hz"
MAP_CACHE_CONFIG = ROOT / "configs" / "cache_gen" / "map_cache_gen_interp.yaml"


def check_path(path: Path) -> tuple[bool, str]:
    """检查目录/文件是否存在且非空"""
    if not path.exists():
        return False, "不存在"
    files = list(path.rglob("*")) if path.is_dir() else [path]
    real_files = [f for f in files if f.is_file()]
    if not real_files:
        return False, "空目录"
    size_gb = sum(f.stat().st_size for f in real_files) / 1e9
    return True, f"{len(real_files)} 个文件, {size_gb:.1f} GB"


def download_from_hf(task: dict):
    """从 HuggingFace 下载单个任务"""
    from huggingface_hub import snapshot_download

    kwargs = dict(
        repo_id=task["repo"],
        local_dir=str(task["local"]),
        repo_type=task.get("repo_type", "model"),
        resume_download=True,
        max_workers=4,
    )
    if "allow_patterns" in task:
        kwargs["allow_patterns"] = task["allow_patterns"]

    print(f"  下载中... ({task['desc']})")
    t0 = time.time()
    snapshot_download(**kwargs)
    elapsed = time.time() - t0
    print(f"  完成 ({elapsed / 60:.1f} min)")


def generate_map_cache():
    """调用 prepare_map_aux.py 生成 BEV 地图 h5 缓存"""
    print("\n--- 生成 BEV 地图缓存 ---")

    # 安装 hydra(如果还没装)
    try:
        import hydra  # noqa
    except ImportError:
        subprocess.check_call([sys.executable, "-m", "pip", "install", "hydra-core==1.3.0"])

    # 生成 train 缓存
    for split in ["val"]:  # 推理只需要 val
        h5_file = ROOT / f"{split}_map_cache.h5"
        if h5_file.exists():
            print(f"  {h5_file} 已存在,跳过")
            continue

        print(f"  生成 {split} 地图缓存...")
        result = subprocess.run(
            [
                sys.executable, "-u",
                str(ROOT / "tools" / "prepare_data" / "prepare_map_aux.py"),
                f"+process={split}",
                f"subfix=map_cache",
            ],
            cwd=str(ROOT),
            capture_output=False,
            env={**os.environ, "PYTHONPATH": str(ROOT)},
        )
        if result.returncode != 0:
            print(f"  [WARN] {split} 缓存生成失败 (exit {result.returncode}),将回退到实时生成")
        else:
            # 移动到正确位置
            MAP_CACHE_DIR.mkdir(parents=True, exist_ok=True)
            h5_file.rename(MAP_CACHE_DIR / h5_file.name)
            print(f"  [OK] 已保存到 {MAP_CACHE_DIR / h5_file.name}")


def main():
    parser = argparse.ArgumentParser(description="HF Space 一次性环境搭建")
    parser.add_argument("--check", action="store_true", help="只检查状态,不执行")
    parser.add_argument("--skip-download", action="store_true", help="跳过下载")
    parser.add_argument("--skip-cache", action="store_true", help="跳过地图缓存生成")
    args = parser.parse_args()

    os.chdir(str(ROOT))
    sys.path.insert(0, str(ROOT))

    print("=" * 60)
    print("MagicDrive-V2 HF Space 环境检查")
    print(f"Root: {ROOT}")
    print("=" * 60)

    # ---- 检查状态 ----
    all_ok = True
    print("\n【权重/数据文件】")
    for task in DOWNLOAD_TASKS:
        ok, msg = check_path(task["local"])
        status = "[OK]" if ok else "[MISS]"
        if not ok:
            all_ok = False
        print(f"  {status} {task['name']:12s} {msg}")

    print("\n【BEV 地图缓存】")
    cache_ok, cache_msg = check_path(MAP_CACHE_DIR)
    if not cache_ok:
        all_ok = False
    print(f"  {'[OK]' if cache_ok else '[MISS]'} map_cache   {cache_msg}")

    if args.check:
        if all_ok:
            print("\n所有文件就绪,可以直接推理!")
        else:
            print("\n部分文件缺失,运行: python scripts/setup_space.py")
        return

    # ---- 下载 ----
    if not args.skip_download:
        print("\n" + "=" * 60)
        print("下载权重与数据")
        print("=" * 60)
        for task in DOWNLOAD_TASKS:
            ok, msg = check_path(task["local"])
            if ok:
                print(f"\n[{task['name']}] 已存在 -> 跳过")
                continue
            print(f"\n[{task['name']}] {task['desc']}")
            try:
                download_from_hf(task)
            except Exception as e:
                print(f"  [ERR] 下载失败: {e}")
                print(f"  你可以稍后重试: python scripts/setup_space.py")

    # ---- 生成地图缓存 ----
    if not args.skip_cache:
        cache_ok, _ = check_path(MAP_CACHE_DIR)
        if cache_ok:
            print("\n[BEV 地图缓存] 已存在 -> 跳过")
        else:
            generate_map_cache()

    # ---- 最终状态 ----
    print("\n" + "=" * 60)
    print("最终状态")
    print("=" * 60)
    all_ok = True
    for task in DOWNLOAD_TASKS:
        ok, msg = check_path(task["local"])
        if not ok:
            all_ok = False
        print(f"  {'[OK]' if ok else '[MISS]'} {task['name']}")
    cache_ok, _ = check_path(MAP_CACHE_DIR)
    if not cache_ok:
        all_ok = False
    print(f"  {'[OK]' if cache_ok else '[MISS]'} map_cache")

    if all_ok:
        print("\n全部就绪!可以运行推理了。")
    else:
        print("\n有缺失项,请重新运行或检查网络。")


if __name__ == "__main__":
    main()