登陆界面:
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)