File size: 3,220 Bytes
59630ba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""Build WorldMem train/test CSV index files from mp4/npz pairs.

Each row: video_path, action_path (paths relative to the project cwd when possible).

When --root is a symlink under the project (e.g. ./data), CSV rows use paths like
data/training/.... Avoid resolving symlinks so relative paths stay valid.

Example:
    python scripts/build_worldmem_csv.py
"""

from __future__ import annotations

import argparse
import csv
import random
from pathlib import Path


def _path_for_csv(path: Path) -> str:
    """Path for CSV rows, relative to cwd when possible (preserves symlinks)."""
    if not path.is_absolute():
        return path.as_posix()
    try:
        return path.relative_to(Path.cwd()).as_posix()
    except ValueError:
        return path.as_posix()


def collect_pairs(split_dir: Path, root: Path) -> list[tuple[str, str]]:
    """Collect (video_path, action_path) for every mp4 with a matching npz."""
    if not split_dir.is_dir():
        raise FileNotFoundError(f"Split directory not found: {split_dir}")

    pairs: list[tuple[str, str]] = []
    missing_npz: list[str] = []

    for mp4 in sorted(split_dir.rglob("*.mp4")):
        npz = mp4.with_suffix(".npz")
        if not npz.is_file():
            missing_npz.append(mp4.relative_to(root).as_posix())
            continue
        pairs.append((
            _path_for_csv(mp4),
            _path_for_csv(npz),
        ))

    if missing_npz:
        print(f"  warning: {len(missing_npz)} mp4 files without matching npz "
              f"(first: {missing_npz[0]})")

    return pairs


def write_csv(rows: list[tuple[str, str]], output_path: Path) -> None:
    output_path.parent.mkdir(parents=True, exist_ok=True)
    with output_path.open("w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(["video_path", "action_path"])
        writer.writerows(rows)
    print(f"  wrote {len(rows)} rows -> {output_path}")


def main() -> None:
    parser = argparse.ArgumentParser(description="Build WorldMem train/test CSV metadata.")
    parser.add_argument(
        "--root",
        type=str,
        default='./data',
        help=f"Dataset root directory",
    )
    parser.add_argument(
        "--output_dir",
        type=str,
        default='./data',
        help="Directory for train.csv / test.csv (default: same as --root)",
    )
    parser.add_argument(
        "--seed",
        type=int,
        default=42,
        help="Random seed for shuffling rows",
    )
    args = parser.parse_args()

    root = Path(args.root)
    output_dir = Path(args.output_dir) if args.output_dir else root

    splits = {
        "train.csv": root / "training",
        "test.csv": root / "test",
    }

    print(f"Dataset root : {root} (-> {root.resolve()})")
    print(f"Output dir   : {output_dir}")
    print(f"Shuffle seed : {args.seed}")

    rng = random.Random(args.seed)

    for csv_name, split_dir in splits.items():
        print(f"\n[{csv_name}] scanning {split_dir.relative_to(root)}/")
        pairs = collect_pairs(split_dir, root)
        rng.shuffle(pairs)
        write_csv(pairs, output_dir / csv_name)

    print("\nDone.")


if __name__ == "__main__":
    main()