| |
| """ |
| map_real_manifest.py |
| ==================== |
| 把 STARSS22/23 real-FOA manifest(ov1/ov2/ov3_real_static_foa.jsonl)里的 |
| class_name/class_id(DCASE 29-class)映射到 FSD50K 63-class 名称, |
| 生成 *_mapped.jsonl,让真实数据可以直接接入仿真训练流水线。 |
| |
| 映射规则(STARSS class → FSD50K label): |
| 近似匹配原则:选择语义最接近、且在仿真数据中出现频率合理的类。 |
| |
| 用法: |
| python scripts/map_real_manifest.py |
| # 输出到 /data/metadata/ov1_real_static_foa_mapped.jsonl 等 |
| """ |
|
|
| import json |
| import copy |
| import argparse |
| from pathlib import Path |
|
|
| |
| |
| |
| |
| STARSS_TO_FSD50K: dict[str, str] = { |
| "female_speech": "female_speech", |
| "male_speech": "male_speech", |
| "speech": "speech", |
| "laughter": "laughter", |
| "clapping": "body_sound", |
| "telephone": "telephone_alarm", |
| "knock": "knock", |
| "footsteps": "footsteps", |
| "door": "door", |
| "drawer": "drawer_cabinet", |
| "music": "musical_instrument", |
| "piano": "keyboard_instrument", |
| "bell": "bell", |
| "alarm": "alarm", |
| "car_horn": "car", |
| "domestic_sounds": "home_sound", |
| "water_tap_and_faucet":"water", |
| "dog_bark": "dog", |
| "crying_baby": "human_vocalization", |
| "crash": "crushing", |
| "cough": "body_sound", |
| "clearthroat": "body_sound", |
| "keyboard": "typing", |
| "pageturn": "paper", |
| "keysdrop": "metal_clink", |
| "gun_shot": "war_sound", |
| "drilling": "tool", |
| "engine_idling": "machine", |
| "jackhammer": "tool", |
| } |
|
|
|
|
| def map_manifest(input_path: Path, output_path: Path) -> None: |
| """Map a single JSONL manifest and write the result.""" |
| unmapped: set[str] = set() |
| n_ok = 0 |
| n_dist_null = 0 |
| n_dist_ok = 0 |
|
|
| with open(input_path) as fin, open(output_path, "w") as fout: |
| for line in fin: |
| entry = json.loads(line) |
| entry = copy.deepcopy(entry) |
|
|
| for source in entry.get("sources", []): |
| original_name = source.get("class_name", source.get("mono_target_label", "")) |
| mapped_name = STARSS_TO_FSD50K.get(original_name) |
|
|
| if mapped_name is None: |
| unmapped.add(original_name) |
| |
| mapped_name = original_name |
|
|
| |
| source["mono_target_label"] = mapped_name |
| source["class_name"] = mapped_name |
| |
| source.pop("class_id", None) |
|
|
| |
| raw_dist = source.get("distance_cm") |
| if raw_dist is None: |
| source["distance_valid"] = False |
| n_dist_null += 1 |
| else: |
| source["distance_valid"] = True |
| n_dist_ok += 1 |
|
|
| fout.write(json.dumps(entry, ensure_ascii=False) + "\n") |
| n_ok += 1 |
|
|
| print(f" {input_path.name} → {output_path.name}") |
| print(f" entries: {n_ok}") |
| print(f" dist_ok={n_dist_ok} dist_null={n_dist_null}") |
| if unmapped: |
| print(f" ⚠️ UNMAPPED class names: {unmapped}") |
| else: |
| print(f" all class names mapped ✓") |
|
|
|
|
| def main() -> None: |
| parser = argparse.ArgumentParser(description="Map real FOA manifest to FSD50K vocab") |
| parser.add_argument( |
| "--metadata-dir", |
| default="/apdcephfs_cq10/share_1603164/user/schmittzhu/data/metadata", |
| ) |
| parser.add_argument( |
| "--datasets", |
| nargs="+", |
| default=["ov1_real_static_foa", "ov2_real_static_foa", "ov3_real_static_foa"], |
| ) |
| args = parser.parse_args() |
|
|
| meta_dir = Path(args.metadata_dir) |
| for dataset in args.datasets: |
| in_path = meta_dir / f"{dataset}.jsonl" |
| out_path = meta_dir / f"{dataset}_mapped.jsonl" |
| if not in_path.exists(): |
| print(f" SKIP (not found): {in_path}") |
| continue |
| print(f"\nProcessing {dataset} ...") |
| map_manifest(in_path, out_path) |
|
|
| print("\nDone.") |
| print("\nClass mapping used:") |
| for starss, fsd in sorted(STARSS_TO_FSD50K.items()): |
| arrow = "≈" if starss != fsd else "=" |
| print(f" {starss:30s} {arrow} {fsd}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|