Spaces:
Running on Zero
Running on Zero
Download dnsmos.py from Azuredraco/audio-denoise: direct link, hf CLI and curl.
- Browser
- Download file 3.16 kB
-
https://huggingface.co/spaces/Azuredraco/audio-denoise/resolve/main/dnsmos.py
- Command line
-
hf download hf://spaces/Azuredraco/audio-denoise/dnsmos.py
-
curl -L -o dnsmos.py https://huggingface.co/spaces/Azuredraco/audio-denoise/resolve/main/dnsmos.py
3.16 kB
| """ | |
| dnsmos.py — Calidad percibida de la voz con DNSMOS P.835 (Microsoft). | |
| Estima en una escala 1–5 lo que puntuarían oyentes humanos: | |
| SIG = calidad de la voz (¿suena natural, sin distorsión?) | |
| BAK = fondo (¿se oye ruido?) | |
| OVRL = valoración global | |
| Trabaja a 16 kHz: evalúa la voz, no los agudos extremos. | |
| Para que sea rápido en CPUs modestas se evalúan como mucho MAX_WINDOWS ventanas de 9 s | |
| repartidas por el audio (la media se estabiliza con pocas). | |
| Basado en DNSMOS/dnsmos_local.py del repositorio microsoft/DNS-Challenge. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import threading | |
| import numpy as np | |
| from models import dnsmos_paths | |
| SR = 16000 | |
| WIN_SEC = 9.01 | |
| MAX_WINDOWS = 10 | |
| _lock = threading.Lock() | |
| _sessions = None | |
| def _load(): | |
| global _sessions | |
| with _lock: | |
| if _sessions is None: | |
| import onnxruntime as ort | |
| paths = dnsmos_paths() | |
| opts = ort.SessionOptions() | |
| opts.log_severity_level = 3 | |
| # En contenedores, onnxruntime puede lanzar más hilos que núcleos reales y ralentizarse. | |
| opts.intra_op_num_threads = max(1, min(4, os.cpu_count() or 1)) | |
| opts.inter_op_num_threads = 1 | |
| _sessions = ort.InferenceSession(str(paths["sig_bak_ovr.onnx"]), opts, providers=["CPUExecutionProvider"]) | |
| return _sessions | |
| def available() -> bool: | |
| try: | |
| _load() | |
| return True | |
| except Exception as e: | |
| print(f"[DNSMOS] No disponible: {e}") | |
| return False | |
| def _polyfit(sig, bak, ovr): | |
| p_ovr = np.poly1d([-0.06766283, 1.11546468, 0.04602535]) | |
| p_sig = np.poly1d([-0.08397278, 1.22083953, 0.0052439]) | |
| p_bak = np.poly1d([-0.13166888, 1.60915514, -0.39604546]) | |
| return p_sig(sig), p_bak(bak), p_ovr(ovr) | |
| def score(mono: np.ndarray, sr: int) -> dict[str, float] | None: | |
| """Devuelve {'SIG','BAK','OVRL'} o None si el modelo no está disponible.""" | |
| try: | |
| sess = _load() | |
| except Exception as e: | |
| print(f"[DNSMOS] No disponible: {e}") | |
| return None | |
| import soxr | |
| audio = soxr.resample(np.asarray(mono, dtype=np.float32), sr, SR, quality="HQ") if sr != SR else mono | |
| audio = np.asarray(audio, dtype=np.float32) | |
| if not np.any(audio): | |
| return None | |
| win = int(WIN_SEC * SR) | |
| while len(audio) < win: | |
| audio = np.concatenate([audio, audio]) | |
| n_hops = int(np.floor(len(audio) / SR) - WIN_SEC) + 1 | |
| starts = np.arange(n_hops) * SR | |
| if len(starts) > MAX_WINDOWS: | |
| starts = starts[np.linspace(0, len(starts) - 1, MAX_WINDOWS).round().astype(int)] | |
| rows = [] | |
| for s in starts: | |
| seg = audio[int(s) : int(s) + win] | |
| if len(seg) < win: | |
| continue | |
| sig, bak, ovr = sess.run(None, {"input_1": seg[None, :]})[0][0] | |
| rows.append(_polyfit(sig, bak, ovr)) | |
| if not rows: | |
| return None | |
| m = np.mean(rows, axis=0) | |
| return {"SIG": float(m[0]), "BAK": float(m[1]), "OVRL": float(m[2])} | |
| def label(ovrl: float) -> str: | |
| if ovrl >= 3.6: | |
| return "excelente" | |
| if ovrl >= 3.2: | |
| return "buena" | |
| if ovrl >= 2.7: | |
| return "aceptable" | |
| return "mejorable" | |