| |
| """Stream Argoverse 2 Sensor -> unified clips, conserving disk. |
| |
| For each log: selectively download (3 front cameras + lidar timestamps + map + |
| calibration + annotations + poses, ~0.5 GB vs 1.1 GB full), convert to unified, |
| then delete the raw log. Resumable (tracks processed log ids) and disk-guarded. |
| |
| python scripts/av2_pipeline.py --split train --n-logs 100 \ |
| --out data/unified/av2 --store-longside 1024 --min-free-gb 15 |
| """ |
|
|
| import argparse |
| import os |
| import shutil |
| import subprocess |
| import time |
|
|
| from mapgs.config import load_config |
| from mapgs.data.convert import convert_argoverse2 |
|
|
| BUCKET = "s3://argoverse/datasets/av2/sensor" |
| NEEDED_DIRS = ["calibration", "map", "sensors/lidar"] |
| FRONT = ["ring_front_left", "ring_front_center", "ring_front_right"] |
| FILES = ["annotations.feather", "city_SE3_egovehicle.feather"] |
|
|
|
|
| def s5(s5cmd, *args): |
| subprocess.run([s5cmd, "--no-sign-request", *args], check=False, |
| stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
|
|
|
|
| def list_logs(s5cmd, split): |
| out = subprocess.run([s5cmd, "--no-sign-request", "ls", f"{BUCKET}/{split}/"], |
| capture_output=True, text=True).stdout |
| return [ln.split()[-1].rstrip("/") for ln in out.splitlines() if "DIR" in ln] |
|
|
|
|
| def download_log(s5cmd, split, lid, raw_root): |
| base = f"{BUCKET}/{split}/{lid}" |
| dst = os.path.join(raw_root, split, lid) |
| for d in NEEDED_DIRS: |
| os.makedirs(os.path.join(dst, d), exist_ok=True) |
| s5(s5cmd, "cp", f"{base}/{d}/*", os.path.join(dst, d) + "/") |
| for c in FRONT: |
| os.makedirs(os.path.join(dst, "sensors/cameras", c), exist_ok=True) |
| s5(s5cmd, "cp", f"{base}/sensors/cameras/{c}/*", os.path.join(dst, "sensors/cameras", c) + "/") |
| for f in FILES: |
| s5(s5cmd, "cp", f"{base}/{f}", dst + "/") |
| return dst |
|
|
|
|
| def main(): |
| ap = argparse.ArgumentParser() |
| ap.add_argument("--config", default="configs/base.yaml") |
| ap.add_argument("--override", nargs="*", default=["data.num_frames=20"]) |
| ap.add_argument("--split", default="train") |
| ap.add_argument("--n-logs", type=int, default=100) |
| ap.add_argument("--out", default="data/unified/av2") |
| ap.add_argument("--raw-tmp", default="/mnt/william/_av2_stream") |
| ap.add_argument("--s5cmd", default="/tmp/s5cmd") |
| ap.add_argument("--store-longside", type=int, default=1024) |
| ap.add_argument("--max-clips-per-log", type=int, default=None) |
| ap.add_argument("--min-free-gb", type=float, default=15.0) |
| ap.add_argument("--keep-raw", action="store_true") |
| ap.add_argument("--shard", type=int, default=0, help="this worker's shard index") |
| ap.add_argument("--num-shards", type=int, default=1, help="total parallel workers") |
| args = ap.parse_args() |
|
|
| cfg = load_config(args.config, args.override) |
| args.raw_tmp = os.path.join(args.raw_tmp, f"shard{args.shard}") |
| out_split = os.path.join(args.out, args.split) |
| os.makedirs(out_split, exist_ok=True) |
| |
| processed_path = os.path.join(args.out, f"processed_{args.split}_{args.shard}of{args.num_shards}.txt") |
| processed = set(open(processed_path).read().split()) if os.path.exists(processed_path) else set() |
|
|
| all_logs = list_logs(args.s5cmd, args.split) |
| shard_logs = all_logs[args.shard::args.num_shards] |
| logs = [l for l in shard_logs if l not in processed] |
| print(f"[shard {args.shard}/{args.num_shards}] {len(logs)} logs to process " |
| f"(of {len(shard_logs)} in shard, {len(processed)} done); target {args.n_logs}") |
|
|
| done = 0 |
| for lid in logs: |
| if done >= args.n_logs: |
| break |
| free_gb = shutil.disk_usage(args.out).free / 1e9 |
| if free_gb < args.min_free_gb: |
| print(f"stopping: only {free_gb:.1f} GB free (< {args.min_free_gb})") |
| break |
| t = time.time() |
| try: |
| download_log(args.s5cmd, args.split, lid, args.raw_tmp) |
| convert_argoverse2(args.raw_tmp, args.out, cfg, split=args.split, only_logs=[lid], |
| store_longside=args.store_longside, max_clips_per_log=args.max_clips_per_log) |
| except Exception as e: |
| print(f"[skip {lid[:8]}] {type(e).__name__}: {e}") |
| finally: |
| if not args.keep_raw: |
| shutil.rmtree(os.path.join(args.raw_tmp, args.split, lid), ignore_errors=True) |
| with open(processed_path, "a") as fh: |
| fh.write(lid + "\n") |
| done += 1 |
| n_clips = len([d for d in os.listdir(out_split) if d.startswith(args.split)]) |
| print(f"[{done}/{args.n_logs}] {lid[:8]} done in {time.time()-t:.0f}s | " |
| f"{n_clips} clips total | {shutil.disk_usage(args.out).free/1e9:.1f} GB free") |
| print(f"pipeline finished: {done} logs processed") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|