
简介这是一份面向高校学生与深度学习入门者的课程设计级人脸识别考勤演示项目基于卷积神经网络实现人脸检测与签到流程适合作为人工智能、机器学习相关课程的实践参考或毕业设计起步模板。压缩包共30个文件约32.54MB包含11个Python源码、6个pyc编译文件、3个ui界面文件以及caffemodel与prototxt网络模型、haarcascade人脸检测xml、字体与示例图片等覆盖从模型加载、人脸搜索到签到保存的完整链路。项目按功能拆分为cam、sign_face、save_face、launcher等模块并配有README说明目录结构清晰便于按模块阅读与二次修改。目前已有173人学习下载读者可借此理解CNN人脸识别在考勤场景中的工程落地方式掌握模型调用、界面交互与数据保存的排错思路快速搭建可运行的小型演示系统。1. 从一份大二课设说起CNN 人脸识别考勤 Demo 能跑出什么如果你手头正好有一份cnn_face_detection-master的压缩包里面躺着cnn_deploy.prototxt、cnn_iter_3560000.caffemodel、haarcascade_frontalface_alt.xml和一堆.ui文件那你大概率拿到的是一个典型的「CNN 人脸识别考勤 demo」。它不是什么工业级门禁系统而是一个用 Python OpenCV Caffe 模型拼起来的课程设计摄像头抓帧、Haar 级联做人脸检测、CNN 做特征提取和比对最后把签到结果写进界面。适合谁适合正在做深度学习课程设计、想找一个能跑通的人脸识别考勤系统设计参考、或者需要一份带 UI 的 Python 课设源码来改的学生和刚入行的工程师。它最大的价值不是算法多先进而是把「检测 → 识别 → 签到 → 界面」这条链路完整串起来了你能看到每个环节的真实文件长什么样、参数写在哪、哪里容易翻车。下面我按实际拆包和复现的顺序把这份资源讲透。2. 拆包看结构CNN 模型、Haar 检测器和 UI 到底怎么分工2.1 模型文件与检测器谁负责“找到脸”谁负责“认出人”拿到压缩包先别急着pip install把目录树看清楚比什么都重要。这份 demo 的核心资产集中在models和driver两个目录。models里放的是 Caffe 格式的 CNN 部署文件cnn_deploy.prototxt定义网络结构cnn_iter_3560000.caffemodel是训练了 356 万次迭代后的权重。这两个文件必须成对出现缺一个都加载不了。driver目录下的haarcascade_frontalface_alt.xml是 OpenCV 自带的 Haar 级联人脸检测器它的任务是在每一帧里框出人脸区域然后把这块区域裁剪、缩放后送进 CNN。很多人第一次跑的时候会疑惑为什么不用 CNN 直接做检测因为这份课设的定位是「识别」检测环节用 Haar 是为了降低门槛让没有 GPU 的机器也能实时跑。images目录里的yellow.png、mask.jpg、mark.jpg、black.jpg是 UI 素材和掩膜图font/fzyy.TTF是界面中文字体少了它签到界面会显示方块。文件/目录作用是否可替换cnn_deploy.prototxtCNN 网络结构定义不建议改改了权重就对不上cnn_iter_3560000.caffemodel训练好的权重可用自己训练的替换但输入尺寸要一致haarcascade_frontalface_alt.xml人脸检测器可换_default版本召回率更高但误检也多frame.ui/sign_face.uiQt Designer 界面文件可改布局但控件名要和.py里一致config.py路径、阈值、摄像头编号等配置必须按自己环境改2.2 从cam.py到sign_face.py一条完整的签到链路这份 demo 的代码入口不止一个但主线很清晰。cam.py负责打开摄像头、循环抓帧、调用检测和识别sign_face.py负责签到逻辑和界面刷新save_face.py负责录入新用户的人脸数据launcher.py和launcher_sing_face.py是启动器。face_search.py是识别核心里面会加载 Caffe 模型、做前向传播、计算特征向量之间的相似度。buffer和tmp目录是运行时缓存第一次跑之前可以是空的。config.py里通常会有摄像头编号、识别阈值、模型路径、字体路径这几项这是你复现时第一个要动的文件。常见做法是先把config.py里的路径改成你本机的绝对路径尤其是caffemodel和prototxt的位置。如果你在 Windows 上跑路径里的反斜杠要转义或者直接用正斜杠。frame.py和frame.pyc是等价的.pyc是编译后的字节码Python 版本不一致时可能加载失败优先用.py源文件。__init__.pyc同理如果导入报错把对应的.py文件找出来放同级目录。2.3 环境依赖Python 2 还是 Python 3Caffe 还是 OpenCV DNN这是整份资源里最容易翻车的地方。从.pyc文件和print风格判断这份课设大概率是 Python 2.7 时代写的但face_search.py里如果用了cv2.dnn.readNetFromCaffe那 Python 3 也能跑。关键看两点一是print有没有括号二是ConfigParser的导入方式。如果你不想折腾 Python 2我一般会先把face_search.py和cam.py里的print语句批量加括号再把import ConfigParser改成import configparser。Caffe 本身在 Windows 上编译很痛苦但 OpenCV 的dnn模块可以直接读 Caffe 模型不需要装完整的 Caffe 框架。所以实际依赖可以压缩成pip install opencv-python opencv-contrib-python numpy pyqt5如果你用的是 Python 3.8 以上opencv-python建议锁 4.5.x 版本太新的版本对 Caffe 模型的支持偶发兼容问题。pyqt5是用来加载.ui文件的如果你不想装 Qt Designer直接用pyuic5把.ui转成.py也行。numpy版本不要低于 1.19否则cv2.dnn.blobFromImage的返回类型会有差异。提示先跑python -c import cv2; print(cv2.__version__)确认 OpenCV 能正常导入再动模型加载的代码。很多“模型加载失败”其实是 OpenCV 没装好。3. 让摄像头认出你从录入人脸到签到成功的完整操作3.1 改config.py四个必须确认的参数在跑任何脚本之前打开config.py逐项核对。第一项是模型路径cnn_deploy.prototxt和cnn_iter_3560000.caffemodel要指向你解压后的实际位置。第二项是haarcascade_frontalface_alt.xml的路径这个文件在driver目录下。第三项是摄像头编号笔记本内置摄像头通常是0外接 USB 摄像头可能是1或2。第四项是识别阈值这个值决定了特征比对时判定为“同一个人”的宽松程度常见初始值是0.6到0.8之间太低会认错人太高会认不出自己。# config.py 示例片段按自己路径改 MODEL_PROTOTXT rD:/cnn_face_detection-master/models/cnn_deploy.prototxt MODEL_WEIGHTS rD:/cnn_face_detection-master/models/cnn_iter_3560000.caffemodel HAAR_CASCADE rD:/cnn_face_detection-master/driver/haarcascade_frontalface_alt.xml CAMERA_INDEX 0 RECOGNITION_THRESHOLD 0.72 FONT_PATH rD:/cnn_face_detection-master/font/fzyy.TTF逻辑说明MODEL_PROTOTXT和MODEL_WEIGHTS是 Caffe 模型的标准加载方式OpenCV 的readNetFromCaffe需要这两个参数。HAAR_CASCADE用于cv2.CascadeClassifier。CAMERA_INDEX传给cv2.VideoCapture。RECOGNITION_THRESHOLD是余弦距离或欧氏距离的判定门限具体看face_search.py里用的是哪种度量。参数怎么改如果你发现签到总是失败先把阈值调到0.8试一次如果别人站你前面也能签到调到0.6。3.2 录入人脸save_face.py的采集逻辑与命名规则录入是签到的前提。跑save_face.py之前确认buffer目录存在且可写。这个脚本通常会打开摄像头用 Haar 检测人脸把检测到的人脸区域裁剪成固定尺寸比如128x128或224x224然后保存到buffer或指定目录下。文件名一般用你的学号或姓名拼音这样后续识别时可以直接从文件名读标签。采集时注意三点光线要均匀不要背光脸要正对摄像头偏转角度不要超过 15 度每次采集保持同一表情不要一会儿笑一会儿严肃。# save_face.py 核心采集逻辑示意 import cv2 import os cam cv2.VideoCapture(0) detector cv2.CascadeClassifier(HAAR_CASCADE) count 0 person_id input(输入学号或姓名拼音: ) while True: ret, frame cam.read() gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces detector.detectMultiScale(gray, 1.3, 5) for (x, y, w, h) in faces: count 1 face_img gray[y:yh, x:xw] face_img cv2.resize(face_img, (128, 128)) cv2.imwrite(fbuffer/{person_id}_{count}.jpg, face_img) cv2.rectangle(frame, (x, y), (xw, yh), (0, 255, 0), 2) cv2.imshow(save_face, frame) if cv2.waitKey(1) 0xFF 27 or count 30: break cam.release() cv2.destroyAllWindows()逻辑说明detectMultiScale的1.3是缩放步长5是邻域阈值这两个值越大检测越少但越快。count 30表示采集 30 张就停实际可以按需调整。保存的图片是灰度图因为 Haar 检测和后续 CNN 输入通常都用灰度。参数说明person_id建议用纯英文或数字避免中文文件名在跨平台时出问题。如果你采集完发现buffer里图片数量不对检查detectMultiScale是不是把侧脸漏掉了可以换成haarcascade_frontalface_default.xml再试。3.3 跑通签到cam.py与sign_face.py的启动顺序录入完成后先跑cam.py看摄像头能不能正常出画面再跑sign_face.py走签到流程。有些版本的launcher.py会同时启动摄像头和界面但依赖关系没理清的话容易卡死。我一般会分开跑先python cam.py确认窗口标题和画面正常再python sign_face.py这时界面会加载frame.ui或sign_face.ui签到按钮触发识别逻辑。识别时face_search.py会做三件事用 Haar 框脸、把脸送进 CNN 得到特征向量、和buffer里已录入的特征做比对。如果比对成功界面会显示姓名和签到时间如果失败检查buffer里有没有对应人的图片以及阈值是不是太严。# face_search.py 识别核心示意 net cv2.dnn.readNetFromCaffe(MODEL_PROTOTXT, MODEL_WEIGHTS) def extract_feature(face_img): blob cv2.dnn.blobFromImage(face_img, 1.0, (128, 128), (104, 117, 123)) net.setInput(blob) return net.forward().flatten() def recognize(face_img, known_features): feat extract_feature(face_img) best_id, best_score None, float(inf) for pid, ref_feat in known_features.items(): dist np.linalg.norm(feat - ref_feat) if dist best_score: best_id, best_score pid, dist return best_id if best_score RECOGNITION_THRESHOLD else unknown逻辑说明blobFromImage的(104, 117, 123)是 Caffe 常用的均值减除参数顺序是 BGR。net.forward()返回的是全连接层输出flatten()拉成一维向量。距离度量用欧氏距离阈值RECOGNITION_THRESHOLD直接决定判定结果。参数怎么改如果你换了自训练的模型输入尺寸(128, 128)要和cnn_deploy.prototxt里的input_dim一致均值减除参数也要和训练时保持一致否则特征分布会偏移。注意sign_face.py和cam.py同时占用摄像头会报Device or resource busy确保同一时间只有一个进程在抓帧。4. 避坑与排查这份课设最容易翻车的五个地方4.1 现象cv2.dnn.readNetFromCaffe报错“Cant load layer”原因cnn_deploy.prototxt里用了 OpenCV DNN 不支持的层类型比如某些自定义的Python层或旧版 Caffe 的Slice层参数格式。解决先用cat cnn_deploy.prototxt | grep layer看层类型如果出现Python或MemoryData说明这个模型不是为 OpenCV DNN 准备的。常见做法是把prototxt里不支持的层注释掉或者换用cv2.dnn.readNetFromCaffe能识别的标准层版本。如果改不动就装完整 Caffe 用caffe.Net加载但 Windows 上编译 Caffe 的血泪经验告诉我能绕就绕。4.2 现象摄像头画面卡在第一帧或者窗口无响应原因cv2.waitKey的延时参数太小或者 Qt 界面和 OpenCV 的imshow在同一个线程里抢事件循环。解决把cv2.waitKey(1)改成cv2.waitKey(30)给 GUI 留出刷新时间。如果用了 PyQt5不要在 Qt 主线程里直接跑while True的摄像头循环改成QTimer定时触发抓帧。另外检查cam.py里有没有cam.release()和cv2.destroyAllWindows()没有的话第二次跑就会卡住。4.3 现象签到界面中文显示为方块原因font/fzyy.TTF没有正确加载或者config.py里的FONT_PATH指向了不存在的文件。解决确认fzyy.TTF在font目录下路径用绝对路径。如果还是方块用cv2.putText画中文本身就不支持需要转成 PIL 的ImageFont再画回去。常见做法是先把 OpenCV 的 BGR 帧转成 PIL Image用ImageDraw.text写中文再转回 OpenCV 格式显示。4.4 现象识别率极低同一个人每次都被判成 unknown原因录入时的人脸和签到时的光照、角度差异太大或者 CNN 特征提取时的预处理不一致。解决录入和签到必须用同一套预处理流程包括灰度化、尺寸缩放、均值减除。如果save_face.py保存的是彩色图而face_search.py按灰度图处理特征就会对不上。另外检查buffer里的图片是不是被覆盖了有些脚本每次启动会清空buffer导致之前录入的数据丢失。4.5 现象ImportError: No module named ConfigParser原因Python 2 的ConfigParser在 Python 3 里改名为configparser大小写敏感。解决全局搜索import ConfigParser改成import configparser同时把ConfigParser.ConfigParser()改成configparser.ConfigParser()。如果代码里还有print xxx这种 Python 2 语法一并加括号。.pyc文件如果和.py源文件版本不匹配直接删掉.pyc让 Python 重新编译。5. 进阶玩法换模型、加活体检测和把签到数据落库这份 demo 跑通之后真正有意思的是改它。第一个方向是换模型cnn_iter_3560000.caffemodel是别人训练好的你可以用自己的数据集重新训练一个 Caffe 模型或者把face_search.py里的readNetFromCaffe换成readNetFromONNX用 ONNX 格式的轻量人脸识别模型替换。换模型时唯一要盯住的是输入尺寸和均值减除参数这两个不一致特征向量就是乱的。第二个方向是加活体检测Haar 检测加 CNN 识别有一个天然漏洞拿一张照片对着摄像头也能签到。常见做法是在cam.py抓帧后加一个眨眼检测或纹理分析用dlib的 68 点关键点算眼睛纵横比连续几帧低于阈值就判定为活体。第三个方向是把签到结果落库现在很多课设版本是把签到记录写进tmp目录的文本文件你可以改成 SQLite建一张attendance表字段包括学号、姓名、签到时间、置信度。import sqlite3 from datetime import datetime conn sqlite3.connect(attendance.db) conn.execute( CREATE TABLE IF NOT EXISTS attendance ( id INTEGER PRIMARY KEY AUTOINCREMENT, person_id TEXT NOT NULL, sign_time TEXT NOT NULL, confidence REAL ) ) def log_attendance(person_id, confidence): conn.execute( INSERT INTO attendance (person_id, sign_time, confidence) VALUES (?, ?, ?), (person_id, datetime.now().strftime(%Y-%m-%d %H:%M:%S), confidence) ) conn.commit()逻辑说明person_id对应录入时的学号或姓名拼音confidence存识别时的距离值或相似度分数方便后续排查误判。参数说明sqlite3不需要额外安装Python 自带。如果你要支持多摄像头在表里再加一个camera_id字段。落库之后签到界面可以加一个“今日签到记录”的表格直接从数据库读比读文本文件稳得多。从那以后我每次拿到这种课设压缩包都强制先跑一遍python -c import cv2; print(cv2.__version__)和python -c import numpy; print(numpy.__version__)确认基础环境没坑再动模型文件。这份 CNN 人脸识别考勤 demo 的价值不在算法多深而在于它把一条完整的链路摊开给你看改哪里、卡哪里、怎么绕都清清楚楚。希望帮到你。本文还有配套的精品资源点击获取