基于OpenCV的MR智能视频打卡系统(《Python OpenCV从入门到精通》内容总结(第16章))

阅读: 评论:0

基于OpenCV的MR智能视频打卡系统(《Python OpenCV从入门到精通》内容总结(第16章))

基于OpenCV的MR智能视频打卡系统(《Python OpenCV从入门到精通》内容总结(第16章))

目录

需求分析

数据模型

打卡功能

数据维护

考勤报表

系统设计

环境配置

功能结构

业务流程

项目结构

文件系统设计

数据实体模块设计

工具模块设计

公共工具模块

IO流模块

摄像头工具模块

服务模块设计

人事服务模块

人脸识别服务模块

程序入口设计

需求分析

数据模型

  1. 由于不使用第三方库,所有的数据都以文本的形式保存在文件中,因此要对数据建立统一模型
  2. 数据模型中应该包含员工数据类,员工数据类必须包含姓名选项和员工编号
  3. 人脸识别必须要包含员工照片,员工照片采用特征码+随机值的规则进行命名
  4. 员工与打卡记录是一对多的关系,员工姓名作为key,打卡记录列表作为value

打卡功能

  1. 由于OpenCV中提供的人脸识别器中LBPH识别器正确率较高,因此采用LBPH识别器实现人脸识别的功能
  2. 采集员工照片样本时,按住Enter键生成一张正面照片文件,为增加识别准确率,每位员工拍3张照片
  3. OpenCV的人脸识别器存在一个缺陷,必须比对2种不同的样本才能进行判断,因此系统中应预存几张无脸照片,保证即使只有一个员工也能正常打卡
  4. 员工打卡成功后,要记录员工打卡时间然后保存到文件中

数据维护

具体功能可以解释为员工的增改删查4种操作,删除员工信息前需要验证权限,确认删除员工信息后同时要删除员工的打卡记录和文件照片。

考勤报表

考勤标准采用上班打卡和下班打卡进行判断,未在正常时间段内打卡属于打卡异常,包括迟到、早退和缺席,具体情况定义为:

全勤:上班时间前打卡且下班时间后打卡

迟到:上班时间前未打卡,在上班时间后至中午12点前打卡

早退:下班时间后未打卡,在中午12点后至下班时间前打卡

缺席:员工当天无打卡记录

系统设计

环境配置

python=3.10,opencv=4.7.0.72,numpy=1.21.5,pycharm=2021.3.2,windows10

功能结构

业务流程

项目结构

文件系统设计

文件路径文件名说明
/data/保存所有员工信息的文件
/data/保存所有员工打卡记录的文件
/data/保存上下班时间
/data/保存管理员账号和密码
/data/face/*.png员工照片(包括默认图像)

员工数据保存格式如下:

以字符串的形式保存内容:

{'张三': ['2021-03-01 08:33:17'], '李四': ['2021-03-01 08:28:49', '2021-03-01 17:17:46'], '王五': ['2021-03-01 08:44:40', '2021-03-01 17:38:19', '2021-03-02 08:14:33'], '赵六': ['2021-03-01 08:54:12']}

以字符串的形式保存数据,格式如下:

以字符串的形式保存数据:

/data/face/文件夹下保存的是所有员工的照片,格式为png,照片大小为,每名员工保存3张照片,以及2个默认的图像文件,文件名为1000000000.png和2000000000.png。

数据实体模块设计

  • 构建员工类:
class Employee:def __init__(self, id, name, code):self.name = name  # 员工编号self.id = id  # 员工姓名de = code  # 员工的特征码
  • 全局变量:
LOCK_RECORD = dict()  # 打卡记录字典,格式为{姓名:[时间1,时间2]}
EMPLOYEES = list()  # 全体员工列表
MAX_ID = 0  # 目前可用的最大ID
CODE_LEN = 6  # 特征码的默认长度
WORK_TIME = ""  # 上班时间
CLOSING_TIME = ""  # 工下班时间
USERS = dict()  # 管理员账号密码
  • 增删员工:
# 添加新员工
def add(e: Employee):EMPLOYEES.append(e)# 删除指定ID的员工记录
def remove(id):for emp in EMPLOYEES:if str(id) == str(emp.id):ve(emp)  # 员工列表中删除员工if emp.name in LOCK_RECORD.keys():  # 如果存在该员工的打卡记录del LOCK_RECORD[emp.name]  # 删除该员工的打卡记录break
  • 分配员工id:
# 获取新员工的ID
def get_new_id():global MAX_ID  # 调用全局变量MAX_ID += 1  # 当前最大的ID + 1return MAX_ID

工具模块设计

工具模块一共包含3个文件:public_tools.py(公共工具模块),io_tools.py(IO流模块),camera.py(摄像头工具模块)。

公共工具模块

公共工具模块主要包含以下功能:

  1. 生成随机数和随机特征码
  2. 校验时间字符串格式
  • 导入模块:
import random
import datetime
from entity import organizations as o
  • 生成随机数:
# 随机生成长度为len的数字
def randomNumber(len):first = str(random.randint(1, 9))  # 第一位取非0数last = "".join(random.sample("1234567890", len - 1))  # 后几位随机拼接任意数字return first + last# 随机生成与特征码长度相等的数字
def randomCode():return randomNumber(o.CODE_LEN)  # 特征码的长度
  • 校验时间格式
# 校验时间格式
def valid_time(str):try:datetime.datetime.strptime(str, "%H:%M:%S")return Trueexcept:return False# 校验年月格式
def valid_year_month(str):try:datetime.datetime.strptime(str, "%Y-%m")return Trueexcept:return False# 校验日期格式
def valid_date(date):try:datetime.datetime.strptime(date, "%Y-%m-%d")return Trueexcept:return False

IO流模块

IO流模块主要包含以下功能:

  1. 封装所有对文件的读写操作,包括加载员工信息、加载打卡记录、加载照片文件、删除员工信息、删除打卡记录等
  2. 文件自检功能
  3. 创建CSV文件
  • 导入模块
from service import hr_service as hr
from entity import organizations as o
from service import recognize_service as rs
import os
import cv2
import numpy as np
  • 全局变量
PATH = os.getcwd() + "\data\"  # 数据文件夹根目录
PIC_PATH = PATH + "faces\"  # 照片文件夹
DATA_FILE = PATH + ""  # 员工信息文件
WORK_TIME = PATH + ""  # 上下班时间配置文件
USER_PASSWORD = PATH + ""  # 管理员账号密码文件
RECORD_FILE = PATH + ""  # 打卡记录文件
IMG_WIDTH = 640  # 图像的统一宽度
IMG_HEIGHT = 480  # 图像的统一高度
  • 文件自检
# 自检,检查默认文件缺失
def checking_data_files():if not ists(PATH):os.mkdir(PATH)print("数据文件夹丢失,已重新创建:" + PATH)if not ists(PIC_PATH):os.mkdir(PIC_PATH)print("照片文件夹丢失,已重新创建:" + PIC_PATH)sample1 = PIC_PATH + "1000000000.png"  # 样本1文件路径if not ists(sample1):sample_img_1 = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), np.uint8)  # 创建一个空内容图像sample_img_1[:, :, 0] = 255  # 改为纯蓝图像cv2.imwrite(sample1, sample_img_1)  # 保存此图像print("默认样本1已补充")sample2 = PIC_PATH + "2000000000.png"  # 样本2文件路径if not ists(sample2):sample_img_2 = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), np.uint8)  # 创建一个空内容图像sample_img_2[:, :, 1] = 255  # 改为纯蓝图像cv2.imwrite(sample2, sample_img_2)  # 保存此图像print("默认样本2已补充")if not ists(DATA_FILE):open(DATA_FILE, "a+")  # 附加读写方式打开文件,达到创建空文件目的print("员工信息文件丢失,已重新创建:" + DATA_FILE)if not ists(RECORD_FILE):open(RECORD_FILE, "a+")  # 附加读写方式打开文件,达到创建空文件目的print("打卡记录文件丢失,已重新创建:" + RECORD_FILE)if not ists(USER_PASSWORD):file = open(USER_PASSWORD, "a+", encoding="utf-8")  # 附加读写方式打开文件,达到创建空文件目的user = dict()user["mr"] = "mrsoft"file.write(str(user))  # 将默认管理员账号密码写入到文件中file.close()  # 关闭文件print("管理员账号密码文件丢失,已重新创建:" + RECORD_FILE)if not ists(WORK_TIME):file = open(WORK_TIME, "a+", encoding="utf-8")  # 附加读写方式打开文件,达到创建空文件目的file.write("09:00:00/17:00:00")  # 将默认时间写入到文件中file.close()  # 关闭文件print("上下班时间配置文件丢失,已重新创建:" + RECORD_FILE)
  • 从文件中加载数据
# 加载全部员工信息
def load_employee_info():max_id = 1  # 最大员工IDfile = open(DATA_FILE, "r", encoding="utf-8")  # 打开文件,只读for line adlines():  # 遍历文件中的行内容id, name, code = line.rstrip().split(",")  # 去除换行符,并分割字符串信息o.add(o.Employee(id, name, code))  # 组织结构中添加员工信息if int(id) > max_id:  # 如果发现某员工的id更大max_id = int(id)  # 修改最大IDo.MAX_ID = max_id  # 记录最大IDfile.close()  # 关闭文件# 加载所有打卡记录
def load_lock_record():file = open(RECORD_FILE, "r", encoding="utf-8")  # 打开打卡记录文件,只读text = ad()  # 读取所有文本if len(text) > 0:  # 如果存在文本o.LOCK_RECORD = eval(text)  # 将文本转换成打卡记录字典file.close()  # 关闭文件# 加载员工图像
def load_employee_pic():photos = list()  # 样本图像列表lables = list()  # 标签列表pics = os.listdir(PIC_PATH)  # 读取所有照片if len(pics) != 0:  # 如果照片文件不是空的for file_name in pics:  # 遍历所有图像文件code = file_name[0:o.CODE_LEN]  # 截取文件名开头的特征码photos.append(cv2.imread(PIC_PATH + file_name, 0))  # 以灰度图像的方式读取样本lables.append(int(code))  # 样本的特征码作为训练标签rs.train(photos, lables)  # 识别器训练样本else:  # 不存在任何照片print("Error >> 员工照片文件丢失,请重新启动程序并录入员工信息!")# 加载上下班时间数据
def load_work_time_config():file = open(WORK_TIME, "r", encoding="utf-8")  # 打开打卡记录文件,只读text = ad().rstrip()  # 读取所有文本times = text.split("/")  # 分割字符串o.WORK_TIME = times[0]  # 第一个值是上班时间o.CLOSING_TIME = times[1]  # 第二个值是下班时间file.close()  # 关闭文件# 加载管理员账号密码
def load_users():file = open(USER_PASSWORD, "r", encoding="utf-8")  # 打开打卡记录文件,只读text = ad()  # 读取所有文本if len(text) > 0:  # 如果存在文本o.USERS = eval(text)  # 将文本转换成打卡记录字典file.close()  # 关闭文件
  • 将数据保存在文件中:
# 将员工信息持久化
def save_employee_all():file = open(DATA_FILE, "w", encoding="utf-8")  # 打开员工信息文件,只写,覆盖info = ""  # 待写入的字符串for emp in o.EMPLOYEES:  # 遍历所有员工信息# 拼接员工信息info += str(emp.id) + "," + str(emp.name) + "," + de) + "n"file.write(info)  # 将这些员工信息写入到文件中file.close()  # 关闭文件# 将打卡记录持久化
def save_lock_record():file = open(RECORD_FILE, "w", encoding="utf-8")  # 打开打卡记录文件,只写,覆盖info = str(o.LOCK_RECORD)  # 将打卡记录字典转换成字符串file.write(info)  # 将字符串内容写入到文件中file.close()  # 关闭文件# 将上下班时间写到文件中
def save_work_time_config():file = open(WORK_TIME, "w", encoding="utf-8")  # 打开打卡记录文件,只写,覆盖times = str(o.WORK_TIME) + "/" + str(o.CLOSING_TIME)file.write(times)  # 将字符串内容写入到文件中file.close()  # 关闭文件
  • 删除照片:
# 删除指定员工的所有照片
def remove_pics(id):pics = os.listdir(PIC_PATH)  # 读取所有照片文件code = _code_with_id(id))  # 获取该员工的特征码for file_name in pics:  # 遍历文件if file_name.startswith(code):  # 如果文件名以特征码开头os.remove(PIC_PATH + file_name)  # 删除此文件print("删除照片:" + file_name)
  • 生成CSV文件:
# 生成csv文件,采用Windows默认的gbk编码
def create_CSV(file_name, text):file = open(PATH + file_name + ".csv", "w", encoding="gbk")  # 打开文件,只写,覆盖file.write(text)  # 将文本写入文件中file.close()  # 关闭文件print("已生成文件,请注意查看:" + PATH + file_name + ".csv")

摄像头工具模块

摄像头工具模块主要包含以下功能:

  1. 开启摄像头打卡
  2. 开启摄像头为员工拍照
  • 导入模块:
import cv2
from util import public_tools as tool
from util import io_tools as io
from service import recognize_service as rs
from service import hr_service as hr
  • 全局变量:
ESC_KEY = 27  # Esc键的ASCII码
ENTER_KEY = 13  # Enter键的ASCII码
  • 为新员工拍照:
# 打开摄像头进行登记
def register(code):cameraCapture = cv2.VideoCapture(0, cv2.CAP_DSHOW)  # 获得默认摄像头success, frame = ad()  # 读取一帧shooting_time = 0  # 拍摄次数while success:  # 如果读到有效帧数cv2.imshow("register", frame)  # 展示当前画面success, frame = ad()  # 再读一帧key = cv2.waitKey(1)  # 记录当前用户敲下的按键if key == ESC_KEY:  # 如果直接按ESC键break  # 停止循环if key == ENTER_KEY:  # 如果按Enter键# 将当前帧缩放成统一大小photo = size(frame, (io.IMG_WIDTH, io.IMG_HEIGHT))# 拼接照片名:照片文件夹+特征码+随机数字+图片后缀img_name = io.PIC_PATH + str(code) + str(tool.randomNumber(8)) + ".png"cv2.imwrite(img_name, photo)  # 保存将图像shooting_time += 1  # 拍摄次数递增if shooting_time == 3:  # 如果拍完三张照片break  # 停止循环cv2.destroyAllWindows()  # 释放所有窗体lease()  # 释放摄像头io.load_employee_pic()  # 让人脸识别服务重新载入员工照片
  • 打开摄像头打卡:
# 打开摄像头打卡
def clock_in():cameraCapture = cv2.VideoCapture(0, cv2.CAP_DSHOW)  # 获得默认摄像头success, frame = ad()  # 读取一帧while success and cv2.waitKey(1) == -1:  # 如果读到有效帧数cv2.imshow("check in", frame)  # 展示当前画面gary = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # 将彩色图片转为灰度图片if rs.found_face(gary):  # 如果屏幕中出现正面人脸gary = size(gary, (io.IMG_WIDTH, io.IMG_HEIGHT))  # 将当前帧缩放成统一大小code = rs.recognise_face(gary)  # 识别图像if code != -1:  # 如果识别成功name = hr.get_name_with_code(code)  # 获取此特征码对应的员工if name != None:  # 如果返回的结果不是空的cv2.destroyAllWindows()  # 释放所有窗体lease()  # 释放摄像头return name  # 返回打卡成功者的姓名success, frame = ad()  # 再读一帧cv2.destroyAllWindows()  # 释放所有窗体lease()  # 释放摄像头

服务模块设计

服务模块包含以下两个文件:hr_service.py(人事服务模块)和recognize_servise.py(人脸识别服务模块)。

人事服务模块

人事服务模块主要包含以下功能:

  1. 添加新员工
  2. 删除某员工
  3. 为指定员工添加打卡记录
  4. 多种获取员工信息的方法
  5. 生成考勤月报
  6. 生成考勤月报(CSV文件)
  • 导入模块:
from entity import organizations as o
from util import public_tools as tool
from util import io_tools as io
import datetime
import calendar
  • 加载所有数据:
# 加载数据
def load_emp_data():io.checking_data_files()  # 文件自检io.load_users()  # 载入管理员账号io.load_lock_record()  # 载入打卡记录io.load_employee_info()  # 载入员工信息io.load_employee_pic()  # 载入员工照片
  • 添加新员工:
# 添加新员工
def add_new_employee(name):code = tool.randomCode()  # 生成随机特征码newEmp = o._new_id(), name, code)  # 创建员工对象o.add(newEmp)  # 组织结构中添加新员工io.save_employee_all()  # 保存最新的员工信息return code  # 新员工的特征码
  • 删除员工:
# 删除某个员工
def remove_employee(id):io.remove_pics(id)  # 删除该员工所有图片o.remove(id)  # 从组织结构中删除io.save_employee_all()  # 保存最新的员工信息io.save_lock_record()  # 保存最新的打卡记录
  • 添加打卡记录:
# 为指定员工添加打卡记录
def add_lock_record(name):record = o.LOCK_RECORD  # 所有打卡记录now_time = w().strftime("%Y-%m-%d %H:%M:%S")  # 当前时间if name in record.keys():  # 如果这个人有打卡记录r_list = record[name]  # 去除他的记录if len(r_list) == 0:  # 如果记录为空r_list = list()  # 创建新列表r_list.append(now_time)  # 记录当前时间else:  # 如果这个人从未打过卡r_list = list()  # 创建新列表r_list.append(now_time)  # 记录当前时间record[name] = r_list  # 将记录保存在字典中io.save_lock_record()  # 保存所有打卡记录
  • 获取员工数据:
# 所有员工信息报表
def get_employee_report():# report = list()   # 员工信息列表report = "###########################################n"report += "员工名单如下:n"i = 0  # 换行计数器for emp in o.EMPLOYEES:  # 遍历所有员工report += "(" + str(emp.id) + ")" + emp.name + "t"i += 1  # 计数器自增if i == 4:  # 每四个员工换一行report += "n"i = 0  # 计数器归零report = report.strip()  # 清除报表结尾可能出现的换行符report += "n###########################################"return report# 检查id是否存在
def check_id(id):for emp in o.EMPLOYEES:if str(id) == str(emp.id):return Truereturn False# 通过特征码获取员工姓名
def get_name_with_code(code):for emp in o.EMPLOYEES:if str(code) == de):return emp.name# 通过id获取员工特征码
def get_code_with_id(id):for emp in o.EMPLOYEES:if str(id) == str(emp.id):de
  • 获取所有员工的打卡记录:
# 获取所有员工的打卡记录信息
def get_record_all():record = o.LOCK_RECORD  # 获得打卡记录字典report = ""  # 报表内容for name in record.keys():  # 遍历所有名字report += "-----------------------------------n"report += name + "  打卡记录如下:n"for date in record[name]:  # 遍历所有时间字符串report += date + "n"return report
  • 验证管理员账号和密码:
# 验证管理员账号和密码
def valid_user(username, password):if username in o.USERS.keys():  # 如果有这个账号if (username) == password:  # 如果账号和密码匹配return True  # 验证成功return False  # 验证失败
  • 保存上下班时间:
# 保存上下班时间
def save_work_time(work_time, close_time):o.WORK_TIME = work_timeo.CLOSING_TIME = close_timeio.save_work_time_config()  # 上下班时间保存到文件中
  • 打印考勤日报:
def get_day_report(date):io.load_work_time_config()  # 读取上下班时间earliest_time = datetime.datetime.strptime(date + " 00:00:00", "%Y-%m-%d %H:%M:%S")  # 今天0点noon_time = datetime.datetime.strptime(date + " 12:00:00", "%Y-%m-%d %H:%M:%S")  # 今天中午12点latest_time = datetime.datetime.strptime(date + " 23:59:59", "%Y-%m-%d %H:%M:%S")  # 今晚0点之前work_time = datetime.datetime.strptime(date + " " + o.WORK_TIME, "%Y-%m-%d %H:%M:%S")  # 上班时间closing_time = datetime.datetime.strptime(date + " " + o.CLOSING_TIME, "%Y-%m-%d %H:%M:%S")  # 下班时间late_list = []  # 迟到名单left_early = []  # 早退名单absent_list = []  # 缺席名单for emp in o.EMPLOYEES:  # 遍历所有员工if emp.name in o.LOCK_RECORD.keys():  # 如果该员工有打卡记录emp_lock_list = o.(emp.name)  # 获取该员工所有的打卡记录is_absent = True  # 缺席状态for lock_time_str in emp_lock_list:  # 遍历所有打卡记录lock_time = datetime.datetime.strptime(lock_time_str, "%Y-%m-%d %H:%M:%S")  # 打卡记录转为日期格式if earliest_time < lock_time < latest_time:  # 如果当天有打卡记录is_absent = False  # 不缺席if work_time < lock_time <= noon_time:  # 上班时间后、中午之前打卡late_list.append(emp.name)  # 加入迟到名单if noon_time < lock_time < closing_time:  # 中午之后、下班之前打卡left_early.append(emp.name)  # 加入早退名单if is_absent:  # 如果仍然是缺席状态absent_list.append(emp.name)  # 加入缺席名单else:  # 该员工没有打卡记录absent_list.append(emp.name)  # 加入缺席名单emp_count = len(o.EMPLOYEES)  # 员工总人数print("--------" + date + "--------")print("应到人数:" + str(emp_count))print("缺席人数:" + str(len(absent_list)))absent_name = ""  # 缺席名单if len(absent_list) == 0:  # 如果没有缺席的absent_name = "(空)"else:  # 有缺席的for name in absent_list:  # 遍历缺席列表absent_name += name + " "  # 拼接名字print("缺席名单:" + absent_name)print("迟到人数:" + str(len(late_list)))late_name = ""  # 迟到名单if len(late_list) == 0:  # 如果没有迟到的late_name = "(空)"else:  # 有迟到的for name in late_list:  # 遍历迟到列表late_name += name + " "  # 拼接名字print("迟到名单:" + str(late_name))print("早退人数:" + str(len(left_early)))early_name = ""  # 早退名单if len(left_early) == 0:  # 如果没有早退的early_name = "(空)"else:  # 有早退的for name in left_early:  # 遍历早退列表early_name += name + " "  # 拼接名字print("早退名单:" + early_name)# 打印今天的打卡日报
def get_today_report():date = w().strftime("%Y-%m-%d")  # 今天的日期get_day_report(str(date))  # 打印今天的日报
  • 生成考勤月报:
def get_month_report(month):io.load_work_time_config()  # 读取上下班时间date = datetime.datetime.strptime(month, "%Y-%m")  # 月份转为时间对象monthRange = ar, h)[1]  # 该月最后一天的天数month_first_day = datetime.ar, h, 1)  # 该月的第一天month_last_day = datetime.ar, h, monthRange)  # 该月的最后一天clock_in = "I"  # 正常上班打卡标志clock_out = "O"  # 正常下班打卡标志late = "L"  # 迟到标志left_early = "E"  # 早退标志absent = "A"  # 缺席标志lock_report = dict()  # 键为员工名,值为员工打卡情况列表for emp in o.EMPLOYEES:emp_lock_data = []  # 员工打卡情况列表if emp.name in o.LOCK_RECORD.keys():  # 如果员工有打卡记录emp_lock_list = o.(emp.name)  # 从打卡记录中获取该员工的记录index_day = month_first_day  # 遍历日期,从该月第一天开始while index_day <= month_last_day:is_absent = True  # 缺席状态earliest_time = datetime.datetime.strptime(str(index_day) + " 00:00:00", "%Y-%m-%d %H:%M:%S")  # 当天0点noon_time = datetime.datetime.strptime(str(index_day) + " 12:00:00", "%Y-%m-%d %H:%M:%S")  # 当天中午12点latest_time = datetime.datetime.strptime(str(index_day) + " 23:59:59", "%Y-%m-%d %H:%M:%S")  # 当天0点之前work_time = datetime.datetime.strptime(str(index_day) + " " + o.WORK_TIME,"%Y-%m-%d %H:%M:%S")  # 当天上班时间closing_time = datetime.datetime.strptime(str(index_day) + " "+ o.CLOSING_TIME, "%Y-%m-%d %H:%M:%S")  # 当天下班时间emp_today_data = ""  # 员工打卡标记汇总for lock_time_str in emp_lock_list:  # 遍历所有打卡记录lock_time = datetime.datetime.strptime(lock_time_str, "%Y-%m-%d %H:%M:%S")  # 打卡记录转为日期格式if earliest_time < lock_time < latest_time:  # 如果当前日期有打卡记录is_absent = False  # 不缺席if lock_time <= work_time:  # 上班时间前打卡emp_today_data += clock_in  # 追加正常上班打卡标志elif lock_time >= closing_time:  # 下班时间后打卡emp_today_data += clock_out  # 追加正常下班打卡标志elif work_time < lock_time <= noon_time:  # 上班时间后、中午之前打卡emp_today_data += late  # 追加迟到标志elif noon_time < lock_time < closing_time:  # 中午之后、下班之前打卡emp_today_data += left_early  # 追加早退标志if is_absent:  # 如果缺席emp_today_data = absent  # 直接赋予缺席标志emp_lock_data.append(emp_today_data)  # 员工打卡标记添加到打卡情况列表中index_day = index_day + datetime.timedelta(days=1)  # 遍历天数递增else:  # 没有打卡记录的员工index_day = month_first_day  # 从该月第一天开始while index_day <= month_last_day:  # 遍历整月emp_lock_data.append(absent)  # 每天都缺席index_day = index_day + datetime.timedelta(days=1)  # 日期递增lock_report[emp.name] = emp_lock_data  # 将打卡情况列表保存到该员工之下report = ""姓名/日期""  # cvs文件的文本内容,第一行第一列index_day = month_first_day  # 从该月第一天开始while index_day <= month_last_day:  # 遍历整月report += ","" + str(index_day) + """  # 添加每一天的日期index_day = index_day + datetime.timedelta(days=1)  # 日期递增report += "n"for emp in lock_report.keys():  # 遍历报表中的所有员工report += """ + emp + """  # 第一列为员工名data_list = (emp)  # 取出员工的打卡情况列表for data in data_list:  # 取出每一天的打卡情况text = ""  # CSV中显示的内容if absent == data:  # 如果是缺席text = "【缺席】"elif clock_in in data and clock_out in data:  # 如果是全勤,不考虑迟到和早退text = ""  # 显示空白else:  # 如果不是全勤if late in data and clock_in not in data:  # 有迟到记录且无上班打卡text += "【迟到】"if left_early in data and clock_out not in data:  # 有早退记录且无下班打卡text += "【早退】"if clock_out not in data and left_early not in data:  # 无下班打卡和早退记录text += "【下班未打卡】"if clock_in not in data and late not in data:  # 有无上班打卡和迟到记录text += "【上班未打卡】"report += ","" + text + """report += "n"title_date = month_first_day.strftime("%Y{y}%m{m}").format(y="年", m="月")  # csv文件标题日期file_name = title_date + "考勤月报"  # CSV的文件名io.create_CSV(file_name, report)  # 生成csv文件# 创建上个月打卡记录月报
def get_pre_month_report():today = day()  # 得到今天的日期pre_month_first_day = datetime.ar, h - 1, 1)  # 获得上个月的第一天的日期pre_month = pre_month_first_day.strftime("%Y-%m")  # 转成年月格式字符串get_month_report(pre_month)  # 生成上个月的月报

人脸识别服务模块

该模块主要包含以下功能:

  1. 检测图像中是否包含人脸
  2. 判断图像中的人脸属于哪个人
  • 导入包:
import cv2
import numpy as np
import os
  • 全局变量
RECOGNIZER = cv2.face.LBPHFaceRecognizer_create()  # LBPH识别器
# RECOGNIZER = cv2.face.EigenFaceRecognizer_create()#特征脸识别器
# RECOGNIZER = cv2.face.FisherFaceRecognizer_create()  # LBPH识别器
PASS_CONF = 45  # 最高评分,LBPH最高建议用45,特征脸最高建议用22000
FACE_CASCADE = cv2.wd() + "\cascades\haarcascade_l")  # 加载人脸识别级联分类器
  • 训练识别器
# 训练识别器
def train(photos, lables):ain(photos, np.array(lables))  # 识别器开始训练
  • 发现人脸
# 判断图像中是否有正面人脸
def found_face(gary_img):faces = FACE_CASCADE.detectMultiScale(gary_img, 1.15, 4)  # 找出图像中所有的人脸return len(faces) > 0  # 返回人脸数量大于0的结果
  • 识别人脸
# 识别器识别图像中的人脸
def recognise_face(photo):label, confidence = RECOGNIZER.predict(photo)  # 识别器开始分析人脸图像if confidence > PASS_CONF:  # 忽略评分大于最高评分的结果return -1return label

程序入口设计

程序入口文件(main.py)主要负责在控制台中打印菜单界面,用户通过指令可以使用系统中的全部功能,包括打卡、员工管理等。

  • 导入模块:
from util import camera
from util import public_tools as tool
from service import hr_service as hr
  • 用户权限管理:
ADMIN_LOGIN = False  # 管理员登录状态# 管理员登录
def login():while True:username = input("请输入管理员账号(输入0取消操作):")if username == "0":  # 如果只输入0return  # 结束方法passowrd = input("请输入管理员密码:")if hr.valid_user(username.strip(), passowrd.strip()):  # 校验账号密码global ADMIN_LOGIN  # 读取全局变量ADMIN_LOGIN = True  # 设置为管理员已登录状态print(username + "登录成功!请选择重新选择功能菜单")breakelse:print("账号或密码错误,请重新输入!")print("---------------------------")
  • 主菜单设计:
def start():finish = False  # 程序结束标志menu = """
+--------------------------------------------------+
|                   主功能菜单                     |
+--------------------------------------------------+①打卡  ②查看记录  ③员工管理  ④考勤报表  ⑤退出
---------------------------------------------------"""while not finish:print(menu)  # 打印菜单option = input("请输入菜单序号:")if option == "1":  # 如果选择“打卡”face_clock()  # 启动人脸打卡elif option == "2":  # 如果选择“查看记录”if ADMIN_LOGIN:  # 如果管理员已登录check_record()  # 进入查看记录方法else:login()  # 先让管理员登录elif option == "3":  # 如果选择“员工管理”if ADMIN_LOGIN:employee_management()  # 进入员工管理方法else:login()elif option == "4":  # 如果选择“考勤报表”if ADMIN_LOGIN:check_report()  # 进入考勤报表方法else:login()elif option == "5":  # 如果选择“退出”finish = True  # 确认结束,循环停止else:print("输入的指令有误,请重新输入!")print("Bye Bye !")
  • 人脸打卡功能:
# 人脸打卡
def face_clock():print("请正面对准摄像头进行打卡")name = camera.clock_in()  # 开启摄像头,返回打卡员工名称if name is not None:  # 如果员工名称有效hr.add_lock_record(name)  # 保存打卡记录print(name + " 打卡成功!")
  • 员工管理:
def employee_management():menu = """+-------------------------------------------------+
|                员工管理功能菜单                 |
+-------------------------------------------------+①录入新员工  ②删除员工  ③返回上级菜单
---------------------------------------------------"""while True:print(menu)  # 打印菜单option = input("请输入菜单序号:")if option == "1":  # 如果选择“录入新员工”name = str(input("请输入新员工姓名(输入0取消操作):")).strip()if name != "0":  # 只要输入的不是0code = hr.add_new_employee(name)  # 人事服务添加新员工,并获得该员工的特征码print("请面对摄像头,敲击三次回车键完成拍照!")ister(code)  # 打开摄像头为员工照相print("录入成功!")# return  # 退出员工管理功能菜单elif option == "2":  # 如果选择“删除员工”# show_employee_all()  # 展示员工列表_employee_report())  # 打印员工信息报表id = int(input("请输入要删除的员工编号(输入0取消操作):"))if id > 0:  # 只要输入的不是0if hr.check_id(id):  # 若此编号有对应员工verification = tool.randomNumber(4)  # 生成随机4位验证码inputVer = input("[" + str(verification) + "] 请输入验证码:")  # 让用户输入验证码if str(verification) == str(inputVer).strip():  # 如果验证码正确hr.remove_employee(id)  # 人事服务删除该员工print(str(id) + "号员工已删除!")else:  # 无效编号print("验证码有误,操作取消")else:print("无此员工,操作取消")elif option == "3":  # 如果选择“返回上级菜单”return  # 退出员工管理功能菜单else:print("输入的指令有误,请重新输入!")
  • 查询员工打卡记录:
# 查看记录
def check_record():menu = """+-------------------------------------------------+
|                 查看记录功能菜单                |
+-------------------------------------------------+①查看员工列表  ②查看打卡记录  ③返回上级菜单
---------------------------------------------------"""while True:print(menu)  # 打印菜单option = input("请输入菜单序号:")if option == "1":  # 如果选择“查看员工列表”_employee_report())  # 打印员工信息报表elif option == "2":  # 如果选择“查看打卡记录”report = hr.get_record_all()print(report)elif option == "3":  # 如果选择“返回上级菜单”return  # 退出查看记录功能菜单else:print("输入的指令有误,请重新输入!")
  • 生成考勤报表:
# 考勤报表
def check_report():menu = """+-------------------------------------------------+
|                考勤报表功能菜单                 |
+-------------------------------------------------+①日报  ②月报  ③报表设置  ④返回上级菜单
---------------------------------------------------"""while True:print(menu)  # 打印菜单option = input("请输入菜单序号:")if option == "1":  # 如果选择“日报”while True:date = input("输入查询日期,格式为(2008-08-08),输入0则查询今天:")if date == "0":  # 如果只输入_today_report()  # 打印今天的日报break  # 打印完之后结束循环elif tool.valid_date(date):  # 如果输入的日期格式有效hr.get_day_report(date)  # 打印指定日期的日报break  # 打印完之后结束循环else:  # 如果输入的日期格式无效print("日期格式有误,请重新输入!")elif option == "2":  # 如果选择“月报”while True:date = input("输入查询月份,格式为(2008-08),输入0则查询上个月:")if date == "0":  # 如果只输入_pre_month_report()  # 生成上个月的月报break  # 生成完毕之后结束循环elif tool.valid_year_month(date):  # 如果输入的月份格式有效hr.get_month_report(date)  # 生成指定月份的月报break  # 生成完毕之后结束循环else:print("日期格式有误,请重新输入!")elif option == "3":  # 如果选择“报表设置”report_config()  # 进入“报表设置”菜单elif option == "4":  # 如果选择“返回上级菜单”return  # 退出查看记录功能菜单else:print("输入的指令有误,请重新输入!")
  • 定义上下班时间:
# 报表设置
def report_config():menu = """+-------------------------------------------------+
|                报表设置功能菜单                 |
+-------------------------------------------------+
①作息时间设置  ②返回上级菜单
---------------------------------------------------"""while True:print(menu)  # 打印菜单option = input("请输入菜单序号:")if option == "1":  # 如果选择“作息时间设置”while True:work_time = input("请设置上班时间,格式为(08:00:00):")if tool.valid_time(work_time):  # 如果时间格式正确break  # 结束循环else:  # 如果时间格式不对print("上班时间格式错误,请重新输入")while True:close_time = input("请设置下班时间,格式为(23:59:59):")if tool.valid_time(close_time):  # 如果时间格式正确breakelse:  # 如果时间格式不对print("下班时间格式错误,请重新输入")hr.save_work_time(work_time, close_time)  # 保存用户设置的上班时间和下班时间print("设置完成,上班时间:" + work_time + ",下班时间为:" + close_time)elif option == "2":  # 如果选择“返回上级菜单”return  # 退出查看记录功能菜单else:print("输入的指令有误,请重新输入!")
  • 启动程序:
hr.load_emp_data()  # 数据初始化
tital = """
***************************************************
*                MR智能视频打卡系统               *
***************************************************"""
print(tital)  # 打印标题
start()  # 启动程序

本文发布于:2024-02-01 10:08:59,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170675333935888.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:入门   智能   内容   系统   视频
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23