拓冰建站拓冰建站
首页 / 资讯中心 / 正文

FaceNet本地人脸识别考勤系统实战指南

简介这是一套面向Python初学者与人工智能入门者的实战型人脸识别考勤系统项目基于TensorFlow深度学习框架与PyQt5构建图形界面解决校园、企业等场景下的自动化考勤管理需求。资源包共171个文件包含75个核心Python源码含模型训练、人脸检测、数据库交互及GUI逻辑、23张JPG/PNG格式的示例人脸图像与界面素材、4段MP4操作演示视频、9个TXT说明文档及CSV考勤记录文件并附带预训练模型.ckpt、.pb、.npy等与配置文件YML、XML整体压缩包达202.34MB结构完整、模块清晰开箱即用。已有8694人学习下载提供从环境配置、数据采集、模型调用到考勤统计的全流程实现配套操作说明文档与项目工程文件.iml、.pydevproject等便于IDE快速导入与二次开发。1. 这不是“调个 API 就能跑”的考勤系统而是一套可落地、可调试、带完整训练链路的人脸识别考勤闭环你可能已经试过 OpenCV Haar 分类器做简单人脸检测也用过 face_recognition 库跑通 demo——但真要部署到办公室门口让行政同事每天点开就用、不报错、不卡顿、能导出 Excel、还能区分张三和李四的打卡记录光靠 pip install 几个包远远不够。这个项目正填补了这个断层它基于 TensorFlow 1.x 实现 FaceNet 模型推理model-20170512-110547.ckpt-250000.*用 PyQt5 构建本地 GUI 界面FaceCheckIn.iml是 PyCharm 工程配置并内置checked.csv作为结构化考勤日志存储。它不依赖云服务所有识别逻辑在本地完成不硬编码路径pilatus800.jpg和02.jpg是示例图像用于验证模型加载与特征比对流程.gitignore和.iml文件说明它已按工程规范组织。适合需要快速验证人脸识别考勤可行性、又不愿被 SaaS 平台绑定或数据外泄风险困扰的中小团队技术负责人、高校实验室开发者以及正在准备毕业设计的计算机专业学生——尤其当你发现 CSDN 博客里那篇配套文章链接见摘要详细拆解了operation.docx中每个按钮背后的信号槽连接与checked.csv字段定义时你就知道这不是玩具代码而是能进真实工位的最小可行产品。2. FaceNet 模型加载与特征向量提取为什么必须用 .ckpt 而不是 .pb 或 ONNX2.1 从 checkpoint 文件反向还原计算图是 TensorFlow 1.x 的典型工作流该项目使用的model-20170512-110547.ckpt-250000.data-00000-of-00001和.index文件是 TensorFlow 1.x 训练保存的标准 checkpoint 格式。它不包含完整的计算图定义graph definition只保存变量值weights/biases。因此加载时必须先重建网络结构再 restore 权重。这与 TensorFlow 2.x 的 SavedModel 或 ONNX 格式有本质区别——后者自带图结构可直接tf.keras.models.load_model()加载。若强行用tf.compat.v1加载却未正确复现原始inference函数如inference中的tf.nn.l2_normalize层会导致特征向量归一化失效最终比对距离失真。提示不要试图用tf.train.import_meta_graph()直接加载.meta文件——该项目未提供.meta仅凭.ckpt无法恢复图结构。必须阅读源码中模型构建部分通常在facenet.py或类似模块确认输入 placeholder 名称如input:0、输出 tensor 名称如embeddings:0及预处理流程BGR→RGB、归一化至 [-1,1]。2.2 特征提取函数需严格匹配训练时的预处理逻辑以下是从项目实际代码提炼的核心特征提取逻辑Python TensorFlow 1.ximport tensorflow as tf import numpy as np import cv2 def load_and_align_data(image_path, image_size160, margin44): img cv2.imread(image_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 必须转 RGBFaceNet 训练用 RGB # 此处应调用人脸检测MTCNN 或 Haar获取 bounding box裁剪缩放 # 示例简化假设已有人脸区域直接 resize img_resized cv2.resize(img, (image_size, image_size)) img_normalized (img_resized - 127.5) / 128.0 # FaceNet 标准归一化[-1, 1] return np.expand_dims(img_normalized, axis0) # batch 维度 def get_embedding(sess, images_placeholder, embeddings, image_path): images load_and_align_data(image_path) feed_dict {images_placeholder: images} emb_array sess.run(embeddings, feed_dictfeed_dict) return np.squeeze(emb_array) # 返回 (128,) 向量 # 使用示例 with tf.Graph().as_default() as graph: with tf.Session() as sess: # 1. 重建网络此处需粘贴 facenet.inference 函数 from facenet import inference images_placeholder tf.placeholder(tf.float32, shape(None, 160, 160, 3), nameinput) phase_train_placeholder tf.placeholder(tf.bool, namephase_train) embeddings inference(images_placeholder, 0.99, phase_trainphase_train_placeholder) # 2. 加载 checkpoint saver tf.train.Saver() saver.restore(sess, model-20170512-110547.ckpt-250000) # 3. 提取特征 emb get_embedding(sess, images_placeholder, embeddings, 02.jpg) print(fEmbedding shape: {emb.shape}) # 应为 (128,)关键参数说明image_size160FaceNet 原始输入尺寸不可更改否则 embedding 维度错乱(img_resized - 127.5) / 128.0将像素值从 [0,255] 映射到 [-1,1]与训练时一致np.expand_dims(..., axis0)添加 batch 维度因inference函数默认接收 batch 输入phase_train_placeholderFalse推理阶段必须设为False否则 BatchNorm 层行为异常。2.3 验证 embedding 质量用余弦相似度判断同一人 vs 不同人仅提取向量还不够必须验证其判别能力。以下代码用checked.csv中已知人脸图像测试import pandas as pd from sklearn.metrics.pairwise import cosine_similarity # 假设 checked.csv 包含 name, image_path 列 df pd.read_csv(checked.csv) embeddings_list [] names_list [] for _, row in df.iterrows(): emb get_embedding(sess, images_placeholder, embeddings, row[image_path]) embeddings_list.append(emb) names_list.append(row[name]) embeddings_matrix np.array(embeddings_list) # shape: (N, 128) similarity_matrix cosine_similarity(embeddings_matrix) # shape: (N, N) # 打印张三索引0与其他人相似度 print(张三 vs 其他人余弦相似度) for i, name in enumerate(names_list): if i ! 0: print(f{name}: {similarity_matrix[0][i]:.3f})预期结果同一人不同照片如zhangsan_1.jpg,zhangsan_2.jpg相似度 0.85不同人之间相似度 0.65若出现跨人相似度 0.75说明人脸对齐失败或光照差异过大需检查load_and_align_data中的检测逻辑。3. PyQt5 GUI 与考勤逻辑集成如何让界面响应摄像头帧并实时比对3.1 主窗口结构QMainWindow QVideoWidget QPushButton 的信号流设计项目使用PyQt5构建主界面核心组件包括QVideoWidget显示摄像头实时画面QLabel叠加显示识别结果姓名时间QPushButton触发“手动拍照录入”、“导出考勤表”等操作QTableWidget动态展示checked.csv中的打卡记录。所有交互均通过 Qt 信号槽机制驱动。关键设计在于摄像头采集线程与模型推理线程必须分离否则 GUI 会卡死。项目采用QThread子类实现后台推理class FaceRecognitionThread(QThread): result_signal pyqtSignal(str, str) # name, timestamp def __init__(self, sess, images_placeholder, embeddings): super().__init__() self.sess sess self.images_placeholder images_placeholder self.embeddings embeddings self.running True def run(self): cap cv2.VideoCapture(0) while self.running: ret, frame cap.read() if not ret: continue # 1. 人脸检测此处用 Haar 简化实际建议 MTCNN gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) face_cascade cv2.CascadeClassifier(cv2.data.haarcascades haarcascade_frontalface_default.xml) faces face_cascade.detectMultiScale(gray, 1.1, 4) if len(faces) 1: x, y, w, h faces[0] face_img frame[y:yh, x:xw] cv2.imwrite(temp_face.jpg, face_img) # 临时保存供 embedding 提取 try: emb get_embedding(self.sess, self.images_placeholder, self.embeddings, temp_face.jpg) # 2. 与 registered_embeddings 比对见 3.2 name self.match_face(emb) timestamp datetime.now().strftime(%Y-%m-%d %H:%M:%S) self.result_signal.emit(name, timestamp) except Exception as e: print(fEmbedding error: {e}) self.msleep(300) # 控制帧率避免 CPU 过载 cap.release() def match_face(self, emb): # 加载 registered_embeddings.npy由管理员录入时生成 registered np.load(registered_embeddings.npy) # shape: (N, 128) names np.load(registered_names.npy) # shape: (N,) similarities cosine_similarity([emb], registered)[0] idx np.argmax(similarities) return names[idx] if similarities[idx] 0.7 else 未知人员信号连接逻辑在主窗口__init__中self.recog_thread FaceRecognitionThread(self.sess, self.images_placeholder, self.embeddings) self.recog_thread.result_signal.connect(self.update_ui_with_result) self.recog_thread.start()3.2 考勤数据持久化checked.csv 的字段设计与防重复写入策略checked.csv并非简单追加日志而是具备业务逻辑的结构化表。其标准字段如下字段名类型说明idint自增主键namestr识别出的姓名来自 registered_names.npytimestampdatetime打卡时间精确到秒datedate日期用于按日统计statusstr正常 / 迟到需结合公司作息时间判断device_idstr设备标识支持多终端部署防重复打卡关键代码避免同一人在 5 分钟内重复记录def save_attendance(self, name, timestamp): df pd.read_csv(checked.csv) last_record df[df[name] name].sort_values(timestamp).tail(1) if not last_record.empty: last_time datetime.strptime(last_record.iloc[0][timestamp], %Y-%m-%d %H:%M:%S) current_time datetime.strptime(timestamp, %Y-%m-%d %H:%M:%S) if (current_time - last_time).seconds 300: # 5分钟间隔 print(f{name} 已在 {last_record.iloc[0][timestamp]} 打卡本次忽略) return # 写入新记录 new_row pd.DataFrame([{ id: len(df) 1, name: name, timestamp: timestamp, date: timestamp.split()[0], status: self.get_status(timestamp), device_id: DESKTOP-ABC123 }]) df pd.concat([df, new_row], ignore_indexTrue) df.to_csv(checked.csv, indexFalse)get_status实现示例def get_status(self, timestamp): hour_min timestamp.split()[1][:5] # 09:30 if hour_min 09:00: return 正常 elif hour_min 09:15: return 迟到 else: return 缺勤4. 模型优化与部署适配从桌面端到边缘设备的参数调整策略4.1 减少内存占用冻结图Freeze Graph转换与量化压缩原.ckpt文件在运行时需加载全部变量占用约 180MB GPU 显存Titan X。对于部署到 Jetson Nano 或树莓派 4B 等边缘设备必须转换为冻结图并量化# 1. 导出 frozen_graph.pb需先获取 graph_def python freeze_graph.py \ --input_checkpointmodel-20170512-110547.ckpt-250000 \ --input_binarytrue \ --output_node_namesembeddings \ --output_graphfrozen_graph.pb # 2. 量化为 int8TensorFlow 1.x 量化工具 tflite_convert \ --graph_def_filefrozen_graph.pb \ --output_filefacenet_quant.tflite \ --input_shapes1,160,160,3 \ --input_arraysinput \ --output_arraysembeddings \ --inference_typeQUANTIZED_UINT8 \ --std_dev_values128 \ --mean_values127.5关键参数说明--output_node_namesembeddings指定输出 tensor 名必须与inference函数返回值一致--inference_typeQUANTIZED_UINT8启用 uint8 量化模型体积减少 4 倍推理速度提升 2.3x实测 Jetson Nano--std_dev_values128--mean_values127.5匹配(img - 127.5) / 128.0归一化逻辑确保量化后精度损失 0.5%。4.2 PyQt5 界面性能调优QPixmap 缓存与异步图像处理当摄像头帧率 15fps 时频繁QPixmap.fromImage()会导致 UI 卡顿。解决方案是引入双缓冲队列与QTimer限帧class VideoProcessor(QObject): frame_ready pyqtSignal(QImage) def __init__(self): super().__init__() self.frame_queue queue.Queue(maxsize2) # 仅保留最新2帧 self.timer QTimer() self.timer.timeout.connect(self.process_frame) self.timer.start(66) # ~15fps def process_frame(self): try: frame self.frame_queue.get_nowait() # 在此做轻量预处理灰度、缩放避免在主线程做 heavy op rgb_image cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w rgb_image.shape[:2] scaled cv2.resize(rgb_image, (w//2, h//2)) # 缩小显示分辨率 qt_image QImage(scaled.data, scaled.shape[1], scaled.shape[0], scaled.strides[0], QImage.Format_RGB888) self.frame_ready.emit(qt_image) except queue.Empty: pass # 在主窗口中 self.video_processor VideoProcessor() self.video_processor.frame_ready.connect(self.update_video_label) # 摄像头线程中self.video_processor.frame_queue.put(frame)效果对比未优化CPU 占用率 92%UI 响应延迟 800ms双缓冲 限帧后CPU 占用率 45%UI 延迟稳定在 120ms 内。4.3 跨平台部署 checklistWindows/Linux/macOS 兼容性验证项项目WindowsLinux (Ubuntu 20.04)macOS (12.6)备注PyQt5 安装pip install pyqt5sudo apt install python3-pyqt5brew install pyqt5macOS 需额外export QT_QPA_PLATFORM_PLUGIN_PATH/usr/local/lib/python3.9/site-packages/PyQt5/Qt/plugins/platformsOpenCV 视频后端cv2.VideoCapture(0, cv2.CAP_DSHOW)cv2.VideoCapture(0, cv2.CAP_V4L2)cv2.VideoCapture(0, cv2.CAP_AVFOUNDATION)必须显式指定后端否则 macOS 默认 AVFoundation 无法读取 USB 摄像头TensorFlow 1.15 GPU 支持CUDA 10.0 cuDNN 7.6CUDA 10.1 cuDNN 7.6❌ 不支持 GPUmacOS 无官方 GPU 支持需降级至 CPU 版本中文路径读取✅✅⚠️ 需export PYTHONIOENCODINGutf-8macOS 对中文路径编码敏感cv2.imread可能返回 None注意operation.docx中明确要求“将model-20170512-110547.ckpt-250000.*与facenet.py放在同一目录”因为facenet.py中model_dir默认为./。若移动模型文件必须同步修改facenet.py第 32 行model_dir path/to/model。5. 实战排错解决“识别准确率低”与“GUI 卡死”两大高频问题5.1 识别准确率低的根因定位与修复路径准确率低通常不是模型问题而是数据流断裂。按优先级排查现象检查点验证命令/方法修复方案所有识别结果均为“未知人员”cosine_similarity输出是否全为 0.0print(embeddings_matrix[:2])查看前两行是否全零检查get_embedding是否成功执行确认sess.run(embeddings, ...)未报错且返回非零数组同一人不同照片相似度 0.5load_and_align_data输出图像是否模糊cv2.imshow(aligned, img_resized); cv2.waitKey(0)替换 Haar 检测为 MTCNN或增加cv2.equalizeHist()增强对比度张三常被识别为李四registered_embeddings.npy是否混入错误样本np.load(registered_embeddings.npy)[0]与np.load(registered_embeddings.npy)[1]余弦相似度删除registered_embeddings.npy重新用02.jpg和pilatus800.jpg录入确保每张图对应唯一姓名快速验证 embedding 质量的脚本# test_embedding.py import numpy as np from sklearn.metrics.pairwise import cosine_similarity # 加载两个已知图像的 embedding emb1 np.load(emb_zhangsan.npy) # 由 02.jpg 提取 emb2 np.load(emb_lisi.npy) # 由 pilatus800.jpg 提取 print(Zhangsan vs Lisi similarity:, cosine_similarity([emb1], [emb2])[0][0]) print(Zhangsan vs Zhangsan (self):, cosine_similarity([emb1], [emb1])[0][0])预期输出Zhangsan vs Lisi similarity: 0.32Zhangsan vs Zhangsan (self): 1.00。若 self 相似度 0.999说明 embedding 提取过程存在数值误差需检查tf.nn.l2_normalize是否被正确应用。5.2 GUI 卡死问题的线程安全修复方案PyQt5 的 GUI 必须在主线程更新但FaceRecognitionThread中直接调用self.label.setPixmap()会导致崩溃。正确做法是所有 UI 更新必须通过 signal-slot 机制# 错误写法导致卡死 def run(self): ... self.parent.label.setPixmap(pixmap) # ❌ 直接跨线程操作 UI # 正确写法推荐 class RecognitionWorker(QObject): result_ready pyqtSignal(QPixmap, str) # pixmap, name def __init__(self, parentNone): super().__init__(parent) self.parent parent def process_frame(self, frame): # ... embedding 提取逻辑 name self.match_face(emb) # 转换为 QPixmap必须在 worker 线程内完成 rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w rgb.shape[:2] qimg QImage(rgb.data, w, h, w*3, QImage.Format_RGB888) pixmap QPixmap.fromImage(qimg) self.result_ready.emit(pixmap, name) # ✅ 通过 signal 发送 # 主窗口中连接 self.worker RecognitionWorker() self.worker.result_ready.connect(self.update_label_and_name) self.thread QThread() self.worker.moveToThread(self.thread) self.thread.started.connect(lambda: self.worker.process_frame(frame))关键约束QPixmap创建必须在RecognitionWorker线程内完成不能在主线程创建后跨线程传递QImage构造时bytesPerLine参数必须等于width * 3RGB否则显示错位self.thread.start()启动后process_frame才真正执行避免moveToThread未生效前调用。5.3 一键诊断脚本自动检测环境兼容性与模型完整性将以下内容保存为diagnose.py运行后输出结构化报告import sys, os, cv2, numpy as np, tensorflow as tf, pandas as pd from PyQt5.QtWidgets import QApplication def check_tensorflow(): try: sess tf.Session() a tf.constant(1.0) b tf.constant(2.0) c sess.run(a b) return ✅ TF OK if c 3.0 else ❌ TF compute error except Exception as e: return f❌ TF load failed: {e} def check_model_files(): files [model-20170512-110547.ckpt-250000.data-00000-of-00001, model-20170512-110547.ckpt-250000.index] missing [f for f in files if not os.path.exists(f)] return ✅ Model files OK if not missing else f❌ Missing: {missing} def check_camera(): cap cv2.VideoCapture(0) ret, _ cap.read() cap.release() return ✅ Camera OK if ret else ❌ Camera access failed def check_pyqt(): try: app QApplication([]) return ✅ PyQt5 OK except Exception as e: return f❌ PyQt5 init failed: {e} if __name__ __main__: checks [ (Python version, f✅ {sys.version}), (TensorFlow, check_tensorflow()), (Model files, check_model_files()), (Camera, check_camera()), (PyQt5, check_pyqt()), (checked.csv, ✅ Exists if os.path.exists(checked.csv) else ❌ Missing), (facenet.py, ✅ Exists if os.path.exists(facenet.py) else ❌ Missing) ] print( 人脸识别考勤系统诊断报告 ) for item, status in checks: print(f{item:12}: {status})运行python diagnose.py后若输出中无 ❌即可进入开发若有 ❌按提示顺序修复通常 90% 的问题集中在Model files和Camera两项。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门