Добавить отчёт (tex+pdf) и ipynb с анализом восстановления аудио DeepFilterNet3
This commit is contained in:
+262
@@ -0,0 +1,262 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Сравнение эффективности восстановления звука: DeepFilterNet3\n",
|
||||
"\n",
|
||||
"Сравнение оригинального, зашумлённого и восстановленного (DeepFilterNet3) аудиосигналов."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"from scipy.io import wavfile\n",
|
||||
"from scipy import signal\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import pandas as pd\n",
|
||||
"import librosa\n",
|
||||
"import librosa.display\n",
|
||||
"from IPython.display import Audio, display\n",
|
||||
"\n",
|
||||
"plt.rcParams['figure.dpi'] = 120\n",
|
||||
"plt.rcParams['figure.figsize'] = (14, 4)"
|
||||
],
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"sr, orig = wavfile.read('VoiceA.wav')\n",
|
||||
"_, noisy = wavfile.read('VoiceA_s1-3_Mix.wav')\n",
|
||||
"_, rest = wavfile.read('VoiceA_s1-3_Mix_Restored.wav')\n",
|
||||
"\n",
|
||||
"def to_mono(x):\n",
|
||||
" return x.mean(axis=1).astype(np.float64) if x.ndim > 1 else x.astype(np.float64)\n",
|
||||
"\n",
|
||||
"orig_m = to_mono(orig)\n",
|
||||
"noisy_m = to_mono(noisy)\n",
|
||||
"rest_m = to_mono(rest)"
|
||||
],
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Прослушивание"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"print('Оригинал:')\n",
|
||||
"display(Audio(orig_m, rate=sr))\n",
|
||||
"print('Зашумлённый:')\n",
|
||||
"display(Audio(noisy_m, rate=sr))\n",
|
||||
"print('Восстановленный:')\n",
|
||||
"display(Audio(rest_m, rate=sr))"
|
||||
],
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Метрики качества"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"def snr(clean, proc):\n",
|
||||
" return 10*np.log10(np.sum(clean**2)/(np.sum((clean-proc)**2)+1e-10))\n",
|
||||
"\n",
|
||||
"def seg_snr(clean, proc, sr, seg_ms=30):\n",
|
||||
" seg_len = int(sr*seg_ms/1000)\n",
|
||||
" n = len(clean)//seg_len\n",
|
||||
" vals = []\n",
|
||||
" for i in range(n):\n",
|
||||
" s = clean[i*seg_len:(i+1)*seg_len]\n",
|
||||
" p = proc[i*seg_len:(i+1)*seg_len]\n",
|
||||
" v = 10*np.log10((np.sum(s**2)+1e-10)/(np.sum((s-p)**2)+1e-10))\n",
|
||||
" vals.append(np.clip(v, -10, 35))\n",
|
||||
" return np.mean(vals)\n",
|
||||
"\n",
|
||||
"def lsd(clean, proc, sr, n_fft=512):\n",
|
||||
" _, _, Sx_c = signal.spectrogram(clean, sr, nperseg=n_fft)\n",
|
||||
" _, _, Sx_p = signal.spectrogram(proc, sr, nperseg=n_fft)\n",
|
||||
" Sx_c = np.abs(Sx_c)/np.max(np.abs(Sx_c))+1e-10\n",
|
||||
" Sx_p = np.abs(Sx_p)/np.max(np.abs(Sx_p))+1e-10\n",
|
||||
" return np.sqrt(np.mean((10*np.log10(Sx_c**2/Sx_p**2))**2))\n",
|
||||
"\n",
|
||||
"def stoi_score(clean, proc, sr):\n",
|
||||
" try:\n",
|
||||
" import pystoi\n",
|
||||
" return pystoi.stoi(clean/np.max(np.abs(clean)), proc/np.max(np.abs(proc)), sr)\n",
|
||||
" except ImportError:\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
"metrics = {\n",
|
||||
" 'SNR (дБ)': [snr(orig_m, noisy_m), snr(orig_m, rest_m)],\n",
|
||||
" 'Segmental SNR (дБ)': [seg_snr(orig_m, noisy_m, sr), seg_snr(orig_m, rest_m, sr)],\n",
|
||||
" 'LSD (дБ)': [lsd(orig_m, noisy_m, sr), lsd(orig_m, rest_m, sr)],\n",
|
||||
" 'Корреляция': [np.corrcoef(orig_m, noisy_m)[0,1], np.corrcoef(orig_m, rest_m)[0,1]],\n",
|
||||
" 'RMSE': [np.sqrt(np.mean((orig_m-noisy_m)**2)), np.sqrt(np.mean((orig_m-rest_m)**2))],\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"df = pd.DataFrame(metrics, index=['Noisy', 'Restored']).T\n",
|
||||
"df.columns = ['Шумный', 'Восстановленный']\n",
|
||||
"df['Улучшение'] = df['Восстановленный'] - df['Шумный']\n",
|
||||
"df"
|
||||
],
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## STOI (при наличии pystoi)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"stoi_n = stoi_score(orig_m, noisy_m, sr)\n",
|
||||
"stoi_r = stoi_score(orig_m, rest_m, sr)\n",
|
||||
"if stoi_n is not None:\n",
|
||||
" print(f'STOI Noisy: {stoi_n:.4f}')\n",
|
||||
" print(f'STOI Restored: {stoi_r:.4f}')\n",
|
||||
" print(f'Улучшение: {stoi_r-stoi_n:+.4f}')\n",
|
||||
"else:\n",
|
||||
" print('pystoi не установлен — пропускаем')"
|
||||
],
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Спектрограммы"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"fig, axes = plt.subplots(1, 3, figsize=(16, 4))\n",
|
||||
"for ax, data, title in zip(axes, [orig_m, noisy_m, rest_m], ['Original', 'Noisy', 'Restored']):\n",
|
||||
" D = librosa.amplitude_to_db(np.abs(librosa.stft(data)), ref=np.max)\n",
|
||||
" librosa.display.specshow(D, sr=sr, x_axis='time', y_axis='hz', ax=ax, cmap='magma')\n",
|
||||
" ax.set_title(title)\n",
|
||||
" ax.set_ylim([0, 8000])\n",
|
||||
"plt.tight_layout()\n",
|
||||
"plt.show()"
|
||||
],
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Спектральное сравнение (усреднённый спектр)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"fig, ax = plt.subplots(figsize=(12, 4))\n",
|
||||
"for data, label, color in zip([orig_m, noisy_m, rest_m], ['Original', 'Noisy', 'Restored'], ['C0', 'C1', 'C2']):\n",
|
||||
" spec = np.abs(librosa.stft(data))\n",
|
||||
" mean_spec = np.mean(spec, axis=1)\n",
|
||||
" freqs = librosa.fft_frequencies(sr=sr)\n",
|
||||
" ax.semilogy(freqs, mean_spec, color=color, label=label, alpha=0.8)\n",
|
||||
"ax.set_xlim([0, 8000])\n",
|
||||
"ax.set_xlabel('Frequency (Hz)')\n",
|
||||
"ax.set_ylabel('Magnitude')\n",
|
||||
"ax.legend()\n",
|
||||
"ax.grid(alpha=0.3)\n",
|
||||
"plt.tight_layout()\n",
|
||||
"plt.show()"
|
||||
],
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## LSD(t): Логарифмическое спектральное расстояние во времени"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"def lsd_t(clean, proc, sr, n_fft=512):\n",
|
||||
" _, t, Sx_c = signal.spectrogram(clean, sr, nperseg=n_fft)\n",
|
||||
" _, _, Sx_p = signal.spectrogram(proc, sr, nperseg=n_fft)\n",
|
||||
" Sx_c = np.abs(Sx_c)/np.max(np.abs(Sx_c))+1e-10\n",
|
||||
" Sx_p = np.abs(Sx_p)/np.max(np.abs(Sx_p))+1e-10\n",
|
||||
" lsd_f = np.sqrt(np.mean((10*np.log10(Sx_c**2/Sx_p**2))**2, axis=0))\n",
|
||||
" return t, lsd_f\n",
|
||||
"\n",
|
||||
"t, lsd_n = lsd_t(orig_m, noisy_m, sr)\n",
|
||||
"_, lsd_r = lsd_t(orig_m, rest_m, sr)\n",
|
||||
"\n",
|
||||
"fig, ax = plt.subplots(figsize=(12, 3))\n",
|
||||
"ax.plot(t, lsd_n, label=f'Noisy (mean={np.mean(lsd_n):.1f} dB)', alpha=0.7, lw=0.8)\n",
|
||||
"ax.plot(t, lsd_r, label=f'Restored (mean={np.mean(lsd_r):.1f} dB)', alpha=0.7, lw=0.8)\n",
|
||||
"ax.set_xlabel('Time (s)')\n",
|
||||
"ax.set_ylabel('LSD (dB)')\n",
|
||||
"ax.legend()\n",
|
||||
"ax.grid(alpha=0.3)\n",
|
||||
"plt.tight_layout()\n",
|
||||
"plt.show()"
|
||||
],
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Выводы\n",
|
||||
"\n",
|
||||
"- **SNR**: $0.92 \\rightarrow 2.36$ дБ (+1.44)\n",
|
||||
"- **Segmental SNR**: $-1.45 \\rightarrow 1.26$ дБ (+2.71)\n",
|
||||
"- **LSD**: $73.78 \\rightarrow 31.34$ дБ (снижение в 2.4 раза)\n",
|
||||
"- **RMSE**: снижение на 15%\n",
|
||||
"\n",
|
||||
"DeepFilterNet3 эффективно подавляет широкополосный шум, особенно в паузах. При исходном SNR~1 дБ полное восстановление невозможно, но качество сигнала заметно улучшается."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.10.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
BIN
Binary file not shown.
+109
@@ -0,0 +1,109 @@
|
||||
\documentclass[12pt,a4paper]{article}
|
||||
\usepackage{fontspec}
|
||||
\usepackage{polyglossia}
|
||||
\setmainlanguage[spelling=modern]{russian}
|
||||
\setotherlanguage{english}
|
||||
\setmainfont{Times New Roman}
|
||||
\setmonfont{Courier New}
|
||||
|
||||
\usepackage{graphicx}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{booktabs}
|
||||
\usepackage{multirow}
|
||||
\usepackage{geometry}
|
||||
\geometry{top=2cm, bottom=2cm, left=2.5cm, right=2cm}
|
||||
\usepackage{setspace}
|
||||
\onehalfspacing
|
||||
\usepackage{hyperref}
|
||||
\usepackage{float}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\section{Введение}
|
||||
|
||||
Цель работы~--- оценка эффективности метода восстановления звуковых сигналов с использованием нейросетевой модели DeepFilterNet3, интегрированной в среду Audacity через OpenVINO. В качестве исходного материала использована запись голоса (VoiceA), к которой был добавлен шум (SNR~1~dB). Восстановление выполнялось алгоритмом DeepFilterNet3.
|
||||
|
||||
\section{Метрики оценки качества}
|
||||
|
||||
Для количественной оценки использовались следующие метрики:
|
||||
\begin{itemize}
|
||||
\item \textbf{SNR (Signal-to-Noise Ratio)}~--- отношение мощности сигнала к мощности шума;
|
||||
\item \textbf{Segmental SNR}~--- усреднённый SNR по сегментам 30~ms с клиппированием {[}-10, 35{]}~dB;
|
||||
\item \textbf{LSD (Log-Spectral Distance)}~--- среднеквадратичное логарифмическое спектральное расстояние;
|
||||
\item \textbf{Коэффициент корреляции Пирсона} между сигналами;
|
||||
\item \textbf{RMSE (Root Mean Square Error)}~--- среднеквадратичная ошибка;
|
||||
\item \textbf{Фактор шумоподавления}~--- отношение дисперсии шума до и после обработки.
|
||||
\end{itemize}
|
||||
|
||||
\section{Результаты}
|
||||
|
||||
\subsection{Сводная таблица метрик}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\caption{Сравнение метрик для зашумлённого и восстановленного сигналов}
|
||||
\label{tab:metrics}
|
||||
\begin{tabular}{lrrr}
|
||||
\toprule
|
||||
Метрика & Шумный & Восстановленный & Улучшение \\
|
||||
\midrule
|
||||
SNR (dB) & 0.92 & 2.36 & +1.44 \\
|
||||
Segmental SNR (dB)& $-$1.45 & 1.26 & +2.71 \\
|
||||
LSD (dB) & 73.78 & 31.34 & $-$42.44 \\
|
||||
Корреляция & 0.6664 & 0.6633 & $-$0.0031 \\
|
||||
RMSE & 9202 & 7801 & 15.2\% \\
|
||||
Шумоподавление (dB)& --- & --- & +1.44 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\subsection{Анализ результатов}
|
||||
|
||||
DeepFilterNet3 демонстрирует следующие результаты:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{SNR +1.44~dB}: прирост отношения сигнал/шум положительный, однако скромный. Исходный SNR~0.9~dB был крайне низким~--- модель работает в условиях сильного шума.
|
||||
\item \textbf{Segmental SNR +2.71~dB}: посегментный анализ показывает более существенное улучшение на коротких интервалах, что говорит о качественном подавлении шума в паузах.
|
||||
\item \textbf{LSD $-$42.44~dB}: резкое снижение логарифмического спектрального расстояния~--- спектральная форма восстановленного сигнала значительно ближе к оригиналу, чем у шумного.
|
||||
\item \textbf{Корреляция}: практически не изменилась (0.666 против 0.663). Это связано с тем, что шум аддитивный и не разрушает линейную структуру сигнала, а слабое подавление не восстанавливает корреляцию.
|
||||
\item \textbf{RMSE}: снизилась на 15\%~--- амплитудная ошибка уменьшилась.
|
||||
\end{itemize}
|
||||
|
||||
Ключевое наблюдение: модель эффективно подавляет шум в \textit{спектральной} области (LSD падает в 2.4~раза), но вклад шума в амплитудную ошибку остаётся значительным. Это характерно для DeepFilterNet, которая работает в частотной области.
|
||||
|
||||
\subsection{Визуализация}
|
||||
|
||||
На рис.~\ref{fig:analysis} представлен комплексный анализ сигналов.
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=\textwidth]{analysis_plot.png}
|
||||
\caption{Сравнение исходного, зашумлённого и восстановленного сигналов. Строки: (1)~осциллограммы, (2)~спектрограммы, (3)~усреднённые спектры, (4)~спектрограмма шума, ошибки восстановления и LSD(t).}
|
||||
\label{fig:analysis}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Спектральный анализ}
|
||||
|
||||
По спектрограммам (рис.~\ref{fig:analysis}, строка~2) видно:
|
||||
\begin{itemize}
|
||||
\item Шум занимает широкий частотный диапазон (0--8~kHz) и маскирует формантные структуры голоса.
|
||||
\item Восстановленный сигнал имеет более чистую спектральную картину; шумовой фон в паузах подавлен.
|
||||
\item Высокочастотные компоненты (>4~kHz) восстанавливаются хуже~--- часть гармоник теряется.
|
||||
\end{itemize}
|
||||
|
||||
Спектрограммы остаточной ошибки (строка~4, центр) показывают, что наибольшая ошибка восстановления сосредоточена в области 1--3~kHz, где энергия голоса максимальна~--- модель осторожничает и не подавляет шум в этой области, чтобы не исказить речь.
|
||||
|
||||
\section{Заключение}
|
||||
|
||||
DeepFilterNet3 в Audacity (OpenVINO) показал следующие результаты:
|
||||
\begin{itemize}
|
||||
\item Улучшение SNR на +1.44~dB и Segmental SNR на +2.71~dB;
|
||||
\item Значительное снижение спектрального искажения (LSD с 73.8 до 31.3~dB);
|
||||
\item Снижение RMSE на 15\%;
|
||||
\item Корреляция с оригиналом практически не изменилась.
|
||||
\end{itemize}
|
||||
|
||||
Метод эффективен для подавления широкополосного шума, особенно заметен эффект в паузах между речевыми сегментами. При очень низком исходном SNR ($\sim$1~dB) полное восстановление невозможно, однако модель демонстрирует разумный компромисс между подавлением шума и сохранением речевого сигнала. Для лучших результатов рекомендовано использовать более высокое качество входного сигнала (SNR~5--10~dB) или комбинировать DeepFilterNet с предварительной фильтрацией.
|
||||
|
||||
\end{document}
|
||||
Reference in New Issue
Block a user