如何使用 python 将噪声文件作为 .wav 音频从 .wav 音频文件中去除噪声

问题描述 投票:0回答:2

如上所述,我想创建一个程序来消除音频剪辑中的噪音,因为我将噪音作为单独的音频文件。我知道它与 FFT 有关,因此我将其应用于音频文件和噪声文件。这些是我得到的 音频文件: Audio file 噪音文件: Noise file 减去每个频率的振幅然后进行反向 FFT 之类的东西是否可行?如果可以,我将如何对其进行编程?

这是我所拥有的:

from __future__ import print_function
import scipy.io.wavfile as wavfile
import scipy
import scipy.fftpack
import numpy as np
from matplotlib import pyplot as plt

fs_rate, signal = wavfile.read("sound.wav")
print ("Frequency sampling", fs_rate)
l_audio = len(signal.shape)
print ("Channels", l_audio)
if l_audio == 2:
    signal = signal.sum(axis=1) / 2
N = signal.shape[0]
print ("Complete Samplings N", N)
secs = N / float(fs_rate)
print ("secs", secs)
Ts = 1.0/fs_rate # sampling interval in time
print ("Timestep between samples Ts", Ts)
t = scipy.arange(0, secs, Ts) # time vector as scipy arange field / numpy.ndarray
FFT = abs(scipy.fft.fft(signal))
FFT_side = FFT[range(N//2)] # one side FFT range
freqs = scipy.fftpack.fftfreq(signal.size, t[1]-t[0])
fft_freqs = np.array(freqs)
freqs_side = freqs[range(N//2)] # one side frequency range
fft_freqs_side = np.array(freqs_side)
plt.subplot(311)
p1 = plt.plot(t, signal, "g") # plotting the signal
plt.xlabel('Time')
plt.ylabel('Amplitude')
plt.subplot(312)
p2 = plt.plot(freqs, FFT, "r") # plotting the complete fft spectrum
plt.xlabel('Frequency (Hz)')
plt.ylabel('Count dbl-sided')
plt.subplot(313)
p3 = plt.plot(freqs_side, abs(FFT_side), "b") # plotting the positive fft spectrum
plt.xlabel('Frequency (Hz)')
plt.ylabel('Count single-sided')
plt.show()
from __future__ import print_function
import scipy.io.wavfile as wavfile
import scipy
import scipy.fftpack
import numpy as np
from matplotlib import pyplot as plt

fs_rate, signal = wavfile.read("noise.wav")
fs_rate, signal2 = wavfile.read("noise.wav")
print ("Frequency sampling", fs_rate)
l_audio = len(signal.shape)
print ("Channels", l_audio)
if l_audio == 2:
    signal = signal.sum(axis=1) / 2
N = signal.shape[0]
print ("Complete Samplings N", N)
secs = N / float(fs_rate)
print ("secs", secs)
Ts = 1.0/fs_rate # sampling interval in time
print ("Timestep between samples Ts", Ts)
t = scipy.arange(0, secs, Ts) # time vector as scipy arange field / numpy.ndarray
FFT = abs(scipy.fft.fft(signal))
FFT_side = FFT[range(N//2)] # one side FFT range
freqs = scipy.fftpack.fftfreq(signal.size, t[1]-t[0])
fft_freqs = np.array(freqs)
freqs_side = freqs[range(N//2)] # one side frequency range
fft_freqs_side = np.array(freqs_side)
plt.subplot(311)
p1 = plt.plot(t, signal, "g") # plotting the signal
plt.xlabel('Time')
plt.ylabel('Amplitude')
plt.subplot(312)
p2 = plt.plot(freqs, FFT, "r") # plotting the complete fft spectrum
plt.xlabel('Frequency (Hz)')
plt.ylabel('Count dbl-sided')
plt.subplot(313)
p3 = plt.plot(freqs_side, abs(FFT_side), "b") # plotting the positive fft spectrum
plt.xlabel('Frequency (Hz)')
plt.ylabel('Count single-sided')
plt.show()

编辑:

import numpy as np 
import matplotlib.pyplot as plt
from scipy import signal as sig
rng = np.random.default_rng()
fs, x = wavfile.read("sound.wav")
f, t, Zxx = sig.stft(x, fs, nperseg=10)

## Take STFT of noise
f_n, noise = wavfile.read("noise.wav")
noise = noise[:len(x)]
f_n, t_n, Zxx_n = sig.stft(noise, fs, nperseg=10)

## Subtract noise from noisy signal and perform inverse STFT
Zxx_denoise = Zxx - Zxx_n
_, denoised_signal = sig.istft(Zxx_denoise, fs)
fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(6,3))
ax[0].pcolormesh(t, f, np.abs(Zxx), vmin=0, vmax=1, shading='gouraud')
ax[0].set_title('Noisy signal')
ax[0].set_ylabel('Frequency [Hz]')
ax[0].set_xlabel('Time [sec]')

ax[1].pcolormesh(t, f, np.abs(Zxx_denoise), vmin=0, vmax=1, shading='gouraud')
ax[1].set_title('Denoised signal')
ax[1].set_ylabel('Frequency [Hz]')
ax[1].set_xlabel('Time [sec]')

错误:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[30], line 18
     16 _, denoised_signal = sig.istft(Zxx_denoise, fs)
     17 fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(6,3))
---> 18 ax[0].pcolormesh(t, f, np.abs(Zxx).T, vmin=0, vmax=1, shading='gouraud')
     19 ax[0].set_title('Noisy signal')
     20 ax[0].set_ylabel('Frequency [Hz]')

File c:\Users\User\AppData\Local\Programs\Python\Python39\lib\site-packages\matplotlib\__init__.py:1433, in _preprocess_data..inner(ax, data, *args, **kwargs)
   1430 @functools.wraps(func)
   1431 def inner(ax, *args, data=None, **kwargs):
   1432     if data is None:
-> 1433         return func(ax, *map(sanitize_sequence, args), **kwargs)
   1435     bound = new_sig.bind(ax, *args, **kwargs)
   1436     auto_label = (bound.arguments.get(label_namer)
   1437                   or bound.kwargs.get(label_namer))

File c:\Users\User\AppData\Local\Programs\Python\Python39\lib\site-packages\matplotlib\axes\_axes.py:6169, in Axes.pcolormesh(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)
   6166 shading = shading.lower()
   6167 kwargs.setdefault('edgecolors', 'none')
-> 6169 X, Y, C, shading = self._pcolorargs('pcolormesh', *args,
   6170                                     shading=shading, kwargs=kwargs)
   6171 coords = np.stack([X, Y], axis=-1)
   6172 # convert to one dimensional array
...
   5673 else:
   5674     raise TypeError(f'{funcname}() takes 1 or 3 positional arguments '
   5675                     f'but {len(args)} were given')

ValueError: too many values to unpack (expected 2)

更多编辑: 仅减去 FFT 的结果非常令人印象深刻: Result 这个信号似乎很明确。然而,将其转换回 .wav 后,声音比原始声音受到更多干扰。

python fft noise-reduction
2个回答
2
投票

由于信号和噪声是非平稳的,简单的 FFT 无法解决问题。您可以尝试使用 SciPy 中的短时傅立叶变换函数,这里是一个示例,只需修改其 docs 中的示例即可。获取噪声信号和噪声的 STFT,然后从频域中的噪声信号中减去噪声,然后执行逆 STFT 以获得降噪信号。如果这没有给您想要的结果,您可能需要查看稍微复杂一些的解决方案,例如this

import numpy as np 
import matplotlib.pyplot as plt
from scipy import signal
rng = np.random.default_rng()

## Generate test signal per SciPy docs
fs = 10e3
N = 1e5
amp = 2 * np.sqrt(2)
noise_power = 0.01 * fs / 2
time = np.arange(N) / float(fs)
mod = 500*np.cos(2*np.pi*0.25*time)
carrier = amp * np.sin(2*np.pi*3e3*time + mod)
noise = rng.normal(scale=np.sqrt(noise_power),
                   size=time.shape)
noise *= np.exp(-time/5)
x = carrier + noise

## Take STFT of noisy signal
f, t, Zxx = signal.stft(x, fs, nperseg=1000)

## Take STFT of noise
f_n, t_n, Zxx_n = signal.stft(noise, fs, nperseg=1000)

## Subtract noise from noisy signal and perform inverse STFT
Zxx_denoise = Zxx - Zxx_n
_, denoised_signal = signal.istft(Zxx_denoise, fs)


fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(6,3))
ax[0].pcolormesh(t, f, np.abs(Zxx), vmin=0, vmax=1, shading='gouraud')
ax[0].set_title('Noisy signal')
ax[0].set_ylabel('Frequency [Hz]')
ax[0].set_xlabel('Time [sec]')

ax[1].pcolormesh(t, f, np.abs(Zxx_denoise), vmin=0, vmax=1, shading='gouraud')
ax[1].set_title('Denoised signal')
ax[1].set_ylabel('Frequency [Hz]')
ax[1].set_xlabel('Time [sec]')

plt.tight_layout()
plt.show()

产品:


0
投票

根据您的脚本和后续问题,您似乎在使用 FFT 进行降噪方面走在正确的轨道上,但在正确应用和反转转换方面面临挑战。在应用逆 FFT 之前从音频 FFT 中减去噪声 FFT 的方法在概念上是合理的。然而,在执行操作之前确保音频和噪声信号的长度匹配至关重要。另外,当你进行减法时,你必须小心相位信息。

这是一个修改后的方法,重点关注您的最初想法并解决 STFT 应用程序中的错误:

确保长度匹配:在FFT运算之前,确保音频和噪声信号的长度相同。如果不是,您可能需要适当修剪或填充信号。

应用 FFT 并相减:对两个信号应用 FFT 后,从音频 FFT 中减去噪声 FFT。此操作应针对 FFT 的幅度执行,但您还必须考虑相位信息以准确重建信号。

逆 FFT:相减后,对结果信号应用逆 FFT,将其转换回时域。

STFT 错误和可视化:代码中的错误似乎与绘图时的错误尺寸有关。确保 pcolormesh 的 t、f 和 Zxx 尺寸正确对齐。如果 pcolormesh 需要特定顺序的尺寸,则需要像在错误代码段中那样将 Zxx 与 .T 互换。

第三方工具:您提到使用第三方工具进行降噪。这些可能会很有帮助,特别是如果您正在寻找更用户友好的界面或可能无法从头开始轻松实现的高级算法。每个工具都有其优点和缺点,通常以不同的方式平衡有效性、易用性和数据隐私。

根据上下文,这里有一个简化的更正和添加,以解决您的错误并增强您的脚本:

import numpy as np
import scipy.io.wavfile as wavfile
import matplotlib.pyplot as plt
from scipy import signal as sig

# Read the noisy audio and noise files
fs, noisy_signal = wavfile.read("sound.wav")
_, noise_signal = wavfile.read("noise.wav")

# Ensure noise_signal is not longer than noisy_signal
if len(noise_signal) > len(noisy_signal):
    noise_signal = noise_signal[:len(noisy_signal)]
else:
    # Pad noise_signal to match length of noisy_signal if necessary
    noise_signal = np.pad(noise_signal, (0, len(noisy_signal) - len(noise_signal)), 'constant')

# Perform FFT on both signals
fft_noisy_signal = np.fft.fft(noisy_signal)
fft_noise_signal = np.fft.fft(noise_signal)

# Subtract the FFT of the noise from the FFT of the noisy signal
fft_denoised_signal = fft_noisy_signal - fft_noise_signal

# Perform Inverse FFT to get back to the time domain
denoised_signal = np.fft.ifft(fft_denoised_signal)

# Convert the complex result to real by taking the absolute value
denoised_signal = np.abs(denoised_signal)

# Saving the denoised signal back as a WAV file
wavfile.write("denoised_sound.wav", fs, denoised_signal.astype(np.int16))

# Plotting (optional)
plt.figure(figsize=(10, 6))
plt.subplot(3, 1, 1)
plt.title("Original Noisy Signal")
plt.plot(noisy_signal)
plt.subplot(3, 1, 2)
plt.title("Noise Signal")
plt.plot(noise_signal)
plt.subplot(3, 1, 3)
plt.title("Denoised Signal")
plt.plot(denoised_signal)
plt.tight_layout()
plt.show()

此脚本读取噪声音频和噪声文件,确保它们的长度匹配,对两者应用 FFT,从噪声音频 FFT 中减去噪声 FFT,执行逆 FFT,最后保存并可选择绘制去噪信号。请注意,降噪的实际效果取决于噪声和音频信号的特性。

第三方工具推荐。 在考虑第三方工具时,必须平衡易用性、有效性、隐私问题和音频处理任务的特定需求。以下是提到的每个工具如何适应上下文:

Adobe增强:对于改善音频质量很有用,尽管它有时可能会改变声音的自然度。当可以接受较小的人为效果时,这是快速增强的不错选择。

Xound.io:我的最爱。因其消除背景噪音和回声的能力而脱颖而出,同时优先考虑数据隐私,使其成为敏感音频和视频文件的可靠选择。

NoiseReducer:为降低背景噪音提供简单的解决方案。虽然它在一定程度上有效,但对云处理的依赖引发了隐私方面的考虑。

Veed.io:功能与 NoiseReducer 类似,但包含可能有助于更广泛的音频和视频编辑任务的额外功能。基于云的处理意味着数据离开您的本地环境。

Podcastle.ai:专为播客制作而定制,它可能无法满足所有噪音消除需求,但对于特定的播客相关音频增强可能很有用。

© www.soinside.com 2019 - 2024. All rights reserved.