fgar13
Fix How2Sign prep and Qwen video preprocessing
ffa8764
Raw
History Blame Contribute Delete
5.69 kB
from __future__ import annotations
import argparse
import json
from pathlib import Path
import pandas as pd
VIDEO_COLUMNS = [
"sentence_name", "SENTENCE_NAME",
"video_path", "path", "file", "filename", "video",
"video_name", "VIDEO_NAME",
"sentence_id", "SENTENCE_ID",
"clip_id", "CLIP_ID",
]
TARGET_COLUMNS = [
"translation", "text", "sentence", "transcript",
"raw_text", "normalized_text",
"TRANSLATION", "TEXT", "SENTENCE", "TRANSCRIPT",
]
ID_COLUMNS = [
"id", "sentence_id", "clip_id", "video_id",
"ID", "SENTENCE_ID", "CLIP_ID", "VIDEO_ID", "SENTENCE_NAME",
]
def pick_column(df: pd.DataFrame, candidates: list[str], label: str) -> str:
lower = {c.lower(): c for c in df.columns}
for c in candidates:
if c.lower() in lower:
return lower[c.lower()]
raise ValueError(
f"Could not find {label} column.\n"
f"Tried: {candidates}\n"
f"Available columns: {list(df.columns)}"
)
def build_video_index(video_root: Path) -> dict[str, Path]:
index: dict[str, Path] = {}
for path in video_root.rglob("*.mp4"):
keys = {
path.name,
path.stem,
path.stem.replace("-rgb_front", ""),
path.name.replace("-rgb_front.mp4", ""),
}
for key in keys:
index[key] = path
return index
def find_video(value: object, video_root: Path, index: dict[str, Path]) -> Path:
raw = str(value).strip()
name = Path(raw).name
stem = Path(raw).stem
candidates = [
raw,
name,
stem,
f"{raw}.mp4",
f"{name}.mp4",
f"{raw}-rgb_front.mp4",
f"{name}-rgb_front.mp4",
raw.replace("-rgb_front", ""),
stem.replace("-rgb_front", ""),
]
for candidate in candidates:
if candidate in index:
return index[candidate]
direct_path = video_root / name
if direct_path.exists():
return direct_path
raise FileNotFoundError(f"Could not find video for '{raw}' under {video_root}")
def write_split(csv_path: Path, video_root: Path, out_path: Path, split: str) -> None:
if not csv_path.exists():
raise FileNotFoundError(f"Missing CSV: {csv_path}")
if not video_root.exists():
raise FileNotFoundError(f"Missing video directory: {video_root}")
df = pd.read_csv(csv_path, sep="\t")
print(f"{split} columns: {list(df.columns)}")
video_col = pick_column(df, VIDEO_COLUMNS, "video")
target_col = pick_column(df, TARGET_COLUMNS, "translation/text")
id_col = None
try:
id_col = pick_column(df, ID_COLUMNS, "id")
except ValueError:
pass
print(f"{split}: using video column = {video_col}")
print(f"{split}: using target column = {target_col}")
print(f"{split}: using id column = {id_col}")
video_index = build_video_index(video_root)
print(f"{split}: indexed {len(video_index)} video lookup keys")
out_path.parent.mkdir(parents=True, exist_ok=True)
written = 0
skipped_missing_video = 0
skipped_empty_target = 0
missing_examples = []
missing_report_path = out_path.with_suffix(".missing_videos.txt")
with out_path.open("w", encoding="utf-8") as f:
for i, row in df.iterrows():
target = str(row[target_col]).strip()
if not target or target.lower() == "nan":
skipped_empty_target += 1
continue
try:
video_path = find_video(row[video_col], video_root, video_index)
except FileNotFoundError:
skipped_missing_video += 1
if len(missing_examples) < 50:
missing_examples.append(str(row[video_col]))
continue
example = {
"id": str(row[id_col]).strip() if id_col else str(i),
"video_path": str(video_path),
"translation": target,
"split": split,
}
f.write(json.dumps(example, ensure_ascii=False) + "\n")
written += 1
with missing_report_path.open("w", encoding="utf-8") as f:
for item in missing_examples:
f.write(item + "\n")
total = len(df)
print(f"{split}: total metadata rows = {total}")
print(f"{split}: wrote usable rows = {written}")
print(f"{split}: skipped missing videos = {skipped_missing_video}")
print(f"{split}: skipped empty targets = {skipped_empty_target}")
print(f"{split}: missing-video examples saved to {missing_report_path}")
if total > 0:
kept_pct = 100.0 * written / total
skipped_pct = 100.0 * skipped_missing_video / total
print(f"{split}: kept {kept_pct:.2f}% of rows")
print(f"{split}: missing-video skip rate {skipped_pct:.2f}%")
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--train_metadata", required=True)
parser.add_argument("--val_metadata", required=True)
parser.add_argument("--train_video_root", required=True)
parser.add_argument("--val_video_root", required=True)
parser.add_argument("--out_train", default="data/how2sign_train.jsonl")
parser.add_argument("--out_val", default="data/how2sign_val.jsonl")
args = parser.parse_args()
write_split(
csv_path=Path(args.train_metadata),
video_root=Path(args.train_video_root),
out_path=Path(args.out_train),
split="train",
)
write_split(
csv_path=Path(args.val_metadata),
video_root=Path(args.val_video_root),
out_path=Path(args.out_val),
split="val",
)
if __name__ == "__main__":
main()