news 2026/8/9 1:19:22

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/9 1:19:10

AI 替代传统 GUI:基于 MCP 的 OBCloud 工作流(五)

作为一名与数据库密切相关且经验丰富的开发人员&#xff0c;在定位问题时&#xff0c;通常需要查看 OceanBase 实例。在 OB Cloud 控制台中&#xff0c;用户可以查看 OceanBase 实例、租户、节点以及代理等信息。此外&#xff0c;控制台提供了多达几十甚至上百项的监控指标&…

作者头像 李华
网站建设 2026/8/9 1:17:26

基于LLM与工具调用的终端AI代码智能体构建实践

在实际开发大型项目时&#xff0c;我们常常面临一个困境&#xff1a;代码库规模庞大&#xff0c;理解上下文、定位问题、执行跨文件修改等操作变得异常耗时。传统的IDE工具和命令行辅助虽然强大&#xff0c;但往往需要开发者自己串联起“理解需求 -> 定位代码 -> 分析逻辑…

作者头像 李华
网站建设 2026/8/9 1:16:11

如何用GetQzonehistory找回那些被遗忘的QQ空间记忆?

如何用GetQzonehistory找回那些被遗忘的QQ空间记忆&#xff1f; 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾经试图回忆多年前在QQ空间留下的文字&#xff0c;却发现它们早已…

作者头像 李华
网站建设 2026/8/9 1:15:31

为什么老板一定要建设营销型网站的目的详解以及SEO优化策略

在现在这个数字化时代,很多老板或者市场部的负责人总会问我这样一个问题:“老师,我都已经有公众号、有小红书了,为啥还要非得折腾一个官网?直接投流或者做内容不行吗?”说实话,每次听到这个问题,我都忍不住想笑,但随即又觉得挺无奈的。因为这种思维模式,还是停留在十…

作者头像 李华
网站建设 2026/8/9 1:15:35

Diablo Edit2技术架构深度剖析:开源游戏数据编辑器的实现原理

Diablo Edit2技术架构深度剖析&#xff1a;开源游戏数据编辑器的实现原理 【免费下载链接】diablo_edit Diablo II Character editor. 项目地址: https://gitcode.com/gh_mirrors/di/diablo_edit Diablo Edit2作为一款开源游戏数据编辑器&#xff0c;通过精确的二进制数…

作者头像 李华
网站建设 2026/8/9 1:13:28

显卡内存健康大检查:5分钟快速诊断显卡稳定性问题

显卡内存健康大检查&#xff1a;5分钟快速诊断显卡稳定性问题 【免费下载链接】memtest_vulkan Vulkan compute tool for testing video memory stability 项目地址: https://gitcode.com/gh_mirrors/me/memtest_vulkan 你是否经历过游戏突然崩溃、画面出现雪花点、或者…

作者头像 李华