每日一小时打卡pc端毕业设计(具有人脸专注度检测和人脸识别的监控功能)

Posted pengsuoqun123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一小时打卡pc端毕业设计(具有人脸专注度检测和人脸识别的监控功能)相关的知识,希望对你有一定的参考价值。

 

 

 

 

 

 

 

 

 

 

 代码:

# coding=utf-8
import cv2 as cv
import cv2
import datetime
import base64
import time
import imutils
import dlib
from imutils import face_utils
from scipy.spatial import distance as dist
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import requests
from PyQt5.QtCore import Qt, QTimer, QTime
from PyQt5.QtGui import QImage, QPixmap, QFont, QStandardItemModel, QStandardItem
import pymysql
from PyQt5.QtMultimedia import QCamera
from PyQt5.QtMultimediaWidgets import QCameraViewfinder
from PyQt5.QtWidgets import *



# 人脸识别
def getToken():
    ak = \'B7E2OqVuDAyDs7OsuGPuKa4y\'
    sk = \'idObOz6jqA2GdU49L2VG4VPVhgmiidvD\'
    host = f\'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=ak&client_secret=sk\'
    response = requests.get(host)
    return response.json().get("access_token")


def img_to_base64(file_path):
    with open(file_path, \'rb\') as f:
        base_64_data = base64.b64encode(f.read())
        s = base_64_data.decode()
        return s


# 人脸库对比寻找人脸
def FaceDetect1(token_, base_64_data):
    params = 
    request_url = "https://aip.baidubce.com/rest/2.0/face/v3/search"
    params["image"] = base_64_data
    params["image_type"] = "BASE64"
    params["group_id_list"] = "face"
    access_token = token_
    request_url = request_url + "?access_token=" + access_token
    headers = \'content-type\': \'application/json\'
    response = requests.post(request_url, data=params, headers=headers)
    if response:
        return response.json()["result"]["user_list"][0]["score"]


def FaceDetect2(token_, base_64_data):
    params = 
    request_url = "https://aip.baidubce.com/rest/2.0/face/v3/search"
    params["image"] = base_64_data
    params["image_type"] = "BASE64"
    params["group_id_list"] = "face"
    access_token = token_
    request_url = request_url + "?access_token=" + access_token
    headers = \'content-type\': \'application/json\'
    response = requests.post(request_url, data=params, headers=headers)
    if response:
        return response.json()["result"]["user_list"][0]["user_info"]


def FaceDetect(token_):
    params = 
    request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/getusers"

    params["group_id"] = "face"

    access_token = token_
    request_url = request_url + "?access_token=" + access_token
    headers = \'content-type\': \'application/json\'
    response = requests.post(request_url, data=params, headers=headers)
    if response:
        array1 = response.json()["result"]["user_id_list"]
        return len(array1)


# 添加人脸信息
def addface(a):
    token_ = getToken()
    num = FaceDetect(token_)
    num += 1
    ak = \'B7E2OqVuDAyDs7OsuGPuKa4y\'
    sk = \'idObOz6jqA2GdU49L2VG4VPVhgmiidvD\'
    host = f\'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=ak&client_secret=sk\'
    response = requests.get(host)
    token_ = response.json().get("access_token")
    file_path = "photo.png"
    with open(file_path, \'rb\') as f:
        base_64_data = base64.b64encode(f.read())
        base_64_data1 = base_64_data.decode()
    params = 
    request_url = "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add"
    params["image"] = base_64_data1
    params["image_type"] = "BASE64"
    params["group_id"] = "face"
    params["user_id"] = "user_" + str(num)
    params["user_info"] = a
    access_token = token_
    request_url = request_url + "?access_token=" + access_token
    headers = \'content-type\': \'application/json\'
    response = requests.post(request_url, data=params, headers=headers)
    if response:
        print(response.json()["error_msg"])


# 连接数据库,添加数据
def addsjk(a, b, c, d):
    db = pymysql.connect(host="localhost", user="root", password="PENGsuoqun123", db="bb")
    cur = db.cursor()
    sql1 = "insert into information(id,name,classname,pwd) values(%s, %s, %s, %s)"
    val = (a, b, c, d)
    try:
        cur.execute(sql1, val)
        db.commit()
    except Exception as a:
        raise a
    finally:
        db.close()


class Login(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Login")
        self.setFixedSize(1000, 700)

        # 学号输入框
        self.username_label = QLabel("学号:")
        self.username_edit = QLineEdit()
        self.username_layout = QHBoxLayout()
        self.username_layout.addWidget(self.username_label)
        self.username_layout.addWidget(self.username_edit)

        # 设置用户名输入框字体
        username_font = QFont()
        username_font.setPointSize(20)
        self.username_label.setFont(username_font)

        # 密码输入框
        self.password_label = QLabel("密码:")
        self.password_edit = QLineEdit()
        self.password_edit.setEchoMode(QLineEdit.Password)
        self.password_layout = QHBoxLayout()
        self.password_layout.addWidget(self.password_label)
        self.password_layout.addWidget(self.password_edit)

        # 设置密码输入框字体
        password_font = QFont()
        password_font.setPointSize(20)
        self.password_label.setFont(password_font)

        # 登录按钮
        self.login_button = QPushButton("登录")
        self.login_button.clicked.connect(self.login)
        self.login_button.setFixedSize(150, 60)

        # 注册按钮
        self.signup_button = QPushButton("注册")
        self.signup_button.clicked.connect(self.show_signup)
        self.signup_button.setFixedSize(150, 60)

        # 创建水平布局
        self.buttons_layout = QHBoxLayout()

        # 在水平布局中添加两个按钮
        self.buttons_layout.addWidget(self.login_button)
        self.buttons_layout.addStretch(1)
        self.buttons_layout.addWidget(self.signup_button)

        # 页面布局
        self.layout = QVBoxLayout()
        self.layout.addLayout(self.username_layout)
        self.layout.addLayout(self.password_layout)
        self.layout.addLayout(self.buttons_layout)

        # 设置当前窗口的布局
        self.setLayout(self.layout)

    def login(self):
        # 在此处写登录逻辑
        username = self.username_edit.text()
        password = self.password_edit.text()
        # 连接MySQL数据库
        conn = pymysql.connect(host=\'localhost\', user=\'root\', password=\'PENGsuoqun123\', db=\'bb\')
        cursor = conn.cursor()

        # 执行查询语句
        query = "SELECT * FROM information WHERE id=%s AND pwd=%s"
        cursor.execute(query, (username, password))
        # 判断是否查询到结果
        try:
            if cursor.fetchone() is not None:
                QMessageBox.information(self, "登录结果", "请验证人脸")
                self.camera = Camera()
                self.camera.exec_()
                img1 = img_to_base64("photo.png")
                token_ = getToken()
                score = FaceDetect1(token_, img1)
                iid = FaceDetect2(token_, img1)
                if score > 85:
                    if iid == username:
                        QMessageBox.information(self, "登录结果", "登录成功!")
                        self.Main_window = Main(username, password)
                        self.Main_window.show()
                        self.hide()
                    else:
                        QMessageBox.information(self, "登录结果", "人脸不匹配!")
                else:
                    QMessageBox.information(self, "登录结果", "人脸不匹配!")
            else:
                QMessageBox.information(self, "登录结果", "账号或密码错误!")
            # 关闭连接和游标
            cursor.close()
            conn.close()
        except Exception as e:
            QMessageBox.information(self, "登录结果", "人脸不匹配!")
            print(e)

    def show_signup(self):
        self.signup_window = Signup()
        self.signup_window.show()
        self.hide()


class Signup(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Sign up")
        self.setFixedSize(1000, 700)

        # 注册的姓名
        self.name_label = QLabel("姓名:")
        self.name_edit = QLineEdit()
        self.name_layout = QHBoxLayout()
        self.name_layout.addWidget(self.name_label)
        self.name_layout.addWidget(self.name_edit)
        name_font = QFont()
        name_font.setPointSize(20)
        self.name_label.setFont(name_font)

        # 注册的学号
        self.id_label = QLabel("学号:")
        self.id_edit = QLineEdit()
        self.id_layout = QHBoxLayout()
        self.id_layout.addWidget(self.id_label)
        self.id_layout.addWidget(self.id_edit)
        id_font = QFont()
        id_font.setPointSize(20)
        self.id_label.setFont(id_font)

        # 注册的班级
        self.class_label = QLabel("班级:")
        self.class_edit = QLineEdit()
        self.class_layout = QHBoxLayout()
        self.class_layout.addWidget(self.class_label)
        self.class_layout.addWidget(self.class_edit)
        class_font = QFont()
        class_font.setPointSize(20)
        self.class_label.setFont(class_font)

        # 注册的密码
        self.password1_label = QLabel("密码:")
        self.password1_edit = QLineEdit()
        self.password1_layout = QHBoxLayout()
        self.password1_layout.addWidget(self.password1_label)
        self.password1_layout.addWidget(self.password1_edit)
        password1_font = QFont()
        password1_font.setPointSize(20)
        self.password1_label.setFont(password1_font)

        # 注册按钮
        self.signup_button = QPushButton("注册")
        self.signup_button.clicked.connect(self.signup)
        self.signup_button.setFixedSize(150, 60)

        # 返回按钮
        self.back_button = QPushButton("返回登录页面")
        self.back_button.clicked.connect(self.show_login)
        self.back_button.setFixedSize(150, 60)
        # 创建水平布局
        self.button1_layout = QHBoxLayout()

        # 在水平布局中添加两个按钮
        self.button1_layout.addWidget(self.signup_button)
        self.button1_layout.addStretch(1)
        self.button1_layout.addWidget(self.back_button)

        # 页面布局
        self.layout = QVBoxLayout()
        self.layout.addLayout(self.name_layout)
        self.layout.addLayout(self.id_layout)
        self.layout.addLayout(self.class_layout)
        self.layout.addLayout(self.password1_layout)
        self.layout.addLayout(self.button1_layout)
        self.setLayout(self.layout)

    def signup(self):
        # 在此处写注册逻辑
        name = self.name_edit.text()
        id = self.id_edit.text()
        password1 = self.password1_edit.text()
        classname = self.class_edit.text()
        if len(id) != 8:
            QMessageBox.warning(self, "注册失败", "请输入正确长度的学号!!!")
        else:
            addsjk(id, name, classname, password1)
            self.camera = Camera()
            self.camera.exec_()
            # 此处人脸录入代码
            addface(id)
            QMessageBox.information(self, "注册结果", "人脸已录入,注册成功")
            self.show_login()

    def show_login(self):
        self.login_window = Login()
        self.login_window.show()
        self.hide()


class Camera(QDialog):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.setWindowTitle("人脸录入")
        self.setFixedSize(640, 480)

        # 初始化摄像头
        self.cap = cv2.VideoCapture(0)

        # 定时器,每30毫秒更新一次图像
        self.timer = QTimer()
        self.timer.timeout.connect(self.show_camera)
        self.timer.start(10)

    def init_ui(self):
        # 标签控件,用于显示摄像头捕获的图像
        self.label = QLabel(self)
        self.label.setAlignment(Qt.AlignCenter)

        # 拍照按钮
        self.Photo_button = QPushButton(\'拍照\', self)
        self.Photo_button.clicked.connect(self.take_photo)

        # 垂直布局
        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.Photo_button)
        self.setLayout(layout)

    def show_camera(self):
        # 读取摄像头捕获的图像
        ret, frame = self.cap.read()

        # 将图像转换为Qt可显示的格式
        if ret:
            img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, c = img.shape
            qimg = QImage(img.data, w, h, w * c, QImage.Format_RGB888)
            self.label.setPixmap(QPixmap.fromImage(qimg))

    def take_photo(self):
        # 读取摄像头捕获的图像
        ret, frame = self.cap.read()

        # 将图像保存为文件
        if ret:
            cv2.imwrite(\'photo.png\', frame)
            self.cap.release()  # 关闭摄像头,否则与专注度检测冲突
            self.accept()  # 关闭当前窗口

    def closeEvent(self, event):
        # 释放摄像头资源
        self.cap.release()
        super().closeEvent(event)


class Main(QMainWindow):
    def __init__(self, username, password):
        super().__init__()

        self.setFixedSize(1000, 700)
        self.setWindowTitle(\'专注度检测\')

        self.username = username
        self.password = password

        # 创建左侧部件
        self.btn_1 = QPushButton(\'专注度检测\', self)
        self.btn_2 = QPushButton(\'每日记录\', self)
        self.btn_3 = QPushButton(\'个人资料查看及修改\', self)
        self.btn_4 = QPushButton(\'修改密码\', self)

        left_layout = QVBoxLayout()
        left_layout.addWidget(self.btn_1)
        left_layout.addWidget(self.btn_2)
        left_layout.addWidget(self.btn_3)
        left_layout.addWidget(self.btn_4)
        left_layout.addStretch(5)
        left_layout.setSpacing(20)
        left_widget = QWidget()
        left_widget.setLayout(left_layout)

        # 创建右侧部件
        self.stack_widget = QStackedWidget(self)

        self.EYE_AR_THRESH = 0.2
        self.EYE_AR_CONSEC_FRAMES = 3

        self.detector = dlib.get_frontal_face_detector()
        self.predictor = dlib.shape_predictor(
            \'D:/myworkspace/fatigue_detecting-master/fatigue_detecting-master/model/shape_predictor_68_face_landmarks.dat\')
        (self.lStart, self.lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
        (self.rStart, self.rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]
        self.num1 = 0
        self.num2 = 0
        self.COUNTER = 0
        self.TOTAL = 0
        self.time_sum = 0
        self.fps = 30

        self.timer_camera = QTimer(self)

        page1 = QWidget()
        layout1 = QVBoxLayout(page1)
        label1 = QLabel(\'专注度检测\')
        label1.setAlignment(Qt.AlignHCenter)
        label1.setFont(QFont("宋体", 23))
        layout1.addWidget(label1)
        layout1.addSpacing(20)
        self.button1 = QPushButton(\'开始学习\', self)
        self.button2 = QPushButton(\'结束学习\', self)
        hbox = QHBoxLayout()
        hbox.addWidget(self.button1)
        hbox.addWidget(self.button2)
        layout1.addLayout(hbox)
        self.capp = cv2.VideoCapture(0)
        self.frame = QLabel(page1)
        layout1.setAlignment(Qt.AlignTop)
        layout1.addWidget(self.frame)
        self.code_label = QLabel(\'代码提交:\', page1)
        self.code_edit = QTextEdit(page1)
        self.code_edit.setLineWrapMode(QTextEdit.NoWrap)  # 关闭自动换行
        code_layout= QHBoxLayout()
        code_layout.addWidget(self.code_label)
        code_layout.addWidget(self.code_edit)
        layout1.addLayout(code_layout)


        #label2用于更换摄像头照片

        page1.setStyleSheet("background-color: #fff;")

        self.stack_widget.addWidget(page1)
        self.button1.clicked.connect(self.start_camera)
        self.button2.clicked.connect(self.stop_camera)

        page2 = QWidget()
        layout2 = QHBoxLayout(page2)

        # 连接到 MySQL 数据库
        conn = pymysql.connect(
            host=\'localhost\',
            user=\'root\',
            password=\'PENGsuoqun123\',
            db=\'bb\'
        )

        # 执行查询语句
        with conn.cursor() as cursor:
            sql = \'SELECT time,totaltime,focustime,focus,codelen FROM learn_record WHERE id=%s\'
            cursor.execute(sql, (self.username))
            results = cursor.fetchall()

        # 创建 QStandardItemModel 模型
        self.model = QStandardItemModel()

        # 设置表头
        self.model.setHorizontalHeaderLabels([\'提交时间\', \'学习总时间(秒)\', \'专注时间(秒)\', \'专注度(%)\', \'代码行数\'])

        # 将查询结果添加到模型中
        for row_data in results:
            row_items = []
            for col_data in row_data:
                item = QStandardItem(str(col_data))
                item.setTextAlignment(Qt.AlignCenter)  # 居中显示
                row_items.append(item)
            self.model.appendRow(row_items)

        # 创建 QTableView 控件并设置模型
        table_view = QTableView()
        table_view.setModel(self.model)

        # 设置列宽
        table_view.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        table_view.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeToContents)
        table_view.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeToContents)
        table_view.horizontalHeader().setStretchLastSection(True)

        # 关闭数据库连接
        conn.close()

        # 将表格添加到布局中
        layout2.addWidget(table_view)
        self.stack_widget.addWidget(page2)
        page2.setStyleSheet("background-color: #fff;")

        # 个人资料显示以及修改
        conn = pymysql.connect(host=\'localhost\', user=\'root\', password=\'PENGsuoqun123\', db=\'bb\')
        cursor = conn.cursor()
        query = "SELECT * FROM information WHERE id=%s AND pwd=%s"
        cursor.execute(query, (username, password))
        result = cursor.fetchone()
        name = result[1]
        classname = result[2]
        page3 = QWidget()
        layout3 = QVBoxLayout(page3)
        label31 = QLabel(\'个人资料查看及修改\')
        label31.setAlignment(Qt.AlignHCenter)
        label31.setFont(QFont("宋体", 23))
        layout32 = QHBoxLayout()
        label32 = QLabel(\'姓名:\')
        label32.setFont(QFont("宋体", 20))
        label32.setAlignment(Qt.AlignHCenter)
        self.label32x = QLineEdit(name)
        self.label32x.setAlignment(Qt.AlignLeft)
        layout32.addWidget(label32)
        layout32.addWidget(self.label32x)

        layout34 = QHBoxLayout()
        label34 = QLabel(\'班级:\')
        label34.setFont(QFont("宋体", 20))
        label34.setAlignment(Qt.AlignHCenter)
        self.label34x = QLineEdit(classname)
        layout34.addWidget(label34)
        layout34.addWidget(self.label34x)

        layout33 = QHBoxLayout()
        label331 = QLabel(\'学号:\' + username + \'(学号不可修改)\')
        label331.setAlignment(Qt.AlignHCenter)
        label331.setFont(QFont("宋体", 15))
        layout33.addWidget(label331)

        button = QPushButton(\'确认\')
        button.clicked.connect(self.reviseinf)
        button.setFixedSize(120, 40)
        button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # 再将整个子布局添加到主布局中
        layout3.addWidget(label31)
        layout3.addStretch(1)
        layout3.addLayout(layout33)
        layout3.addStretch(1)
        layout3.addLayout(layout32)
        layout3.addStretch(1)
        layout3.addLayout(layout34)
        layout3.addStretch(1)
        layout3.addWidget(button, alignment=Qt.AlignHCenter)

        page3.setStyleSheet("background-color: #fff;")
        self.stack_widget.addWidget(page3)

        page4 = QWidget()
        layout4 = QVBoxLayout(page4)
        layout4.setAlignment(Qt.AlignTop)
        label41 = QLabel(\'修改密码\')
        label41.setAlignment(Qt.AlignHCenter)
        label41.setFont(QFont("宋体", 23))
        old_password_layout = QHBoxLayout()
        old_password_label = QLabel(\'旧密码:\')
        old_password_label.setFont(QFont("宋体", 20))
        self.old_password_edit = QLineEdit()
        old_password_layout.addWidget(old_password_label)
        old_password_layout.addWidget(self.old_password_edit)
        new_password_layout = QHBoxLayout()
        new_password_label = QLabel(\'新密码:\')
        new_password_label.setFont(QFont("宋体", 20))
        self.new_password_edit = QLineEdit()
        new_password_layout.addWidget(new_password_label)
        new_password_layout.addWidget(self.new_password_edit)
        confirm_password_layout = QHBoxLayout()
        confirm_password_label = QLabel(\'确认密码:\')
        confirm_password_label.setFont(QFont("宋体", 20))
        self.confirm_password_edit = QLineEdit()
        confirm_password_layout.addWidget(confirm_password_label)
        confirm_password_layout.addWidget(self.confirm_password_edit)
        confirm_button = QPushButton(\'确认\')
        confirm_button.clicked.connect(self.revise)
        confirm_button.setFixedSize(120, 40)
        confirm_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        layout4.addWidget(label41)
        layout4.setSpacing(90)
        layout4.addLayout(old_password_layout)
        layout4.addLayout(new_password_layout)
        layout4.addLayout(confirm_password_layout)
        layout4.addWidget(confirm_button, alignment=Qt.AlignHCenter)
        page4.setStyleSheet("background-color: #fff;")
        self.stack_widget.addWidget(page4)

        # 将左侧和右侧部件添加到主布局中
        main_layout = QHBoxLayout()
        main_layout.addWidget(left_widget)
        main_layout.addWidget(self.stack_widget)
        main_layout.setStretch(0, 1)
        main_layout.setStretch(1, 4)

        main_widget = QWidget()
        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)

        # 连接按钮的单击信号到相应的页面
        self.btn_1.clicked.connect(lambda: self.stack_widget.setCurrentIndex(0))
        self.btn_2.clicked.connect(lambda: self.stack_widget.setCurrentIndex(1))
        self.btn_3.clicked.connect(lambda: self.stack_widget.setCurrentIndex(2))
        self.btn_4.clicked.connect(lambda: self.stack_widget.setCurrentIndex(3))


    def revise(self):
        old = self.old_password_edit.text()
        new = self.new_password_edit.text()
        confirm = self.confirm_password_edit.text()
        if new != confirm:
            QMessageBox.information(self, " 修改密码", "新密码与确定密码不一样!!!")
        else:
            if old != self.password:
                QMessageBox.information(self, " 修改密码", "原密码错误!!!")
            else:
                self.password = new
                conn = pymysql.connect(host=\'localhost\', user=\'root\', password=\'PENGsuoqun123\', db=\'bb\')
                cursor = conn.cursor()

                # 根据id和pwd更新信息表中的记录
                query = "UPDATE information SET pwd=%s WHERE id=%s"
                cursor.execute(query, (new, self.username))

                # 提交更改
                conn.commit()

                # 关闭连接
                cursor.close()
                conn.close()
                QMessageBox.information(self, " 修改密码", "修改成功!!!")


    # 修改资料代码
    def reviseinf(self):
        name = self.label32x.text()
        classname = self.label34x.text()
        conn = pymysql.connect(host=\'localhost\', user=\'root\', password=\'PENGsuoqun123\', db=\'bb\')
        cursor = conn.cursor()

        # 根据id和pwd更新信息表中的记录
        query = "UPDATE information SET name=%s , classname=%s WHERE id=%s"
        cursor.execute(query, (name, classname, self.username))

        # 提交更改
        conn.commit()

        # 关闭连接
        cursor.close()
        conn.close()
        QMessageBox.information(self, " 修改信息", "修改成功!!!")

    def start_camera(self):
        self.time_start = time.time()
        global shifou
        shifou=1
        self.timer_camera.timeout.connect(self.show_cameraa)
        self.timer_camera.start(1000 // self.fps)
        self.button1.setDisabled(True)

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

    def show_cameraa(self):
        ret, image = self.capp.read()
        frame = imutils.resize(image, width=720)
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        rects = self.detector(gray, 0)
        self.num2 = len(rects)
        self.num1 += 1
        if (self.num2 == 0):
            self.TOTAL += 1

        for rect in rects:
            shape = self.predictor(gray, rect)
            shape = face_utils.shape_to_np(shape)
            leftEye = shape[self.lStart:self.lEnd]
            rightEye = shape[self.rStart:self.rEnd]
            leftEAR = self.eye_aspect_ratio(leftEye)
            rightEAR = self.eye_aspect_ratio(rightEye)
            ear = (leftEAR + rightEAR) / 2.0
            leftEyeHull = cv2.convexHull(leftEye)
            rightEyeHull = cv2.convexHull(rightEye)
            cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1)
            cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)
            left = rect.left()
            top = rect.top()
            right = rect.right()
            bottom = rect.bottom()
            cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 3)
            if ear < self.EYE_AR_THRESH:
                self.COUNTER += 1
            else:
                if self.COUNTER >= self.EYE_AR_CONSEC_FRAMES:
                    self.TOTAL += 5
                self.COUNTER = 0

            for (x, y) in shape:
                cv2.circle(frame, (x, y), 1, (0, 0, 255), -1)

            cv2.putText(frame, "Faces: ".format(len(rects)), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            cv2.putText(frame, "Blinks: ".format(self.TOTAL), (150, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            cv2.putText(frame, "COUNTER: ".format(self.COUNTER), (300, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            cv2.putText(frame, "EAR: :.2f".format(ear), (450, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            cv2.putText(frame, "Total time:" + str(int(self.time_sum)) + "s", (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            cv2.putText(frame, "Focus on time:" + str(int(self.time_sum * (self.num1 - self.TOTAL) / self.num1)) + "s", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

        self.time_end = time.time()
        self.time_sum = self.time_end - self.time_start
        self.show_imagee(frame)
        # 等待20ms,进入下一次循环
        cv2.waitKey(20)

    def show_imagee(self, image):
        show_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        height, width, bytesPerComponent = show_image.shape
        bytesPerLine = bytesPerComponent * width
        q_image = QImage(show_image.data, width, height, bytesPerLine, QImage.Format_RGB888)
        pixmap = QPixmap.fromImage(q_image)
        self.frame.setPixmap(pixmap)

    def closeEvent(self, event):
        self.capp.release()
        self.timer_camera.stop()
        cv2.destroyAllWindows()
        self.button1.setDisabled(False)

    def stop_camera(self):
        if shifou==0:
            QMessageBox.information(self, "警告", "您还没有开始学习")
        if shifou==1:
            now = datetime.datetime.now()
            self.capp.release()
            self.timer_camera.stop()
            cv2.destroyAllWindows()
            self.button1.setDisabled(False)
            text = self.code_edit.toPlainText()  # 获取文本内容
            lines = text.splitlines()  # 分隔每一行
            line_count = len(lines)  # 计算行数
            focust = int(100 * (int(self.time_sum * (self.num1 - self.TOTAL) / self.num1)) / int(self.time_sum))
            QMessageBox.information(self, "学习结果", "您一共学习了" + str(int(self.time_sum)) + "s  专注度为" +
                                    str(focust) + "%")
            # 把数据存入数据库里面
            db = pymysql.connect(host="localhost", user="root", password="PENGsuoqun123", db="bb")
            cur = db.cursor()
            sql1 = "insert into learn_record(id,time,totaltime,focustime,focus,codelen) values(%s, %s, %s, %s,%s,%s)"
            val = (
            self.username, now, int(self.time_sum), int(self.time_sum * (self.num1 - self.TOTAL) / self.num1), focust,
            line_count)
            try:
                cur.execute(sql1, val)
                db.commit()
            except Exception as a:
                raise a
            finally:
                db.close()
            self.model.removeRows(0, self.model.rowCount())
            conn = pymysql.connect(
                host=\'localhost\',
                user=\'root\',
                password=\'PENGsuoqun123\',
                db=\'bb\'
            )

            # 执行查询语句
            with conn.cursor() as cursor:
                sql = \'SELECT time,totaltime,focustime,focus,codelen FROM learn_record WHERE id=%s\'
                cursor.execute(sql, (self.username))
                results = cursor.fetchall()

            # 将查询结果添加到模型中
            for row_data in results:
                row_items = []
                for col_data in row_data:
                    item = QStandardItem(str(col_data))
                    item.setTextAlignment(Qt.AlignCenter)  # 居中显示
                    row_items.append(item)
                self.model.appendRow(row_items)




if __name__ == "__main__":
    shifou = 0
    app = QApplication([])
    login_window = Login()
    login_window.show()
    app.exec_()

 

基于QT设计的人脸考勤打卡签到系统

1. 项目介绍

近几年,生物特征识别技术获得快速发展。人脸作为一种生物特征,具有很强的自身稳定性和个体差异性,是进行身份验证的最理想依据,主要方法包括步态识别、虹膜识别、皮肤芯片、脸像识别、多模态(即多生物特征融合)技术等。其中,人脸识别技术因为具有方便、直观、易于普及等优点尤为受到关注与研究。

文章针对当前常用考勤方式中普遍存在的代签到、考勤数据整合较慢等问题,利用华为云的人脸识别技术实现了一种基于QT框架设计的在线考勤系统。该系统可大大提高考勤效率,满足各类场环境景下的考勤需求,具有识别度高、检测速度快、操作简单的特点,能够为用户提供更多便利帮助。

实现大致流程:

  1. 注册华为云账号
  2. 开通华为云的人脸识别服务
  3. 通过API创建人脸库
  4. 将需要考勤识别的人脸,添加到人脸库
  5. 考勤时,调用本地摄像头获取人脸图像,与人脸库里注册的人脸进行对比,找到考勤人,完成考勤。

核心思路总结:

华为云人脸识别服务支持,人脸库创建,向人脸库添加人脸,在人脸库里搜索匹配的人脸,.......很多功能。当前

考勤系统主要用到这3个功能。

创建人脸库: 在创建人脸库的时候,支持创建自定义字段,也就是每个人脸可以加一些自定义的属性描述,但是不支持中文字符,如果是字符串字段,范围是0~255长度;

向人脸库添加人脸: 如果在创建人脸库的时候,注册了自定义字段,在添加人脸的时候就可以带上自定义字段的描述,除了添加自定义字段,也可以添加ID,这个ID关联数据库方便考勤的时候进行查找对比。

在人脸库搜索人脸: 可以取一张本地的图片与人脸库里存在的人脸进行匹配,得到相识度,在搜索人脸的时候也可以设置过滤条件,常见的条件就是相识度阀值,低于设置阀值就不返回,也可以设置自定义字段返回,如果注册人脸的时候添加了自定义属性,在识别到人脸的时候就可以通过返回的自定义属性判断这个人脸是谁。

考勤实现核心思路:

向人脸库添加人脸的时候,可以指定external_image_id字段,这个是代表图片外部ID,与当前图像绑定,这个字段可以存放在本地的数据库里,在考勤的时候,从当前摄像头取一帧图像与华为云人脸库里的图像进行对比,找到相识度最高的一张图片,然后这张图片的external_image_id字段,然后与数据库里的external_image_id字段匹配,就找到这个人的详细信息了(详细信息是存放到本地数据库里的),然后就可以实现考勤逻辑了。

软件最终的效果:

(1)主界面

(2)打开摄像头

如果视频里没有人脸,会有错误提示的。

(3)点击人脸注册,添加工号自定义属性

(4)点击人脸搜索:搜索到之后会把自定义的属性显示出来--工号

其他功能都不在演示了,详细实现看下面章节的代码流程。

项目源码下载地址: https://download.csdn.net/download/xiaolong1126626497/71245801

2. 创建人脸库

2.1 开通人脸识别服务

官网地址: https://console.huaweicloud.com/frs/?region=cn-north-4&locale=zh-cn#/frs/manage/index

如果没有华为云账号,打开上面地址时,需要先注册,如果有账号登录之后就可以看到下面的页面,鼠标移到最右边,开通对应的服务。

2.2 创建人脸库

官方帮助文档地址: https://support.huaweicloud.com/api-face/face_02_0088.html

2.3 AK/SK、ID、域名参数获取

在使用API访问接口时,需要填充很多的参数,endpointproject_id 等等。

关于这些API需要使用的签名参数介绍在这个页面里:https://support.huaweicloud.com/devg-apisign/api-sign-provide-start.html

Endpoint 是代表地区与终端节点,即云服务在不同Region有不同的访问域名。

查看地址: https://developer.huaweicloud.com/endpoint

打开链接之后,选择自己的服务,然后往下翻就可以看到对应服务的地址。

华北-北京四  cn-north-4  face.cn-north-4.myhuaweicloud.com   HTTPS

获取AK/SK

  1. 打开控制台页面

  2. 将鼠标移至用户名,在下拉列表中单击“我的凭证”。

  3. 单击“访问密钥”。

  4. 单击“新增访问密钥”,进入“新增访问密钥”页面。

  5. 输入描述信息,单击“确定”,下载密钥,请妥善保管。

在API凭证页面可以看到项目ID:

2.4 在线调试接口

华为云提供了在调试API接口,非常方便,可以提前验证功能是否正常。

地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=FRS&api=CreateFaceSet

选择自己要调试的API,然后填充对应的参考,进行调试即可。

下面的例子是创建人脸库。

如果同一个人脸库创建两次,就会报错,可以在调试页面看到错误的提示,方便自己写代码时进行判断,处理。

如果不清楚访问的域名地址是多少,在调试接口页面是可以直接获取查看的。

创建人脸库的时候,还可以指定自定义字段,方便对这张人脸进行打个性化标签属性,方便知道这张脸是谁的。

请求头:

 "User-Agent": "API Explorer",
 "X-Auth-Token": "******",
 "Content-Type": "application/json"

请求体:

 "external_fields": 
  "face_name": 
   "type": "string"
  ,
  "face_phone": 
   "type": "string"
  ,
  "face_class": 
   "type": "string"
  
 ,
 "face_set_name": "face3"

2.5 代码实现

/*
功能: 创建人脸库
*/
void Widget::HuaweiCreatesFaceDatabase(QString face_lib_name)

    //表示创建人脸库
    function_select=2;

    QString requestUrl;
    QNetworkRequest request;

    //设置请求地址
    QUrl url;

    //人脸注册的请求地址
    requestUrl = QString("https://face.%1.myhuaweicloud.com/v2/%2/face-sets")
                 .arg(SERVER_ID)
                 .arg(PROJECT_ID);

    //自己创建的TCP服务器,测试用
    //requestUrl="http://10.0.0.6:8080";

    //设置数据提交格式
    request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));

    //设置token
    request.setRawHeader("X-Auth-Token",Token);
    //构造请求
    url.setUrl(requestUrl);

    request.setUrl(url);

    //添加自定义字段   external_fields这个对象就是自定义字段
    //我这里只是定义了3个字段,可以自己增加
    //添加人脸的时候也需要添加这里设置的自定义字段
    QString post_param=QString(""
            "\\"external_fields\\": "
             "\\"face_name\\": "
              "\\"type\\": \\"string\\""
             ","
             "\\"face_phone\\": "
              "\\"type\\": \\"string\\""
             ","
             "\\"face_class\\": "
              "\\"type\\": \\"string\\""
             ""
            ","
            "\\"face_set_name\\": \\"%1\\""
           "").arg(face_lib_name);

    //发送请求
    manager->post(request, post_param.toUtf8());

3. 如何获取X-Subject-Token

使用API访问华为云的所有服务接口,都需要填X-Subject-Token参数,下面介绍步骤:

3.1 创建一个新的IAM帐户

鼠标悬停在右上角的用户名称上,弹出下拉框,选择统一身份认证。

3.2 选择创建用户

3.3 使用调试接口测试获取oken

调试接口地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=IAM&api=KeystoneCreateUserTokenByPassword

右边响应头里的X-Subject-Token就是获取的token。

3.4 上面的这些账户名称从哪里获取?

3.5 请求地址和数据格式

获取X-Subject-Token请求的地址:https://iam.cn-north-4.myhuaweicloud.com/v3/auth/tokens

请求头数据:


 "User-Agent": "API Explorer",
 "X-Auth-Token": "******",
 "Content-Type": "application/json;charset=UTF-8"

请求体数据:


  "auth": 
    "identity": 
      "methods": [
        "password"
      ],
      "password": 
        "user": 
          "domain":   
            "name": "xxxxx"  //这里填当前主账户名称
          ,
          "name": "xxxx",  //这个新建的子账户名称
          "password": "xxxxx"    //这个是新建的子账户密码
        
      
    ,
    "scope": 
      "project": 
        "name": "cn-north-4"
      
    
  

3.5 代码实现

/*
功能: 获取token
*/
void Widget::GetToken()

    //表示获取token
    function_select=3;

    QString requestUrl;
    QNetworkRequest request;

    //设置请求地址
    QUrl url;

    //获取token请求地址
    requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens")
                 .arg(SERVER_ID);

    //自己创建的TCP服务器,测试用
    //requestUrl="http://10.0.0.6:8080";

    //设置数据提交格式
    request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));

    //构造请求
    url.setUrl(requestUrl);

    request.setUrl(url);

    QString text =QString("\\"auth\\":\\"identity\\":\\"methods\\":[\\"password\\"],\\"password\\":"
    "\\"user\\":\\"domain\\": "
    "\\"name\\":\\"%1\\",\\"name\\": \\"%2\\",\\"password\\": \\"%3\\","
    "\\"scope\\":\\"project\\":\\"name\\":\\"%4\\"")
            .arg(MAIN_USER)
            .arg(IAM_USER)
            .arg(IAM_PASSWORD)
            .arg(SERVER_ID);

    //发送请求
    manager->post(request, text.toUtf8());

4. 向人脸库添加人脸

4.1 调试接口

官方地址: https://support.huaweicloud.com/api-face/face_02_0093.html

添加人脸调试接口地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=FRS&api=AddFacesByFile

4.2 请求接口与地址总结

请求地址: https://face.cn-north-4.myhuaweicloud.com/v2/项目ID/face-sets/人脸库名称/faces
请求方式: post
请求头:

 "User-Agent": "API Explorer",
 "X-Auth-Token": "******",  //替换成自己的token
 "Content-Type": "application/json"

请求体:

 "image_base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAk..........."

4.3 查询人脸

文档地址: https://support.huaweicloud.com/api-face/face_02_0094.html

调试地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=FRS&api=ShowFaceSet

4.4 代码实现: 添加人脸时增加自定义字段

//注册人脸,添加人脸,添加自定义数据
//QString name 这个是自定义字段,0~255 字节,只能英文字母和数字
void Widget::RegFace2(const QImage image,QString face_lib,QString name)

    function_select=0;

    QString requestUrl;
    QNetworkRequest request;

    //存放图片BASE64编码
    QString imgData;

    //设置请求地址
    QUrl url;

    //人脸注册的请求地址
    requestUrl = QString("https://face.%1.myhuaweicloud.com/v2/%2/face-sets/%3/faces")
            .arg(SERVER_ID)
            .arg(PROJECT_ID)
            .arg(face_lib);
    qDebug()<<"requestUrl:"<<requestUrl;
   // requestUrl="http://192.168.1.100:8080";
    //设置数据提交格式
    request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));

    //将图片进行Base64编码
    imgData = QString(toBase64(image)); //编码后的图片大小不超过2M
    //设置token
    request.setRawHeader("X-Auth-Token",Token);

    //构造请求
    url.setUrl(requestUrl);
    request.setUrl(url);

   QString post_param=QString
              (""
                "\\"image_base64\\": \\"%1\\","
                "\\"external_fields\\":\\"face_name\\":\\"%2\\","
                "\\"single\\": true"
               "").arg(imgData).arg(name);

    //发送请求
    manager->post(request, post_param.toUtf8());

5. 从人脸库搜索匹配的人脸

5.1 参数介绍

官方文档地址: https://support.huaweicloud.com/api-face/face_02_0086.html

5.2 调试接口

接口地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=FRS&api=SearchFaceByFile

我这里调试接口里选择本地指定文件与人脸库的人脸进行匹配,返回相识度。

返回的结果:


 "faces": [
  
   "face_id": "6ED4NSsS",
   "external_image_id": "FPhviHgr",
   "bounding_box": 
    "width": 197,
    "top_left_x": 201,
    "top_left_y": 80,
    "height": 241
   ,
   "similarity": 1
  ,
  
   "face_id": "EpBvdmp5",
   "external_image_id": "ubxcVUpN",
   "bounding_box": 
    "width": 104,
    "top_left_x": 111,
    "top_left_y": 100,
    "height": 123
   ,
   "similarity": 0.9802261
  ,
  
   "face_id": "cSKntQmF",
   "external_image_id": "nBoRpVab",
   "bounding_box": 
    "width": 113,
    "top_left_x": 65,
    "top_left_y": 106,
    "height": 127
   ,
   "similarity": 0.97810614
  ,
  
   "face_id": "TZGv2cKI",
   "external_image_id": "49y0pLG9",
   "bounding_box": 
    "width": 104,
    "top_left_x": 90,
    "top_left_y": 97,
    "height": 125
   ,
   "similarity": 0.9768342
  ,
  
   "face_id": "3p1Ho8Vw",
   "external_image_id": "vpuYDUvU",
   "bounding_box": 
    "width": 108,
    "top_left_x": 100,
    "top_left_y": 108,
    "height": 122
   ,
   "similarity": 0.9619592
  ,
  
   "face_id": "nIuMJ1fA",
   "external_image_id": "v8mNyJSY",
   "bounding_box": 
    "width": 102,
    "top_left_x": 58,
    "top_left_y": 141,
    "height": 98
   ,
   "similarity": 0.95380914
  ,
  
   "face_id": "IamCTWR9",
   "external_image_id": "KzUyJDxU",
   "bounding_box": 
    "width": 108,
    "top_left_x": 32,
    "top_left_y": 142,
    "height": 97
   ,
   "similarity": 0.5663861
  
 ]

5.3 接口总结

这是总结的bas64编码的接口,方便编写代码进行搜索人脸。

请求地址: https://face.cn-north-4.myhuaweicloud.com/v2/项目ID/face-sets/人脸库名称/search
请求方式: POST
请求头:

 "User-Agent": "API Explorer",
 "X-Auth-Token": "******",    自己的token
 "Content-Type": "application/json"

请求体:

 "image_base64": "..........................................."   图片BAS64编码后的数据

5.4 代码实现

//人脸搜索功能
// threshold参数表示人脸相似度阈值,低于这个阈值则不返回,取值范围0~1,一般情况下建议取值0.93,默认为0。
void Widget::FindFace(const QImage image,QString face_lib,double threshold)

    function_select=1;
    QString requestUrl;
    QNetworkRequest request;

    //存放图片BASE64编码
    QString imgData;

    //设置请求地址
    QUrl url;

    //人脸搜索请求地址
    requestUrl = QString("https://face.%1.myhuaweicloud.com/v2/%2/face-sets/%3/search")
            .arg(SERVER_ID)
            .arg(PROJECT_ID)
            .arg(face_lib);

    //设置数据提交格式
    request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));

    //将图片进行Base64编码
    imgData = QString(toBase64(image)); //编码后的图片大小不超过2M
    //设置token
    request.setRawHeader("X-Auth-Token",Token);

    //构造请求
    url.setUrl(requestUrl);
    request.setUrl(url);

    QString post_param=QString
               (""
                 "\\"image_base64\\": \\"%1\\","
                 "\\"return_fields\\":[\\"face_name\\"],"   //需要返回的自定义字段
                "\\"threshold\\":%2"
                "").arg(imgData).arg(threshold);

    //发送请求
    manager->post(request, post_param.toUtf8());

6. 删除人脸库

文档地址: https://support.huaweicloud.com/api-face/face_02_0091.html

调试地址: https://apiexplorer.developer.huaweicloud.com/apiexplorer/debug?product=FRS&api=DeleteFaceSet

总结:

请求接口: https://face.cn-north-4.myhuaweicloud.com/v2/项目ID/face-sets/人脸库名称

以上是关于每日一小时打卡pc端毕业设计(具有人脸专注度检测和人脸识别的监控功能)的主要内容,如果未能解决你的问题,请参考以下文章

基于华为云人脸服务接口设计的人脸考勤打卡签到系统

基于C++QT设计的人脸考勤打卡签到系统

基于CNN的人脸相似度检测

旋转矩阵(leetcode4.7每日打卡)

Python OpenCV开发MR智能人脸识别打卡系统(四服务模块设计)

Python OpenCV开发MR智能人脸识别打卡系统(三工具模块设计)