#!/usr/bin/env python3 """Stream Waymo v1.4.x (tfrecord) -> unified clips, conserving disk. Per segment: gcloud-download the ~1 GB tfrecord, convert (TF-free) to unified, delete the raw file. Sharded + resumable + disk-guarded, like the AV2 pipeline. Stores JPEG by default to fit more clips on a small disk. python scripts/waymo_pipeline.py --split train --num-shards 4 --shard 0 \ --n-logs 40 --out data/unified/waymo --image-ext jpg --min-free-gb 6 """ import argparse, os, shutil, subprocess, time from mapgs.config import load_config from mapgs.data.convert import convert_waymo GC = "/mnt/william/google-cloud-sdk/bin/gcloud" BUCKET = "gs://waymo_open_dataset_v_1_4_3/individual_files" SPLIT_DIR = {"train": "training", "val": "validation"} def gs(*args): env = dict(os.environ, CLOUDSDK_PYTHON="python3") return subprocess.run([GC, "storage", *args], capture_output=True, text=True, env=env) def list_segments(split): out = gs("ls", f"{BUCKET}/{SPLIT_DIR[split]}/*.tfrecord").stdout return [ln.strip().split("/")[-1] for ln in out.splitlines() if ln.strip().endswith(".tfrecord")] 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=40) ap.add_argument("--out", default="data/unified/waymo") ap.add_argument("--raw-tmp", default="/mnt/william/_waymo_stream") ap.add_argument("--store-longside", type=int, default=1024) ap.add_argument("--image-ext", default="jpg") ap.add_argument("--max-clips-per-log", type=int, default=None) ap.add_argument("--min-free-gb", type=float, default=6.0) ap.add_argument("--shard", type=int, default=0) ap.add_argument("--num-shards", type=int, default=1) args = ap.parse_args() cfg = load_config(args.config, args.override) raw_dir = os.path.join(args.raw_tmp, f"shard{args.shard}") os.makedirs(raw_dir, exist_ok=True) 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() segs = list_segments(args.split)[args.shard::args.num_shards] segs = [s for s in segs if s not in processed] print(f"[shard {args.shard}/{args.num_shards}] {len(segs)} segments to do; target {args.n_logs}", flush=True) done = 0 for seg in segs: if done >= args.n_logs: break if shutil.disk_usage(args.out).free / 1e9 < args.min_free_gb: print(f"stopping: low disk ({shutil.disk_usage(args.out).free/1e9:.1f} GB)", flush=True) break t = time.time() try: r = gs("cp", f"{BUCKET}/{SPLIT_DIR[args.split]}/{seg}", raw_dir + "/") if r.returncode != 0: raise RuntimeError(r.stderr.strip()[:200]) convert_waymo(raw_dir, args.out, cfg, split=args.split, only_segments=[seg], store_longside=args.store_longside, image_ext=args.image_ext, max_clips_per_log=args.max_clips_per_log) except Exception as e: print(f"[skip {seg[:24]}] {type(e).__name__}: {e}", flush=True) finally: for f in os.listdir(raw_dir): os.remove(os.path.join(raw_dir, f)) with open(processed_path, "a") as fh: fh.write(seg + "\n") done += 1 n = len([d for d in os.listdir(out_split) if d.startswith(args.split)]) print(f"[{done}/{args.n_logs}] {seg[:24]} {time.time()-t:.0f}s | {n} clips | " f"{shutil.disk_usage(args.out).free/1e9:.1f} GB free", flush=True) print(f"shard {args.shard} done: {done} segments", flush=True) if __name__ == "__main__": main()