人脸识别考勤系统

登陆界面:

login.py

import sys
#from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import *

from _login import try_login
from open_win import Win
from open_staff import staff_win
from open_sign import sign_win

# 登陆界面,ID,密码,登陆按钮,打卡按钮,主要这四个控件
class QlabelDemo(QWidget):
    def __init__(self):
        super().__init__()




        #设置主窗口的名称以及尺寸位置
        self.setWindowTitle('考勤管理系统')
        self.resize(400, 200)
        self.move(400,300)
        #导入应用程序的图标
        #self.setWindowIcon(QIcon('.\image\Icon'))


        #设置文本框内容
        #flo = QFormLayout()
        nameLb1 = QLabel('&账号', self)
        self.nameEd1 = QLineEdit(self)
        nameLb1.setBuddy(self.nameEd1)
        #nameEd1.setPlaceholderText("纯数字组成")


        nameLb2 = QLabel('&密码', self)
        self.nameEd2 = QLineEdit(self)
        #self.nameEd2.setPlaceholderText("大小写字母和数字组成")
        nameLb2.setBuddy(self.nameEd2)

        #设置主按钮的名称
        btnOk = QPushButton('&登录')
        #设置主按钮的链接窗口
        btnOk.clicked.connect(self.showdialog)

        # 设置次按钮的名称
        btnCancel = QPushButton('&打卡')
        mainLayout = QGridLayout(self)
        btnCancel.clicked.connect(self.clock)

        #文本框及输入框的位置
        mainLayout.addWidget(nameLb1, 0, 0)
        mainLayout.addWidget(self.nameEd1, 0, 1, 1, 2)

        mainLayout.addWidget(nameLb2, 1, 0)
        mainLayout.addWidget(self.nameEd2, 1, 1, 1, 2)

        mainLayout.addWidget(btnOk, 2, 1)
        mainLayout.addWidget(btnCancel, 2, 2)

    # 弹出相应提示的函数,提示内容是str
    def tip(self,str):
        QMessageBox.question(self, 'Message',str, QMessageBox.Yes, QMessageBox.Yes)

    #定义弹出页面
    def showdialog(self):
        # 获取界面id和密码的控件text内容
        id = self.nameEd1.text()
        pw = self.nameEd2.text()

        # 登陆
        # 空输入 -1
        # 查无此人 0
        # 密码错误 -2
        # 员工登陆 1
        # 管理员登陆 2
        # _login函数中的用户区分的函数
        result = try_login(id, pw)
        # 相应的返回值进行相应的提示
        if result == -1:
            self.tip("请输入完整信息")
        elif result == 0:
            self.tip("查无此人")
        elif result == -2:
            self.tip("密码错误")
        #员工登陆成功,弹出员工界面
        elif result == 1:
            self.tip("员工"+id+"登陆成功")
            self.staff = staff_win(id)
            self.staff.show()
        # 管理员登陆成功,弹出管理员界面
        else:
            self.tip("管理员" + id + "登陆成功")
            self.vipui = Win()
            self.vipui.show()

    # 点击打卡按钮,直接进入打卡界面
    def clock(self):
        self.tip("打卡界面 登陆成功")
        self.signlogin = sign_win()
        self.signlogin.show()

# 主函数,实例化一个登陆界面
if __name__ == '__main__':
    app = QApplication(sys.argv)
    labelDemo = QlabelDemo()
    labelDemo.show()
    sys.exit(app.exec_())

判断用户登陆:

_login.py
import os

# 该文件用于判断登陆情况
# 人脸文件的位置
path = "Faces\\"
# 每个人脸的名字
people_name = os.listdir(path)
# 密码文本
password_path = "password.txt"
# number = "s" + str(1+len(os.listdir(path)))+"\\"
#
# password_path = "password.txt"
#
# for name in people_name:
#     file = open(path+name+"\\"+password_path, 'w')
#     file.write('123456')
#     file.close()

# 空输入 -1
# 查无此人 0
# 密码错误 -2
# 员工登陆 1
# 管理员登陆 2
def try_login(id, password):
    # 当id或者密码有一个为空时,提示要输入完整信息
    if id == '' or password == '':
        return -1

    # 先对id进行判断是谁
    for name in people_name:
        # 这里设置s1为管理员
        if id == 's1':
            # 读取相应用户密码
            file = open(path + name + "\\" + password_path)
            pw = file.read()
            # 如果密码正确,管理员登陆成功,否则提示密码错误
            if password == pw:
                return  2
            else:
                return -2
        # 对其他员工进行判断
        if id == name:
            # 找到对应员工时进行读取密码并进行比较
            file = open(path+name+"\\"+password_path)
            pw = file.read()
            if password == pw:
                return 1
            else:
                return -2
    # 如果走了一圈没有return出来则代表没有此ID
    return 0

管理员登陆界面

ui代码:vip_ui.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'vip_ui.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_vip_ui(object):
    def setupUi(self, vip_ui):
        vip_ui.setObjectName("vip_ui")
        vip_ui.resize(443, 602)
        self.save_image = QtWidgets.QPushButton(vip_ui)
        self.save_image.setGeometry(QtCore.QRect(340, 60, 91, 31))
        self.save_image.setObjectName("save_image")
        self.train_data = QtWidgets.QPushButton(vip_ui)
        self.train_data.setGeometry(QtCore.QRect(340, 160, 91, 31))
        self.train_data.setObjectName("train_data")
        self.setting_time = QtWidgets.QPushButton(vip_ui)
        self.setting_time.setGeometry(QtCore.QRect(270, 320, 101, 41))
        self.setting_time.setObjectName("setting_time")
        self.work_time = QtWidgets.QDateTimeEdit(vip_ui)
        self.work_time.setEnabled(True)
        self.work_time.setGeometry(QtCore.QRect(170, 290, 51, 22))
        self.work_time.setWrapping(False)
        self.work_time.setFrame(True)
        self.work_time.setReadOnly(False)
        self.work_time.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)
        self.work_time.setObjectName("work_time")
        self.go_home_time = QtWidgets.QDateTimeEdit(vip_ui)
        self.go_home_time.setEnabled(True)
        self.go_home_time.setGeometry(QtCore.QRect(170, 350, 51, 22))
        self.go_home_time.setWrapping(False)
        self.go_home_time.setFrame(True)
        self.go_home_time.setReadOnly(False)
        self.go_home_time.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows)
        self.go_home_time.setObjectName("go_home_time")
        self.label = QtWidgets.QLabel(vip_ui)
        self.label.setGeometry(QtCore.QRect(60, 290, 81, 21))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(vip_ui)
        self.label_2.setGeometry(QtCore.QRect(60, 350, 81, 21))
        self.label_2.setObjectName("label_2")
        self.data_path = QtWidgets.QLineEdit(vip_ui)
        self.data_path.setGeometry(QtCore.QRect(60, 560, 191, 20))
        self.data_path.setObjectName("data_path")
        self.look_all_data = QtWidgets.QPushButton(vip_ui)
        self.look_all_data.setGeometry(QtCore.QRect(260, 520, 111, 23))
        self.look_all_data.setObjectName("look_all_data")
        self.save_data = QtWidgets.QPushButton(vip_ui)
        self.save_data.setGeometry(QtCore.QRect(260, 560, 111, 21))
        self.save_data.setObjectName("save_data")
        self.work_e = QtWidgets.QLineEdit(vip_ui)
        self.work_e.setGeometry(QtCore.QRect(60, 320, 161, 20))
        self.work_e.setObjectName("work_e")
        self.home_e = QtWidgets.QLineEdit(vip_ui)
        self.home_e.setGeometry(QtCore.QRect(60, 380, 161, 20))
        self.home_e.setObjectName("home_e")
        self.show_data = QtWidgets.QTextEdit(vip_ui)
        self.show_data.setGeometry(QtCore.QRect(60, 420, 311, 101))
        self.show_data.setLineWidth(13)
        self.show_data.setDocumentTitle("")
        self.show_data.setPlaceholderText("")
        self.show_data.setObjectName("show_data")
        self.image = QtWidgets.QLabel(vip_ui)
        self.image.setGeometry(QtCore.QRect(10, 10, 320, 240))
        self.image.setFrameShape(QtWidgets.QFrame.WinPanel)
        self.image.setText("")
        self.image.setObjectName("image")

        self.retranslateUi(vip_ui)
        QtCore.QMetaObject.connectSlotsByName(vip_ui)

    def retranslateUi(self, vip_ui):
        _translate = QtCore.QCoreApplication.translate
        vip_ui.setWindowTitle(_translate("vip_ui", "管理员界面"))
        self.save_image.setText(_translate("vip_ui", "保存图像"))
        self.train_data.setText(_translate("vip_ui", "训练数据"))
        self.setting_time.setText(_translate("vip_ui", "设置"))
        self.work_time.setDisplayFormat(_translate("vip_ui", "H:mm"))
        self.go_home_time.setDisplayFormat(_translate("vip_ui", "H:mm"))
        self.label.setText(_translate("vip_ui", "设置上班时间:"))
        self.label_2.setText(_translate("vip_ui", "设置下班时间:"))
        self.look_all_data.setText(_translate("vip_ui", "展示所有签到信息"))
        self.save_data.setText(_translate("vip_ui", "导出文件"))
        self.work_e.setPlaceholderText(_translate("vip_ui", "上班评语"))
        self.home_e.setPlaceholderText(_translate("vip_ui", "下班评语"))

继承管理员界面类

open_win.py

from vip_ui import Ui_vip_ui
import cv2
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

# 检测人脸函数,创建文件夹
from load_face import find_face, mkdir
# 训练所有图片
from train import train_all_image
# 删除文件夹
import shutil

class Win(QWidget, Ui_vip_ui):
    def __init__(self):
        super(Win, self).__init__()
        self.setupUi(self)

        #self.isplay = False
        # 储存人脸信息
        self.face_data = None
        # 常用的数据集路径
        self.path = "Faces\\"

        # 每次登陆都会创建一个新的文件夹,未避免空文件夹过多,每次登陆先删除没有照片的文件夹
        # 数据集人名,s1,s2......
        people_list = os.listdir(self.path)
        # 遍历数据集文件
        for i in people_list:
            # 遍历人名下一级文件夹,人名
            img = os.listdir(self.path+i+"\\")
            # 是否删除该文件夹
            dete = True
            # 再次遍历
            for j in img:
                # 如果文件夹下有照片则不删除文件夹
                if j[len(j)-3:len(j)] == "BMP" or j[len(j)-3:len(j)] == "jpg":
                    dete = False
            # 如果文件夹下没有照片,删除文件夹
            if dete:
                shutil.rmtree(self.path+i)

        # 创建一个人名文件夹,用于存放检测到的人脸,取名字就叫s文件个数
        self.number = "s" + str(1 + len(os.listdir(self.path))) + "\\"
        # 创建文件夹
        mkdir(self.path + self.number)
        # 创建文件夹下的密码默认123456
        flie = open(self.path + self.number+"password.txt", 'w')
        flie.write("123456")
        flie.close()

        # 创建文件夹下签到信息默认什么都没有,就一个data
        flie = open(self.path + self.number + "work_data.txt", 'w')
        flie.write("data:\n")
        flie.close()

        # 打开摄像头
        self.cap = cv2.VideoCapture(0)
        # 同上一个打开摄像头窗口一致,得到画布大小
        self.w = self.image.width()
        self.h = self.image.height()
        #self.open_camera.clicked.connect(self.opencv_video)
        #self.close_camera.clicked.connect(self.opencv_video_clos)
        # 保存图像按钮触发事件
        self.save_image.clicked.connect(self.save_face)
        # 训练数据按钮触发事件
        self.train_data.clicked.connect(self.train)
        # 设置上班下班时间,上班下班评语按钮触发事件
        self.setting_time.clicked.connect(self.set_time)
        # 查看所有员工信息按钮触发事件
        self.look_all_data.clicked.connect(self.show_work_data)
        # 导出所有人签到信息按钮触发事件
        self.save_data.clicked.connect(self.save_work_data)

        # 设置刷新时间,用于显示摄像头画面
        self._timer = QTimer(self)
        self._timer.timeout.connect(self.play)
        self._timer.start(25)

    # def opencv_video(self):
    #     self.isplay = True
    #
    #
    # def opencv_video_clos(self):
    #     self.isplay = False
    # 显示摄像头画面函数
    def play(self):
        # 打开摄像头
        r, f = self.cap.read()
        if r:
            # 对画面进行resize到画布大小
            f = cv2.resize(f, (self.w, self.h))
            # 检测人脸,这里不是识别,管理员登陆,只是检测导入新的人脸数据
            img, face_data1 = find_face(f)
            # 保存出人脸区域信息
            self.face_data = face_data1
            # 显示摄像头画面信息,已经标注检测人脸的图像
            self.image.setPixmap(QPixmap.fromImage(
                QImage(cv2.cvtColor(img, cv2.COLOR_BGR2RGB),
                       self.w,
                       self.h,
                       13)))
        # self.closevideo = False
        # if self.isplay:
        #     self.closevideo = True
        #     self.cap = cv2.VideoCapture(0)
        #     sucss, img = self.cap.read()
        #     img = cv2.resize(img, (self.w, self.h))
        #     img, face_data1 = find_face(img)
        #     self.face_data = face_data1
        #     self.image.setPixmap(QPixmap.fromImage(
        #         QImage(cv2.cvtColor(img, cv2.COLOR_BGR2RGB),
        #                self.w,
        #                self.h,
        #                13)))
        # else:
        #     if self.closevideo:
        #         self.cap.release()
        #         self.closevideo = False
        #     self.face_data = None
        #self.isplay = True
        # while True:
        #     if self.isplay:
        #         sucss, img = self.cap.read()
        #         img, face_data1 = find_face(img)
        #         self.face_data = face_data1
        #         # print(self.face_data)
        #         cv2.imshow("video", img)
        #         # cv2.imshow("face", face_data1)
        #         cv2.waitKey(16)
        #     else:
        #         self.cap.release()
        #         cv2.destroyAllWindows()
        #         self.face_data = None
        #         break

    # 提示窗口
    def tip(self,str):
        QMessageBox.question(self, 'Message',str, QMessageBox.Yes, QMessageBox.Yes)

    # 保存检测到的人脸
    def save_face(self):
        try:
            # if self.isplay == False:
            #     self.tip("请打开摄像头")
            #     return False
            # 保存图像的路径
            number_img = self.path + self.number + str(1 + len(os.listdir(self.path + self.number + "\\"))) + ".jpg"
            # 保存图像
            cv2.imwrite(number_img, self.face_data, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
            return True
        except:
            # 如果self.face_data为None则未检测到人脸无法保存
            self.tip("未找到人脸")
            return False

    # 训练按钮触发事件
    def train(self):
        try:
            # 训练所有图片,需要一定时间
            train_all_image()
            self.tip("训练完成")
        except:
            # 设置好了,一般不会弹出来
            self.tip("bug")

    # 保存设定的上班,下班时间,上班,下班评语
    def set_time(self):
        # print(self.work_time.text())
        # print(self.go_home_time.text())
        # 把控件内容保存到这四个文件里面,覆盖原有信息
        w_path = "time_data\\work_time.txt"
        h_path = "time_data\\home_time.txt"

        f_w = open(w_path, 'w')
        f_h = open(h_path, 'w')

        f_w.write(self.work_time.text())
        f_h.write(self.go_home_time.text())

        f_w.close()
        f_h.close()

        w_path = "time_data\\work_evaluate.txt"
        h_path = "time_data\\home_evaluate.txt"

        f_w = open(w_path, 'w')
        f_h = open(h_path, 'w')

        f_w.write(self.work_e.text())
        f_h.write(self.home_e.text())

        f_w.close()
        f_h.close()

        self.tip("设置成功")

    # 返回所有员工签到信息
    def return_work_data(self):
        # 所有员工名字
        prople_list = os.listdir(self.path)
        # 添加每个员工签到信息
        str = ""
        # 遍历每个员工的签到信息
        for i in prople_list:
            # 打开并读取信息
            w_d = self.path + i + "\\" + "work_data.txt"
            f = open(w_d, 'r')
            # 添加进去
            str += i + " " + f.read() + " \n"
        # 返回出来
        return str

    # 展示所有员工签到信息
    def show_work_data(self):
        self.show_data.setText(self.return_work_data())

    # 保存所有员工签到信息到指定路径
    def save_work_data(self):
        # p为控件内的内容
        p = self.data_path.text()
        if p == "":
            self.tip("输出路径不能为空!")
            return False
        # 尝试保存
        try:
            data = self.return_work_data()
            f = open(p, "w")
            f.write(data)
            f.close()
            self.tip("保存成功!")
            return True
        except:
            self.tip("路径有误!")
            return False

    # 关闭窗口函数,关闭窗口同样先关闭摄像头
    def closeEvent(self, event):
        """
        重写closeEvent方法,实现dialog窗体关闭时执行一些代码
        :param event: close()触发的事件
        :return: None
        """
        reply = QMessageBox.question(self,
                                               '提示',
                                               "是否要退出?",
                                               QMessageBox.Yes | QMessageBox.No,
                                               QMessageBox.No)
        if reply == QMessageBox.Yes:
            # 关闭摄像头
            self.cap.release()
            event.accept()
        else:

            event.ignore()

load_face.py

import cv2
import os

# 创建文件夹函数,用于新增人员
def mkdir(path):
    # 去除首位空格
    path = path.strip()
    # 去除尾部 \ 符号
    path = path.rstrip("\\")

    # 判断路径是否存在
    # 存在     True
    # 不存在   False
    isExists = os.path.exists(path)

    # 判断结果
    if not isExists:
        # 如果不存在则创建目录
        # 创建目录操作函数
        os.makedirs(path)

        #print(path + ' 创建成功')

        return True
    else:
        # 如果目录存在则不创建,并提示目录已存在
        #print(path + ' 目录已存在')

        return False

# 使用haar模型进行检测人脸
def find_face(img):
    # 定义一个分类器
    classfier = cv2.CascadeClassifier("xml//haarcascade_frontalface_alt.xml")
    # 对输入图像进行灰度化
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 检测人脸
    faceRects = classfier.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
    # 用于储存人脸区域图片
    face_trait = None
    # 当检测到人脸时,只取第一个人脸进行处理
    if len(faceRects) > 0:
        # 检测到人脸的位置信息
        x, y, w, h = faceRects[0]
        # 框选出人脸
        cv2.rectangle(img, (x - 10, y - 10), (x + w + 10, y + h + 10), (0, 255, 0), 2)
        # 对人脸区域进行resize到原数据集图像大小
        face_trait = cv2.resize(gray[y:y + h, x:x + w], (92, 112))
    # 返回检测后的图像和人脸数据,点击保存图像时会保存人脸图像到新增员工文件夹
    return img, face_trait

# cap = cv2.VideoCapture(0)
#
# path = "Faces\\"
# number = "s" + str(1+len(os.listdir(path)))+"\\"
# mkdir(path+number)
# while True:
#     suess, img = cap.read()
#     gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#     faceRects = classfier.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
#     if len(faceRects) > 0:
#         x, y, w, h = faceRects[0]
#         cv2.rectangle(img, (x - 10, y - 10), (x + w + 10, y + h + 10), (0, 255, 0), 2)
#         face_trait = cv2.resize(gray[y:y + h, x:x + w], (92, 112))
#
#         k = cv2.waitKey(16)
#         if k == ord(" "):
#             number_img = path+number+str(1+len(os.listdir(path+number+"\\")))+".jpg"
#             print("保存成功\n")
#             cv2.imwrite(number_img,face_trait,[int(cv2.IMWRITE_JPEG_QUALITY),100])
#     cv2.imshow("video", img)
#     cv2.waitKey(1)

train.py

#from numpy import *
import cv2
import os
import math
import numpy as np
from sklearn.svm import SVC
from sklearn.multiclass import OneVsRestClassifier
from sklearn.model_selection import train_test_split
from sklearn.externals import joblib

# 圆形LBP算子
def circular_LBP(src, radius, n_points):
    height = src.shape[0]
    width = src.shape[1]
    dst = src.copy()
    src.astype(dtype=np.float32)
    dst.astype(dtype=np.float32)

    neighbours = np.zeros((1, n_points), dtype=np.uint8)
    lbp_value = np.zeros((1, n_points), dtype=np.uint8)
    for x in range(radius, width - radius - 1):
        for y in range(radius, height - radius - 1):
            lbp = 0.
            # 先计算共n_points个点对应的像素值,使用双线性插值法
            for n in range(n_points):
                theta = float(2 * np.pi * n) / n_points
                x_n = x + radius * np.cos(theta)
                y_n = y - radius * np.sin(theta)

                # 向下取整
                x1 = int(math.floor(x_n))
                y1 = int(math.floor(y_n))
                # 向上取整
                x2 = int(math.ceil(x_n))
                y2 = int(math.ceil(y_n))

                # 将坐标映射到0-1之间
                tx = np.abs(x - x1)
                ty = np.abs(y - y1)

                # 根据0-1之间的x,y的权重计算公式计算权重
                w1 = (1 - tx) * (1 - ty)
                w2 = tx * (1 - ty)
                w3 = (1 - tx) * ty
                w4 = tx * ty

                # 根据双线性插值公式计算第k个采样点的灰度值
                neighbour = src[y1, x1] * w1 + src[y2, x1] * w2 + src[y1, x2] * w3 + src[y2, x2] * w4

                neighbours[0, n] = neighbour

            center = src[y, x]

            for n in range(n_points):
                if neighbours[0, n] > center:
                    lbp_value[0, n] = 1
                else:
                    lbp_value[0, n] = 0

            for n in range(n_points):
                lbp += lbp_value[0, n] * 2**n

            # 转换到0-255的灰度空间,比如n_points=16位时结果会超出这个范围,对该结果归一化
            dst[y, x] = int(lbp / (2**n_points-1) * 255)

    return dst

#归一化
def MaxMinScaler(data):
    Max = np.max(data)
    Min = np.min(data)
    result = np.zeros(len(data), dtype=np.float64)
    for i in range(len(data)):
        result[i] = (data[i] - Min) / (Max - Min)
    return result

#统计直方图 输入图片,统计直方图0-255之间,然后归一化处理
def PixelStatistics(area):
    pixelfre = np.zeros(256, dtype=np.int64)
    size = area.shape
    rows, cols = size
    for i in range(rows):
        for j in range(cols):
            pixelfre[area[i, j]] = pixelfre[area[i, j]] + 1
    result = MaxMinScaler(pixelfre)
    return result

# 训练所有的图片,在管理员界面训练数据时调用
def train_all_image():
    # 数据集文件夹
    flie = 'Faces\\'
    # 所以人的名字标签,s1,s2,s3,s4,s5,s6.....
    face_name = os.listdir(flie)

    # 一个储存人的名字,作为训练表情的列表
    lable = []
    # 储存人脸特征
    trait = []

    # 遍历所有图片
    for name in face_name:
        # 每个人的文件夹下的所有文件
        img_name = os.listdir(flie+name+"\\")
        for img_path in img_name:
            # print(img_path[len(img_path)-3:len(img_path)])
            # 取出人的文件夹下的BMP和jpg图片
            if img_path[len(img_path)-3:len(img_path)] == 'BMP' or img_path[len(img_path)-3:len(img_path)] == 'jpg':
                # 保存人的名字
                lable.append(name)

                # 输出一下当前处理的图片路径
                print(flie + name + "\\" + img_path)
                # 读取图片
                img = cv2.imread(flie + name + "\\" + img_path, 0)
                # 使用圆形LBP算子,半径为2,像素个数为8
                img_lbp = circular_LBP(img, 2, 8)

                # 保存特征,对LBP特征进行归一化处理后保存
                trait.append(PixelStatistics(img_lbp))

    # 将标签类型转换成数组
    label = np.array(lable)
    # 将特征类型转换成数组
    data = np.array(trait)

    # 输出查看一下标签和特征的行列
    print(label)
    print(data.shape)

    # 定义一个svm模型
    clf = SVC(kernel='rbf', C=1e3, gamma='scale')
    # 循环训练数据
    while 1:
        # 将数据集分成训练集(0.8),预测集(0.2)
        x_train, x_test, y_train, y_test = train_test_split(data, label, test_size=0.2)
        # 定义一个多分类模型,然后训练
        clfc = OneVsRestClassifier(clf, -1).fit(x_train, y_train)
        # 对预测集进行预测
        result_one = clfc.score(x_test, y_test)
        # 当识别率达到90%时保存模型并跳出循环结束训练
        if result_one > 0.90:
            #print('\n模型训练完成')
            joblib.dump(clfc, "./Models/lbp_svm_model.m")
            #print('\n模型保存完成')
            break

    # np.save("data\\lable.npy", lable)
    # np.save("data\\trait.npy", trait)

员工登陆界面:

ui:staff.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'staff.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_staff(object):
    def setupUi(self, staff):
        staff.setObjectName("staff")
        staff.resize(358, 444)
        self.name_data = QtWidgets.QLabel(staff)
        self.name_data.setGeometry(QtCore.QRect(20, 10, 121, 51))
        font = QtGui.QFont()
        font.setFamily("方正粗黑宋简体")
        font.setPointSize(18)
        self.name_data.setFont(font)
        self.name_data.setObjectName("name_data")
        self.work_data = QtWidgets.QTextEdit(staff)
        self.work_data.setGeometry(QtCore.QRect(30, 70, 301, 221))
        self.work_data.setObjectName("work_data")
        self.show_data = QtWidgets.QPushButton(staff)
        self.show_data.setGeometry(QtCore.QRect(240, 300, 91, 31))
        self.show_data.setObjectName("show_data")
        self.path = QtWidgets.QLineEdit(staff)
        self.path.setGeometry(QtCore.QRect(30, 350, 201, 20))
        self.path.setObjectName("path")
        self.save_data = QtWidgets.QPushButton(staff)
        self.save_data.setGeometry(QtCore.QRect(240, 350, 91, 23))
        self.save_data.setObjectName("save_data")

        self.retranslateUi(staff)
        QtCore.QMetaObject.connectSlotsByName(staff)

    def retranslateUi(self, staff):
        _translate = QtCore.QCoreApplication.translate
        staff.setWindowTitle(_translate("staff", "员工界面"))
        self.name_data.setText(_translate("staff", "员工:"))
        self.show_data.setText(_translate("staff", "展示签到信息"))
        self.save_data.setText(_translate("staff", "导出文件"))

继承类

open_staff.py

from PyQt5.QtWidgets import *
from staff import Ui_staff

# 继承员工主界面的类
class staff_win(QWidget, Ui_staff):
    # 传入一个参数,就是是哪位员工登陆的
    def __init__(self, name):
        super(staff_win, self).__init__()
        self.setupUi(self)

        # 员工的文件路径
        self.name = "Faces\\" + name + "\\"
        # 显示员工名字
        self.name_data.setText("员工:"+name)
        # 展示签到信息的触发函数
        self.show_data.clicked.connect(self.show_work_data)
        # 保存出员工的签到信息
        self.save_data.clicked.connect(self.save_work_data)

    # 一个提示弹窗
    def tip(self,str):
        QMessageBox.question(self, 'Message',str, QMessageBox.Yes, QMessageBox.Yes)

    # 读取员工签到信息,并返回
    def return_data(self):
        f = open(self.name + "work_data.txt", "r")
        str = f.read()
        return str

    # 展示员工签到信息到信息框里,构造函数中设置好的展示签到信息触发函数
    def show_work_data(self):
        self.work_data.setText(self.return_data())

    # 保存出签到信息文件
    def save_work_data(self):
        # 输出路径
        p = self.path.text()
        # 如果填入为空则提示
        if p == "":
            self.tip("输出路径不能为空!")
            return False
        try:
            # 得到员工签到信息
            data = self.return_data()
            # 新建一个文件并保存后提示
            f = open(p, "w")
            f.write(data)
            f.close()
            self.tip("保存成功!")
            return True

        except:
            # 文件路径输入有误
            self.tip("路径有误!")
            return False

签到打卡窗口:

ui:sign.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'sign.ui'
#
# Created by: PyQt5 UI code generator 5.13.0
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_sign(object):
    def setupUi(self, sign):
        sign.setObjectName("sign")
        sign.resize(637, 452)
        self.image = QtWidgets.QLabel(sign)
        self.image.setGeometry(QtCore.QRect(20, 20, 320, 240))
        self.image.setFrameShape(QtWidgets.QFrame.WinPanel)
        self.image.setText("")
        self.image.setObjectName("image")
        self.name = QtWidgets.QLabel(sign)
        self.name.setGeometry(QtCore.QRect(420, 80, 151, 91))
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.name.sizePolicy().hasHeightForWidth())
        self.name.setSizePolicy(sizePolicy)
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(22)
        self.name.setFont(font)
        self.name.setText("")
        self.name.setAlignment(QtCore.Qt.AlignCenter)
        self.name.setObjectName("name")
        self.working = QtWidgets.QLabel(sign)
        self.working.setGeometry(QtCore.QRect(390, 180, 201, 31))
        font = QtGui.QFont()
        font.setFamily("方正粗黑宋简体")
        font.setPointSize(14)
        self.working.setFont(font)
        self.working.setText("")
        self.working.setObjectName("working")
        self.gohome = QtWidgets.QLabel(sign)
        self.gohome.setGeometry(QtCore.QRect(390, 220, 201, 31))
        font = QtGui.QFont()
        font.setFamily("方正粗黑宋简体")
        font.setPointSize(14)
        self.gohome.setFont(font)
        self.gohome.setText("")
        self.gohome.setObjectName("gohome")
        self.mal = QtWidgets.QLabel(sign)
        self.mal.setGeometry(QtCore.QRect(110, 310, 411, 101))
        font = QtGui.QFont()
        font.setFamily("方正粗黑宋简体")
        font.setPointSize(18)
        self.mal.setFont(font)
        self.mal.setText("")
        self.mal.setAlignment(QtCore.Qt.AlignCenter)
        self.mal.setObjectName("mal")
        self.timetime = QtWidgets.QLabel(sign)
        self.timetime.setGeometry(QtCore.QRect(370, 30, 241, 51))
        font = QtGui.QFont()
        font.setFamily("黑体")
        font.setPointSize(12)
        self.timetime.setFont(font)
        self.timetime.setFrameShape(QtWidgets.QFrame.Box)
        self.timetime.setText("")
        self.timetime.setAlignment(QtCore.Qt.AlignCenter)
        self.timetime.setObjectName("timetime")

        self.retranslateUi(sign)
        QtCore.QMetaObject.connectSlotsByName(sign)

    def retranslateUi(self, sign):
        _translate = QtCore.QCoreApplication.translate
        sign.setWindowTitle(_translate("sign", "签到打卡"))

继承类:open_sign.py

from sign import Ui_sign
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import cv2
# 识别人脸函数
from video_open import know_who
#import sys

# 签到界面,继承sign界面
class sign_win(QWidget, Ui_sign):
    def __init__(self):
        super(sign_win, self).__init__()
        self.setupUi(self)

        #self.timeEdit.setDateTime(QTime.currentTime())
        # 读取出time_data文件夹下上班时间
        path = "time_data\\"
        f = open(path+"work_time.txt", 'r')
        # 放到自身熟悉中在下面进行使用
        self.star_work_time = f.read()
        f.close()
        #将上班时间打印到相应的控件上
        self.working.setText("上班时间:"+self.star_work_time)
        # 同上一步操作一样,打印并保存下班时间
        f = open(path + "home_time.txt", 'r')
        self.go_home_time = f.read()
        f.close()
        self.gohome.setText("下班时间:"+self.go_home_time)

        # 读取上班评语
        f = open(path+"work_evaluate.txt", 'r')
        self.work_mal = f.read()
        f.close()

        # 读取下班评语
        f = open(path+"home_evaluate.txt", 'r')
        self.home_mal = f.read()
        f.close()

        # 打开摄像头
        self.cap = cv2.VideoCapture(0)
        # 获取摄像头区域的宽高,用于对摄像头画面进行resize到统一大小
        self.w = self.image.width()
        self.h = self.image.height()

        # 设置一个刷新率,用于实时显示时间和摄像头画面
        self._timer = QTimer(self)
        self._timer.timeout.connect(self.play)
        self._timer.start(25)

    # 提示窗口,提示内容为身str
    def tip(self,str):
        QMessageBox.question(self, 'Message',str, QMessageBox.Yes, QMessageBox.Yes)

    # 已经打卡 1
    # 未迟到 2
    # 已超时 3
    # 早退 4
    # 下班 5
    # 设置打卡状态的返回值, 参数:识别的人名,现在的时间
    def work_time_data(self, result, now_time_str):
        try:
            # write_bool = False
            # 读取识别员工的打卡信息文件
            path = "Faces\\" + result + "\\" + "work_data.txt"
            work_data = open(path, 'r+')
            data_str = work_data.readlines()
            # 读取文件最后一行信息,用于判断是否已经打卡
            time_data = data_str[-1]
            # if now_time_str[0:16] == time_data[0:16]:
            #     self.mal.setText("已经打卡!")
            #     work_data.close()
            #     return 1
            # 如果最后一行数据长度大于16,和前13个字符(年月日时)与现在相等,则已经打过卡了
            if len(time_data) >= 16 and now_time_str[0:13] == time_data[0:13]:
                self.mal.setText("已经打卡!")
                work_data.close()
                return 1
            # 现在的时
            now_h = int(now_time_str[11:13])
            # 现在的分
            now_m = int(now_time_str[14:16])
            # 上班
            # 分为上班和下班时间段,上班时间段为13点前,下班时间段在13点后
            if now_h <= 12:
                # 取管理员设置好的时间段,在构造函数里已经读取到的
                # 因为设置的时间信息长度有4,也有5的,比如设置的9:00和10:00上班,长度不一致,所以区分取出时与分
                if len(self.star_work_time) == 4:
                    # 如果长度为4,则时是个位数
                    work_h = int(self.star_work_time[0])
                    # 分为余下字段
                    work_m = int(self.star_work_time[2:])
                else:
                    # 否则长度为5,时为前两个字符,分为后两个字符
                    work_h = int(self.star_work_time[0:2])
                    work_m = int(self.star_work_time[3:])
                # 如果现在时间比定的时间早,则为正常上班,未迟到
                if (now_h < work_h) or (now_h == work_h and now_m < work_m):
                    # 提示上班打卡到相应控件
                    self.mal.setText("上班!"+self.work_mal)
                    # 将上班信息写入相应文件
                    work_data.write(now_time_str + " 上班\n")
                    # 关闭文件流
                    work_data.close()
                    return 2
                else:
                    # 否则则为迟到,输出相应信息,并保存至文件
                    self.mal.setText("您迟到了!已被记录!")
                    work_data.write(now_time_str + " 迟到\n")
                    work_data.close()
                    return 3
            else:
                # 否则为下午下班时间段
                # 取管理员设置好的时,分,与上方一致
                if len(self.go_home_time) == 4:
                    home_h = int(self.go_home_time[0])
                    home_m = int(self.go_home_time[2:])
                else:
                    home_h = int(self.go_home_time[0:2])
                    home_m = int(self.go_home_time[3:])
                # 进行比较,如果现在时间比下班早,则为早退,否则正常下班,同样都保存起来
                if (now_h < home_h) or (now_h == home_h and now_m < home_m):
                    self.mal.setText("您早退了!已被记录!")
                    work_data.write(now_time_str + " 早退\n")
                    work_data.close()
                    return 4
                else:
                    self.mal.setText("下班!" + self.home_mal)
                    work_data.write(now_time_str + " 下班\n")
                    work_data.close()
                    return 5
        except:
            # 没有相应文件进行提示,但这些都是有设置好的,不会弹出来
            self.tip("信息不完整!")

    # 设置的刷新函数,在构造函数里有运行,在设置刷新时间里
    def play(self):
        # 获取系统当前时间
        now_time = QDateTime.currentDateTime()
        # 转换成相应的时间格式
        timeDisplay = now_time.toString("yyyy-MM-dd hh:mm:ss dddd")
        # 显示在右上角
        self.timetime.setText(timeDisplay)
        # 读取摄像头内容,r为是否打开,f为当前画面
        r, f = self.cap.read()
        # 如果打开了
        if r:
            # 对图像进行resize到画布大小
            f = cv2.resize(f, (self.w, self.h))
            # 预测是谁,f为标记好的画面,result为识别的人脸
            f, result = know_who(f)
            # 显示识别的人名
            self.name.setText(result)
            # 如果不是未识别,就是识别到了
            if result != "未识别":
                # 则运行上一个函数,进行签到判断
                self.work_time_data(result, timeDisplay)
                #time.sleep(1)
            #实时显示当前画面
            self.image.setPixmap(QPixmap.fromImage(
                            QImage(cv2.cvtColor(f, cv2.COLOR_BGR2RGB),
                                   self.w,
                                   self.h,
                                   13)))
    # 关闭窗口函数,如果关闭,也相应关闭摄像头
    def closeEvent(self, event):
        """
        重写closeEvent方法,实现dialog窗体关闭时执行一些代码
        :param event: close()触发的事件
        :return: None
        """
        # 进行提示是否关闭窗口
        reply = QMessageBox.question(self,
                                               '签到',
                                               "是否要退出签到?",
                                               QMessageBox.Yes | QMessageBox.No,
                                               QMessageBox.No)
        if reply == QMessageBox.Yes:
            # 如果关闭窗口也相应关闭摄像头再关闭窗口
            self.cap.release()
            event.accept()
        else:
            event.ignore()

# app = QApplication(sys.argv)
# ui = sign_win()
# ui.setupUi(ui)
# ui.show()
# sys.exit(app.exec_())

video_open.py识别人脸

import cv2
from sklearn.externals import joblib
# 导入lbp算法,归一化函数
from train import circular_LBP, PixelStatistics

# 准备好haar识别模型
classfier = cv2.CascadeClassifier("xml//haarcascade_frontalface_alt.xml")
# 读取训练好的svm模型
clf = joblib.load("./Models/lbp_svm_model.m")

# 设置字体大小,类型,颜色
font_size = 1.0
font = cv2.FONT_HERSHEY_SIMPLEX
font_color = (0, 255, 0)

# cap = cv2.VideoCapture(0)
# 识别函数
def know_who(img):
    # 对图像进行灰度化
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 检测人脸
    faceRects = classfier.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
    # 如果检测到人脸则对第一个人进行预测
    if len(faceRects) > 0:
        # 人脸位置
        x, y, w, h = faceRects[0]
        # 画出人脸
        cv2.rectangle(img, (x - 10, y - 10), (x + w + 10, y + h + 10), (0, 255, 0), 2)
        # 对检测到的人脸部分进行resize到原数据集大小
        face_trait = cv2.resize(gray[y:y+h,x:x+w], (92, 112))
        # 对人脸部分进行lbp特征提取
        face_trait_lbp = PixelStatistics(circular_LBP(face_trait, 2, 8))
        # 识别结果
        result = clf.predict([face_trait_lbp])
        # 识别的人名放到图片上
        cv2.putText(img, result[0], (x-10, y-10), font, font_size, font_color, 2)
        # 返回画好的图,识别的人名
        return img, result[0]
    # 如果没检测到人脸,返回未识别
    return img, "未识别"

# while True:
#     suess, img = cap.read()
#     # 灰度处理
#     gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#     faceRects = classfier.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
#     if len(faceRects) > 0:
#         x, y, w, h = faceRects[0]
#         cv2.rectangle(img, (x - 10, y - 10), (x + w + 10, y + h + 10), (0, 255, 0), 2)
#         face_trait = cv2.resize(gray[y:y+h,x:x+w], (92, 112))
#         face_trait_lbp = PixelStatistics(circular_LBP(face_trait, 2, 8))
#         result = clf.predict([face_trait_lbp])
#         cv2.putText(img, result[0], (x, y), font, font_size, font_color, 2)
#         cv2.imshow("face", face_trait)
#
#     cv2.imshow("video", img)
#     cv2.waitKey(16)

 

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 人脸识别考勤系统是一种基于最新的计算机视觉技术和算法实现的,具有无需任何身份识别码或卡片的快速准确识别人脸信息的考勤系统。它可以在较短时间内提取出人脸信息,并进行快速比对,从而完成员工的考勤管理。Java是一种跨平台的编程语言,它可以有效地处理复杂的应用程序和算法。在人脸识别考勤系统中使用Java编写是非常常见的。 人脸识别考勤系统的开发需要具备一定的技术基础和专业知识。系统需要先进行人脸信息的采集和重构,然后对图像进行处理,提取人脸特征值进行比对。Java作为一种高效的编程语言,可以充分发挥它的优势,帮助开发人员快速、准确地实现人脸识别算法的开发和应用。 在人脸识别考勤系统中,Java除了作为编程工具外,还可以作为系统的运行环境,提供更好的性能和稳定性,使得系统更加可靠。此外,Java还可以与其他编程语言和应用程序进行无缝集成,实现系统的全面管理和监控。 总之,人脸识别考勤系统Java应用十分广泛,可以帮助企业快速地实现员工的考勤管理,减少管理成本和工作量,提高管理效率,实现企业的数字化转型和升级发展。 ### 回答2: 人脸识别考勤系统是一款基于Java语言开发的考勤系统,可以通过人脸识别技术对员工的考勤进行自动化管理。该系统通过采集员工的面部信息,对其进行建模和比对,从而实现对员工出勤、迟到、早退以及加班等考勤信息的实时监测和处理。 该系统不仅可以提高企业的考勤效率,避免了传统考勤方式带来的信息不准确、虚假打卡等问题,同时还能有效减轻 HR 管理人员的工作负担。 该系统通过多种技术手段来实现人脸识别功能,包括图像处理算法、人脸识别算法、机器学习等。这些技术的运用不仅保证了系统的精准性和稳定性,而且还增强了其对不同环境下的识别能力,比如光照、表情等方面。 在该系统的应用中,需要预先在系统中录入员工的面部信息,并在考勤过程中将员工的面部信息与系统中保存的模板进行比对。系统将自动生成考勤记录,并及时反馈给 HR 管理人员或者管理员。 总之,人脸识别考勤系统是一款先进的考勤工具,可以有效提高企业的考勤管理效率和精准度,为企业的管理提供了重要的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值