現在已經完成了人臉識別跟蹤 ?,接下來要:
? 加入「聲紋識別(說話人識別)」功能,識別誰在講話,并在視頻中“這個人”的名字旁邊加上「正在講話」。
這屬于多模態識別(視覺 + 音頻):
-
說明實現原理與推薦工具
-
給出整體流程架構
-
推薦穩定的開源工具包
-
展示如何與當前人臉識別結合
? 整體目標
希望實現:
-
攝像頭中顯示所有人的名字(已完成)
-
通過麥克風監聽講話內容
-
根據聲紋判斷當前是誰在講話
-
在視頻中對這個人顯示“正在講話”
? 實現路徑(視覺 + 音頻)
🔊 聲紋識別部分(說話人識別)
目標 | 技術名稱 | 推薦工具 |
---|---|---|
聲音中識別是誰 | 說話人識別(Speaker Identification) | pyannote-audio不推薦, resemblyzer推薦 |
聲音中找誰在說話 | 說話人活動檢測(Voice Activity Detection, VAD) | webrtcvad , silero-vad |
聲紋注冊(采集說話人音頻) | Speaker Embedding | resemblyzer , speechbrain |
1. pyannote-audio和resemblyzer區別核心功能對比
特性 | pyannote-audio | resemblyzer |
---|---|---|
主要用途 | 說話人分割(Diarization)、語音活動檢測(VAD)、重疊語音檢測 | 聲紋嵌入(Speaker Embedding)、實時聲紋比對 |
模型復雜度 | 高(多模塊聯合優化,端到端流程) | 低(輕量級,專注聲紋向量提取) |
預訓練模型 | 提供完整的說話人分割管道(如?speaker-diarization-3.1 ) | 僅提供聲紋編碼器(如?VoiceEncoder ) |
實時性 | 較慢(適合離線處理) | 快(支持實時流式處理) |
依賴項 | 依賴 PyTorch 和 Hugging Face 模型庫 | 僅需 NumPy 和 PyTorch |
典型應用場景 | 會議記錄、廣播分析、多說話人數據集標注 | 實時聲紋驗證、說話人聚類 |
2. 技術實現差異
pyannote-audio
-
模塊化設計:
包含獨立的子模塊(如?segmentation
、embedding
、clustering
),可靈活組合或替換16。 -
端到端流程:
支持從語音活動檢測到說話人聚類的完整流程,適合復雜場景(如重疊語音處理)6。 -
性能優化:
部分版本(如?speaker-diarization-3.1
)處理長音頻較慢,推薦使用 v2 版本加速12。
resemblyzer
-
輕量級:
僅實現聲紋向量提取(512 維嵌入),需自行結合聚類算法(如 K-Means)完成分割5。 -
實時性:
適合流式處理,單次推理速度快(約 0.1 秒/語音片段)5。 -
易用性:
無需復雜配置,適合快速驗證聲紋相似度
? 推薦方案(輕量 + 可行)
🔧 聲紋識別用 resemblyzer
,搭配 YOLO 人臉
注意!!!!!!!!!!!!
先安裝ffmpeg,resemblyzer會依賴
1. 為什么?resemblyzer
?需要?ffmpeg
?
resemblyzer
?的核心功能是處理音頻文件(如 MP3、WAV 等),但它本身?不直接調用?ffmpeg
,而是通過以下依賴鏈間接使用:
resemblyzer → librosa(音頻加載) → audioread(解碼非WAV格式) → ffmpeg(實際解碼工具)
-
關鍵點:
只有處理?非WAV格式(如MP3、MP4)?時才需要?ffmpeg
。如果僅使用 WAV 文件,可跳過安裝。
? 安裝【ffmpeg】
https://ffmpeg.org/打開網站下載自己的版本
接下來📦 安裝其他依賴:
pip install resemblyzer sounddevice webrtcvad numpy
💡 resemblyzer 是 Facebook 開源的輕量級聲紋識別庫,能實時從麥克風提取聲紋并判斷是否是目標用戶。
? 總體流程圖(視覺 + 音頻結合):
? 示例代碼框架(概要)
1、查看在PyCharm里面能不能使用ffmpeg
import subprocesstry:result = subprocess.run(["ffmpeg", "-version"], capture_output=True, text=True)if result.returncode == 0:print("? ffmpeg 已安裝")else:print("? ffmpeg 未安裝或異常")
except FileNotFoundError:print("? 未找到 ffmpeg 命令")
2、拿兩段音頻識別
from resemblyzer import VoiceEncoder, preprocess_wav
import numpy as np# 初始化編碼器
encoder = VoiceEncoder()# 加載并預處理音頻
wav1 = preprocess_wav("/Users/lianying/Desktop/yolo/1.m4a") # 支持 .wav/.mp4 文件
wav2 = preprocess_wav("/Users/lianying/Desktop/yolo/2.m4a")# 提取聲紋嵌入(512 維向量)
embed1 = encoder.embed_utterance(wav1)
embed2 = encoder.embed_utterance(wav2)# 計算相似度(余弦相似度)
similarity = np.dot(embed1, embed2)
print(f"聲紋相似度: {similarity:.4f}") # 0.0~1.0,>0.8 可視為同一人
已經成功識別聲紋了 !!!!!!!
? 接下來整合以下功能:
模塊 | 狀態 |
---|---|
🧠 人臉識別 + 聲紋識別 | ? 已完成(已有) |
🎙? Whisper 語音識別 | ? 可用(ffmpeg 已安裝) |
💬 實時字幕顯示 | 🔜 馬上整合 |
已經安裝好了 ffmpeg
?,那現在可以 正式接入 Whisper 實現“說話內容轉字幕”。
🧠 模型大小選擇:使用 Whisper 的哪個版本?(默認推薦 base
)
-
tiny
(最快) /base
(準確+輕)/small
(較高準確)
? 安裝 Whisper(OpenAI 官方)
pip install -U openai-whisper
? Whisper 使用示例(語音轉文字)
import whisper
model = whisper.load_model("base") # 也可以用 "tiny", "small", "medium", "large"# 錄音 → 保存為 temp.wav
import sounddevice as sd
from scipy.io.wavfile import writefs = 16000
duration = 3
audio = sd.rec(int(duration * fs), samplerate=fs, channels=1)
sd.wait()
write("temp.wav", fs, audio)# 語音識別
result = model.transcribe("temp.wav", language="zh")
print("識別內容:", result["text"])
🎯 目標
基于攝像頭視頻流,識別誰在講話,并把“張三(正在講話):你好啊,我是張三”樣式的字幕實時顯示在畫面上(支持中文)
? 內容包含:
模塊 | 技術 | 狀態 |
---|---|---|
人臉識別 | YOLOv8 + face_recognition | ? |
聲紋識別 | Resemblyzer | ? |
語音轉文字 | Whisper(base) | ? |
實時錄音 | sounddevice | ? |
字幕顯示 | PIL + 中文字體支持 | ? |
? 使用前請確認:
如下文件夾結構:
import os
import cv2
import numpy as np
from PIL import ImageFont, ImageDraw, Image
import face_recognition
from resemblyzer import VoiceEncoder, preprocess_wav
import sounddevice as sd
from scipy.io.wavfile import write
import whisper
from ultralytics import YOLO# === 參數設置 ===
VOICE_DB = "voice_db"
FACES_DB = "faces"
FONT_PATH = "font/AlimamaDaoLiTi-Regular.ttf" # 中文黑體字體路徑(請確保該字體文件存在)
SAMPLE_RATE = 16000
RECORD_DURATION = 1 # 錄音時間(秒)
CONFIDENCE_THRESHOLD = 0.75# === 初始化模型 ===
print("加載 YOLOv8 模型...")
yolo_model = YOLO("/Users/lianying/Desktop/yolo/model_face.pt") # 檢測人臉用
print("加載人臉識別數據庫...")
known_encodings, known_names = [], []
for name in os.listdir(FACES_DB):for img_file in os.listdir(os.path.join(FACES_DB, name)):img_path = os.path.join(FACES_DB, name, img_file)img = face_recognition.load_image_file(img_path)enc = face_recognition.face_encodings(img)if enc:known_encodings.append(enc[0])known_names.append(name)print("加載聲紋識別數據庫...")
encoder = VoiceEncoder()
speaker_embeddings = {}
for file in os.listdir(VOICE_DB):if file.endswith(".wav"):name = file.split(".")[0]wav = preprocess_wav(os.path.join(VOICE_DB, file))embed = encoder.embed_utterance(wav)speaker_embeddings[name] = embedprint("加載 Whisper 模型...")
asr_model = whisper.load_model("base")# === 字體 ===
font = ImageFont.truetype(FONT_PATH, 24)# === 攝像頭 ===
cap = cv2.VideoCapture(0)
frame_count = 0
interval = int(30 * RECORD_DURATION) # 每 interval 幀識別一次
current_speaker = ""
current_text = ""while True:ret, frame = cap.read()if not ret:breakframe_count += 1# 每 interval 幀錄音 + 聲紋識別 + Whisperif frame_count % interval == 0:print("\n[INFO] 正在錄音并分析說話人...")audio = sd.rec(int(RECORD_DURATION * SAMPLE_RATE), samplerate=SAMPLE_RATE, channels=1)sd.wait()write("temp.wav", SAMPLE_RATE, audio)# 聲紋識別wav = preprocess_wav("temp.wav")embed = encoder.embed_utterance(wav)best_name, best_score = "", 0.5for name, ref in speaker_embeddings.items():sim = np.dot(embed, ref)if sim > best_score:best_score = simbest_name = namecurrent_speaker = best_name if best_score > CONFIDENCE_THRESHOLD else ""# Whisper 語音識別result = asr_model.transcribe("temp.wav", language="zh")current_text = result["text"].strip()print(f"[識別] {current_speaker}:{current_text}")# 人臉檢測 + 標注results = yolo_model.predict(frame, classes=[0], conf=0.4, verbose=False)for result in results:boxes = result.boxes.xyxy.cpu().numpy().astype(int)for box in boxes:x1, y1, x2, y2 = boxface_crop = frame[y1:y2, x1:x2]rgb_crop = cv2.cvtColor(face_crop, cv2.COLOR_BGR2RGB)encs = face_recognition.face_encodings(rgb_crop)name = "未知"if encs:matches = face_recognition.compare_faces(known_encodings, encs[0])face_distances = face_recognition.face_distance(known_encodings, encs[0])if True in matches:best_match = np.argmin(face_distances)name = known_names[best_match]# 準備顯示信息label = nameif name == current_speaker:label += "(正在講話)"if current_text:label += f":{current_text}"# 繪圖(PIL 支持中文)pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(pil_img)draw.rectangle([x1, y1, x2, y2], outline="green", width=2)draw.text((x1, y1 - 30), label, font=font, fill=(255, 0, 0))frame = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)cv2.imshow("實時人臉 + 聲紋 + 字幕識別", frame)if cv2.waitKey(1) & 0xFF == ord("q"):breakcap.release()
cv2.destroyAllWindows()