Python + MySQL + OpenCV 人脸识别门禁考勤系统

发布时间:2026/8/9 1:19:29
Python + MySQL + OpenCV 人脸识别门禁考勤系统 Python MySQL OpenCV 人脸识别门禁考勤系统这是一套集人脸检测、识别、打卡记录与异常报警于一体的完整系统️ 系统架构概览[摄像头] → [OpenCV人脸检测] → [特征提取(FaceNet/Dlib)] → [特征比对] ↓ ┌──────────────┐ │ MySQL数据库 │ │ - 员工信息 │ │ - 人脸特征 │ │ - 打卡记录 │ └──────────────┘ ↓ ┌──────────────┐ │ 异常报警 │ │ - 陌生人报警 │ │ - 多次失败 │ │ - 超时未签退 │ └──────────────┘ 项目目录结构face_attendance/ ├── main.py # 主程序入口 ├── face_recognition.py # 人脸识别核心模块 ├── database.py # MySQL数据库操作 ├── alarm.py # 异常报警模块 ├── config.py # 配置文件 ├── templates/ # Web管理界面模板 │ ├── index.html │ └── records.html ├── static/ # 静态资源 ├── known_faces/ # 已知员工照片 └── unknown_captures/ # 陌生人抓拍️ 数据库设计 (MySQL)-- 员工表 CREATE TABLE employees ( id INT AUTO_INCREMENT PRIMARY KEY, employee_id VARCHAR(20) UNIQUE NOT NULL, -- 工号 name VARCHAR(50) NOT NULL, department VARCHAR(50), phone VARCHAR(20), photo_path VARCHAR(255), -- 注册照片路径 face_feature BLOB, -- 128维特征向量二进制存储 registered_at DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 1 -- 1在职 0离职 ); -- 打卡记录表 CREATE TABLE attendance_records ( id BIGINT AUTO_INCREMENT PRIMARY KEY, employee_id VARCHAR(20), check_time DATETIME DEFAULT CURRENT_TIMESTAMP, direction ENUM(in, out), -- 进/出 confidence FLOAT, -- 识别置信度 snapshot_path VARCHAR(255), -- 抓拍照片路径 device_id VARCHAR(50), -- 设备编号 INDEX idx_emp_time (employee_id, check_time) ); -- 异常日志表 CREATE TABLE anomaly_logs ( id BIGINT AUTO_INCREMENT PRIMARY KEY, event_type ENUM(unknown_face, low_confidence, multiple_fail, abnormal_time), description TEXT, snapshot_path VARCHAR(255), occurred_at DATETIME DEFAULT CURRENT_TIMESTAMP, handled TINYINT DEFAULT 0 ); -- 考勤汇总表每日统计 CREATE TABLE daily_summary ( id INT AUTO_INCREMENT PRIMARY KEY, employee_id VARCHAR(20), date DATE, first_check_in DATETIME, last_check_out DATETIME, total_hours DECIMAL(4,2), status ENUM(normal, late, early_leave, absent), UNIQUE KEY uk_emp_date (employee_id, date) ); 核心代码实现1. 人脸特征提取与比对 (face_recognition.py)import cv2 import numpy as np import face_recognition # 基于dlib的封装库 from config import Config class FaceRecognizer: def __init__(self): self.known_encodings [] # 已知员工特征列表 self.known_ids [] # 对应工号 self.tolerance 0.45 # 匹配阈值越小越严格 def load_known_faces(self, db_cursor): 从数据库加载所有在职员工的人脸特征 db_cursor.execute(SELECT employee_id, face_feature FROM employees WHERE status1) rows db_cursor.fetchall() for emp_id, feature_blob in rows: encoding np.frombuffer(feature_blob, dtypenp.float64) self.known_encodings.append(encoding) self.known_ids.append(emp_id) def extract_encoding(self, image): 提取单张图片的人脸特征128维向量 rgb_img cv2.cvtColor(image, cv2.COLOR_BGR2RGB) face_locations face_recognition.face_locations(rgb_img) if len(face_locations) 0: return None, None # 取最大人脸离镜头最近的 largest max(face_locations, keylambda rect: (rect[2]-rect[0])*(rect[1]-rect[3])) encodings face_recognition.face_encodings(rgb_img, [largest]) return encodings[0], largest def recognize(self, frame): 实时帧识别返回 (employee_id, confidence, face_location) encoding, location self.extract_encoding(frame) if encoding is None: return None, 0, None if len(self.known_encodings) 0: return None, 0, location # 计算与所有已知人脸的距离 distances face_recognition.face_distance(self.known_encodings, encoding) min_idx np.argmin(distances) min_dist distances[min_idx] if min_dist self.tolerance: confidence round((1 - min_dist) * 100, 2) return self.known_ids[min_idx], confidence, location else: return None, 0, location2. 主循环与打卡逻辑 (main.py)import cv2 import time from datetime import datetime from database import Database from face_recognition import FaceRecognizer from alarm import AlarmSystem class AttendanceSystem: def __init__(self): self.db Database() self.recognizer FaceRecognizer() self.alarm AlarmSystem() self.last_check_time {} # 防止同一人短时间内重复打卡 self.cooldown 5 # 冷却时间秒 def initialize(self): 初始化连接数据库、加载已知人脸、打开摄像头 self.db.connect() cursor self.db.get_cursor() self.recognizer.load_known_faces(cursor) self.cap cv2.VideoCapture(0) # 0为默认摄像头 if not self.cap.isOpened(): raise Exception(无法打开摄像头) def process_frame(self, frame): 处理每一帧 emp_id, confidence, face_loc self.recognizer.recognize(frame) if emp_id: # 检查冷却时间 now time.time() if emp_id in self.last_check_time and \ now - self.last_check_time[emp_id] self.cooldown: return # 跳过 self.last_check_time[emp_id] now # 判断进出方向可根据人脸框位置或单独按钮决定 direction self.determine_direction(frame, face_loc) # 保存抓拍快照 snapshot_path self.save_snapshot(frame, emp_id) # 写入打卡记录 self.db.insert_record(emp_id, direction, confidence, snapshot_path) # 更新每日汇总 self.db.update_daily_summary(emp_id, direction) print(f[打卡] {emp_id} {direction} 置信度:{confidence}%) else: # 陌生人检测 if face_loc: self.alarm.unknown_face_warning(frame, face_loc) def run(self): 主循环 self.initialize() while True: ret, frame self.cap.read() if not ret: break # 镜像翻转更自然 frame cv2.flip(frame, 1) # 处理 self.process_frame(frame) # 显示画面可选 cv2.imshow(Attendance System, frame) if cv2.waitKey(1) 0xFF ord(q): break self.cap.release() cv2.destroyAllWindows() self.db.close()3. 异常报警模块 (alarm.py)import cv2 import smtplib import requests from email.mime.text import MIMEText from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart from config import Config class AlarmSystem: def __init__(self): self.fail_count {} # 连续识别失败计数 self.max_fails 5 # 触发报警阈值 def unknown_face_warning(self, frame, face_loc): 陌生人报警保存截图 发送通知 # 保存陌生人截图 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) path funknown_captures/unknown_{timestamp}.jpg cv2.imwrite(path, frame) # 记录到异常日志 db.insert_anomaly(unknown_face, f陌生人闯入 {path}, path) # 发送邮件/钉钉通知 self.send_email_alert(陌生人检测, path) self.send_dingtalk_alert(f⚠️ 检测到陌生人截图{path}) def low_confidence_warning(self, emp_id, confidence): 低置信度报警 db.insert_anomaly(low_confidence, f员工{emp_id}置信度过低({confidence}%)) def send_email_alert(self, subject, image_path): 发送带截图的邮件 msg MIMEMultipart() msg[Subject] f[门禁报警] {subject} msg[From] Config.EMAIL_FROM msg[To] Config.EMAIL_TO body MIMEText(f时间{datetime.now()}\n请立即查看监控。) msg.attach(body) with open(image_path, rb) as f: img MIMEImage(f.read()) img.add_header(Content-ID, image1) msg.attach(img) server smtplib.SMTP(Config.SMTP_SERVER, Config.SMTP_PORT) server.login(Config.EMAIL_USER, Config.EMAIL_PASS) server.send_message(msg) server.quit() def send_dingtalk_alert(self, message): 钉钉机器人通知 webhook Config.DINGTALK_WEBHOOK data {msgtype: text, text: {content: message}} requests.post(webhook, jsondata) Web管理界面Flask示例from flask import Flask, render_template, jsonify from database import Database app Flask(__name__) app.route(/) def index(): return render_template(index.html) app.route(/api/today_records) def today_records(): db Database() records db.get_today_records() return jsonify(records) app.route(/api/daily_summary) def daily_summary(): db Database() summary db.get_daily_summary() return jsonify(summary) app.route(/api/anomalies) def anomalies(): db Database() logs db.get_recent_anomalies(limit20) return jsonify(logs) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)前端使用ECharts展示当日打卡趋势、部门出勤率、异常事件分布等。 进阶功能与优化功能实现方式活体检测​眨眼检测、头部转动、红外双目摄像头多人同时识别​遍历所有人脸框分别比对口罩识别​训练口罩分类器或使用眼部以上特征离线模式​本地缓存特征库断网自动切换语音播报​识别成功后通过TTS播报姓名远程开门​对接继电器控制电磁锁 性能优化建议特征预加载启动时将所有人脸特征加载到内存字典GPU加速使用CUDA版本的dlib/OpenCV异步写入打卡记录通过消息队列Redis/RabbitMQ异步写入MySQL定时清理定期删除超过30天的抓拍截图索引优化attendance_records表建立复合索引(employee_id, check_time)