项目文件夹

文件
2026-07-13 12:35:45 +08:00

244 行
10 KiB
Python

import os.path
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import functools
import argparse
import threading
import time
from pathlib import Path
from mvector.predict import MVectorPredictor
from mvector.utils.utils import add_arguments, print_arguments
parser = argparse.ArgumentParser(description=__doc__)
add_arg = functools.partial(add_arguments, argparser=parser)
add_arg('configs', str, 'configs/cam++.yml', '配置文件')
add_arg('use_gpu', bool, True, '是否使用GPU预测')
add_arg('model_path', str, 'models/CAMPPlus_Fbank/best_model/', '导出的预测模型文件路径')
args = parser.parse_args()
print_arguments(args=args)
class VoiceContrastGUI:
def __init__(self, master):
self.master = master
master.title("夜雨飘零声纹对比系统")
master.geometry('700x670')
master.resizable(True, True)
master.configure(bg='#f0f0f0')
# 使用ttk样式
self.style = ttk.Style()
self.style.theme_use('clam')
# 配置样式
self.style.configure('TButton', font=('微软雅黑', 10), padding=5)
self.style.configure('TLabel', font=('微软雅黑', 10), background='#f0f0f0')
self.style.configure('Header.TLabel', font=('微软雅黑', 14, 'bold'), background='#f0f0f0')
self.style.configure('Result.TLabel', font=('微软雅黑', 16, 'bold'), foreground='#007bff', background='#f0f0f0')
self.style.configure("Green.Horizontal.TProgressbar", background='#4CAF50', troughcolor='#f0f0f0', borderwidth=0, thickness=20)
# 创建主框架
self.main_frame = ttk.Frame(master, padding="20 20 20 20")
self.main_frame.pack(fill=tk.BOTH, expand=True)
# 创建标题
self.title_label = ttk.Label(self.main_frame, text="声纹对比系统", style='Header.TLabel')
self.title_label.grid(row=0, column=0, columnspan=4, pady=(0, 20))
# 音频选择区域
self.audio_frame = ttk.LabelFrame(self.main_frame, text="音频选择", padding="10 10 10 10")
self.audio_frame.grid(row=1, column=0, columnspan=4, sticky="ew", pady=(0, 20))
# 音频1选择
self.label1 = ttk.Label(self.audio_frame, text="音频文件1:")
self.label1.grid(row=0, column=0, padx=10, pady=10, sticky="w")
self.audio1_path = tk.StringVar()
self.entry_audio1 = ttk.Entry(self.audio_frame, width=50, textvariable=self.audio1_path)
self.entry_audio1.grid(row=0, column=1, padx=10, pady=10, sticky="ew")
self.btn_audio1 = ttk.Button(self.audio_frame, text="选择文件", command=self.select_audio1)
self.btn_audio1.grid(row=0, column=2, padx=10, pady=10)
# 音频2选择
self.label2 = ttk.Label(self.audio_frame, text="音频文件2:")
self.label2.grid(row=1, column=0, padx=10, pady=10, sticky="w")
self.audio2_path = tk.StringVar()
self.entry_audio2 = ttk.Entry(self.audio_frame, width=50, textvariable=self.audio2_path)
self.entry_audio2.grid(row=1, column=1, padx=10, pady=10, sticky="ew")
self.btn_audio2 = ttk.Button(self.audio_frame, text="选择文件", command=self.select_audio2)
self.btn_audio2.grid(row=1, column=2, padx=10, pady=10)
# 设置列的权重
self.audio_frame.columnconfigure(1, weight=1)
# 参数设置区域
self.settings_frame = ttk.LabelFrame(self.main_frame, text="参数设置", padding="10 10 10 10")
self.settings_frame.grid(row=2, column=0, columnspan=4, sticky="ew", pady=(0, 20))
# 判断阈值
self.label3 = ttk.Label(self.settings_frame, text="对比阈值:")
self.label3.grid(row=0, column=0, padx=10, pady=10, sticky="w")
self.threshold = tk.StringVar(value="0.6")
self.entry_threshold = ttk.Entry(self.settings_frame, width=10, textvariable=self.threshold)
self.entry_threshold.grid(row=0, column=1, padx=10, pady=10, sticky="w")
self.threshold_info = ttk.Label(self.settings_frame, text="(取值范围0-1,越大表示要求越严格)")
self.threshold_info.grid(row=0, column=2, padx=10, pady=10, sticky="w")
# 操作按钮区域
self.button_frame = ttk.Frame(self.main_frame)
self.button_frame.grid(row=3, column=0, columnspan=4, sticky="ew", pady=(0, 20))
self.btn_predict = ttk.Button(self.button_frame, text="开始对比", command=self.predict_thread)
self.btn_predict.pack(side=tk.LEFT, padx=10)
self.btn_clear = ttk.Button(self.button_frame, text="清空", command=self.clear)
self.btn_clear.pack(side=tk.LEFT, padx=10)
self.btn_quit = ttk.Button(self.button_frame, text="退出", command=self.quit)
self.btn_quit.pack(side=tk.RIGHT, padx=10)
# 状态区域
self.status_frame = ttk.LabelFrame(self.main_frame, text="状态", padding="10 10 10 10")
self.status_frame.grid(row=4, column=0, columnspan=4, sticky="ew", pady=(0, 10))
# 进度条
self.progress_var = tk.DoubleVar()
self.progress_bar = ttk.Progressbar(self.status_frame, orient="horizontal",
mode="determinate", variable=self.progress_var,
style="Green.Horizontal.TProgressbar")
self.progress_bar.pack(fill=tk.X, padx=5, pady=5)
# 结果显示区域
self.result_frame = ttk.LabelFrame(self.main_frame, text="对比结果", padding="10 10 10 10")
self.result_frame.grid(row=5, column=0, columnspan=4, sticky="ew")
self.result_label = ttk.Label(self.result_frame, text="请选择两个音频文件进行对比",
style='Result.TLabel', anchor=tk.CENTER)
self.result_label.pack(fill=tk.X, pady=10)
# 结果详情
self.detail_frame = ttk.Frame(self.result_frame)
self.detail_frame.pack(fill=tk.X, pady=5)
self.similarity_label = ttk.Label(self.detail_frame, text="相似度: ")
self.similarity_label.pack(side=tk.LEFT, padx=10)
self.similarity_value = ttk.Label(self.detail_frame, text="--", font=('微软雅黑', 12, 'bold'))
self.similarity_value.pack(side=tk.LEFT)
# 设置列的权重
for i in range(4):
self.main_frame.columnconfigure(i, weight=1)
# 预测器
self.predictor = MVectorPredictor(configs=args.configs, model_path=args.model_path, use_gpu=args.use_gpu)
self.is_predicting = False
def select_audio1(self):
filename = filedialog.askopenfilename(initialdir='./dataset',
filetypes=[("音频文件", "*.wav *.mp3 *.flac *.ogg *.m4a"),
("所有文件", "*.*")])
if filename:
self.audio1_path.set(filename)
def select_audio2(self):
filename = filedialog.askopenfilename(initialdir='./dataset',
filetypes=[("音频文件", "*.wav *.mp3 *.flac *.ogg *.m4a"),
("所有文件", "*.*")])
if filename:
self.audio2_path.set(filename)
def predict_thread(self):
"""在线程中执行预测"""
if self.is_predicting:
messagebox.showinfo("提示", "正在处理中,请稍候...")
return
audio_path1 = self.audio1_path.get()
audio_path2 = self.audio2_path.get()
if not audio_path1 or not audio_path2:
messagebox.showerror("错误", "请选择两个音频文件")
return
try:
threshold = float(self.threshold.get())
if threshold < 0 or threshold > 1:
messagebox.showerror("错误", "阈值必须在0-1之间")
return
except ValueError:
messagebox.showerror("错误", "请输入有效的阈值")
return
self.is_predicting = True
self.btn_predict.config(state=tk.DISABLED)
self.result_label.config(text="正在处理...")
self.similarity_value.config(text="--")
self.update_progress_bar(0)
# 启动线程进行预测
threading.Thread(target=self._predict, args=(audio_path1, audio_path2, threshold)).start()
def _predict(self, audio_path1, audio_path2, threshold):
"""执行预测"""
try:
# 模拟进度
for i in range(1, 101):
if i < 90: # 预留最后10%用于实际计算结果
self.update_progress_bar(i)
time.sleep(0.02) # 调整速度使进度条看起来更自然
# 执行实际预测
dist = self.predictor.contrast(audio_path1, audio_path2)
# 完成进度
self.update_progress_bar(100)
# 更新UI显示结果
self.similarity_value.config(text=f"{dist:.5f}")
if dist > threshold:
result_text = f"两段语音来自同一个人"
self.result_label.config(text=result_text, foreground="#4CAF50")
else:
result_text = f"两段语音来自不同的人"
self.result_label.config(text=result_text, foreground="#F44336")
except Exception as e:
messagebox.showerror("错误", f"预测失败: {str(e)}")
self.result_label.config(text="预测失败,请检查音频文件格式", foreground="#F44336")
finally:
self.btn_predict.config(state=tk.NORMAL)
self.is_predicting = False
def clear(self):
"""清空所有输入和结果"""
self.audio1_path.set("")
self.audio2_path.set("")
self.threshold.set("0.6")
self.result_label.config(text="请选择两个音频文件进行对比", foreground="#007bff")
self.similarity_value.config(text="--")
self.update_progress_bar(0)
def update_progress_bar(self, value):
"""更新进度条"""
self.progress_var.set(value)
self.master.update_idletasks()
def quit(self):
self.master.destroy()
if __name__ == '__main__':
root = tk.Tk()
app = VoiceContrastGUI(root)
root.mainloop()