forked from mergeos-bounties/Loru
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwlasl.py
More file actions
127 lines (104 loc) · 4.55 KB
/
Copy pathwlasl.py
File metadata and controls
127 lines (104 loc) · 4.55 KB
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
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
SEQUENCE_PATH_KEYS = ("sequence_path", "loru_sequence", "sample_path")
SOURCE_URL_KEYS = ("url", "source_url", "video_url")
METADATA_KEYS = ("bbox", "fps", "signer_id", "source", "variation_id")
def normalize_gloss(gloss: object) -> str:
"""Normalize WLASL gloss text to the sample-file style used by Loru."""
value = str(gloss or "").strip().lower().replace("-", " ")
return "_".join(part for part in value.split() if part)
def load_wlasl_manifest(index_path: Path, samples_dir: Path | None = None) -> list[dict[str, Any]]:
"""Convert a WLASL-style index JSON file into Loru manifest entries.
The adapter reads local metadata only. It does not download source videos; if
a matching local Loru sample exists, the manifest points at that JSON file.
"""
index_path = Path(index_path)
payload = json.loads(index_path.read_text(encoding="utf-8"))
manifests: list[dict[str, Any]] = []
for entry in _index_entries(payload):
original_gloss = _lookup(entry, (), ("gloss", "word"))
gloss = normalize_gloss(original_gloss)
if not gloss:
continue
instances = entry.get("instances") if isinstance(entry.get("instances"), list) else [entry]
for position, instance in enumerate(instances):
if not isinstance(instance, dict):
continue
sequence_path = _resolve_sequence_path(instance, index_path.parent, gloss, samples_dir)
manifests.append(
{
"dataset": "wlasl",
"gloss": gloss,
"original_gloss": str(original_gloss or gloss),
"video_id": str(_lookup(instance, entry, ("video_id", "id")) or f"{gloss}-{position}"),
"split": _lookup(instance, entry, ("split", "subset")),
"source_url": _lookup(instance, entry, SOURCE_URL_KEYS),
"frame_start": _optional_int(_lookup(instance, entry, ("frame_start", "start_frame"))),
"frame_end": _optional_int(_lookup(instance, entry, ("frame_end", "end_frame"))),
"sequence_path": str(sequence_path) if sequence_path else None,
"sequence_exists": bool(sequence_path and sequence_path.exists()),
"metadata": {
key: instance[key]
for key in METADATA_KEYS
if key in instance and instance[key] is not None
},
}
)
return manifests
def write_wlasl_manifest(
index_path: Path,
out_path: Path,
samples_dir: Path | None = None,
) -> list[dict[str, Any]]:
"""Write a Loru WLASL manifest JSON file and return its entries."""
manifest = load_wlasl_manifest(index_path, samples_dir=samples_dir)
out_path = Path(out_path)
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
return manifest
def _index_entries(payload: object) -> list[dict[str, Any]]:
if isinstance(payload, list):
return [entry for entry in payload if isinstance(entry, dict)]
if isinstance(payload, dict):
entries = payload.get("entries") or payload.get("data")
if isinstance(entries, list):
return [entry for entry in entries if isinstance(entry, dict)]
return [payload]
return []
def _lookup(
primary: dict[str, Any],
fallback: dict[str, Any] | tuple[()],
keys: tuple[str, ...],
) -> Any:
for key in keys:
if key in primary and primary[key] is not None:
return primary[key]
if isinstance(fallback, dict):
for key in keys:
if key in fallback and fallback[key] is not None:
return fallback[key]
return None
def _resolve_sequence_path(
instance: dict[str, Any],
index_root: Path,
gloss: str,
samples_dir: Path | None,
) -> Path | None:
for key in SEQUENCE_PATH_KEYS:
raw_path = instance.get(key)
if raw_path:
path = Path(str(raw_path))
return path if path.is_absolute() else (index_root / path).resolve()
if samples_dir is None:
return None
path = Path(samples_dir) / f"{gloss}.json"
return path if path.is_absolute() else path.resolve()
def _optional_int(value: object) -> int | None:
if value is None or value == "":
return None
try:
return int(value)
except (TypeError, ValueError):
return None