当前位置: 首页 > news >正文

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 status=1") rows = db_cursor.fetchall() for emp_id, feature_blob in rows: encoding = np.frombuffer(feature_blob, dtype=np.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, key=lambda 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, location

2. 主循环与打卡逻辑 (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 = f"unknown_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, json=data)

🌐 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(limit=20) return jsonify(logs) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)

前端使用ECharts展示当日打卡趋势、部门出勤率、异常事件分布等。


🚀 进阶功能与优化

功能

实现方式

活体检测

眨眼检测、头部转动、红外双目摄像头

多人同时识别

遍历所有人脸框,分别比对

口罩识别

训练口罩分类器,或使用眼部以上特征

离线模式

本地缓存特征库,断网自动切换

语音播报

识别成功后通过TTS播报姓名

远程开门

对接继电器控制电磁锁


📊 性能优化建议

  1. 特征预加载:启动时将所有人脸特征加载到内存字典

  2. GPU加速:使用CUDA版本的dlib/OpenCV

  3. 异步写入:打卡记录通过消息队列(Redis/RabbitMQ)异步写入MySQL

  4. 定时清理:定期删除超过30天的抓拍截图

  5. 索引优化attendance_records表建立复合索引(employee_id, check_time)

http://www.cnnetsun.cn/news/3918663.html

相关文章:

  • AI 替代传统 GUI:基于 MCP 的 OBCloud 工作流(五)
  • 基于LLM与工具调用的终端AI代码智能体构建实践
  • 如何用GetQzonehistory找回那些被遗忘的QQ空间记忆?
  • 为什么老板一定要建设营销型网站的目的详解以及SEO优化策略
  • Diablo Edit2技术架构深度剖析:开源游戏数据编辑器的实现原理
  • 显卡内存健康大检查:5分钟快速诊断显卡稳定性问题
  • SSM框架实现Java社团管理系统的核心技术解析
  • 三水网站建设企业怎么避坑?揭秘本地团队从零基础到全网爆发的真实内幕与实战建议
  • CTF杂项逆向分析:从乱码图片中提取Flag的系统化方法
  • 济南网站建设艮安:从初创到腾飞,我们如何用代码与诚意重塑中小企业数字生命力
  • traceroute路由追踪实操
  • 国内知名的网站建设公司有哪些:揭秘行业真相与避坑指南
  • 揭秘极速微网站建设cms如何助力中小企业低成本快速搭建品牌官网的终极指南
  • 从零到一:如何用AI智能体框架打造专业级演示文稿
  • 探秘延吉市住房城乡建设局官方网站如何助力城市发展
  • 顺德定制网站建设如何选择靠谱的团队与避坑指南深度解析
  • 网站建设管理制度全解析与企业数字化升级指南
  • 读了4个项目才发现大湾区EMBA优势差别真不小
  • 揭秘SEO自带网站建设的底层逻辑:为什么很多老板在建站时都忽略了最核心的流量引擎
  • Umi-OCR:免费离线文字识别工具,轻松实现图片转文字和PDF识别
  • 本地OCR神器:Umi-OCR如何让你告别云端依赖,实现隐私安全的文字识别?
  • 大模型长对话上下文压缩:摘要与检索混合方案实战
  • Codeforces Round 1076
  • Unity URP屏幕空间描边集成与调优:免费开源方案实战指南
  • 义县城乡建设局网站:连接您与美好家园的数字化桥梁与服务指南
  • 专知智库 · 容度原理颠覆性技术设计系列(十四)
  • 路径穿越漏洞深度解析:从原理到防御的实战指南
  • 免费RAW处理神器:5个简单技巧让照片编辑效率翻倍
  • Vue3可视化布局设计器开发实践与优化
  • 5V系统过压保护(OVP)设计实战:从原理到PCB布局,实现硬件零失效