From a96c78673f38f2d9ac3ad538dc5686a7d2553f0c Mon Sep 17 00:00:00 2001 From: Liu-Kui111 <2072827579@qq.com> Date: Sun, 14 Dec 2025 20:54:44 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E9=80=89=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AI_Drone_Face_Recognition_Human_Tracking/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/AI_Drone_Face_Recognition_Human_Tracking/README.md diff --git a/src/AI_Drone_Face_Recognition_Human_Tracking/README.md b/src/AI_Drone_Face_Recognition_Human_Tracking/README.md new file mode 100644 index 0000000000..bdca2970c7 --- /dev/null +++ b/src/AI_Drone_Face_Recognition_Human_Tracking/README.md @@ -0,0 +1 @@ +该项目是一个基于人工智能的无人机控制系统,能够用YOLOv8检测图像中的人物,用OpenCV检测人脸,用DeepFace识别人,并能飞近用户从视频流中选定的目标。 \ No newline at end of file From e57010ea6a277e5d4dffbe63eec50e3615a86c56 Mon Sep 17 00:00:00 2001 From: Liu-Kui111 <2072827579@qq.com> Date: Mon, 15 Dec 2025 09:57:24 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=89=93=E5=BC=80=E6=91=84=E5=83=8F?= =?UTF-8?q?=E5=A4=B4=EF=BC=8C=E5=BD=95=E5=85=A5=E5=B9=B6=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E4=BA=BA=E8=84=B8=E5=8F=8A=E5=90=8D=E5=AD=97=EF=BC=8C=E6=94=BE?= =?UTF-8?q?=E5=85=A5=E6=95=B0=E6=8D=AE=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../utils.py.py | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/AI_Drone_Face_Recognition_Human_Tracking/utils.py.py diff --git a/src/AI_Drone_Face_Recognition_Human_Tracking/utils.py.py b/src/AI_Drone_Face_Recognition_Human_Tracking/utils.py.py new file mode 100644 index 0000000000..e7962cbefe --- /dev/null +++ b/src/AI_Drone_Face_Recognition_Human_Tracking/utils.py.py @@ -0,0 +1,160 @@ +import os +import cv2 +from deepface import DeepFace +import numpy as np + + +class FaceDB: + """人脸数据库管理类""" + + def __init__(self, face_dir="faces"): + self.face_dir = face_dir + self.face_data = {} # 存储{人名: 人脸特征} + self._load_face_database() + + def _load_face_database(self): + """加载人脸库中的所有人脸特征""" + if not os.path.exists(self.face_dir): + os.makedirs(self.face_dir) + print(f"创建人脸库目录: {self.face_dir}") + + self.face_data.clear() # 清空旧数据 + for img_name in os.listdir(self.face_dir): + if img_name.endswith((".jpg", ".png")): + person_name = os.path.splitext(img_name)[0] + img_path = os.path.join(self.face_dir, img_name) + try: + # 提取人脸特征(Facenet模型) + embedding = DeepFace.represent( + img_path, + model_name="Facenet", + enforce_detection=True + )[0]["embedding"] + self.face_data[person_name] = embedding + print(f"✅ 成功加载人脸: {person_name}") + except Exception as e: + print(f"❌ 加载{img_name}失败: {str(e)[:50]}...") + + def recognize_face(self, frame, threshold=0.6): + """识别人脸,返回匹配的人名或None""" + try: + # 提取当前帧人脸特征 + frame_embedding = DeepFace.represent( + frame, + model_name="Facenet", + enforce_detection=True + )[0]["embedding"] + + # 对比人脸库(计算欧式距离) + min_distance = float("inf") + matched_name = None + for name, embedding in self.face_data.items(): + distance = np.linalg.norm(np.array(frame_embedding) - np.array(embedding)) + if distance < min_distance and distance < threshold: + min_distance = distance + matched_name = name + return matched_name, min_distance # 新增返回距离,方便调试 + except Exception as e: + print(f"❌ 人脸识别失败: {str(e)[:50]}...") + return None, float("inf") + + def add_face(self, frame, person_name): + """添加人脸到数据库""" + # 先检测人脸,确保有效 + try: + # 验证帧中有人脸 + DeepFace.extract_faces(frame, enforce_detection=True) + # 保存人脸图片 + img_path = os.path.join(self.face_dir, f"{person_name}.jpg") + cv2.imwrite(img_path, frame) + # 重新加载数据库 + self._load_face_database() + print(f"✅ 已添加[{person_name}]到人脸库,路径: {img_path}") + return True + except Exception as e: + print(f"❌ 添加人脸失败: {str(e)[:50]}...") + return False + + def list_faces(self): + """列出人脸库中所有已注册的人名""" + return list(self.face_data.keys()) + + +def draw_detection_box(frame, bbox, label, color=(0, 255, 0)): + """绘制检测框和标签""" + x1, y1, x2, y2 = map(int, bbox) + cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) + cv2.putText( + frame, label, (x1, y1 - 10), + cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2 + ) + return frame + + +# ===================== 独立运行测试逻辑 ===================== +if __name__ == "__main__": + # 初始化人脸库 + face_db = FaceDB(face_dir="faces") + print(f"\n当前人脸库已注册: {face_db.list_faces()}\n") + + # 打开摄像头 + cap = cv2.VideoCapture(0) + if not cap.isOpened(): + print("❌ 无法打开摄像头,请检查设备!") + exit(1) + + # 设置摄像头分辨率 + cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) + + print("=" * 50) + print("🎯 FaceDB 测试工具(独立运行模式)") + print("按键说明:") + print(" a → 添加当前帧人脸到数据库") + print(" r → 识别当前帧人脸") + print(" l → 列出已注册人脸") + print(" q → 退出程序") + print("=" * 50) + + while True: + # 读取摄像头帧 + ret, frame = cap.read() + if not ret: + print("❌ 无法读取摄像头画面!") + break + + # 显示实时画面 + cv2.imshow("FaceDB Test (utils.py)", frame) + + # 按键处理 + key = cv2.waitKey(1) & 0xFF + if key == ord('q'): + print("🔚 退出测试程序...") + break + + elif key == ord('a'): + # 添加人脸 + person_name = input("\n请输入要添加的人名: ").strip() + if not person_name: + print("❌ 人名不能为空!") + continue + face_db.add_face(frame, person_name) + + elif key == ord('r'): + # 识别人脸 + print("\n🔍 正在识别人脸...") + name, distance = face_db.recognize_face(frame) + if name: + print(f"✅ 识别成功!匹配到: {name} (距离: {distance:.4f})") + else: + print(f"❌ 未识别到匹配人脸(最小距离: {distance:.4f})") + + elif key == ord('l'): + # 列出已注册人脸 + faces = face_db.list_faces() + print(f"\n📋 人脸库已注册名单: {faces if faces else '空'}") + + # 释放资源 + cap.release() + cv2.destroyAllWindows() + print("\n✅ 资源已释放,程序结束") \ No newline at end of file From 1552a8813f6a42b80a47311e14fb71c3e532c90d Mon Sep 17 00:00:00 2001 From: Liu-Kui111 <2072827579@qq.com> Date: Mon, 15 Dec 2025 10:59:33 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E8=AF=AD?= =?UTF-8?q?=E9=9F=B3=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=AE=9E=E7=8E=B0=E4=BA=86?= =?UTF-8?q?=E4=BA=94=E4=B8=AA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voice_module.py | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 src/AI_Drone_Face_Recognition_Human_Tracking/voice_module.py diff --git a/src/AI_Drone_Face_Recognition_Human_Tracking/voice_module.py b/src/AI_Drone_Face_Recognition_Human_Tracking/voice_module.py new file mode 100644 index 0000000000..168813fa82 --- /dev/null +++ b/src/AI_Drone_Face_Recognition_Human_Tracking/voice_module.py @@ -0,0 +1,172 @@ +import pyttsx3 +import platform + + +class VoiceAssistant: + """语音助手类(增强兼容性+详细日志)""" + + def __init__(self): + self.engine = None + self._init_engine() # 初始化语音引擎 + + def _init_engine(self): + """初始化语音引擎,兼容不同系统""" + try: + self.engine = pyttsx3.init() + # 基础配置 + self.engine.setProperty("rate", 150) # 语速(100-200为宜) + self.engine.setProperty("volume", 1.0) # 音量(0.0-1.0) + + # 系统适配:Windows优先使用中文语音包 + system = platform.system() + if system == "Windows": + self._set_chinese_voice() + + print("✅ 语音引擎初始化成功!") + except Exception as e: + print(f"❌ 语音引擎初始化失败: {str(e)}") + self.engine = None + + def _set_chinese_voice(self): + """Windows系统设置中文语音包(需提前安装)""" + try: + voices = self.engine.getProperty('voices') + # 遍历语音包,选择中文语音 + for voice in voices: + if "zh-CN" in voice.id or "中文" in voice.name: + self.engine.setProperty('voice', voice.id) + print(f"✅ 已切换为中文语音包: {voice.name}") + break + else: + print("⚠️ 未找到中文语音包,使用默认语音(可能为英文)") + except Exception as e: + print(f"⚠️ 设置中文语音包失败: {str(e)}") + + def speak(self, text): + """文本转语音(增加容错)""" + if not self.engine: + print("❌ 语音引擎未初始化,无法合成语音") + return + + if not text.strip(): + print("❌ 合成文本不能为空!") + return + + try: + print(f"🔊 正在合成语音: {text}") + self.engine.say(text) + self.engine.runAndWait() # 等待语音播放完成 + print("✅ 语音合成完成!") + except Exception as e: + print(f"❌ 语音合成失败: {str(e)}") + + def list_voices(self): + """列出当前系统可用的语音包(调试用)""" + if not self.engine: + print("❌ 语音引擎未初始化,无法获取语音包") + return [] + + try: + voices = self.engine.getProperty('voices') + print("\n📋 系统可用语音包列表:") + for idx, voice in enumerate(voices): + print(f" [{idx}] 名称: {voice.name} | ID: {voice.id} | 语言: {voice.languages}") + return voices + except Exception as e: + print(f"❌ 获取语音包列表失败: {str(e)}") + return [] + + def adjust_settings(self, rate=None, volume=None): + """调整语速/音量(调试用)""" + if not self.engine: + print("❌ 语音引擎未初始化,无法调整设置") + return + + if rate is not None: + if 50 <= rate <= 300: + self.engine.setProperty("rate", rate) + print(f"✅ 语速已调整为: {rate}") + else: + print("⚠️ 语速范围需在50-300之间!") + + if volume is not None: + if 0.0 <= volume <= 1.0: + self.engine.setProperty("volume", volume) + print(f"✅ 音量已调整为: {volume}") + else: + print("⚠️ 音量范围需在0.0-1.0之间!") + + def __del__(self): + """析构函数,释放引擎资源""" + if self.engine: + try: + self.engine.stop() + except: + pass + + +# ===================== 独立运行测试逻辑 ===================== +if __name__ == "__main__": + # 初始化语音助手 + voice_assist = VoiceAssistant() + + # 打印系统信息 + print("\n" + "=" * 50) + print("🎯 语音助手测试工具(独立运行模式)") + print(f"💻 当前系统: {platform.system()}") + print("=" * 50) + + # 列出可用语音包 + voice_assist.list_voices() + + # 交互菜单 + print("\n📢 操作菜单:") + print(" 1 → 播放测试文本(中文)") + print(" 2 → 播放测试文本(英文)") + print(" 3 → 自定义文本播放") + print(" 4 → 调整语速(默认150)") + print(" 5 → 调整音量(默认1.0)") + print(" q → 退出程序") + print("-" * 30) + + while True: + choice = input("\n请输入操作编号: ").strip() + + if choice == "q": + print("🔚 退出语音助手测试程序...") + break + + elif choice == "1": + # 中文测试 + voice_assist.speak("你好,我是AI无人机语音助手,测试语音合成功能正常!") + + elif choice == "2": + # 英文测试 + voice_assist.speak("Hello, I am the AI drone voice assistant, test speech synthesis function is normal!") + + elif choice == "3": + # 自定义文本 + custom_text = input("请输入要合成的文本: ").strip() + if custom_text: + voice_assist.speak(custom_text) + else: + print("❌ 自定义文本不能为空!") + + elif choice == "4": + # 调整语速 + try: + new_rate = int(input("请输入新语速(50-300): ").strip()) + voice_assist.adjust_settings(rate=new_rate) + except ValueError: + print("❌ 请输入有效的数字!") + + elif choice == "5": + # 调整音量 + try: + new_volume = float(input("请输入新音量(0.0-1.0): ").strip()) + voice_assist.adjust_settings(volume=new_volume) + except ValueError: + print("❌ 请输入有效的数字(如0.5)!") + + else: + print("❌ 无效的操作编号,请重新输入!") \ No newline at end of file