#!/usr/bin/env python3
"""
DeepFace人脸识别Demo
功能：
1. 摄像头实时人脸识别（输出名字、年龄、性别、情绪）
2. 照片人脸识别
3. 人脸注册到数据库
"""

import cv2
import numpy as np
import os
import time
import argparse
from deepface import DeepFace
import pickle
from pathlib import Path


class DeepFaceDemo:
    """DeepFace人脸识别Demo类"""
    
    def __init__(self, db_path="face_database.pkl", detector_backend="opencv"):
        """
        初始化DeepFace Demo
        
        Args:
            db_path: 人脸数据库保存路径
            detector_backend: 人脸检测后端（opencv, ssd, mtcnn, retinaface等）
        """
        self.db_path = db_path
        self.detector_backend = detector_backend
        self.face_database = {}
        self.load_database()
        
        print(f"DeepFace Demo初始化完成")
        print(f"检测后端: {detector_backend}")
        print(f"数据库路径: {db_path}")
        print(f"已加载 {len(self.face_database)} 张人脸")
    
    def load_database(self):
        """从文件加载人脸数据库"""
        if os.path.exists(self.db_path):
            try:
                with open(self.db_path, 'rb') as f:
                    self.face_database = pickle.load(f)
                print(f"从 {self.db_path} 加载了 {len(self.face_database)} 张人脸")
            except Exception as e:
                print(f"加载数据库失败: {e}")
                self.face_database = {}
        else:
            print(f"未找到数据库文件 {self.db_path}，将创建新数据库")
            self.face_database = {}
    
    def save_database(self):
        """保存人脸数据库到文件"""
        try:
            with open(self.db_path, 'wb') as f:
                pickle.dump(self.face_database, f)
            print(f"人脸数据库已保存到 {self.db_path}")
            return True
        except Exception as e:
            print(f"保存数据库失败: {e}")
            return False
    
    def analyze_face(self, image):
        """
        分析人脸属性（年龄、性别、情绪、种族）
        
        Args:
            image: 图像（numpy数组或文件路径）
        
        Returns:
            dict: 包含年龄、性别、情绪等信息的字典
        """
        try:
            result = DeepFace.analyze(
                image,
                actions=['age', 'gender', 'emotion', 'race'],
                detector_backend=self.detector_backend,
                enforce_detection=False
            )
            return result
        except Exception as e:
            print(f"分析人脸时出错: {e}")
            return None
    
    def recognize_face(self, image, threshold=0.4):
        """
        识别人脸，返回最匹配的名字
        
        Args:
            image: 图像（numpy数组或文件路径）
            threshold: 识别阈值
        
        Returns:
            tuple: (name, confidence) - 识别出的名字和置信度
        """
        if not self.face_database:
            return "Unknown", 0.0
        
        try:
            # 在数据库中查找匹配的人脸
            dfs = DeepFace.find(
                image,
                db_path="database",
                detector_backend=self.detector_backend,
                enforce_detection=False
            )
            
            if dfs is not None and len(dfs) > 0 and not dfs[0].empty:
                # 获取最佳匹配
                best_match = dfs[0].iloc[0]
                confidence = best_match['distance']
                
                # 从文件名中提取名字
                identity = best_match['identity']
                name = os.path.basename(os.path.dirname(identity))
                
                # 如果距离小于阈值，则认为是匹配
                if confidence < threshold:
                    return name, confidence
            
            return "Unknown", 0.0
            
        except Exception as e:
            print(f"识别人脸时出错: {e}")
            return "Unknown", 0.0
    
    def register_face(self, image_path, name):
        """
        注册人脸到数据库
        
        Args:
            image_path: 人脸图像路径
            name: 人脸名字
        
        Returns:
            bool: 注册成功返回True
        """
        try:
            # 创建数据库目录
            db_dir = Path("database") / name
            db_dir.mkdir(parents=True, exist_ok=True)
            
            # 复制图像到数据库
            img = cv2.imread(image_path)
            if img is None:
                print(f"无法读取图像: {image_path}")
                return False
            
            # 保存图像到数据库
            save_path = db_dir / f"{name}_{int(time.time())}.jpg"
            cv2.imwrite(str(save_path), img)
            
            # 更新内存数据库
            self.face_database[name] = str(save_path)
            
            print(f"成功注册人脸: {name} -> {save_path}")
            return True
            
        except Exception as e:
            print(f"注册人脸时出错: {e}")
            return False
    
    def register_face_from_frame(self, frame, name):
        """
        从摄像头帧注册人脸
        
        Args:
            frame: 图像帧
            name: 人脸名字
        
        Returns:
            bool: 注册成功返回True
        """
        try:
            # 创建数据库目录
            db_dir = Path("database") / name
            db_dir.mkdir(parents=True, exist_ok=True)
            
            # 保存图像到数据库
            save_path = db_dir / f"{name}_{int(time.time())}.jpg"
            cv2.imwrite(str(save_path), frame)
            
            # 更新内存数据库
            self.face_database[name] = str(save_path)
            
            print(f"成功注册人脸: {name} -> {save_path}")
            return True
            
        except Exception as e:
            print(f"从帧注册人脸时出错: {e}")
            return False
    
    def process_image(self, image_path):
        """
        处理单张图像，显示人脸分析结果
        
        Args:
            image_path: 图像路径
        """
        print(f"\n处理图像: {image_path}")
        
        # 读取图像
        img = cv2.imread(image_path)
        if img is None:
            print(f"无法读取图像: {image_path}")
            return
        
        # 分析人脸
        result = self.analyze_face(img)
        
        if result is None or len(result) == 0:
            print("未检测到人脸")
            return
        
        # 处理每个检测到的人脸
        for i, face_info in enumerate(result):
            print(f"\n人脸 {i + 1}:")
            print(f"  年龄: {face_info.get('age', 'N/A')}")
            print(f"  性别: {face_info.get('dominant_gender', 'N/A')}")
            print(f"  情绪: {face_info.get('dominant_emotion', 'N/A')}")
            print(f"  种族: {face_info.get('dominant_race', 'N/A')}")
            
            # 识别人脸
            name, confidence = self.recognize_face(img)
            print(f"  识别: {name} (置信度: {confidence:.3f})")
            
            # 绘制结果
            if 'region' in face_info:
                region = face_info['region']
                x, y, w, h = region['x'], region['y'], region['w'], region['h']
                
                # 绘制人脸框
                cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
                
                # 准备显示文本
                text_lines = [
                    f"{name}",
                    f"Age: {face_info.get('age', 'N/A')}",
                    f"Gender: {face_info.get('dominant_gender', 'N/A')}",
                    f"Emotion: {face_info.get('dominant_emotion', 'N/A')}"
                ]
                
                # 绘制文本
                for j, line in enumerate(text_lines):
                    cv2.putText(img, line, (x, y - 10 - j * 20), 
                               cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
        
        # 显示结果
        cv2.imshow('Face Recognition Result', img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    
    def run_camera(self, camera_id=0, enable_recognition=True):
        """
        运行摄像头实时人脸识别
        
        Args:
            camera_id: 摄像头ID
            enable_recognition: 是否启用人脸识别（否则只显示分析结果）
        """
        print(f"\n打开摄像头 {camera_id}...")
        
        # 打开摄像头
        cap = cv2.VideoCapture(camera_id)
        if not cap.isOpened():
            print(f"无法打开摄像头 {camera_id}")
            return
        
        # 设置摄像头参数
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        cap.set(cv2.CAP_PROP_FPS, 30)
        
        # 等待摄像头稳定
        time.sleep(1)
        
        print("开始实时人脸识别")
        print("按 'q' 键退出")
        print("按 's' 键保存当前帧")
        print("按 'r' 键注册当前人脸")
        
        frame_count = 0
        fps_start_time = time.time()
        
        while True:
            # 读取帧
            ret, frame = cap.read()
            if not ret:
                print("无法读取帧")
                break
            
            frame_count += 1
            
            # 计算FPS
            if frame_count % 30 == 0:
                fps = 30 / (time.time() - fps_start_time)
                fps_start_time = time.time()
                print(f"FPS: {fps:.1f}")
            
            # 分析人脸
            result = self.analyze_face(frame)
            
            if result is not None and len(result) > 0:
                for face_info in result:
                    # 获取人脸区域
                    if 'region' in face_info:
                        region = face_info['region']
                        x, y, w, h = region['x'], region['y'], region['w'], region['h']
                        
                        # 绘制人脸框
                        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
                        
                        # 获取属性
                        age = face_info.get('age', 'N/A')
                        gender = face_info.get('dominant_gender', 'N/A')
                        emotion = face_info.get('dominant_emotion', 'N/A')
                        
                        # 识别人脸（如果启用）
                        if enable_recognition:
                            name, confidence = self.recognize_face(frame)
                            display_name = name
                        else:
                            display_name = "Unknown"
                        
                        # 准备显示文本
                        text_lines = [
                            f"{display_name}",
                            f"Age: {age}",
                            f"Gender: {gender}",
                            f"Emotion: {emotion}"
                        ]
                        
                        # 绘制文本
                        for j, line in enumerate(text_lines):
                            cv2.putText(frame, line, (x, y - 10 - j * 20), 
                                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
            
            # 显示帧
            cv2.imshow('DeepFace Real-time Recognition', frame)
            
            # 键盘控制
            key = cv2.waitKey(1) & 0xFF
            if key == ord('q'):
                print("退出...")
                break
            elif key == ord('s'):
                # 保存当前帧
                save_path = f"capture_{int(time.time())}.jpg"
                cv2.imwrite(save_path, frame)
                print(f"已保存帧到: {save_path}")
            elif key == ord('r'):
                # 注册当前人脸
                name = input("请输入要注册的名字: ")
                if name:
                    if self.register_face_from_frame(frame, name):
                        self.save_database()
                        print(f"成功注册: {name}")
        
        # 释放资源
        cap.release()
        cv2.destroyAllWindows()
        
        # 保存数据库
        self.save_database()
    
    def register_from_camera(self, camera_id=0):
        """
        从摄像头注册人脸
        
        Args:
            camera_id: 摄像头ID
        """
        print(f"\n打开摄像头 {camera_id} 进行人脸注册...")
        
        # 打开摄像头
        cap = cv2.VideoCapture(camera_id)
        if not cap.isOpened():
            print(f"无法打开摄像头 {camera_id}")
            return
        
        # 设置摄像头参数
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        
        # 等待摄像头稳定
        time.sleep(1)
        
        print("按 'c' 键捕获人脸，按 'q' 键退出")
        
        while True:
            # 读取帧
            ret, frame = cap.read()
            if not ret:
                print("无法读取帧")
                break
            
            # 显示帧
            cv2.imshow('Register Face', frame)
            
            # 键盘控制
            key = cv2.waitKey(1) & 0xFF
            if key == ord('q'):
                print("退出...")
                break
            elif key == ord('c'):
                # 捕获并注册人脸
                name = input("请输入要注册的名字: ")
                if name:
                    if self.register_face_from_frame(frame, name):
                        self.save_database()
                        print(f"成功注册: {name}")
                    else:
                        print("注册失败")
        
        # 释放资源
        cap.release()
        cv2.destroyAllWindows()


def main():
    """主函数"""
    parser = argparse.ArgumentParser(description="DeepFace人脸识别Demo")
    parser.add_argument("--camera", type=int, default=None, 
                       help="使用摄像头进行实时识别（指定摄像头ID）")
    parser.add_argument("--image", type=str, default=None, 
                       help="处理单张图像")
    parser.add_argument("--register", type=str, default=None, 
                       help="注册人脸（图像路径）")
    parser.add_argument("--register-camera", type=int, default=None, 
                       help="从摄像头注册人脸（指定摄像头ID）")
    parser.add_argument("--name", type=str, default=None, 
                       help="注册时使用的名字")
    parser.add_argument("--db-path", type=str, default="face_database.pkl", 
                       help="人脸数据库路径")
    parser.add_argument("--detector", type=str, default="opencv", 
                       choices=["opencv", "ssd", "mtcnn", "retinaface", "mediapipe"],
                       help="人脸检测后端")
    parser.add_argument("--no-recognition", action="store_true", 
                       help="禁用人脸识别（只显示分析结果）")
    
    args = parser.parse_args()
    
    # 创建DeepFace Demo实例
    demo = DeepFaceDemo(db_path=args.db_path, detector_backend=args.detector)
    
    # 根据参数执行不同操作
    if args.register:
        # 注册人脸
        if args.name is None:
            args.name = input("请输入要注册的名字: ")
        if demo.register_face(args.register, args.name):
            demo.save_database()
            print(f"成功注册: {args.name}")
        else:
            print("注册失败")
    
    elif args.register_camera is not None:
        # 从摄像头注册人脸
        if args.name is None:
            args.name = input("请输入要注册的名字: ")
        demo.register_from_camera(args.register_camera)
    
    elif args.camera is not None:
        # 运行摄像头实时识别
        enable_recognition = not args.no_recognition
        demo.run_camera(camera_id=args.camera, enable_recognition=enable_recognition)
    
    elif args.image:
        # 处理单张图像
        demo.process_image(args.image)
    
    else:
        # 默认运行摄像头
        print("未指定操作，运行摄像头实时识别...")
        demo.run_camera(camera_id=0)


if __name__ == "__main__":
    main()
