assemsbry commited on
Commit
d8bcabf
·
1 Parent(s): 382e603

feat: add GSR conversion tool

Browse files
Files changed (1) hide show
  1. tools/convert_gsr_to_yolo.py +65 -0
tools/convert_gsr_to_yolo.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Convert SoccerNet GSR annotations to a detection-only YOLO dataset.
2
+
3
+ Identity, team, and jersey attributes are intentionally discarded.
4
+ """
5
+
6
+ import argparse
7
+ import json
8
+ import os
9
+ import random
10
+ from pathlib import Path
11
+
12
+
13
+ CLASSES = {1: 0, 2: 1, 3: 2, 4: 3, 7: 4}
14
+
15
+
16
+ def main():
17
+ parser = argparse.ArgumentParser()
18
+ parser.add_argument("root", type=Path)
19
+ parser.add_argument("output", type=Path)
20
+ parser.add_argument("--val-ratio", type=float, default=0.1)
21
+ parser.add_argument("--seed", type=int, default=42)
22
+ args = parser.parse_args()
23
+ sequences = sorted(p for p in args.root.iterdir() if (p / "Labels-GameState.json").exists())
24
+ rng = random.Random(args.seed)
25
+ rng.shuffle(sequences)
26
+ val_count = max(1, round(len(sequences) * args.val_ratio))
27
+ val_sequences = set(sequences[:val_count])
28
+ for split in ("train", "val"):
29
+ (args.output / "images" / split).mkdir(parents=True, exist_ok=True)
30
+ (args.output / "labels" / split).mkdir(parents=True, exist_ok=True)
31
+ total = 0
32
+ for sequence in sequences:
33
+ split = "val" if sequence in val_sequences else "train"
34
+ data = json.loads((sequence / "Labels-GameState.json").read_text(encoding="utf-8"))
35
+ images = {item["image_id"]: item for item in data["images"]}
36
+ grouped = {}
37
+ for annotation in data["annotations"]:
38
+ grouped.setdefault(annotation["image_id"], []).append(annotation)
39
+ for image_id, image in images.items():
40
+ source = sequence / data["info"]["im_dir"] / image["file_name"]
41
+ destination_name = f"{sequence.name}_{image['file_name']}"
42
+ destination = args.output / "images" / split / destination_name
43
+ if not destination.exists():
44
+ os.link(source, destination)
45
+ lines = []
46
+ for annotation in grouped.get(image_id, []):
47
+ category = CLASSES.get(annotation["category_id"])
48
+ box = annotation.get("bbox_image")
49
+ if category is None or not box:
50
+ continue
51
+ x, y, w, h = box["x"], box["y"], box["w"], box["h"]
52
+ width, height = image["width"], image["height"]
53
+ lines.append(f"{category} {(x + w / 2) / width:.8f} {(y + h / 2) / height:.8f} {w / width:.8f} {h / height:.8f}")
54
+ (args.output / "labels" / split / f"{Path(destination_name).stem}.txt").write_text("\n".join(lines) + "\n", encoding="utf-8")
55
+ total += 1
56
+ (args.output / "data.yaml").write_text(
57
+ f"path: {args.output}\ntrain: images/train\nval: images/val\n"
58
+ "names:\n 0: player\n 1: goalkeeper\n 2: referee\n 3: football\n 4: other\n",
59
+ encoding="utf-8",
60
+ )
61
+ print(f"converted {total} frames from {len(sequences)} sequences")
62
+
63
+
64
+ if __name__ == "__main__":
65
+ main()