当前位置: 首页 > news >正文

python实现分离不同人声、wespeaker

文章目录

    • 方案选型
    • 步骤
        • 2、代码
        • 可用的几个模型
    • 其他
      • 如果遇到c++问题
        • wespeaker下载安装步骤
    • 其他
      • 文档

剪映等工具都支持背景音和人声分离。
那么如果想分离不同人声呢?例如想把郭靖和黄蓉的声音分开。

方案选型

1、本地运行、无需上传 # 上传的不但慢,而且大多有限制
2、最好是国内的,如果需要注册hugging face,国内经常连不上

所以方案为:
ffmpeg
demucs(Meta/Facebook开发的-人声分离专家)
wespeaker(阿里达摩院-声纹识别专家)

代码流程:

你可以把我们的程序想象成一个音频加工厂,流程如下:1、原料入库(FFmpeg)输入:input.mp4(杂乱的视频文件)操作:提取音频流->转为标准 WAV 产出:input_temp.wav(纯净的音频原料)2、粗加工-分离人声(Demucs)输入:input_temp.wav 操作:AI 模型分析频谱->剥离背景音乐 产出:input_vocals.wav(只有人声的干音)3、精加工-声纹识别(Wespeaker)输入:input_vocals.wav 操作: 切片:检测哪里有人在说话(VAD)。 特征提取:给每段话打上“声纹指纹”。 聚类:把相同的指纹归为一类(比如“说话人 A”、“说话人 B”)。 产出:speaker_0.wav,speaker_1.wav(按人分好的文件)

步骤

需要提前装好ffmpeg。

1、安装依赖

pip install torch torchaudio demucs scipy numpy pydub pip install s3prl pip install openai-whisper pip install pydub

注:wespeaker国内镜像没有,先从git下载master包,然后再安装到本地即可。

2、代码
importosimporttorchimporttorchaudioimportnumpyasnpfromscipy.cluster.hierarchyimportfcluster,linkagefromscipy.spatial.distanceimportpdistimportwarningsimportsubprocess# 过滤警告warnings.filterwarnings("ignore",message=".*set_audio_backend.*")# ================= 配置区域 =================INPUT_FILE="input.mp4"# 你的原始 MP4 文件WESPEAKER_MODEL_PATH="wespeaker_vox1_en_resnet34.pt"OUTPUT_DIR="output_speakers"# ============================================print("🚀 正在启动音频分离与声纹识别程序...")# --- 核心修复:使用 Pydub/FFmpeg 预处理音频 ---defprepare_audio(input_file):""" 将任意格式音频转换为 WAV,绕过 torchaudio 读取 MP4 的 Bug """ifnotos.path.exists(input_file):print(f"❌ 找不到输入文件:{input_file}")returnNone# 如果已经是 wav,直接返回ifinput_file.lower().endswith('.wav'):returninput_file output_wav=input_file.rsplit('.',1)[0]+"_temp.wav"print(f"🔄 正在使用 FFmpeg 将{input_file}转换为 WAV...")# 调用系统 FFmpeg# 注意:这里直接调用 'ffmpeg',依赖系统环境变量cmd=['ffmpeg','-y','-i',input_file,'-vn',# 不处理视频'-acodec','pcm_s16le',# 编码为 PCM (WAV)'-ar','44100',# 采样率 44100 (Demucs 标准)'-ac','2',# 双声道output_wav]try:# 尝试直接运行 ffmpegsubprocess.run(cmd,check=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)print(f"✅ 转换成功:{output_wav}")returnoutput_wavexceptsubprocess.CalledProcessErrorase:print(f"❌ FFmpeg 转换失败:{e.stderr.decode()}")print("💡 请确保在终端输入 'ffmpeg' 能直接运行")returnNone# --- 步骤 1: 使用 Demucs 去除背景音乐 ---defstep1_separate_vocals(input_file):print("\n"+"="*30)print("🎬 [步骤 1] 正在使用 Demucs 去除背景音乐...")print("="*30)try:fromdemucs.pretrainedimportget_modelfromdemucs.applyimportapply_modelfromdemucs.audioimportsave_audio# 1. 加载模型model=get_model('htdemucs')# 2. 加载音频waveform,sample_rate=torchaudio.load(input_file)print(f"📥 音频加载完成:{waveform.shape}, 采样率:{sample_rate}")# 【关键修复】# demucs 需要 3 维输入: [Batch, Channels, Length]# torchaudio 输出 2 维: [Channels, Length]# 我们需要在第 0 维增加一个 Batch 维度ifwaveform.dim()==2:waveform=waveform.unsqueeze(0)print(f"🔄 调整后的音频形状:{waveform.shape}")# 3. 执行分离print("🔄 正在分离音轨 (这可能需要几分钟)...")# 现在传入 3 维数据,就不会报错了out=apply_model(model,waveform,device='cpu',split=True,overlap=0.25)# out 是 [Batch, Sources, Length],取第 0 个 batchsources=out[0]print(f"🎵 分离完成,共得到{sources.shape[0]}条音轨")# 4. 提取人声 (通常索引 0 是人声)vocals=sources[0]# 5. 保存人声vocals_file=input_file.rsplit('.',1)[0]+"_vocals.wav"save_audio(vocals,vocals_file,samplerate=44100)print(f"✅ 人声提取成功:{vocals_file}")returnvocals_fileexceptExceptionase:print(f"❌ 步骤 1 失败:{e}")importtraceback traceback.print_exc()returnNone# --- 步骤 2: 简单的语音活动检测 ---defsimple_energy_vad(waveform,sample_rate,threshold_ratio=0.02,min_duration=0.5):ifwaveform.shape[0]>1:mono=waveform.mean(dim=0)else:mono=waveform.squeeze()energy=torch.abs(mono)max_energy=energy.max()ifmax_energy==0:return[]threshold=max_energy*threshold_ratio mask=energy>threshold segments=[]start_sample=0in_speech=Falsefori,is_speechinenumerate(mask):ifis_speechandnotin_speech:start_sample=i in_speech=Trueelifnotis_speechandin_speech:end_sample=i duration=(end_sample-start_sample)/sample_rateifduration>min_duration:segments.append((start_sample,end_sample))in_speech=Falsereturnsegments# --- 步骤 3: 声纹聚类 ---defstep2_diarize_speakers(vocals_file,output_dir):print("\n"+"="*30)print("🧠 [步骤 2] 正在识别说话人...")print("="*30)ifnotos.path.exists(WESPEAKER_MODEL_PATH):print(f"❌ 找不到模型文件:{WESPEAKER_MODEL_PATH}")returnwaveform,sample_rate=torchaudio.load(vocals_file)ifwaveform.shape[0]>1:waveform=torch.mean(waveform,dim=0,keepdim=True)print(f"⏳ 正在加载 Wespeaker 模型...")try:importwespeaker speaker_model=wespeaker.load_model(WESPEAKER_MODEL_PATH)device='cuda'iftorch.cuda.is_available()else'cpu'speaker_model.set_device(device)print("✅ 模型加载成功")exceptExceptionase:print(f"❌ 模型加载失败:{e}")returnprint("🔪 正在检测语音片段...")segments=simple_energy_vad(waveform,sample_rate)print(f"🔍 检测到{len(segments)}个有效语音片段")iflen(segments)<2:print("⚠️ 语音片段太少,无法聚类")returnembeddings=[]valid_segments=[]print("📝 正在提取声纹特征...")forstart,endinsegments:segment_wave=waveform[:,start:end]embedding=speaker_model.extract_embedding(segment_wave.numpy())ifembeddingisnotNone:embeddings.append(embedding)valid_segments.append((start,end))iflen(embeddings)<2:print("❌ 无法提取足够的声纹特征")returnembeddings=np.array(embeddings)print("📊 正在进行聚类分析...")distances=pdist(embeddings,metric='cosine')Z=linkage(distances,'average')labels=fcluster(Z,t=0.5,criterion='distance')unique_labels=set(labels)print(f"✅ 识别出{len(unique_labels)}位说话人:{list(unique_labels)}")os.makedirs(output_dir,exist_ok=True)forlabelinunique_labels:indices=np.where(labels==label)[0]longest_segment=Nonemax_len=0foridxinindices:start,end=valid_segments[idx]duration=end-startifduration>max_len:max_len=duration longest_segment=waveform[:,start:end]output_path=os.path.join(output_dir,f"speaker_{label}.wav")torchaudio.save(output_path,longest_segment,sample_rate)print(f" - 保存:{output_path}(时长:{max_len/sample_rate:.2f}s)")# ================= 主程序 =================if__name__=="__main__":# 1. 预处理:MP4 -> WAVwav_file=prepare_audio(INPUT_FILE)ifwav_file:# 2. 分离vocals_path=step1_separate_vocals(wav_file)# 3. 聚类ifvocals_path:step2_diarize_speakers(vocals_path,OUTPUT_DIR)print("\n🎉 全部处理完成!")# 可选:清理临时文件# os.remove(wav_file)

输出结果:

🚀 正在启动音频分离与声纹识别程序...🔄 正在使用 FFmpeg 将input.mp4 转换为 WAV...✅ 转换成功:input_temp.wav==============================🎬[步骤1]正在使用 Demucs 去除背景音乐...==============================Downloading:"https://dl.fbaipublicfiles.com/demucs/hybrid_transformer/955717e8-8726e21a.th"to C:\Users\user/.cache\torch\hub\checkpoints\955717e8-8726e21a.th100%|██████████|80.2M/80.2M[00:15<00:00,5.29MB/s]📥 音频加载完成:torch.Size([2,13218240]),采样率:44100🔄 调整后的音频形状:torch.Size([1,2,13218240])🔄 正在分离音轨(这可能需要几分钟)...🎵 分离完成,共得到4条音轨 ✅ 人声提取成功:input_temp_vocals.wav==============================🧠[步骤2]正在识别说话人...==============================❌ 找不到模型文件:wespeaker_vox1_en_resnet34.pt 🎉 全部处理完成!

果然成功了,效果还不错,就是优点慢。

可用的几个模型

mdx_extra:效果最好,速度最慢(龟速)。
htdemucs_ft:效果不错,速度中等(推荐)。
htdemucs:效果一般,速度很快。

其他

如果遇到c++问题

安装visual studio装下

wespeaker下载安装步骤

1、下文git地址下载master分支zip包。
2、pycharm所在虚拟环境中,执行安装命令

D:/PycharmProjects/transformer_demo/.venv/Scripts/python.exe-m install"安装包路径"

其他

文档

wespeaker官网git地址(这里下载的master分支,可以pip install成功):
https://github.com/wenet-e2e/wespeaker

http://www.cnnetsun.cn/news/1696468.html

相关文章:

  • RANSAC算法
  • 计算机毕业设计:Python新能源车型数据洞察与情感推荐引擎 Django框架 snowNLP 协同过滤推荐算法 requests爬虫 可视化(建议收藏)✅
  • 避坑指南:手把手教你用Java生成定制化词云图(解决中文乱码、背景图加载问题)
  • Python 3 CGI 编程
  • DOM EventListener
  • 别再死磕梯形图了!写给工控新手的ST语言入门指南(附常用指令速查表)
  • 别再用copy /b了!三种制作哥斯拉图片马的保姆级对比(含010 Editor实操)
  • B站缓存视频合并工具:让离线内容重获完整价值
  • Iron Beam高能激光武器系统
  • Gemma 4:Google扔出的开源AI重磅炸弹,手机也能跑的ChatGPT级大脑
  • VS2019项目重构实战:从命名空间到解决方案的全面重命名指南
  • 鸿蒙Flutter混合开发:如何优雅地实现离线TTS/STT的多语言动态切换?
  • 16 指挥AI写数据库SQL代码:增删改查与存储过程实现
  • 大车间降温新选择:蒸发冷省电空调凭关键词优势脱颖而出
  • javaweb小区车辆停车场车位预约管理系统 可视化
  • 从零设计一个AXI Master:手把手教你为Xilinx MIG DDR4控制器编写自定义测试逻辑
  • 宾州大学让AI学会“记忆管理“:多智能体协作破解长期对话难题
  • 残差缩放块改进YOLOv26可学习权重自适应特征融合双重突破
  • 技术文章大纲:用Anaconda驯服AI开发流
  • Canape实战指南:XCP工程配置与调试(一)
  • 算法解析 | 深入EGO Planner:无ESDF的实时避障与轨迹优化
  • YOLOv11 OBB实战:手把手构建旋转目标检测数据集
  • 网络连接故障的常见原因
  • Bert模型
  • 从“炼金术”到“建筑学”:深度学习结构设计的五大范式
  • Go的runtime.GOMAXPROCS:设置最大CPU核心数
  • 零基础新手如何用快马AI一键生成9·1免费版安装教程网站
  • eNSP第三次作业
  • Vibe Coding 有哪些实用技巧?这篇文章讲透工作流、提示词和避坑方法
  • JL杰理AC696N开发板PWM波形生成与控制(1):频率、占空比