commit 053eaa3107c0cc93e010da99db0d485634bf1d0c Author: zhugaoliang Date: Mon Apr 7 08:08:39 2025 +0800 first program diff --git a/README.md b/README.md new file mode 100644 index 0000000..2bac1d8 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# 基于YOLOv5-Face的人脸识别系统 + +这个系统使用YOLOv5-Face进行人脸检测和特征提取,使用SQLite数据库存储人脸特征以便实时比对,在人脸识别成功后会提供语音提示。 + +## 功能特点 + +- 人脸检测和识别 +- 人脸特征提取和存储 +- 人脸特征比对 +- 识别成功语音提示 +- 本地SQLite数据库 + +## 安装依赖 + +``` +pip install -r requirements.txt +``` + +## 使用说明 + +1. 运行人脸注册程序添加人脸到数据库: +``` +python register_face.py +``` + +2. 运行实时人脸识别程序: +``` +python face_recognition_app.py +``` + +## 文件结构 + +- `face_recognition_app.py`: 主应用程序 +- `register_face.py`: 人脸注册程序 +- `face_db.py`: 数据库操作模块 +- `face_utils.py`: 人脸处理工具函数 +- `voice_prompt.py`: 语音提示模块 +- `models/`: YOLOv5-Face模型文件夹 +- `data/`: 存储数据库和临时文件 diff --git a/__pycache__/face_db.cpython-310.pyc b/__pycache__/face_db.cpython-310.pyc new file mode 100644 index 0000000..25d82c2 Binary files /dev/null and b/__pycache__/face_db.cpython-310.pyc differ diff --git a/__pycache__/face_utils.cpython-310.pyc b/__pycache__/face_utils.cpython-310.pyc new file mode 100644 index 0000000..1c42a6d Binary files /dev/null and b/__pycache__/face_utils.cpython-310.pyc differ diff --git a/__pycache__/voice_prompt.cpython-310.pyc b/__pycache__/voice_prompt.cpython-310.pyc new file mode 100644 index 0000000..a8fc0dc Binary files /dev/null and b/__pycache__/voice_prompt.cpython-310.pyc differ diff --git a/data/face_database.db b/data/face_database.db new file mode 100644 index 0000000..6473c96 Binary files /dev/null and b/data/face_database.db differ diff --git a/dlib-19.22.99-cp310-cp310-win_amd64.whl b/dlib-19.22.99-cp310-cp310-win_amd64.whl new file mode 100644 index 0000000..b5b9bca Binary files /dev/null and b/dlib-19.22.99-cp310-cp310-win_amd64.whl differ diff --git a/face_db.py b/face_db.py new file mode 100644 index 0000000..f746643 --- /dev/null +++ b/face_db.py @@ -0,0 +1,123 @@ +import sqlite3 +import numpy as np +import pickle +import os + +class FaceDatabase: + def __init__(self, db_path='data/face_database.db'): + """初始化人脸数据库 + + Args: + db_path: 数据库文件路径 + """ + # 确保数据目录存在 + os.makedirs(os.path.dirname(db_path), exist_ok=True) + + self.db_path = db_path + self.conn = sqlite3.connect(db_path) + self.create_tables() + + def create_tables(self): + """创建必要的数据表""" + cursor = self.conn.cursor() + + # 创建人员表 + cursor.execute(''' + CREATE TABLE IF NOT EXISTS persons ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + register_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + + # 创建人脸特征表 + cursor.execute(''' + CREATE TABLE IF NOT EXISTS face_features ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + person_id INTEGER NOT NULL, + feature_vector BLOB NOT NULL, + FOREIGN KEY (person_id) REFERENCES persons (id) + ) + ''') + + self.conn.commit() + + def add_person(self, name): + """添加人员信息 + + Args: + name: 人员姓名 + + Returns: + person_id: 新增人员的ID + """ + cursor = self.conn.cursor() + cursor.execute("INSERT INTO persons (name) VALUES (?)", (name,)) + self.conn.commit() + return cursor.lastrowid + + def add_face_feature(self, person_id, feature_vector): + """添加人脸特征向量 + + Args: + person_id: 人员ID + feature_vector: 人脸特征向量(numpy数组) + + Returns: + feature_id: 新增特征的ID + """ + # 将numpy数组序列化为二进制数据 + serialized_feature = pickle.dumps(feature_vector) + + cursor = self.conn.cursor() + cursor.execute( + "INSERT INTO face_features (person_id, feature_vector) VALUES (?, ?)", + (person_id, serialized_feature) + ) + self.conn.commit() + return cursor.lastrowid + + def get_all_features(self): + """获取所有人脸特征 + + Returns: + list of tuples: [(person_id, name, feature_vector), ...] + """ + cursor = self.conn.cursor() + cursor.execute(""" + SELECT p.id, p.name, f.feature_vector + FROM persons p + JOIN face_features f ON p.id = f.person_id + """) + + results = [] + for row in cursor.fetchall(): + person_id, name, serialized_feature = row + # 反序列化特征向量 + feature_vector = pickle.loads(serialized_feature) + results.append((person_id, name, feature_vector)) + + return results + + def get_person_by_id(self, person_id): + """根据ID获取人员信息 + + Args: + person_id: 人员ID + + Returns: + dict or None: 人员信息 + """ + cursor = self.conn.cursor() + cursor.execute("SELECT id, name FROM persons WHERE id = ?", (person_id,)) + result = cursor.fetchone() + + if result: + return {"id": result[0], "name": result[1]} + return None + + def close(self): + """关闭数据库连接""" + if self.conn: + self.conn.close() + self.conn = None diff --git a/face_recognition_app.py b/face_recognition_app.py new file mode 100644 index 0000000..42e8896 --- /dev/null +++ b/face_recognition_app.py @@ -0,0 +1,176 @@ +import cv2 +import time +import argparse +import numpy as np +from face_utils import FaceDetector, FaceRecognizer, draw_face_box +from face_db import FaceDatabase +from voice_prompt import VoicePrompt + +def parse_args(): + """解析命令行参数""" + parser = argparse.ArgumentParser(description='人脸识别应用') + parser.add_argument('--camera', type=int, default=0, help='摄像头索引') + parser.add_argument('--confidence', type=float, default=0.5, help='人脸检测置信度阈值') + parser.add_argument('--similarity', type=float, default=0.6, help='人脸相似度阈值') + parser.add_argument('--display_width', type=int, default=640, help='显示窗口宽度') + parser.add_argument('--display_height', type=int, default=480, help='显示窗口高度') + return parser.parse_args() + +def main(): + """人脸识别应用主函数""" + args = parse_args() + + # 初始化组件 + print("正在初始化人脸识别系统...") + voice = VoicePrompt() + detector = FaceDetector(conf_thres=args.confidence) + recognizer = FaceRecognizer(detector=detector, similarity_threshold=args.similarity) + db = FaceDatabase() + + # 加载已知人脸特征 + known_faces = db.get_all_features() + if not known_faces: + print("警告: 数据库中没有注册的人脸,请先运行注册程序") + voice.speak("数据库中没有注册的人脸,请先运行注册程序", block=True) + else: + print(f"已加载 {len(known_faces)} 个人脸特征") + + # 打开摄像头 + cap = cv2.VideoCapture(args.camera) + if not cap.isOpened(): + print("无法打开摄像头") + return + + # 设置摄像头分辨率 + cap.set(cv2.CAP_PROP_FRAME_WIDTH, args.display_width) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, args.display_height) + + print("系统初始化完成,开始人脸识别...") + voice.speak("人脸识别系统已启动", block=True) + + # 跟踪识别状态,避免重复语音提示 + recognized_persons = {} # {person_id: last_recognition_time} + recognition_cooldown = 5.0 # 同一个人识别成功后的冷却时间(秒) + + # 绘制UI相关 + font = cv2.FONT_HERSHEY_SIMPLEX + font_scale = 0.6 + line_thickness = 2 + fps_history = [] + + try: + while True: + # 计时(用于计算FPS) + start_time = time.time() + + # 读取摄像头帧 + ret, frame = cap.read() + if not ret: + print("无法获取摄像头画面") + break + + # 镜像翻转以便更直观 + frame = cv2.flip(frame, 1) + + # 创建显示帧 + display_frame = frame.copy() + + # 检测人脸 + faces = detector.detect_faces(frame) + + # 处理每个检测到的人脸 + for face_box in faces: + # 识别人脸 + result = recognizer.identify_face(frame, known_faces, face_box[:4]) + + # 如果识别成功 + if result: + person_id, name, similarity = result + + # 绘制人脸框和标签(绿色表示识别成功) + draw_face_box(display_frame, face_box, name, similarity, color=(0, 255, 0)) + + # 检查是否需要播放语音提示(避免频繁重复) + current_time = time.time() + if (person_id not in recognized_persons or + current_time - recognized_persons[person_id] > recognition_cooldown): + + # 更新识别时间 + recognized_persons[person_id] = current_time + + # 播放语音提示 + voice.speak(f"您好,{name}", block=False) + print(f"识别成功: {name} (相似度: {similarity:.2f})") + else: + # 未识别的人脸使用红色框 + draw_face_box(display_frame, face_box, "未知", color=(0, 0, 255)) + + # 计算并显示FPS + end_time = time.time() + fps = 1.0 / (end_time - start_time) + fps_history.append(fps) + if len(fps_history) > 30: + fps_history.pop(0) + avg_fps = sum(fps_history) / len(fps_history) + + cv2.putText( + display_frame, + f"FPS: {avg_fps:.1f}", + (10, 30), + font, + font_scale, + (0, 255, 255), + line_thickness + ) + + # 显示数据库信息 + cv2.putText( + display_frame, + f"数据库: {len(known_faces)} 个人脸", + (10, 60), + font, + font_scale, + (0, 255, 255), + line_thickness + ) + + # 显示操作指南 + cv2.putText( + display_frame, + "按ESC退出, 按R刷新数据库", + (10, display_frame.shape[0] - 10), + font, + font_scale, + (255, 255, 255), + line_thickness + ) + + # 显示图像 + cv2.imshow("人脸识别系统", display_frame) + + # 按键处理 + key = cv2.waitKey(1) & 0xFF + + # 按ESC键退出 + if key == 27: + break + + # 按R键刷新数据库 + elif key == ord('r'): + print("正在刷新人脸数据库...") + known_faces = db.get_all_features() + print(f"已刷新: 加载了 {len(known_faces)} 个人脸特征") + voice.speak("已刷新人脸数据库", block=False) + + except Exception as e: + print(f"发生错误: {e}") + + finally: + # 释放资源 + cap.release() + cv2.destroyAllWindows() + db.close() + print("人脸识别系统已关闭") + +if __name__ == "__main__": + main() diff --git a/face_utils.py b/face_utils.py new file mode 100644 index 0000000..fd04b7c --- /dev/null +++ b/face_utils.py @@ -0,0 +1,224 @@ +import os +import cv2 +import numpy as np +import face_recognition +from pathlib import Path +import time + +class FaceDetector: + def __init__(self, conf_thres=0.5): + """初始化人脸检测器,使用face_recognition库 + + Args: + conf_thres: 置信度阈值 + """ + self.conf_thres = conf_thres + + def _load_model(self): + """不再需要加载模型,使用face_recognition库""" + print("使用face_recognition库进行人脸检测,无需加载模型") + pass + + def detect_faces(self, image): + """使用face_recognition库检测图像中的人脸 + + Args: + image: 输入图像 (numpy数组,BGR格式) + + Returns: + list: 检测到的人脸边界框列表 [x1, y1, x2, y2, confidence] + """ + boxes = [] + + # 使用face_recognition库检测人脸位置 + # 将BGR图像转换为RGB格式(face_recognition需要RGB格式) + rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + + # 使用face_recognition检测人脸位置 + face_locations = face_recognition.face_locations(rgb_image) + + # 将检测结果转换为所需的格式 [x1, y1, x2, y2, confidence] + for face_location in face_locations: + # face_location格式为(top, right, bottom, left) + top, right, bottom, left = face_location + + # 转换为[x1, y1, x2, y2, confidence]格式 + # 由于face_recognition不提供置信度,我们使用1.0作为默认值 + boxes.append([left, top, right, bottom, 1.0]) + + print(f"检测到 {len(boxes)} 个人脸") + + # 如果未检测到人脸,尝试使用OpenCV的人脸检测器作为备份 + if len(boxes) == 0: + try: + print("使用OpenCV备用人脸检测...") + face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') + gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + faces = face_cascade.detectMultiScale(gray, 1.1, 5) + + for (x, y, w, h) in faces: + boxes.append([x, y, x+w, y+h, 0.8]) # 使用默认置信度0.8 + print(f"OpenCV检测到 {len(faces)} 个人脸") + except Exception as e: + print(f"OpenCV备用人脸检测失败: {e}") + + return boxes + +class FaceRecognizer: + def __init__(self, detector=None, similarity_threshold=0.6): + """初始化人脸识别器 + + Args: + detector: 人脸检测器实例,如果为None则创建默认检测器 + similarity_threshold: 人脸相似度阈值 + """ + self.detector = detector if detector else FaceDetector() + self.similarity_threshold = similarity_threshold + # 使用OpenCV内置的人脸识别器 + self.face_recognizer = cv2.face.LBPHFaceRecognizer_create() + self.face_size = (128, 128) # 标准化人脸大小 + + def extract_face_features(self, image, box=None): + """提取人脸特征 + + Args: + image: 输入图像 + box: 可选的人脸边界框 [x1, y1, x2, y2],如果未提供则自动检测 + + Returns: + numpy.ndarray: 人脸特征向量 + """ + # 如果没有提供边界框,使用检测器检测人脸 + if box is None: + boxes = self.detector.detect_faces(image) + if not boxes: + return None + box = boxes[0][:4] # 使用第一个检测到的人脸 + + # 裁剪人脸区域 + x1, y1, x2, y2 = box + face_img = image[y1:y2, x1:x2] + + # 转换为灰度图像 + face_gray = cv2.cvtColor(face_img, cv2.COLOR_BGR2GRAY) + + # 调整大小为标准尺寸 + face_resized = cv2.resize(face_gray, self.face_size) + + # 使用直方图均衡化增强对比度 + face_normalized = cv2.equalizeHist(face_resized) + + # 简单特征提取:将图像展平为向量 + # 在实际应用中,可以使用更复杂的特征提取方法,如HOG或深度特征 + feature_vector = face_normalized.flatten().astype(np.float32) + + # 对特征向量进行标准化 + if np.linalg.norm(feature_vector) > 0: + feature_vector = feature_vector / np.linalg.norm(feature_vector) + + return feature_vector + + def compare_faces(self, known_feature, unknown_feature): + """比较两个人脸特征的相似度 + + Args: + known_feature: 已知人脸特征 + unknown_feature: 待比对人脸特征 + + Returns: + float: 相似度 (0-1之间,越大表示越相似) + """ + if known_feature is None or unknown_feature is None: + return 0.0 + + # 计算欧氏距离 + face_distance = np.linalg.norm(known_feature - unknown_feature) + # 将距离转换为相似度 + similarity = 1.0 / (1.0 + face_distance) + + return similarity + + def identify_face(self, image, known_faces, box=None): + """识别人脸 + + Args: + image: 输入图像 + known_faces: 已知人脸特征列表 [(person_id, name, feature_vector), ...] + box: 可选的人脸边界框 + + Returns: + tuple or None: (person_id, name, similarity) 或 None(如果未匹配) + """ + # 提取人脸特征 + face_feature = self.extract_face_features(image, box) + if face_feature is None: + return None + + # 寻找最佳匹配 + best_match = None + best_similarity = 0 + + for person_id, name, known_feature in known_faces: + similarity = self.compare_faces(known_feature, face_feature) + + if similarity > best_similarity: + best_similarity = similarity + best_match = (person_id, name, similarity) + + # 如果最佳匹配的相似度低于阈值,返回None + if best_match and best_match[2] >= self.similarity_threshold: + return best_match + + return None + +def draw_face_box(image, box, name=None, similarity=None, color=(0, 255, 0), thickness=2): + """在图像上绘制人脸边界框和标签 + + Args: + image: 输入图像 + box: 人脸边界框 [x1, y1, x2, y2, ...] + name: 可选的名称标签 + similarity: 可选的相似度标签 + color: 边界框颜色 (BGR格式) + thickness: 边界框线条粗细 + + Returns: + numpy.ndarray: 绘制了边界框的图像 + """ + x1, y1, x2, y2 = box[:4] + + # 绘制边界框 + cv2.rectangle(image, (x1, y1), (x2, y2), color, thickness) + + # 如果提供了名称,则绘制标签 + if name or similarity is not None: + label = "" + if name: + label += name + if similarity is not None: + label += f" ({similarity:.2f})" + + # 设置标签背景 + (label_width, label_height), baseline = cv2.getTextSize( + label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1 + ) + cv2.rectangle( + image, + (x1, y1 - label_height - 5), + (x1 + label_width, y1), + color, + -1 # 填充矩形 + ) + + # 绘制标签文本 + cv2.putText( + image, + label, + (x1, y1 - 5), + cv2.FONT_HERSHEY_SIMPLEX, + 0.5, + (0, 0, 0), # 黑色文本 + 1 + ) + + return image diff --git a/models/yolov5l.pt b/models/yolov5l.pt new file mode 100644 index 0000000..03e98f1 Binary files /dev/null and b/models/yolov5l.pt differ diff --git a/register_face.py b/register_face.py new file mode 100644 index 0000000..090c66c --- /dev/null +++ b/register_face.py @@ -0,0 +1,118 @@ +import cv2 +import time +import argparse +import os +from face_utils import FaceDetector, FaceRecognizer, draw_face_box +from face_db import FaceDatabase +from voice_prompt import VoicePrompt + +def parse_args(): + """解析命令行参数""" + parser = argparse.ArgumentParser(description='人脸注册程序') + parser.add_argument('--name', type=str, help='要注册的人员姓名') + parser.add_argument('--camera', type=int, default=0, help='摄像头索引') + parser.add_argument('--threshold', type=float, default=0.5, help='人脸检测置信度阈值') + return parser.parse_args() + +def register_face(): + """人脸注册主函数""" + args = parse_args() + + # 初始化组件 + voice = VoicePrompt() + detector = FaceDetector(conf_thres=args.threshold) + recognizer = FaceRecognizer(detector=detector) + db = FaceDatabase() + + # 打开摄像头 + cap = cv2.VideoCapture(args.camera) + if not cap.isOpened(): + print("无法打开摄像头") + return + + # 获取姓名(如果命令行没有提供,则交互输入) + name = args.name + if name is None: + name = input("请输入姓名: ") + + # 创建新的人员记录 + person_id = db.add_person(name) + print(f"已创建人员记录: {name} (ID: {person_id})") + voice.speak(f"开始为{name}注册人脸信息,请正视摄像头", block=True) + + # 采集计数器和状态 + face_count = 0 + target_count = 5 # 需要采集的人脸图像数量 + last_capture_time = 0 + capture_interval = 1.0 # 每次采集间隔秒数 + + print(f"请看向摄像头,将采集{target_count}张人脸图像...") + + while face_count < target_count: + # 读取摄像头帧 + ret, frame = cap.read() + if not ret: + print("无法获取摄像头画面") + break + + # 镜像翻转以便更直观 + frame = cv2.flip(frame, 1) + + # 显示帧 + display_frame = frame.copy() + cv2.putText( + display_frame, + f"请直视摄像头 ({face_count}/{target_count})", + (10, 30), + cv2.FONT_HERSHEY_SIMPLEX, + 0.7, + (0, 255, 0), + 2 + ) + + # 检测人脸 + faces = detector.detect_faces(frame) + + # 如果检测到人脸 + current_time = time.time() + if faces and (current_time - last_capture_time) >= capture_interval: + # 只处理最大的一个人脸 + faces.sort(key=lambda x: (x[2]-x[0])*(x[3]-x[1]), reverse=True) + face_box = faces[0] + + # 绘制人脸框 + draw_face_box(display_frame, face_box) + + # 提取人脸特征 + face_feature = recognizer.extract_face_features(frame, face_box[:4]) + + if face_feature is not None: + # 保存人脸特征到数据库 + feature_id = db.add_face_feature(person_id, face_feature) + + face_count += 1 + last_capture_time = current_time + print(f"已采集第 {face_count}/{target_count} 张人脸") + voice.speak(f"已完成第{face_count}次采集", block=False) + + # 显示图像 + cv2.imshow("人脸注册", display_frame) + + # 按ESC键退出 + key = cv2.waitKey(1) & 0xFF + if key == 27: + break + + # 释放资源 + cap.release() + cv2.destroyAllWindows() + db.close() + + if face_count >= target_count: + print(f"成功完成{name}的人脸注册!") + voice.speak(f"已完成{name}的人脸信息注册", block=True) + else: + print("人脸注册未完成") + +if __name__ == "__main__": + register_face() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f91bbd1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +torch>=1.7.0 +torchvision>=0.8.1 +opencv-python>=4.1.2 +numpy>=1.18.5 +Pillow>=9.0.0 +PyYAML>=5.3.1 +tqdm>=4.41.0 +matplotlib>=3.2.2 +scipy>=1.4.1 +pyttsx3>=2.90 +face-recognition>=1.3.0 +scikit-learn>=0.24.2 +sqlite3-wrapper>=0.1.2 diff --git a/ultralytics-yolov5-v7.0-411-gf4d8a84.zip b/ultralytics-yolov5-v7.0-411-gf4d8a84.zip new file mode 100644 index 0000000..94cc884 Binary files /dev/null and b/ultralytics-yolov5-v7.0-411-gf4d8a84.zip differ diff --git a/voice_prompt.py b/voice_prompt.py new file mode 100644 index 0000000..e5a2e62 --- /dev/null +++ b/voice_prompt.py @@ -0,0 +1,120 @@ +import pyttsx3 +import threading +import queue +import time + +class VoicePrompt: + def __init__(self, rate=150, volume=1.0, voice_id=None): + """初始化语音提示组件 + + Args: + rate: 语速 + volume: 音量 (0.0 到 1.0) + voice_id: 语音ID (None表示使用默认值) + """ + self.rate = rate + self.volume = volume + self.voice_id = voice_id + + # 创建语音消息队列 + self.speech_queue = queue.Queue() + + # 启动单独的线程来处理语音播报 + self.speech_thread = threading.Thread(target=self._speech_worker, daemon=True) + self.speech_thread.start() + + # 防止多次初始化 + self.initialized = False + + def _init_engine(self): + """初始化语音引擎""" + if not hasattr(self, 'engine') or self.engine is None: + try: + self.engine = pyttsx3.init() + self.engine.setProperty('rate', self.rate) + self.engine.setProperty('volume', self.volume) + + # 设置语音(如果提供了voice_id) + if self.voice_id: + self.engine.setProperty('voice', self.voice_id) + else: + # 尝试设置中文语音(如果有的话) + voices = self.engine.getProperty('voices') + for voice in voices: + if 'chinese' in voice.id.lower() or 'zh' in voice.id.lower(): + self.engine.setProperty('voice', voice.id) + break + self.initialized = True + except Exception as e: + print(f"初始化语音引擎失败: {e}") + + def _speech_worker(self): + """后台线程,从队列中获取文本并播报""" + # 初始化引擎(仅在这个区域创建和使用引擎) + self._init_engine() + + while True: + try: + # 从队列中读取文本 + text = self.speech_queue.get() + if text == "__EXIT__": # 退出信号 + break + + if not self.initialized: + self._init_engine() + + # 仅当初始化成功时播放语音 + if self.initialized: + try: + self.engine.say(text) + self.engine.runAndWait() + except Exception as e: + print(f"语音播报错误: {e}") + # 重新初始化引擎 + try: + self.engine = None + self._init_engine() + except Exception as e2: + print(f"重新初始化引擎失败: {e2}") + time.sleep(1) # 避免循环过快 + + # 通知队列任务完成 + self.speech_queue.task_done() + except Exception as e: + print(f"语音工作线程错误: {e}") + time.sleep(0.5) # 防止错误时CPU资源过度消耗 + + def speak(self, text, block=False): + """将要播报的文本添加到队列 + + Args: + text: 要播报的文本 + block: 是否等待播报完成 + """ + try: + # 将文本加入队列 + self.speech_queue.put(text) + + # 如果是阻塞模式,等待这个任务完成 + if block: + self.speech_queue.join() + except Exception as e: + print(f"添加语音文本到队列失败: {e}") + + def get_available_voices(self): + """获取所有可用的语音 + + Returns: + list: 可用语音列表 + """ + voices = self.engine.getProperty('voices') + return [(voice.id, voice.name) for voice in voices] + +# 测试代码 +if __name__ == "__main__": + voice = VoicePrompt() + print("可用语音:") + for voice_id, name in voice.get_available_voices(): + print(f" - {name} ({voice_id})") + + voice.speak("您好,欢迎使用人脸识别系统", block=True)