Python双重去除噪音方法事例
针对这一篇和上一篇内容,如果您需要特殊要求,请按情况自行调整参数,谢谢您的观看和支持。
import numpy as np
import scipy.io.wavfile as wav
from scipy import signal
import matplotlib.pyplot as plt
import simpleaudio as sa
import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import threading
class AudioDenoiser:
def __init__(self, root):
self.root = root
self.root.title("音频去噪工具")
self.root.geometry("600x500")
self.root.resizable(False, False)
# 变量
self.input_file = ""
self.output_file = ""
self.audio_data = None
self.sample_rate = None
self.setup_ui()
def setup_ui(self):
# 标题
title = tk.Label(self.root, text="智能音频去噪工具", font=("Arial", 18, "bold"))
title.pack(pady=20)
# 文件选择框架
file_frame = tk.Frame(self.root)
file_frame.pack(pady=10)
tk.Label(file_frame, text="输入文件:", font=("Arial", 12)).grid(row=0, column=0, padx=5)
self.input_label = tk.Label(file_frame, text="未选择", width=40, bg="white", relief="sunken")
self.input_label.grid(row=0, column=1, padx=5)
tk.Button(file_frame, text="浏览", command=self.select_input).grid(row=0, column=2, padx=5)
# 输出文件
output_frame = tk.Frame(self.root)
output_frame.pack(pady=10)
tk.Label(output_frame, text="输出文件:", font=("Arial", 12)).grid(row=0, column=0, padx=5)
self.output_label = tk.Label(output_frame, text="未选择", width=40, bg="white", relief="sunken")
self.output_label.grid(row=0, column=1, padx=5)
tk.Button(output_frame, text="浏览", command=self.select_output).grid(row=0, column=2, padx=5)
# 参数设置
param_frame = tk.LabelFrame(self.root, text="去噪参数", font=("Arial", 12))
param_frame.pack(pady=15, padx=20, fill="x")
# 去噪方法
tk.Label(param_frame, text="去噪方法:", font=("Arial", 11)).grid(row=0, column=0, padx=10, pady=5, sticky="w")
self.method_var = tk.StringVar(value="spectral_subtraction")
methods = [("谱减法", "spectral_subtraction"), ("维纳滤波", "wiener_filter")]
for i, (text, value) in enumerate(methods):
tk.Radiobutton(param_frame, text=text, variable=self.method_var,
value=value, font=("Arial", 10)).grid(row=0, column=i+1, padx=10, pady=5)
# 噪声估计时间
tk.Label(param_frame, text="噪声估计时长(秒):", font=("Arial", 11)).grid(row=1, column=0, padx=10, pady=5, sticky="w")
self.noise_duration = tk.Scale(param_frame, from_=0.1, to=2.0, resolution=0.1,
orient=tk.HORIZONTAL, length=200)
self.noise_duration.set(0.5)
self.noise_duration.grid(row=1, column=1, columnspan=2, padx=10, pady=5)
# 去噪强度
tk.Label(param_frame, text="去噪强度:", font=("Arial", 11)).grid(row=2, column=0, padx=10, pady=5, sticky="w")
self.strength = tk.Scale(param_frame, from_=0.5, to=3.0, resolution=0.1,
orient=tk.HORIZONTAL, length=200)
self.strength.set(1.0)
self.strength.grid(row=2, column=1, columnspan=2, padx=10, pady=5)
# 按钮
btn_frame = tk.Frame(self.root)
btn_frame.pack(pady=20)
self.process_btn = tk.Button(btn_frame, text="开始去噪", font=("Arial", 12),
bg="#4CAF50", fg="white", width=15,
command=self.process_audio)
self.process_btn.pack(side=tk.LEFT, padx=10)
self.play_btn = tk.Button(btn_frame, text="播放原音频", font=("Arial", 12),
bg="#2196F3", fg="white", width=15,
command=self.play_original, state="disabled")
self.play_btn.pack(side=tk.LEFT, padx=10)
self.play_denoised_btn = tk.Button(btn_frame, text="播放去噪音频", font=("Arial", 12),
bg="#FF9800", fg="white", width=15,
command=self.play_denoised, state="disabled")
self.play_denoised_btn.pack(side=tk.LEFT, padx=10)
# 状态栏
self.status_label = tk.Label(self.root, text="就绪", font=("Arial", 10), relief="sunken", anchor="w")
self.status_label.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=10)
# 进度条
self.progress = ttk.Progressbar(self.root, length=400, mode='indeterminate')
self.progress.pack(pady=10)
self.denoised_data = None
def select_input(self):
file_path = filedialog.askopenfilename(
title="选择音频文件",
filetypes=[("音频文件", "*.wav *.mp3 *.flac"), ("所有文件", "*.*")]
)
if file_path:
self.input_file = file_path
self.input_label.config(text=os.path.basename(file_path))
self.load_audio()
def select_output(self):
file_path = filedialog.asksaveasfilename(
title="保存去噪音频",
defaultextension=".wav",
filetypes=[("WAV文件", "*.wav"), ("所有文件", "*.*")]
)
if file_path:
self.output_file = file_path
self.output_label.config(text=os.path.basename(file_path))
def load_audio(self):
try:
self.sample_rate, self.audio_data = wav.read(self.input_file)
if len(self.audio_data.shape) > 1:
self.audio_data = self.audio_data[:, 0] # 转为单声道
self.status_label.config(text=f"音频已加载:{len(self.audio_data)/self.sample_rate:.1f}秒")
self.play_btn.config(state="normal")
except Exception as e:
messagebox.showerror("错误", f"加载音频失败:{str(e)}")
def spectral_subtraction(self, audio, sample_rate, noise_duration=0.5, strength=1.0):
"""谱减法去噪"""
# 估计噪声
noise_samples = int(noise_duration * sample_rate)
if len(audio) < noise_samples:
noise_samples = len(audio) // 4
noise = audio[:noise_samples]
# STFT
n_fft = 2048
hop_length = 512
f, t, Zxx = signal.stft(audio, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)
f_noise, t_noise, Zxx_noise = signal.stft(noise, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)
# 计算噪声功率谱
noise_power = np.mean(np.abs(Zxx_noise)**2, axis=1, keepdims=True)
# 谱减法
magnitude = np.abs(Zxx)
phase = np.angle(Zxx)
# 减去噪声
magnitude_clean = magnitude - strength * np.sqrt(noise_power)
magnitude_clean = np.maximum(magnitude_clean, 0) # 确保非负
# 重建信号
Zxx_clean = magnitude_clean * np.exp(1j * phase)
_, audio_clean = signal.istft(Zxx_clean, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)
return audio_clean
def wiener_filter(self, audio, sample_rate, noise_duration=0.5, strength=1.0):
"""维纳滤波去噪"""
# 估计噪声
noise_samples = int(noise_duration * sample_rate)
if len(audio) < noise_samples:
noise_samples = len(audio) // 4
noise = audio[:noise_samples]
# STFT
n_fft = 2048
hop_length = 512
f, t, Zxx = signal.stft(audio, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)
f_noise, t_noise, Zxx_noise = signal.stft(noise, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)
# 计算功率谱
signal_power = np.abs(Zxx)**2
noise_power = np.mean(np.abs(Zxx_noise)**2, axis=1, keepdims=True)
# 维纳滤波
# H = signal_power / (signal_power + noise_power)
wiener_gain = signal_power / (signal_power + strength * noise_power)
wiener_gain = np.clip(wiener_gain, 0, 1)
# 应用增益
Zxx_clean = Zxx * wiener_gain
# 重建信号
_, audio_clean = signal.istft(Zxx_clean, fs=sample_rate, nperseg=n_fft, noverlap=n_fft-hop_length)
return audio_clean
def process_audio(self):
if self.input_file == "":
messagebox.showwarning("警告", "请先选择输入音频文件")
return
if self.output_file == "":
# 自动生成输出文件名
base_name = os.path.splitext(os.path.basename(self.input_file))[0]
output_dir = os.path.dirname(self.input_file)
self.output_file = os.path.join(output_dir, f"{base_name}_denoised.wav")
self.output_label.config(text=os.path.basename(self.output_file))
# 在后台线程处理
self.process_btn.config(state="disabled")
self.progress.start()
self.status_label.config(text="正在处理音频...")
thread = threading.Thread(target=self._process_thread)
thread.daemon = True
thread.start()
def _process_thread(self):
try:
method = self.method_var.get()
noise_dur = self.noise_duration.get()
strength = self.strength.get()
# 归一化音频
audio = self.audio_data.astype(np.float32)
if np.max(np.abs(audio)) > 0:
audio = audio / np.max(np.abs(audio))
# 应用选定的去噪方法
if method == "spectral_subtraction":
self.denoised_data = self.spectral_subtraction(audio, self.sample_rate, noise_dur, strength)
else: # wiener_filter
self.denoised_data = self.wiener_filter(audio, self.sample_rate, noise_dur, strength)
# 确保数据在有效范围内
self.denoised_data = np.clip(self.denoised_data, -1, 1)
# 保存去噪音频
denoised_int16 = (self.denoised_data * 32767).astype(np.int16)
wav.write(self.output_file, self.sample_rate, denoised_int16)
self.root.after(0, self._process_complete)
except Exception as e:
self.root.after(0, lambda: self._process_error(str(e)))
def _process_complete(self):
self.progress.stop()
self.process_btn.config(state="normal")
self.play_denoised_btn.config(state="normal")
self.status_label.config(text=f"去噪完成!保存至:{os.path.basename(self.output_file)}")
messagebox.showinfo("成功", f"音频去噪完成!\n保存位置:{self.output_file}")
def _process_error(self, error_msg):
self.progress.stop()
self.process_btn.config(state="normal")
self.status_label.config(text="处理失败")
messagebox.showerror("错误", f"处理失败:{error_msg}")
def play_original(self):
if self.audio_data is not None:
audio_int16 = self.audio_data.astype(np.int16)
play_obj = sa.play_buffer(audio_int16, 1, 2, self.sample_rate)
def play_denoised(self):
if self.denoised_data is not None:
denoised_int16 = (self.denoised_data * 32767).astype(np.int16)
play_obj = sa.play_buffer(denoised_int16, 1, 2, self.sample_rate)
def main():
root = tk.Tk()
app = AudioDenoiser(root)
root.mainloop()
if __name__ == "__main__":
main()
求三连求三连求三连呀。
认可我的人请点赞关注评论收藏。
有可能有需要的请及时下载保存。
如果不喜欢我内容的,评论区一位,请说出你的不满。
更多推荐
所有评论(0)