人脸识别训练完整代码,直接用

       训练数据文件夹内按照姓名分文件夹存储照片,训练完成后需要保存模型文件。打开模型文件,打开照片就可以进行识别。

        需要安装CMAKE和相关库(按照import),界面是Tkinter生成的简单界面。

import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
import pickle
import face_recognition
from PIL import Image, ImageTk, ImageDraw
import numpy as np

class FaceRecognitionApp:
    def __init__(self, root):
        self.root = root
        self.root.title("人脸识别人工界面应用")
        self.root.geometry("1200x800")
        
        # 设置中文字体
        self.font = ('SimHei', 10)
        
        # 存储已知人脸的编码和名称
        self.known_face_encodings = []
        self.known_face_names = []
        
        # 当前处理的图像
        self.current_image = None
        self.current_image_path = None
        self.recognized_image = None
        
        # 创建界面
        self.create_widgets()
        
    def create_widgets(self):
        # 创建顶部按钮框架
        top_frame = tk.Frame(self.root)
        top_frame.pack(fill=tk.X, padx=10, pady=10)
        
        # 训练按钮
        self.train_btn = tk.Button(
            top_frame, text="训练模型", command=self.train_model, font=self.font)
        self.train_btn.pack(side=tk.LEFT, padx=5)
        
        # 加载编码文件按钮
        self.load_encodings_btn = tk.Button(
            top_frame, text="加载编码文件", command=self.load_encodings, font=self.font)
        self.load_encodings_btn.pack(side=tk.LEFT, padx=5)
        
        # 选择图片按钮
        self.select_image_btn = tk.Button(
            top_frame, text="选择图片", command=self.select_image, font=self.font, state=tk.DISABLED)
        self.select_image_btn.pack(side=tk.LEFT, padx=5)
        
        # 识别按钮
        self.recognize_btn = tk.Button(
            top_frame, text="开始识别", command=self.recognize_faces, font=self.font, state=tk.DISABLED)
        self.recognize_btn.pack(side=tk.LEFT, padx=5)
        
        # 创建图像显示框架
        image_frame = tk.Frame(self.root)
        image_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 原图显示区域
        self.original_frame = tk.LabelFrame(image_frame, text="原图", font=self.font)
        self.original_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
        
        self.original_label = tk.Label(self.original_frame)
        self.original_label.pack(fill=tk.BOTH, expand=True)
        
        # 结果显示区域
        self.result_frame = tk.LabelFrame(image_frame, text="识别结果", font=self.font)
        self.result_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5)
        
        self.result_label = tk.Label(self.result_frame)
        self.result_label.pack(fill=tk.BOTH, expand=True)
        
        # 创建结果列表框架
        results_frame = tk.LabelFrame(self.root, text="识别结果", font=self.font)
        results_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 提取结果列表和滚动条的创建到单独方法
        self._create_results_listbox(results_frame)
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        self.status_bar = tk.Label(self.root, textvariable=self.status_var, bd=1, relief=tk.SUNKEN, anchor=tk.W, font=self.font)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)

    def _create_results_listbox(self, results_frame):
        """创建结果列表和滚动条"""
        scrollbar = tk.Scrollbar(results_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.results_listbox = tk.Listbox(
            results_frame, yscrollcommand=scrollbar.set, font=self.font)
        self.results_listbox.pack(fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.results_listbox.yview)

    def train_model(self):
        """训练人脸识别模型"""
        train_dir = filedialog.askdirectory(title="选择训练图片目录")
        if not train_dir:
            return

        try:
            self.status_var.set("正在训练模型...")
            total_images = self._count_total_images(train_dir)
            progress_window, progress_label, progress_var, current_file_label = self._create_progress_window(total_images)

            known_encodings, known_names, processed_images, failed_images = self._process_training_images(
                train_dir, total_images, progress_window, progress_label, progress_var, current_file_label
            )

            progress_window.destroy()

            self._handle_training_results(known_encodings, known_names, processed_images, failed_images)

        except Exception as e:
            self._handle_training_error(e)

    def _count_total_images(self, train_dir):
        total_images = 0
        for person_name in os.listdir(train_dir):
            person_dir = os.path.join(train_dir, person_name)
            if not os.path.isdir(person_dir):
                continue
            total_images += len(os.listdir(person_dir))
        return total_images

    def _create_progress_window(self, total_images):
        progress_window = tk.Toplevel(self.root)
        progress_window.title("训练进度")
        progress_window.geometry("400x200")
        progress_window.transient(self.root)
        progress_window.grab_set()

        progress_label = tk.Label(progress_window, text="准备开始训练...", font=self.font)
        progress_label.pack(pady=10)

        progress_var = tk.DoubleVar()
        progress_bar = ttk.Progressbar(progress_window, variable=progress_var, maximum=total_images)
        progress_bar.pack(fill=tk.X, padx=20, pady=10)

        current_file_label = tk.Label(progress_window, text="", font=self.font)
        current_file_label.pack(pady=10)

        return progress_window, progress_label, progress_var, current_file_label

    def _process_training_images(self, train_dir, total_images, progress_window, progress_label, progress_var, current_file_label):
        known_encodings = []
        known_names = []
        processed_images = 0
        failed_images = []
        valid_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']

        for person_name in os.listdir(train_dir):
            person_dir = os.path.join(train_dir, person_name)
            if not os.path.isdir(person_dir):
                continue
            for filename in os.listdir(person_dir):
                image_path = os.path.join(person_dir, filename)
                current_file_label.config(text=f"正在处理: {person_name}/{filename}")
                progress_window.update()
                file_ext = os.path.splitext(filename)[1].lower()
                if file_ext not in valid_extensions:
                    self.status_var.set(f"跳过非图片文件: {filename}")
                    continue
                processed_images += 1
                progress_var.set(processed_images)
                progress_label.config(text=f"已处理 {processed_images}/{total_images} 张图片")
                try:
                    image = face_recognition.load_image_file(image_path)
                    face_locations = face_recognition.face_locations(image, number_of_times_to_upsample=2)
                    if len(face_locations) > 0:
                        face_encoding = face_recognition.face_encodings(image, face_locations)[0]
                        known_encodings.append(face_encoding)
                        known_names.append(person_name)
                        self.status_var.set(f"已成功从 {filename} 中提取人脸")
                    else:
                        self.status_var.set(f"警告: 在 {filename} 中未找到人脸")
                        failed_images.append(image_path)
                except Exception as e:
                    self.status_var.set(f"错误: 处理 {filename} 时出错: {str(e)}")
                    failed_images.append(image_path)
        return known_encodings, known_names, processed_images, failed_images

    def _handle_training_results(self, known_encodings, known_names, processed_images, failed_images):
        if known_encodings:
            save_path = filedialog.asksaveasfilename(
                title="保存编码文件",
                defaultextension=".pkl",
                filetypes=[("Pickle Files", "*.pkl")]
            )
            if save_path:
                self._save_encodings(save_path, known_encodings, known_names)
                success_count = len(known_names)
                success_rate = success_count / processed_images * 100 if processed_images > 0 else 0
                summary = (f"模型训练完成!\n"
                        f"总处理图片数: {processed_images}\n"
                        f"成功提取人脸数: {success_count}\n"
                        f"成功率: {success_rate:.2f}%\n\n"
                        f"已保存 {len(known_names)} 个人脸编码")
                self.status_var.set(summary)
                messagebox.showinfo("成功", summary)
                if failed_images:
                    failed_summary = "以下图片未能检测到人脸:\n" + "\n".join(failed_images)
                    messagebox.showwarning("注意", failed_summary)
            else:
                self.status_var.set("模型训练已取消")
        else:
            error_msg = (f"错误: 在训练图片中未找到人脸!\n"
                        f"总处理图片数: {processed_images}\n\n"
                        f"可能的原因:\n"
                        f"1. 图片中没有清晰的人脸\n"
                        f"2. 人脸太小或模糊\n"
                        f"3. 图片格式不支持\n"
                        f"4. 图片质量问题(如光照不足)\n\n"
                        f"请确保图片中包含清晰可见的人脸")
            self.status_var.set("错误: 在训练图片中未找到人脸")
            messagebox.showerror("错误", error_msg)
            if failed_images:
                failed_summary = "未能检测到人脸的图片:\n" + "\n".join(failed_images)
                messagebox.showwarning("失败的图片", failed_summary)

    def _save_encodings(self, save_path, known_encodings, known_names):
        data = {
            'encodings': known_encodings,
            'names': known_names
        }
        with open(save_path, 'wb') as f:
            pickle.dump(data, f)

    def _handle_training_error(self, e):
        self.status_var.set(f"训练过程中出错: {str(e)}")
        messagebox.showerror("错误", f"训练过程中出错: {str(e)}")
    def load_encodings(self):
        """加载人脸编码文件"""
        file_path = filedialog.askopenfilename(
            title="选择编码文件",
            filetypes=[("Pickle Files", "*.pkl")]
        )
        
        if file_path:
            try:
                with open(file_path, 'rb') as f:
                    data = pickle.load(f)
                    self.known_face_encodings = data['encodings']
                    self.known_face_names = data['names']
                
                self.status_var.set(f"已加载 {len(self.known_face_names)} 个人脸编码")
                self.select_image_btn.config(state=tk.NORMAL)
                messagebox.showinfo("成功", f"已成功加载 {len(self.known_face_names)} 个人脸编码")
            except Exception as e:
                self.status_var.set(f"加载编码文件失败: {str(e)}")
                messagebox.showerror("错误", f"加载编码文件失败: {str(e)}")
    
    def select_image(self):
        """选择要识别的图片"""
        file_path = filedialog.askopenfilename(
            title="选择图片",
            filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")]
        )
        
        if file_path:
            self.current_image_path = file_path
            self.display_image(self.original_label, file_path)
            self.recognize_btn.config(state=tk.NORMAL)
            self.status_var.set(f"已选择图片: {os.path.basename(file_path)}")
            self.results_listbox.delete(0, tk.END)  # 清除之前的结果
            # 清除结果图像区域
            self.result_label.config(image="")
            self.result_label.config(text="识别结果将显示在这里")
    
    def display_image(self, label, image_path, size=(500, 500)):
        """在标签中显示图片"""
        try:
            image = Image.open(image_path)
            # 调整图片大小以适应显示区域,但保持比例
            image.thumbnail(size, Image.LANCZOS)
            photo = ImageTk.PhotoImage(image=image)
            
            # 保存对图片的引用,防止被垃圾回收
            label.image = photo
            label.config(image=photo, text="")
            
            # 保存当前显示的图像
            self.current_image = image
        except Exception as e:
            self.status_var.set(f"显示图片失败: {str(e)}")
            messagebox.showerror("错误", f"显示图片失败: {str(e)}")
    
    def recognize_faces(self):
        """识别图片中的人脸"""
        if not self.current_image_path or not self.known_face_encodings:
            return
        
        try:
            self.status_var.set("正在识别...")
            self.results_listbox.delete(0, tk.END)
            
            # 加载图像
            image = face_recognition.load_image_file(self.current_image_path)
            
            # 找到图像中所有人脸的位置和编码
            face_locations = face_recognition.face_locations(image)
            face_encodings = face_recognition.face_encodings(image, face_locations)
            
            # 转换为PIL图像以便绘制
            pil_image = Image.fromarray(image)
            draw = ImageDraw.Draw(pil_image)
            
            # 成功识别的人脸计数
            recognized_count = 0
            
            # 遍历检测到的每个人脸
            for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
                # 对比人脸编码,寻找匹配
                matches = face_recognition.compare_faces(self.known_face_encodings, face_encoding)
                name = "Unknown"
                
                # 如果有匹配,使用距离最近的人脸
                face_distances = face_recognition.face_distance(self.known_face_encodings, face_encoding)
                best_match_index = np.argmin(face_distances)
                
                if matches[best_match_index]:
                    name = self.known_face_names[best_match_index]
                
                # 计算置信度 (距离越小,置信度越高)
                confidence = 1 - face_distances[best_match_index]
                
                # 只对置信度大于等于0.7的人脸进行标记
                if confidence >= 0.7:
                    recognized_count += 1
                    
                    # 在结果列表中添加识别结果
                    result_text = f"位置: ({left}, {top}), ({right}, {bottom}) - 识别为: {name} - 置信度: {confidence:.2%}"
                    self.results_listbox.insert(tk.END, result_text)
                    
                    # 在人脸周围绘制矩形和标签
                    draw.rectangle(((left, top), (right, bottom)), outline=(0, 0, 255), width=2)
                    
                    # 绘制标签背景和文本
                    text = f"{name} ({confidence:.2%})"
                    
                    # 获取文本边界框
                    text_bbox = draw.textbbox((0, 0), text)
                    text_width = text_bbox[2] - text_bbox[0]  # 宽度 = 右边界 - 左边界
                    text_height = text_bbox[3] - text_bbox[1]  # 高度 = 下边界 - 上边界
                    
                    draw.rectangle(((left, bottom - text_height - 10), (right, bottom)), fill=(0, 0, 255))
                    draw.text((left + 5, bottom - text_height - 5), text, fill=(255, 255, 255))
                else:
                    # 对置信度不足的人脸,在结果中显示但不标记
                    result_text = f"位置: ({left}, {top}), ({right}, {bottom}) - 识别为: Unknown - 置信度不足: {confidence:.2%}"
                    self.results_listbox.insert(tk.END, result_text)
            
            # 释放绘制对象
            del draw
            
            # 保存识别后的图像
            self.recognized_image = pil_image
            
            # 显示识别结果图像
            result_image_path = os.path.splitext(self.current_image_path)[0] + "_result.jpg"
            pil_image.save(result_image_path)
            self.display_image(self.result_label, result_image_path)
            
            self.status_var.set(f"识别完成,共检测到 {len(face_locations)} 张人脸,成功识别 {recognized_count} 张")
            
        except Exception as e:
            self.status_var.set(f"识别过程中出错: {str(e)}")
            messagebox.showerror("错误", f"识别过程中出错: {str(e)}")

if __name__ == "__main__":
    root = tk.Tk()
    app = FaceRecognitionApp(root)
    root.mainloop()

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值