| """νμμ νμ΅ λ°μ΄ν° μ€λΉ μ€ν¬λ¦½νΈ |
| |
| κΉ¨λν μμ±κ³Ό νμμ νμΌλ‘λΆν° HDF5 νμ΅ λ°μ΄ν°λ₯Ό μμ±ν©λλ€. |
| μ΄ μ€ν¬λ¦½νΈλ λ‘컬μμ μ€ννμ¬ λ°μ΄ν°λ₯Ό μ€λΉν ν HF Hubμ μ
λ‘λν©λλ€. |
| |
| μ¬μ©λ²: |
| python prepare_data.py --speech-dir ./data/speech \ |
| --noise-dir ./data/noise \ |
| --output-dir ./data/hdf5 \ |
| [--rir-dir ./data/rir] \ |
| [--sr 48000] |
| """ |
|
|
| import argparse |
| import json |
| import os |
| from pathlib import Path |
|
|
| import numpy as np |
| import soundfile as sf |
|
|
|
|
| def find_audio_files(directory, extensions=(".wav", ".flac", ".ogg", ".mp3")): |
| """λλ ν 리μμ μ€λμ€ νμΌ κ²½λ‘ μμ§""" |
| audio_files = [] |
| for ext in extensions: |
| audio_files.extend(Path(directory).rglob(f"*{ext}")) |
| return sorted(audio_files) |
|
|
|
|
| def split_stereo_to_mono(audio_files, output_dir): |
| """Stereo μ€λμ€ νμΌμ L/R μ±λμ κ°κ° mono WAVλ‘ λΆλ¦¬. |
| |
| DeepFilterNet3μ mono λͺ¨λΈμ΄λ―λ‘, stereo νμΌμ μ±λλ³λ‘ λΆλ¦¬νμ¬ |
| νμ΅ λ°μ΄ν°λ₯Ό 2λ°°λ‘ ν보νλ ν¨κ³Όλ₯Ό μ»μ. |
| μ΄λ―Έ monoμΈ νμΌμ κ·Έλλ‘ λ³΅μ¬. |
| |
| Returns: |
| λΆλ¦¬λ mono νμΌ κ²½λ‘ λ¦¬μ€νΈ |
| """ |
| mono_dir = Path(output_dir) / "mono_split" |
| mono_dir.mkdir(parents=True, exist_ok=True) |
|
|
| mono_files = [] |
| for f in audio_files: |
| info = sf.info(str(f)) |
| stem = f.stem |
|
|
| if info.channels >= 2: |
| data, sr = sf.read(str(f), always_2d=True) |
| |
| left_path = mono_dir / f"{stem}_L.wav" |
| sf.write(str(left_path), data[:, 0], sr, subtype="PCM_16") |
| mono_files.append(left_path) |
| |
| right_path = mono_dir / f"{stem}_R.wav" |
| sf.write(str(right_path), data[:, 1], sr, subtype="PCM_16") |
| mono_files.append(right_path) |
| else: |
| |
| mono_path = mono_dir / f"{stem}.wav" |
| data, sr = sf.read(str(f)) |
| sf.write(str(mono_path), data, sr, subtype="PCM_16") |
| mono_files.append(mono_path) |
|
|
| print(f" Stereo β Mono λΆλ¦¬ μλ£: {len(audio_files)}κ° β {len(mono_files)}κ° νμΌ") |
| return sorted(mono_files) |
|
|
|
|
| def validate_sample_rate(files, target_sr=48000, max_check=50): |
| """μνλ§ λ μ΄νΈ κ²μ¦ (DeepFilterNet3μ 48kHz νμ)""" |
| issues = [] |
| for f in files[:max_check]: |
| info = sf.info(str(f)) |
| if info.samplerate != target_sr: |
| issues.append((str(f), info.samplerate)) |
| return issues |
|
|
|
|
| def create_file_list(audio_files, output_path): |
| """μ€λμ€ νμΌ κ²½λ‘ λͺ©λ‘ μμ± (HDF5 λ³νμ μ¬μ©)""" |
| with open(output_path, "w") as f: |
| for audio_file in audio_files: |
| f.write(str(audio_file.resolve()) + "\n") |
| print(f" νμΌ λͺ©λ‘ μμ±: {output_path} ({len(audio_files)}κ° νμΌ)") |
| return output_path |
|
|
|
|
| def create_dataset_config(output_dir, has_rir=False): |
| """dataset.cfg μμ± (DeepFilterNet νμ΅μ νμν JSON μ€μ )""" |
| config = { |
| "train": [ |
| [os.path.join(output_dir, "train_speech.hdf5"), 1.0], |
| [os.path.join(output_dir, "train_noise.hdf5"), 1.0], |
| ], |
| "valid": [ |
| [os.path.join(output_dir, "valid_speech.hdf5"), 1.0], |
| [os.path.join(output_dir, "valid_noise.hdf5"), 1.0], |
| ], |
| "test": [ |
| [os.path.join(output_dir, "test_speech.hdf5"), 1.0], |
| [os.path.join(output_dir, "test_noise.hdf5"), 1.0], |
| ], |
| } |
|
|
| if has_rir: |
| config["train"].append([os.path.join(output_dir, "train_rir.hdf5"), 1.0]) |
| config["valid"].append([os.path.join(output_dir, "valid_rir.hdf5"), 1.0]) |
|
|
| config_path = os.path.join(output_dir, "dataset.cfg") |
| with open(config_path, "w") as f: |
| json.dump(config, f, indent=2) |
|
|
| print(f" dataset.cfg μμ±: {config_path}") |
| return config_path |
|
|
|
|
| def split_files(files, train_ratio=0.8, valid_ratio=0.1): |
| """νμΌμ train/valid/testλ‘ λΆν """ |
| n = len(files) |
| n_train = int(n * train_ratio) |
| n_valid = int(n * valid_ratio) |
|
|
| np.random.seed(42) |
| indices = np.random.permutation(n) |
|
|
| train_files = [files[i] for i in indices[:n_train]] |
| valid_files = [files[i] for i in indices[n_train:n_train + n_valid]] |
| test_files = [files[i] for i in indices[n_train + n_valid:]] |
|
|
| return train_files, valid_files, test_files |
|
|
|
|
| def print_summary(speech_files, noise_files, rir_files=None): |
| """λ°μ΄ν° μμ½ μΆλ ₯""" |
| print("\n" + "=" * 50) |
| print("λ°μ΄ν° μμ½") |
| print("=" * 50) |
|
|
| total_speech_dur = 0 |
| for f in speech_files[:100]: |
| info = sf.info(str(f)) |
| total_speech_dur += info.duration |
|
|
| est_total = total_speech_dur / min(len(speech_files), 100) * len(speech_files) |
| print(f" μμ± νμΌ: {len(speech_files)}κ° (μμ μ΄ {est_total/3600:.1f}μκ°)") |
|
|
| total_noise_dur = 0 |
| for f in noise_files[:100]: |
| info = sf.info(str(f)) |
| total_noise_dur += info.duration |
|
|
| est_total_n = total_noise_dur / min(len(noise_files), 100) * len(noise_files) |
| print(f" λ
Έμ΄μ¦ νμΌ: {len(noise_files)}κ° (μμ μ΄ {est_total_n/3600:.1f}μκ°)") |
|
|
| if rir_files: |
| print(f" RIR νμΌ: {len(rir_files)}κ°") |
|
|
| print() |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser(description="DeepFilterNet3 νμΈνλμ© λ°μ΄ν° μ€λΉ") |
| parser.add_argument("--speech-dir", required=True, help="κΉ¨λν μμ± λλ ν 리") |
| parser.add_argument("--noise-dir", required=True, help="νμμ λλ ν 리") |
| parser.add_argument("--rir-dir", default=None, help="RIR λλ ν 리 (μ ν)") |
| parser.add_argument("--output-dir", required=True, help="μΆλ ₯ λλ ν 리 (HDF5 + μ€μ νμΌ)") |
| parser.add_argument("--sr", type=int, default=48000, help="μνλ§ λ μ΄νΈ (κΈ°λ³Έ: 48000)") |
| args = parser.parse_args() |
|
|
| os.makedirs(args.output_dir, exist_ok=True) |
|
|
| |
| print("μ€λμ€ νμΌ μμ§ μ€...") |
| speech_files = find_audio_files(args.speech_dir) |
| noise_files = find_audio_files(args.noise_dir) |
| rir_files = find_audio_files(args.rir_dir) if args.rir_dir else None |
|
|
| if not speech_files: |
| print(f"μ€λ₯: {args.speech_dir}μμ μμ± νμΌμ μ°Ύμ μ μμ΅λλ€.") |
| return |
| if not noise_files: |
| print(f"μ€λ₯: {args.noise_dir}μμ λ
Έμ΄μ¦ νμΌμ μ°Ύμ μ μμ΅λλ€.") |
| return |
|
|
| |
| print(f"\nμνλ§ λ μ΄νΈ κ²μ¦ ({args.sr}Hz)...") |
| sr_issues = validate_sample_rate(speech_files, args.sr) |
| sr_issues += validate_sample_rate(noise_files, args.sr) |
| if sr_issues: |
| print(" κ²½κ³ : λ€μ νμΌλ€μ μνλ§ λ μ΄νΈκ° λ§μ§ μμ΅λλ€:") |
| for path, sr in sr_issues[:10]: |
| print(f" {path}: {sr}Hz (νμ: {args.sr}Hz)") |
| print(" 리μνλ§μ΄ νμν©λλ€. librosaλ ffmpegλ‘ 48kHzλ‘ λ³ννμΈμ.") |
| print(" μ: ffmpeg -i input.wav -ar 48000 output.wav") |
| return |
|
|
| print_summary(speech_files, noise_files, rir_files) |
|
|
| |
| print("\nStereo β Mono μ±λ λΆλ¦¬ μ€...") |
| print(" Speech νμΌ:") |
| speech_files = split_stereo_to_mono(speech_files, args.output_dir) |
| print(" Noise νμΌ:") |
| noise_files = split_stereo_to_mono(noise_files, args.output_dir) |
| if rir_files: |
| print(" RIR νμΌ:") |
| rir_files = split_stereo_to_mono(rir_files, args.output_dir) |
|
|
| |
| print("λ°μ΄ν° λΆν μ€ (80/10/10)...") |
| train_speech, valid_speech, test_speech = split_files(speech_files) |
| train_noise, valid_noise, test_noise = split_files(noise_files) |
|
|
| print(f" Speech - train: {len(train_speech)}, valid: {len(valid_speech)}, test: {len(test_speech)}") |
| print(f" Noise - train: {len(train_noise)}, valid: {len(valid_noise)}, test: {len(test_noise)}") |
|
|
| |
| print("\nνμΌ λͺ©λ‘ μμ± μ€...") |
| lists_dir = os.path.join(args.output_dir, "lists") |
| os.makedirs(lists_dir, exist_ok=True) |
|
|
| for split, files in [("train", train_speech), ("valid", valid_speech), ("test", test_speech)]: |
| create_file_list(files, os.path.join(lists_dir, f"{split}_speech.txt")) |
| for split, files in [("train", train_noise), ("valid", valid_noise), ("test", test_noise)]: |
| create_file_list(files, os.path.join(lists_dir, f"{split}_noise.txt")) |
|
|
| if rir_files: |
| train_rir, valid_rir, _ = split_files(rir_files) |
| create_file_list(train_rir, os.path.join(lists_dir, "train_rir.txt")) |
| create_file_list(valid_rir, os.path.join(lists_dir, "valid_rir.txt")) |
|
|
| |
| print("\ndataset.cfg μμ± μ€...") |
| create_dataset_config(args.output_dir, has_rir=rir_files is not None) |
|
|
| |
| print("\n" + "=" * 50) |
| print("λ€μ λ¨κ³: HDF5 λ³ν") |
| print("=" * 50) |
| print() |
| print("DeepFilterNetμ prepare_data.py μ€ν¬λ¦½νΈλ‘ HDF5λ₯Ό μμ±νμΈμ:") |
| print("(Docker 컨ν
μ΄λ λ΄λΆ λλ Linux νκ²½μμ μ€ν)") |
| print() |
|
|
| dfn_script = "python /opt/DeepFilterNet/DeepFilterNet/df/scripts/prepare_data.py" |
| for split in ["train", "valid", "test"]: |
| for dtype in ["speech", "noise"]: |
| txt_path = os.path.join(lists_dir, f"{split}_{dtype}.txt") |
| hdf5_path = os.path.join(args.output_dir, f"{split}_{dtype}.hdf5") |
| print(f" {dfn_script} --sr {args.sr} {dtype} {txt_path} {hdf5_path}") |
| print() |
|
|
| if rir_files: |
| for split in ["train", "valid"]: |
| txt_path = os.path.join(lists_dir, f"{split}_rir.txt") |
| hdf5_path = os.path.join(args.output_dir, f"{split}_rir.hdf5") |
| print(f" {dfn_script} --sr {args.sr} rir {txt_path} {hdf5_path}") |
| print() |
|
|
| print("HDF5 μμ± ν HF Hubμ μ
λ‘λ:") |
| print(f" huggingface-cli upload YOUR_USERNAME/wind-noise-data {args.output_dir} --repo-type dataset") |
| print() |
| print("μλ£!") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|