commit 7a7e9205ac4b521783d42e445dbb657cf09eb9ce Author: SlavaVlad Date: Sat Jun 27 07:11:43 2026 +0300 Добавить отчёт (tex+pdf) и ipynb с анализом восстановления аудио DeepFilterNet3 diff --git a/analysis.ipynb b/analysis.ipynb new file mode 100644 index 0000000..60f7ea2 --- /dev/null +++ b/analysis.ipynb @@ -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 +} diff --git a/report.pdf b/report.pdf new file mode 100644 index 0000000..6fbcf3d Binary files /dev/null and b/report.pdf differ diff --git a/report.tex b/report.tex new file mode 100644 index 0000000..557e29b --- /dev/null +++ b/report.tex @@ -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}