【Pyqt5-Tkinter-Gradio-WxPython-Windows-Linux-香橙派】四种界面库跨平台两种人脸识别+活体检测功能实现

“跨平台人脸识别与活体检测:四大界面库助力您的创意“

前言

在当今数字化时代,人脸识别和活体检测技术的应用越来越广泛,不仅限于安全领域,还涵盖了娱乐、医疗和社交等各种领域。本文旨在为开发者提供一个跨平台的人脸识别与活体检测应用的全面指南,使用四种流行的界面库:PyQt5、Tkinter、Gradio和WxPython。这将帮助您探索不同的界面工具,并将其集成到您的创意项目中。
前两篇代码还较为粗糙,仅仅记录了我的功能测试过程,并没有实现完整的人脸识别流程,本篇文章,我将使用pyqt5,tkinter,gradio,wxpython给出四种界面设计,完整实现两种活体检测+人脸识别功能
【活体检测】“深度学习驱动的人脸反欺诈检测系统:性能提升与多模型支持“
[OpenCV-dlib]人脸识别功能拓展-通过随机要求头部动作实现活体检测
GitHub:Live-Face-GUI

文章目录

  • “跨平台人脸识别与活体检测:四大界面库助力您的创意“
    • 前言
    • 1. 介绍
    • 2. 准备工作
      • 安装Python
      • 安装所需库和依赖项
    • 3. 基础知识
      • 3.1 PyQt5
      • 3.2 Tkinter
      • 3.3 Gradio
      • 3.4 WxPython
    • 4. 创建界面和集成人脸识别
      • 4.1 PyQt5界面
        • qyqt5_gui.py
        • 主要功能:
      • 4.2 Tkinter界面
        • tk_mysql_gui.py
        • dlib_live.py
        • **tk_mysql_gui.py**
          • **主要功能:**
          • **核心代码解析:**
          • **使用方法:**
        • **dlib_live.py**
          • **主要功能:**
          • **使用方法:**
      • 4.3 Gradio界面
        • gradio_gui.py
          • **主要功能:**
      • 4.4 WxPython界面
        • wx_gui.py
          • **主要功能:**
      • 总结

1. 介绍

欢迎阅读这个人脸识别和活体检测应用程序教程。在本教程中,我们将介绍如何使用四种不同的跨平台界面库(PyQt5、Tkinter、Gradio、WxPython)来创建一个应用程序,该应用程序能够进行人脸识别和活体检测。这些任务对于许多应用程序,如安全系统、出席记录和身份验证非常重要。通过本教程,你将学习如何在不同平台上构建用户友好的界面,以进行人脸检测和活体检测。

2. 准备工作

在开始之前,确保你已经准备好所需的工作环境和库。以下是一些步骤,以确保一切就绪:

安装Python

首先,确保你已经安装了Python。你可以从 Python官方网站 下载并安装最新版本。

安装所需库和依赖项

这个应用程序需要一些库和依赖项,包括OpenCV、Dlib等。你可以使用以下命令来安装它们:

dlib库的跨平台安装:
全面横扫:dlib Python API在Linux和Windows的配置方案

【香橙派-OpenCV-Torch-dlib】TF损坏变成RAW格式解决方案及python环境配置

pip install -i https://mirrors.aliyun.com/pypi/simple/ pyqt5 pyqt5-sip pyqt5-tools

确保你的Python环境中已经正确安装了这些库。

3. 基础知识

3.1 PyQt5

PyQt5是一个用于创建桌面应用程序的Python库。它提供了丰富的界面组件,允许你构建用户友好的界面。以下是一个简单的示例,展示如何创建一个PyQt5窗口:

# 导入PyQt5库
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel

# 创建一个PyQt5应用程序
app = QApplication([])

# 创建一个主窗口
window = QMainWindow()
window.setGeometry(100, 100, 400, 200)

# 创建按钮和标签
button = QPushButton("点击我")
label = QLabel("Hello, PyQt5!")

# 添加按钮和标签到窗口
window.setCentralWidget(button)
window.setCentralWidget(label)

# 显示窗口
window.show()

# 运行应用程序
app.exec_()

这是一个简单的PyQt5窗口示例,你可以在此基础上构建人脸识别和活体检测界面。

3.2 Tkinter

Tkinter是Python的标准图形用户界面库。它简单易用,适用于快速创建界面。以下是一个基本的Tkinter示例,展示如何创建一个窗口和按钮:

# 导入Tkinter库
import tkinter as tk

# 创建主窗口
window = tk.Tk()
window.title("Tkinter示例")

# 创建按钮
button = tk.Button(window, text="点击我")
button.pack()

# 运行Tkinter主循环
window.mainloop()

这是一个基本的Tkinter示例,你可以在此基础上构建人脸识别和活体检测界面。

3.3 Gradio

Gradio是一个用于快速构建Web界面的库,支持输入和输出组件的交互。以下是一个简单的Gradio示例,展示如何创建一个Gradio应用:

import gradio as gr

def recognize_face_and_liveness(image):
    # 执行人脸检测和活体检测
    # 返回结果字符串
    result = "Person: John Doe\nLiveness: Real"
    return result

iface = gr.Interface(fn=recognize_face_and_liveness, inputs="image", outputs="text")
iface.launch()

这个示例展示了如何定义一个Gradio界面,接收图像作为输入,显示文本作为输出。你可以在此基础上实现人脸识别和活体检测功能。

3.4 WxPython

WxPython是一个用于创建本机跨平台应用程序的库。以下是一个简单的WxPython示例,展示如何创建一个窗口和按钮:

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="WxPython示例", size=(400, 200))
        panel = wx.Panel(self)
        button = wx.Button(panel, label="点击我")
        button.Bind(wx.EVT_BUTTON, self.on_button_click)

    def on_button_click(self, event):
        # 处理按钮点击事件
        pass

app = wx.App()
frame = MyFrame()
frame.Show()
app.MainLoop()

这是一个简单的WxPython示例,你可以在此基础上构建人脸识别和活体检测界面。

在接下来的部分中,我们将填充每个特定的界面库的详细内容,以创建人脸识别和活体检测应用程序。

继续填充具体内容,我们将逐一填充每个界面库的部分,以创建人脸识别和活体检测应用程序。

4. 创建界面和集成人脸识别

4.1 PyQt5界面

在这一部分,我们将详细介绍如何使用PyQt5创建人脸识别和活体检测应用程序的界面。

qyqt5_gui.py
import os
import sys
import cv2
import numpy as np
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QPixmap
from retinaface import Retinaface
from src.anti_spoof_predict import AntiSpoofPredict
from src.generate_patches import CropImage
from src.utility import parse_model_name

class FaceRecognitionApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

        # Initialize objects for face detection and anti-spoofing
        self.retinaface = Retinaface()
        self.model_dir = "./resources/anti_spoof_models"
        self.device_id = 0
        self.model_test = None
        self.image_cropper = CropImage()
        self.do_face_detection = False
        self.result_image_label = QLabel(self)
        self.result_image_label.setGeometry(50, 180, 400, 400)
        self.result_image_label.setScaledContents(True)

        self.next_button = QPushButton("Next", self)
        self.next_button.setGeometry(50, 600, 100, 50)
        self.next_button.clicked.connect(self.next_face_detection)

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_result_image)

    def initUI(self):
        self.setWindowTitle("Face Recognition App")
        self.setGeometry(100, 100, 800, 700)

        self.start_button = QPushButton("Start Face Detection", self)
        self.start_button.setGeometry(50, 50, 200, 50)
        self.start_button.clicked.connect(self.start_face_detection)

        self.result_label = QLabel("Press 'F' to start face detection", self)
        self.result_label.setGeometry(50, 120, 400, 50)

    def start_face_detection(self):
        self.do_face_detection = True
        self.start_button.setDisabled(True)
        self.result_label.setText("Face detection in progress...")

        # Reset the result image label
        self.result_image_label.clear()

        self.next_face_detection()

    def next_face_detection(self):
        # Capture a photo
        self.take_photo("captured_photo.jpg")
        do_face_detection_result = self.test_and_detect("captured_photo.jpg")

        if do_face_detection_result == "FakeFace":
            self.result_label.setText("FakeFace detected. Press 'Next' to start face detection again.")
        else:
            self.result_label.setText(
                f"Hello! {do_face_detection_result}, RealFace detected. Press 'Next' to start face detection again.")

        self.next_button.setEnabled(True)

    def update_result_image(self):
        # Load and display the last processed image
        pixmap = QPixmap("captured_photo.jpg")
        self.result_image_label.setPixmap(pixmap)

    def take_photo(self, temp_img_path="captured_photo.jpg"):
        cap = cv2.VideoCapture(0)

        if not cap.isOpened():
            print("Unable to open the camera")
            return

        while True:
            ret, frame = cap.read()

            if not ret:
                print("Unable to get a frame")
                break

            cv2.imshow("Capture Photo", frame)

            key = cv2.waitKey(1)
            if key == 32:  # Space key
                break

            # Detect faces
            faces = self.detect_faces(frame)
            if len(faces) > 0:
                cv2.imwrite(temp_img_path, frame)
                print("Photo captured and saved as: " + temp_img_path)
                break

        cap.release()
        cv2.destroyAllWindows()

    def detect_faces(self, frame):
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
        faces = face_detector.detectMultiScale(gray, 1.1, 5, cv2.CASCADE_SCALE_IMAGE, (100, 100), (300, 300))
        return faces

    def load_anti_spoof_model(self):
        self.model_test = AntiSpoofPredict(self.device_id, self.model_dir)

    def test_and_detect(self, image_path):
        if self.model_test is None:
            self.load_anti_spoof_model()

        frame = cv2.imread(image_path)
        image_bbox = self.model_test.get_bbox(frame)
        prediction = np.zeros((1, 3))

        for model_name in os.listdir(self.model_dir):
            h_input, w_input, model_type, scale = parse_model_name(model_name)
            param = {
                "org_img": frame,
                "bbox": image_bbox,
                "scale": scale,
                "out_w": w_input,
                "out_h": h_input,
                "crop": True,
            }
            if scale is None:
                param["crop"] = False
            img = self.image_cropper.crop(**param)

            predictions = self.model_test.predict_batch([img])
            prediction += predictions[model_name]

        label = np.argmax(prediction)
        value = prediction[0][label] / 2
        if label == 1:
            result_text = "RealFace Score: {:.2f}".format(value)
            color = (255, 0, 0)
            name = self.detect_image(image_path, "processed_photo.jpg")
            self.result_label.setText(result_text + " - " + name)
            self.timer.start(1000)  # Update the result image every second
            return name
        else:
            result_text = "FakeFace Score: {:.2f}".format(value)
            color = (0, 0, 255)
            self.result_label.setText(result_text)
            self.timer.start(1000)  # Update the result image every second
            return "FakeFace"

    def detect_image(self, img, temp_img_path):
        image = cv2.imread(img)
        if image is None:
            print('Open Error! Try again!')
            return
        else:
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            r_image, name = self.retinaface.detect_image(image)
            r_image = cv2.cvtColor(r_image, cv2.COLOR_RGB2BGR)
            # cv2.imshow("Processed Image", r_image)
            cv2.waitKey(0)
            if temp_img_path != "":
                cv2.imwrite(temp_img_path, r_image)
                print("Save processed image to the path: " + temp_img_path)
                print("Name: " + name)
                return name

if __name__ == "__main__":
    app = QApplication(sys.argv)
    face_recognition_app = FaceRecognitionApp()
    face_recognition_app.show()
    sys.exit(app.exec_())
主要功能:
  1. 引入库和模块:脚本引入了必要的库和模块,包括OpenCV、NumPy和用于构建GUI的PyQt5组件。

  2. 类定义:主要的应用程序类是FaceRecognitionApp,它继承自QMainWindow。该类管理GUI元素以及人脸检测和防欺诈的逻辑。

  3. 初始化:在构造函数(__init__)中,初始化了GUI元素,例如按钮和标签,并创建了与人脸检测和防欺诈相关的对象。

  4. 人脸检测和防欺诈逻辑

    • start_face_detection方法设置了人脸检测的相关逻辑。
    • take_photo方法使用计算机摄像头拍摄照片。
    • detect_faces方法使用Haar级联来检测给定帧中的人脸。
    • load_anti_spoof_model方法初始化了防欺诈模型。
    • test_and_detect方法处理捕获的照片,执行防欺诈检查,并显示结果。
    • detect_image方法使用RetinaFace检测图像中的人脸。
  5. GUI初始化和事件处理

    • initUI方法中初始化了GUI元素。
    • start_face_detection方法与"开始人脸检测"按钮连接。
    • next_face_detection方法与"下一个"按钮连接。
    • update_result_image方法更新GUI,显示处理后的图像。
  6. 主要代码块if __name__ == "__main__":块创建了FaceRecognitionApp类的实例,并启动了PyQt5应用程序循环。

4.2 Tkinter界面

在这一部分,我们将详细介绍如何使用Tkinter创建人脸识别和活体检测应用程序的界面。

tk_mysql_gui.py

import sys
from tkinter import *
from tkinter import ttk
import cv2
import time
import pymysql
from PIL import ImageTk, Image

import dlib_live
from retinaface import Retinaface
import os


class Database:
    def __init__(self):
        self.conn = pymysql.connect(host="127.0.0.1",
                                    port=3306,
                                    user="root",
                                    password="1300982918",
                                    database="rlsb")
        self.cursor = self.conn.cursor()

    def insert_name(self, name):
        query = "INSERT INTO name_table (name) VALUES (%s)"
        self.cursor.execute(query, (name,))
        self.conn.commit()

    def query_name(self):
        query = "SELECT name FROM name_table"
        self.cursor.execute(query)
        return [row[0] for row in self.cursor.fetchall()]

    def query_record(self):
        query = "SELECT * FROM record_table"
        self.cursor.execute(query)
        return self.cursor.fetchall()

    def insert_record(self, username):
        import datetime
        current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        insert_query = "INSERT INTO record_table (name, check_time) VALUES (%s, %s)"
        self.cursor.execute(insert_query, (username, current_time))
        self.conn.commit()

    def close(self):
        self.cursor.close()
        self.conn.close()

def makeDir():
    if not os.path.exists("face_dataset"):
        os.mkdir("face_dataset")
    if not os.path.exists("FaceData"):
        os.mkdir("FaceData")

def getFace(name):
    cap = cv2.VideoCapture(0)
    num = 0
    while cap.isOpened():
        ret_flag, Vshow = cap.read()  # 得到每帧图像
        cv2.imshow("Capture_Test", Vshow)  # 显示图像
        k = cv2.waitKey(1) & 0xFF  # 按键判断
        if k == ord('s'):  # 保存
            cv2.imwrite("face_dataset/" + name + "_" + str(num) + ".jpg", Vshow)
            print("success to save" + str(num) + ".jpg")
            print("-------------------")
            num += 1
        elif k == ord(' '):  # 退出
            break
    #释放摄像头
    cap.release()
    #释放内存
    cv2.destroyAllWindows()


def encode_faces():
    '''
    在更换facenet网络后一定要重新进行人脸编码,运行encoding.py。
    '''
    retinaface = Retinaface(1)

    list_dir = os.listdir("face_dataset")
    image_paths = []
    names = []
    user_db = Database()
    for name in list_dir:
        image_paths.append("face_dataset/" + name)
        names.append(name.split("_")[0])
        # print(name.split("_")[0],names)
        try:
            user_db.insert_name(name.split("_")[0])
        except Exception as e:
            print(e)

    retinaface.encode_face_dataset(image_paths, names)
    user_db.close()
    return "Encoding complete!"

def add_face(name, names):
    makeDir()
    getFace(name)
    encode_faces()

class APP:
    def __init__(self):

        self.root = Tk()
        self.root.title('FACE')
        self.root.geometry('%dx%d' % (400, 300))

        # 创建数据库连接
        self.conn = pymysql.connect(host="127.0.0.1",
                                    port=3306,
                                    user="root",
                                    password="1300982918",
                                    database="rlsb")

        # 获取数据库游标
        self.cursor = self.conn.cursor()

        self.createFirstPage()

        # 新录入的人的姓名
        self.name = StringVar()

        mainloop()

    def createFirstPage(self):
        self.page1 = Frame(self.root)
        self.page1.grid()
        Label(self.page1, height=4, text='人脸识别系统', font=('粗体', 20)).grid(columnspan=2)

        # 获取用户名字列表
        self.usernames = self.query_name()

        self.button11 = Button(self.page1, width=18, height=2, text="签到打卡", bg='red', font=("宋", 12),
                               relief='raise', command=lambda: self.check(self.usernames))
        self.button11.grid(row=1, column=0, padx=25, pady=10)
        self.button12 = Button(self.page1, width=18, height=2, text="录入新的人脸", bg='green', font=("宋", 12),
                               relief='raise', command=self.createSecondPage)
        self.button12.grid(row=1, column=1, padx=25, pady=10)
        self.button13 = Button(self.page1, width=18, height=2, text="查询签到信息", bg='white', font=("宋", 12),
                               relief='raise', command=self.checkDataView)
        self.button13.grid(row=2, column=0, padx=25, pady=10)
        self.button14 = Button(self.page1, width=18, height=2, text="退出系统", bg='gray', font=("宋", 12),
                               relief='raise', command=self.quitMain)
        self.button14.grid(row=2, column=1, padx=25, pady=10)

    def createSecondPage(self):
        self.page1.grid_forget()
        self.page2 = Frame(self.root)
        self.page2.pack()
        Label(self.page2, text='欢迎使用人脸识别系统', font=('粗体', 20)).pack()

        font1 = ('宋', 18)
        self.text = Entry(self.page2, textvariable=self.name, width=20, font=font1)
        self.text.pack(side=LEFT)
        self.name.set('请输入姓名')

        self.button21 = Button(self.page2, text='确认', bg='white', font=("宋", 12),
                               relief='raise', command=lambda: add_face(self.name.get(), self.usernames))
        self.button21.pack(side=LEFT, padx=5, pady=10)

        self.button22 = Button(self.page2, text="返回", bg='white', font=("宋", 12),
                               relief='raise', command=self.p2back)
        self.button22.pack(side=LEFT, padx=10, pady=10)

    def checkDataView(self):
        self.page3 = Frame(self.root)
        self.page1.grid_forget()
        self.root.geometry('700x360')
        self.page3.pack()
        Label(self.page3, text='今日签到信息', bg='white', fg='red', font=('宋体', 25)).pack(side=TOP, fill='x')

        self.checkDate = ttk.Treeview(self.page3, show='headings', column=('id', 'name', 'record_time'))
        self.checkDate.column('id', width=100, anchor="center")
        self.checkDate.column('name', width=200, anchor="center")
        self.checkDate.column('record_time', width=300, anchor="center")

        self.checkDate.heading('id', text='签到序号')
        self.checkDate.heading('name', text='名字')
        self.checkDate.heading('record_time', text='签到时间')

        self.records = self.query_record()
        for i in self.records:
            self.checkDate.insert('', 'end', values=i)

        yscrollbar = Scrollbar(self.page3, orient=VERTICAL, command=self.checkDate.yview)
        self.checkDate.configure(yscrollcommand=yscrollbar.set)
        yscrollbar.pack(side=RIGHT, fill=Y)

        self.checkDate.pack(expand=1, fill=BOTH)

        Button(self.page3, width=20, height=2, text="返回", bg='gray', font=("宋", 12),
               relief='raise', command=self.p3back).pack(padx=20, pady=20)

    def p2back(self):
        self.page2.pack_forget()
        self.root.geometry('400x300')
        self.page1.grid()

    def p3back(self):
        self.root.geometry('400x300')
        self.page3.pack_forget()
        self.page1.grid()

    def quitMain(self):
        self.conn.close()  # 关闭数据库连接
        sys.exit(0)

    def query_name(self):
        query = "SELECT name FROM name_table"
        self.cursor.execute(query)
        result = self.cursor.fetchall()
        return [row[0] for row in result]

    def query_record(self):
        query = "SELECT id, name, record_time FROM record_table"
        self.cursor.execute(query)
        return self.cursor.fetchall()



    def check(self, names):
        detector = dlib_live.FaceDetection(0)  # 使用摄像头,也可以指定视频文件路径

        while True:
            flag = detector.get_flag()

            ref,frame = detector.process_frame()
            username= detector.get_name()
            if frame is None:
                break
            cv2.imshow("Frame", frame)

            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
            if flag == 1:
                # 插入签到信息到数据库
                self.insert_record(username)
                print(flag)
                cv2.imwrite("last_frame.png", frame)
                # print(fname)
                break
        detector.release()
        cv2.destroyAllWindows()


    def insert_record(self, username):
        insert_query = "INSERT INTO record_table (name, record_time) VALUES (%s, %s)"
        current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        self.cursor.execute(insert_query, (username, current_time))
        self.conn.commit()

if __name__ == '__main__':
    demo = APP()

dlib_live.py
import random
import time
import cv2
import numpy as np
from retinaface import Retinaface
import dlib
from imutils import face_utils





class BlinkDetection:
    def __init__(self):
        self.ear = None
        self.status = None
        self.frame_counter = 0
        self.blink_counter = 0
        self.EAR_THRESHOLD = 0.2  # 眨眼的 EAR 阈值

    def eye_aspect_ratio(self, eye):
        A = np.linalg.norm(eye[1] - eye[5])
        B = np.linalg.norm(eye[2] - eye[4])
        C = np.linalg.norm(eye[0] - eye[3])
        ear = (A + B) / (2.0 * C)
        return ear

    def detect(self, landmarks):
        left_eye = landmarks[36:42]
        right_eye = landmarks[42:48]

        EAR_left = self.eye_aspect_ratio(left_eye)
        EAR_right = self.eye_aspect_ratio(right_eye)

        self.ear = (EAR_left + EAR_right) / 2.0

        if self.ear < 0.21:
            self.frame_counter += 1
            self.status = "Blinking"
        else:
            if self.frame_counter >= 2:  # 改为2次算检测结束
                self.blink_counter += 1
                self.frame_counter = 0
            self.status = "Open"

        return self.blink_counter, self.status, self.ear


class MouthDetection:

    def __init__(self):
        self.mStart, self.mEnd = (48, 68)
        self.mouth_counter = 0
        self.MAR_THRESHOLD = 0.5
        self.mouth_open = False  # 嘴巴状态,初始为闭上

    def mouth_aspect_ratio(self, mouth):
        A = np.linalg.norm(mouth[2] - mouth[9])
        B = np.linalg.norm(mouth[4] - mouth[7])
        C = np.linalg.norm(mouth[0] - mouth[6])
        mar = (A + B) / (2.0 * C)
        return mar

    def detect(self, landmarks):
        mouth = landmarks[self.mStart:self.mEnd]
        mar = self.mouth_aspect_ratio(mouth)

        if mar > self.MAR_THRESHOLD:
            if not self.mouth_open:  # 从闭上到张开
                self.mouth_counter += 1
                self.mouth_open = True
        else:
            if self.mouth_open:  # 从张开到闭上
                self.mouth_open = False

        return self.mouth_counter


class HeadPoseDetection:
    def __init__(self):
        self.left_counter = 0
        self.right_counter = 0

        self.nod_threshold = 10
        self.low_threshold = -10
        self.head_status = "neutral"

    def calculate_head_pose(self, shape):
        x, y = zip(*shape)
        face_center = (int(np.mean(x)), int(np.mean(y)))
        left_eye_center = np.mean(shape[36:42], axis=0)
        right_eye_center = np.mean(shape[42:48], axis=0)
        dX = right_eye_center[0] - left_eye_center[0]
        dY = right_eye_center[1] - left_eye_center[1]
        angle = np.degrees(np.arctan2(dY, dX))
        return angle

    def detect(self, shape):
        angle = self.calculate_head_pose(shape)

        if angle > self.nod_threshold:
            self.head_status = "left"
            self.left_counter += 1
            return self.head_status, self.left_counter

        elif angle < self.low_threshold:
            self.head_status = "right"
            self.right_counter += 1
            return self.head_status, self.right_counter
        else:
            self.head_status = "neutral"

            return self.head_status, 0


class FaceDetection:
    def __init__(self, video_path, video_save_path="", video_fps=25.0, use_camera=False):

        self.name = None
        self.mouth_flag = False
        self.head_flag = False
        self.blink_flag = False
        self.random_flag = random.randint(1, 3)
        if use_camera:
            self.capture = cv2.VideoCapture(0)
        else:
            self.capture = cv2.VideoCapture(video_path)
        self.video_save_path = video_save_path
        if video_save_path != "":
            fourcc = cv2.VideoWriter_fourcc(*'XVID')
            size = (int(self.capture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(self.capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
            self.out = cv2.VideoWriter(video_save_path, fourcc, video_fps, size)
        self.ref, frame = self.capture.read()

        if not self.ref:
            raise ValueError("未能正确读取摄像头(视频),请注意是否正确安装摄像头(是否正确填写视频路径)。")
        self.fps = 0.0
        self.flag = 0
        self.detector = dlib.get_frontal_face_detector()
        self.predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

        self.blink_detector = BlinkDetection()
        self.mouth_detector = MouthDetection()
        self.head_pose_detector = HeadPoseDetection()

        self.nod_threshold = 10
        self.low_threshold = -10
        self.head_status = "neutral"
        self.blink_counter = 0
        self.mouth_counter = 0
        self.head_counter = 0
        self.ear = None
        self.status = None
        self.retinaface = Retinaface()

    def detect_blink(self, frame, landmarks):
        self.blink_counter, self.status, self.ear = self.blink_detector.detect(landmarks)
        cv2.putText(frame, "Blinks: {}".format(self.blink_counter), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7,
                    (0, 0, 255), 2)
        cv2.putText(frame, "EAR: {:.2f}".format(self.ear), (300, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
        cv2.putText(frame, "Eyes Status: {}".format(self.status), (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0),
                    2)
        return self.blink_counter

    def detect_mouth(self, frame, landmarks):
        self.mouth_counter = self.mouth_detector.detect(landmarks)
        cv2.putText(frame, "Mouth Count: {}".format(self.mouth_counter), (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.7,
                    (0, 0, 255), 2)
        return self.mouth_counter

    def detect_head_pose(self, frame, gray, face_rectangle):
        shape = self.predictor(gray, face_rectangle)
        shape = face_utils.shape_to_np(shape)
        self.head_status, self.head_counter = self.head_pose_detector.detect(shape)
        cv2.putText(frame, "Head Status: {}".format(self.head_status), (10, 120), cv2.FONT_HERSHEY_SIMPLEX, 0.7,
                    (0, 0, 255),
                    2)
        cv2.putText(frame, "Head Count: {}".format(self.head_counter), (10, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.7,
                    (0, 0, 255),
                    2)
        return self.head_counter

    def process_frame(self):
        t1 = time.time()
        self.ref, self.frame = self.capture.read()
        if not self.ref:
            return None
        gray = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY)
        faces = self.detector(gray, 0)
        if self.flag == 1:
            self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
            old_image, self.name = self.retinaface.live_detect_image(self.frame, self.flag)
            self.frame = np.array(old_image)
            self.frame = cv2.cvtColor(self.frame, cv2.COLOR_RGB2BGR)
            self.fps = (self.fps + (1. / (time.time() - t1))) / 2
            # print("fps= %.2f" % (self.fps))
            self.frame = cv2.putText(self.frame, "fps= %.2f" % self.fps, (200, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

        elif len(faces) != 0:
            largest_index = self._largest_face(faces)
            face_rectangle = faces[largest_index]
            landmarks = np.matrix([[p.x, p.y] for p in self.predictor(self.frame, face_rectangle).parts()])
            if self.random_flag == 1:
                # 调用眨眼检测函数
                self.detect_blink(self.frame, landmarks)
                if self.blink_counter > 3:
                    self.blink_flag = True
                    self.random_flag = random.randint(1, 3)

            if self.random_flag == 2:

                # 调用嘴巴动作检测函数
                self.detect_mouth(self.frame, landmarks)
                if self.mouth_counter > 3:
                    self.mouth_flag = True
                    self.random_flag = random.randint(1, 3)
            if self.random_flag == 3:
                # 调用头部姿势检测函数
                self.detect_head_pose(self.frame, gray, face_rectangle)
                if self.head_counter == 0:
                    self.head_flag = True
                    self.random_flag = random.randint(1, 3)
            if self.blink_flag and self.mouth_flag and self.head_flag:
                self.flag = 1

        if self.video_save_path != "":
            self.out.write(self.frame)

        return self.ref, self.frame

    def _largest_face(self, dets):
        if len(dets) == 1:
            return 0
        face_areas = [(det.right() - det.left()) * (det.bottom() - det.top()) for det in dets]
        largest_area = face_areas[0]
        largest_index = 0
        for index in range(1, len(dets)):
            if face_areas[index] > largest_area:
                largest_index = index
                largest_area = face_areas[index]
        print("largest_face index is {} in {} faces".format(largest_index, len(dets)))
        return largest_index

    def release(self):
        print("Video Detection Done!")
        self.capture.release()
        if self.video_save_path != "":
            print("Save processed video to the path:" + self.video_save_path)
            self.out.release()

    def get_blink_counter(self):
        return self.blink_counter

    def get_mouth_counter(self):
        return self.mouth_counter

    def get_head_counter(self):
        return self.head_counter

    def get_flag(self):
        return self.flag

    def get_name(self):
        return self.name



if __name__ == "__main__":
    detector = FaceDetection('R.mp4')  # 使用摄像头,也可以指定视频文件路径

    while True:
        flag = detector.get_flag()

        ref, frame = detector.process_frame()
        blink_counter = detector.get_blink_counter()
        mouth_counter = detector.get_mouth_counter()
        head_counter = detector.get_head_counter()

        #        print(blink_counter, mouth_counter, head_counter)
        #       if blink_counter > 9:
        #          break

        if frame is None:
            break
        cv2.imshow("Frame", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
        if flag == 1:
            print(flag)
            cv2.imwrite("last_frame.png", frame)
            # print(fname)
            break
    detector.release()
    cv2.destroyAllWindows()

tk_mysql_gui.py

这个Python脚本是一个使用Tkinter构建的GUI应用程序,用于人脸识别和签到管理。以下是它的主要功能和特点:

主要功能:
  1. GUI界面:提供了用户友好的图形用户界面,包含了签到打卡、录入新人脸、查询签到信息和退出系统等按钮。

  2. 人脸录入:用户可以通过GUI界面输入人名,然后通过摄像头拍摄人脸照片,保存在face_dataset文件夹中。

  3. 人脸编码:使用RetinaFace进行人脸检测,然后将人脸照片进行编码,保存在FaceData文件夹中。

  4. 签到打卡:通过摄像头捕捉图像,使用dlib进行人脸检测,并且识别人脸是否眨眼、张嘴、摇头等动作,满足条件后自动签到打卡。

  5. 签到记录查询:用户可以查看当天的签到记录,包括签到序号、名字和签到时间。

  6. 数据库支持:应用程序与MySQL数据库交互,记录人名和签到时间。

核心代码解析:
  • Database类:负责与MySQL数据库交互,包括插入名字、查询名字、查询签到记录和插入签到记录等功能。

  • APP类:主应用程序类,包含了GUI界面的初始化和各个按钮的事件处理函数,以及与数据库的交互。

  • FaceDetection类:使用dlib和RetinaFace进行人脸检测和特征点定位,同时检测眨眼、张嘴、摇头等动作,实现签到打卡功能。

使用方法:
  • 运行脚本后,通过GUI界面选择签到打卡、录入新人脸、查询签到信息等功能。

  • 对于录入新人脸功能,用户输入人名,然后点击确认按钮,程序会通过摄像头拍摄照片并保存,然后自动进行人脸编码。

  • 对于签到打卡功能,程序会自动检测用户是否眨眼、张嘴、摇头等动作,满足条件后自动签到打卡。

  • 查询签到信息功能会显示当天的签到记录,包括签到序号、名字和签到时间。


dlib_live.py

这个Python脚本包含了三个主要的类用于眨眼、张嘴、摇头等动作的检测,以及人脸姿势的计算。

主要功能:
  1. BlinkDetection类:实现眨眼检测,根据眼睛的EAR(Eye Aspect Ratio)计算是否眨眼,并记录眨眼的次数。

  2. MouthDetection类:实现张嘴检测,根据嘴巴的MAR(Mouth Aspect Ratio)计算是否张嘴,并记录张嘴的次数。

  3. HeadPoseDetection类:实现头部姿势检测,根据人脸特征点计算头部的姿势,包括左右摇头等,根据角度判断头部姿势。

  4. FaceDetection类:使用dlib进行人脸检测,然后调用上述类的方法进行眨眼、张嘴、摇头等动作的检测,实现签到打卡功能。

使用方法:
  • 运行脚本时,会调用摄像头捕捉视频帧,然后通过dlib检测人脸,再分别使用上述类的方法进行眨眼、张嘴、摇头等动作的检测。

  • 当眨眼次数、张嘴次数、摇头次数满足设定条件时,会触发签到打卡动作,然后保存签到的照片。


这两个脚本共同构成了一个基于Tkinter的人脸识别与签到管理系统。通过GUI界面,用户可以方便地进行人脸录入、签到打卡、查询签到记录等操作,同时程序会自动检测用户的眨眼、张嘴、摇头等动作,确保签到的准确性。

4.3 Gradio界面

在这一部分,我们将详细介绍如何使用Gradio创建人脸识别和活体检测应用程序的Web界面。

gradio_gui.py
import os

import cv2
import numpy as np
import gradio as gr
from retinaface2 import Retinaface
from src.anti_spoof_predict import AntiSpoofPredict
from src.generate_patches import CropImage
from src.utility import parse_model_name

# Initialize objects for face detection and anti-spoofing
retinaface = Retinaface()
model_dir = "./resources/anti_spoof_models"
device_id = 0
model_test = AntiSpoofPredict(device_id, model_dir)
image_cropper = CropImage()
prediction = None
def load(model_dir, device_id, image_path = 'captured_photo.jpg'):
    global prediction
    model_test = AntiSpoofPredict(device_id, model_dir)
    image_cropper = CropImage()
    frame = cv2.imread(image_path)  # 从拍摄的照片文件中读取图像
    image_bbox = model_test.get_bbox(frame)
    prediction = np.zeros((1, 3))
    for model_name in os.listdir(model_dir):
        h_input, w_input, model_type, scale = parse_model_name(model_name)
        param = {
            "org_img": frame,
            "bbox": image_bbox,
            "scale": scale,
            "out_w": w_input,
            "out_h": h_input,
            "crop": True,
        }
        if scale is None:
            param["crop"] = False
        img = image_cropper.crop(**param)
        # 使用模型进行预测
        predictions = model_test.predict_batch([img])
        # 将当前模型的预测结果添加到总预测中
        prediction += predictions[model_name]
        break


def test_and_detect(image_path):
    global prediction

    frame = cv2.imread(image_path)
    image_bbox = model_test.get_bbox(frame)
    prediction = np.zeros((1, 3))

    for model_name in os.listdir(model_dir):
        h_input, w_input, model_type, scale = parse_model_name(model_name)
        param = {
            "org_img": frame,
            "bbox": image_bbox,
            "scale": scale,
            "out_w": w_input,
            "out_h": h_input,
            "crop": True,
        }
        if scale is None:
            param["crop"] = False
        img = image_cropper.crop(**param)

        predictions = model_test.predict_batch([img])
        prediction += predictions[model_name]

    label = np.argmax(prediction)
    if label == 1:
        name = detect_image(image_path, "processed_photo.jpg")
        return True,name
    else:
        return False,'UNREAL'


def detect_image(img, imgSAVE_path):
    image = cv2.imread(img)
    if image is None:
        print('Open Error! Try again!')
        return
    else:
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        image, name = retinaface.detect_image(image)
        image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
        if imgSAVE_path != "":
            cv2.imwrite(imgSAVE_path, image)
            print("Save processed image to the path: " + imgSAVE_path)
            print("Name: " + name)
            return name


def capture_photo(img):
    """
    :param img:
    :return:
    """
    # if name == "":
    #     return "Name cannot be empty!"
    # print(img)
    if img is None:
        return "img cannot be empty"
    else:
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

        face = face_detector.detectMultiScale(gray, 1.1, 5, cv2.CASCADE_SCALE_IMAGE, (100, 100), (300, 300))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

        if len(face) > 0:  # 检查是否检测到人脸
            # 检测到人脸,立即拍照
            cv2.imwrite("tempx.jpg", img)

            flag, name = test_and_detect("tempx.jpg")
            return "Hello!,"+name,"tempx.jpg"
        else:
            cv2.imwrite("tempx.jpg", img)
            return 'NO FACE',"tempx.jpg"

demo = gr.Interface(
    capture_photo,
    [
        gr.components.Image(source="webcam", label="Webcam"),
    ],
    [
        gr.components.Text(label='output'),
        gr.components.Image(label="output")
    ],
    live=True,
)

if __name__ == '__main__':
    load(model_dir, device_id)

    demo.launch(share=True)
主要功能:
  1. 首先,代码导入了所需的Python库和模块,包括OpenCV、NumPy、Gradio、Retinaface等,以支持图像处理和界面创建。

  2. 通过Gradio库创建了一个图形用户界面(GUI)。这个界面允许用户通过Webcam(网络摄像头)捕获图像并进行处理。

  3. 初始化了用于人脸检测和防欺诈预测的对象:

    • retinaface是用于人脸检测的Retinaface模型。
    • model_dir是包含防欺诈模型的目录。
    • device_id指定了要在哪个设备上运行模型。
    • model_test是AntiSpoofPredict类的实例,用于进行防欺诈预测。
    • image_cropper是CropImage类的实例,用于裁剪图像。
    • prediction是一个NumPy数组,用于存储模型的预测结果。
  4. 定义了一系列函数,用于不同的图像处理任务:

    • load函数接受一个图像路径并从该图像中加载图像,然后进行处理,将结果存储在prediction变量中。处理过程包括裁剪图像和模型预测。
    • test_and_detect函数接受一个图像路径,进行人脸检测和防欺诈预测,最后返回处理结果。如果检测到人脸并且通过了防欺诈检测,将返回人脸识别的结果和人名,否则返回"NO FACE"。
    • detect_image函数接受一个图像路径,进行人脸检测,如果检测到人脸,返回处理后的图像和人名。
    • capture_photo函数用于处理从Webcam捕获的图像。它首先将图像转换为灰度,然后使用OpenCV的人脸检测器检测人脸。如果检测到人脸,它会保存临时图像文件,然后调用test_and_detect函数进行进一步处理。
  5. 最后,通过Gradio库创建了一个界面,命名为demo,该界面允许用户使用Webcam捕获图像,并将图像传递给capture_photo函数进行处理。处理结果包括输出文本和显示处理后的图像。该界面可以通过demo.launch(share=True)启动。

总结:该代码实现了一个简单的Webcam图像处理应用,包括人脸检测、防欺诈预测和人名识别。用户可以通过Gradio界面与应用进行交互,捕获图像并查看处理结果。应用的主要功能是检测人脸并确定其真实性。

4.4 WxPython界面

在这一部分,我们将详细介绍如何使用WxPython创建人脸识别和活体检测应用程序的本机跨平台界面。

wx_gui.py
import wx
import cv2
import numpy as np
from retinaface import Retinaface
from src.anti_spoof_predict import AntiSpoofPredict
from src.generate_patches import CropImage
from src.utility import parse_model_name
import os

retinaface = Retinaface()
model_dir = "./resources/anti_spoof_models"
device_id = 0
model_test = AntiSpoofPredict(device_id, model_dir)
image_cropper = CropImage()
prediction = None
class FaceDetectionApp(wx.Frame):
    def __init__(self):
        super().__init__(None, title="人脸检测应用", size=(600, 400))
        self.retinaface = Retinaface()
        self.model_dir = "./resources/anti_spoof_models"
        self.device_id = 0
        self.model_test = AntiSpoofPredict(self.device_id, self.model_dir)
        self.image_cropper = CropImage()
        self.prediction = None

        self.face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

        panel = wx.Panel(self)
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        vbox = wx.BoxSizer(wx.VERTICAL)

        self.start_button = wx.Button(panel, label="开始拍照检测")
        self.result_label = wx.StaticText(panel, label="检测结果:")
        self.image_ctrl = wx.StaticBitmap(panel, size=(300, 300))

        self.start_button.Bind(wx.EVT_BUTTON, self.start_detection)

        hbox.Add(self.image_ctrl, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        vbox.Add(self.start_button, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        vbox.Add(self.result_label, 0, wx.ALIGN_CENTER | wx.ALL, 5)

        hbox.Add(vbox, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        panel.SetSizer(hbox)

        self.cap = cv2.VideoCapture(0)  # 打开默认摄像头
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.update_camera, self.timer)
        self.timer.Start(10)  # 更新频率(毫秒)

    def update_camera(self, event):
        ret, frame = self.cap.read()
        if ret:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            height, width = frame.shape[:2]
            img = wx.Image(width, height, frame.tobytes())
            bmp = wx.Bitmap(img)
            self.image_ctrl.SetBitmap(bmp)

    def start_detection(self, event):
        ret, frame = self.cap.read()
        if ret:
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            faces = self.face_cascade.detectMultiScale(gray, 1.1, 5, cv2.CASCADE_SCALE_IMAGE, (100, 100), (300, 300))

            if len(faces) > 0:  # 如果检测到人脸
                cv2.imwrite("captured_photo.jpg", frame)  # 拍照并保存为 "captured_photo.jpg"
                print("已拍照并保存为:captured_photo.jpg")

                result = self.silent_liveness_detection("captured_photo.jpg")

                if result == 1:
                    name = self.face_recognition("captured_photo.jpg")
                    self.result_label.SetLabel(f"检测结果:真人,姓名:{name}")
                else:
                    self.result_label.SetLabel("检测结果:非真人")
            else:
                self.result_label.SetLabel("未检测到人脸")

    def silent_liveness_detection(self, image_path):
        # 在这里执行静默活体检测,返回1表示真人,0表示非真人
        # 请添加相应的活体检测逻辑
        frame = cv2.imread(image_path)
        image_bbox = model_test.get_bbox(frame)
        prediction = np.zeros((1, 3))

        for model_name in os.listdir(model_dir):
            h_input, w_input, model_type, scale = parse_model_name(model_name)
            param = {
                "org_img": frame,
                "bbox": image_bbox,
                "scale": scale,
                "out_w": w_input,
                "out_h": h_input,
                "crop": True,
            }
            if scale is None:
                param["crop"] = False
            img = image_cropper.crop(**param)

            predictions = model_test.predict_batch([img])
            prediction += predictions[model_name]

        label = np.argmax(prediction)
        if label == 1:
            # name = self.face_recognition(image_path)
            return 1
        else:
            return 0

    def face_recognition(self, image_path):
        image = cv2.imread(image_path)
        if image is None:
            print('Open Error! Try again!')
            return
        else:
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            image, name = retinaface.detect_image(image)
            image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
            if image_path != "":
                cv2.imwrite(image_path, image)
                print("Save processed image to the path: " + image_path)
                print("Name: " + name)
                return name

if __name__ == '__main__':
    app = wx.App()
    frame = FaceDetectionApp()
    frame.Show()
    app.MainLoop()

主要功能:
  1. 实时图像捕获:应用程序打开默认的计算机摄像头,实时捕获摄像头传输的图像。这允许用户查看摄像头的内容并执行后续的人脸检测和识别。

  2. 人脸检测:应用程序使用 OpenCV 中的 Haar 级联分类器对实时图像中的人脸进行检测。如果检测到一个或多个人脸,应用程序将框出这些人脸,并在图像上标记它们。

  3. 图像保存:如果检测到人脸,应用程序可以让用户手动触发图像捕获,将当前帧保存为图像文件(通常保存为 “captured_photo.jpg”)。这个图像可以用于后续的活体检测和人脸识别。

  4. 静默活体检测:应用程序调用静默活体检测模型来确定捕获的图像中是否存在真实人脸。活体检测的目标是确认人脸不是静态照片或屏幕上的图像,而是实际的、活着的人。这可以用于减少虚假身份验证。

  5. 人脸识别:如果静默活体检测结果为真实人脸,应用程序将使用 Retinaface 模型来尝试识别捕获图像中的人脸,并提供相关的人脸识别信息,如姓名。这允许应用程序对已知身份进行识别。

  6. 图形用户界面 (GUI):该应用程序提供一个图形用户界面,其中包含一个按钮,用于启动人脸检测过程,并一个图像显示区域,用于显示摄像头捕获的实时图像。检测结果会在应用程序界面上以文本的形式呈现。

总的来说,这个应用程序结合了实时图像捕获、人脸检测、活体检测和人脸识别功能,适用于需要安全验证和身份识别的场景。用户可以通过点击按钮来触发人脸检测和识别过程,获取有关检测结果的信息。请注意,模型和文件路径需要根据实际情况进行配置和设置。

总结

在本文中,我们学习了如何使用四种不同的界面库创建跨平台的人脸识别与活体检测应用。每个界面库都有其独特的特点,适用于不同的项目和需求。PyQt5提供了强大的界面设计工具,Tkinter是Python自带的库,Gradio简化了Web界面的创建,而WxPython适用于创建桌面应用程序。无论您是开发人员还是爱好者,这篇文章都为您提供了灵感和工具,帮助您开始开发自己的人脸识别与活体检测应用。不断尝试和探索各种库和技术,将帮助您提升开发技能并实现更多创意。

你可能感兴趣的:(python,深度学习,gradio,windows,linux,pyqt,python)