尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
基于深度学习的人脸识别签到系统开发指南
简介基于深度学习的人脸识别签到系统毕业设计源码包主要面向计算机相关专业正在完成课程设计、毕业设计或需要项目实战练习的学生可帮助解决人脸识别与自动签到相结合的系统实现问题。项目在导师指导下完成并获评审98分源码经本地编译调试均能运行整体难度适中流程完整覆盖人脸录入、特征提取、识别签到及后台管理等关键环节。资源压缩包共27个文件约101.48MB除8个Python后端逻辑文件与7个HTML前端页面外还包含SQLite数据库、4个dat特征数据文件、数据库迁移脚本、CSS样式、中文字体、依赖清单及使用说明等便于快速配置环境并启动项目目前已有63人学习下载。整套资源既可作为人脸识别签到系统的完整设计范例也便于二次开发与功能扩展适合用于毕业设计文档撰写、系统演示及答辩展示对入门深度学习者也有较强的参考价值。1. 从“刷卡机”到“刷脸”这篇毕业设计到底要写什么答辩时导师最容易追问的一句话不是“你怎么识别出这个人”而是“你怎么证明他当时在场”。基于深度学习的人脸识别签到系统表面上是把摄像头里的人脸变成名字实际上是三个问题的叠加人脸检测做得好不好、特征比对准不准、签到业务闭环严不严。很多人把精力全放在深度学习模型上最后被一句“两个人并排走你签给谁”问住。这篇指南从源代码组织、模型选型、参数调节到验证方法拆开讲适合正在做软件综合实践选题或毕业设计、手里只有基础 Python 经验的读者也适合想把人脸识别门禁机那套逻辑移植到 PC 上做演示的同学。读完能跑通一条完整链路也能在答辩时讲清楚每个模块为什么这么设计。2. 深度学习怎么把人脸变成一串数字特征提取模型与选型依据早期人脸识别系统采用的是传统手工特征路线检测到人脸后把图像转成 LBP 纹理直方图再和库里的直方图算卡方距离。问题在于光照一变、角度偏一点、分辨率低一些同一个人的直方图差异比不同人还大。深度学习把这件事换了个思路不去设计“什么样的纹理更像人脸”而是用大量人脸图片训练一个神经网络让网络输出一个固定长度的向量比如 128 维或 512 维。要求是同一个人的不同照片映射到向量空间后距离很近不同人的照片距离很远这个向量就叫 embedding 或特征向量。2.1 认识路线之争分类任务和度量学习为什么不是一回事最容易写进开题报告里的一点人脸识别不是做分类。分类任务训练时输出的是“这个人是谁”的概率类别数量锁死来一个新人就要重新训练。人脸识别的目标是得到一个特征提取器把一张图“压”成一个向量之后无论库里多少人都只是向量之间的相似度比较。常见的开源实现里有两套训练范式。一套是三元组损失输入锚点样本、正样本、负样本三张图让锚点与正样本的距离尽量小与负样本的距离尽量大。FaceNet 系列用的就是这类思路输出通常是 128 维向量。另一套是带间隔的 Softmax 变体如 ArcFace 角度间隔损失不直接训练距离而是在分类层的余弦夹角上加入角度余量训练出的 embedding 判别性更强维度通常取 512。ArcFace 在很多人脸识别算法榜单和实际项目里表现更稳原因在于角度间隔比欧氏距离约束更符合人脸特征的分布特性。2.2 从输入到向量一条最简特征提取链路实际写代码时不需要自己训练模型。常见做法是找一个在公开人脸数据集上预训练好的模型用 ONNX Runtime 或 PyTorch 加载直接做推理。下面是用 ONNX Runtime 加载人脸特征提取模型的典型调用链输入一张对齐后的人脸图像输出 512 维向量。import cv2 import numpy as np import onnxruntime as ort class FaceEncoder: def __init__(self, model_path: str): self.session ort.InferenceSession( model_path, providers[CUDAExecutionProvider, CPUExecutionProvider] ) input_info self.session.get_inputs()[0] self.input_name input_info.name self.input_size (input_info.shape[2], input_info.shape[1]) # (h, w) def encode(self, face_bgr: np.ndarray) - np.ndarray: # 缩放到模型要求尺寸并转为 RGB、归一化到 [-1, 1] resized cv2.resize(face_bgr, self.input_size) rgb cv2.cvtColor(resized, cv2.COLOR_BGR2RGB) rgb (rgb.astype(np.float32) / 127.5) - 1.0 blob np.transpose(rgb, (2, 0, 1))[None, ...] feat self.session.run(None, {self.input_name: blob})[0] # 余弦相似度比较前必须做 L2 归一化 feat feat / (np.linalg.norm(feat) 1e-10) return feat.flatten()注意providers的写法优先使用 CUDA机器没有 GPU 时自动回落 CPU。self.input_size直接取自模型输入张量避免手写写错。最后一步归一化非常关键因为后续计算余弦相似度时向量的模长会被约掉不归一化相当于把“脸的清晰程度”也当作区分依据了。2.3 选型对照给 CPU 机器留条活路毕业设计最常见的情况是实验室一台普通电脑没有独立显卡。这种条件下直接上 512 维的 ArcFace ResNet50 不是不行而是摄像头画面里每来一帧人脸都要推理一次CPU 占用率会被拉满签到演示时画面一顿一顿的。选型要在模型精度和推理延迟之间取舍。模型方案嵌入维度损失函数典型主干部署倾向FaceNet 风格128TripletInception-ResNetGPU 推理ArcFace 风格512加性角度间隔ResNet50 / IR-SE50GPU 最佳CPU 可量化MobileFaceNet 风格128 或 512ArcFace 变体Mobile 轻量骨干CPU 推荐边缘设备可用我建议 CPU 机器优先选择 MobileFaceNet 这类轻量模型即便把输入分辨率压到 112×112单次推理也能控制在几十毫秒。如果还想再快可以把模型转成 FP16 或 INT8 量化。演示场景对“认出人”的要求高于“区分双胞胎”轻量模型完全够用。3. 源代码怎么组织模块划分、关键代码与数据库设计很多毕业设计的代码是单个main.py从头写到尾人脸检测、特征提取、数据库操作全塞在一起答辩时老师一问“改阈值要动哪一行”就翻车。比较好的做法是把系统按职责拆成三个包engine放模型推理app放签到业务storage放持久化。这样源代码本身就能当使用指南读也方便后续扩展门禁机模式或换模型。3.1 一个可维护的目录结构下面这份结构不是唯一标准但适合作为起点目录里的每个文件职责明确答辩时可以对着讲。face_checkin/ ├── app/ │ ├── __init__.py │ ├── checkin_service.py # 签到业务去重、时间窗口、结果写入 │ └── camera_bridge.py # 摄像头采集与抽帧 ├── engine/ │ ├── __init__.py │ ├── detector.py # 人脸检测器封装 │ ├── encoder.py # 特征提取器封装 │ └── matcher.py # 向量检索与阈值判定 ├── storage/ │ ├── __init__.py │ ├── db.py # SQLite 连接与初始化 │ └── face_repo.py # 特征库读写与热更新 ├── config.py # 所有可调参数集中放这里 ├── requirements.txt └── main.py # 入口组装各模块config.py单独存在的意义是所有阈值、路径、模型参数集中管理而不是散落在各个模块里。engine层不知道签到业务的任何逻辑它只负责“给你一张图还你一个向量”和“给你两个向量告诉你相似度是多少”。业务层只调engine的接口不碰模型文件。这样后期把签到系统改成门禁逻辑engine和storage都不用动。3.2 人脸检测与特征提取的完整调用链人脸识别算法流程上分两段先用检测器把人脸框出来再把框内图像送入特征提取器。检测器可以直接用 OpenCV 的 DNN 人脸检测模块也可以接 RetinaFace 类模型。为了少引入依赖项目里我用 OpenCV DNN模型文件放在models/目录下。# engine/detector.py import cv2 import numpy as np class FaceDetector: def __init__(self, proto_path: str, model_path: str, conf_thresh: float 0.7): self.net cv2.dnn.readNetFromCaffe(proto_path, model_path) self.conf_thresh conf_thresh def detect(self, frame_bgr: np.ndarray): h, w frame_bgr.shape[:2] blob cv2.dnn.blobFromImage( cv2.resize(frame_bgr, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0) ) self.net.setInput(blob) detections self.net.forward() faces [] for i in range(detections.shape[2]): conf detections[0, 0, i, 2] if conf self.conf_thresh: continue box detections[0, 0, i, 3:7] * np.array([w, h, w, h]) x1, y1, x2, y2 box.astype(int) # 裁掉边缘越界部分并把边缘稍微外扩一点 x1, y1 max(x1 - 10, 0), max(y1 - 10, 0) x2, y2 min(x2 10, w), min(y2 10, h) faces.append(((x1, y1, x2, y2), conf)) return faces这里conf_thresh是检测置信度阈值默认 0.7实际场景里如果漏检可以降到 0.5但代价是误检变多。框体边缘外扩 10 像素是为了保证额头和下巴不截断特征提取模型对这两块区域很敏感。OpenCV DNN 的 Caffe 模型是 300×300 输入和特征提取模型的 112×112 不一样两套分辨率分开记。3.3 签到业务逻辑去重、时间窗口与并发写入签到系统最容易漏的环节是“重复签到”。一次识别成功后同一人 10 秒内再次出现在画面里不应该再写一条记录。如果一天只有一节签到课就按“同一个人、同一天、同一课程”做唯一约束。下面这段服务层代码把“识别结果”变成“签到事件”。# app/checkin_service.py from datetime import datetime class CheckinService: def __init__(self, face_repo, db, time_window_minutes: int 15): self.face_repo face_repo self.db db self.time_window_minutes time_window_minutes def try_checkin(self, user_id: int, course_id: int, similarity: float, min_score: float) - tuple[bool, str]: if similarity min_score: return False, score_low now datetime.now() # 1. 判定当前时间是否在课程签到窗口内 if not self._in_window(course_id, now): return False, out_of_window # 2. 查数据库同人同日同课程是否已签到 if self.db.has_checkin(user_id, course_id, now.date()): return False, duplicated self.db.insert_checkin(user_id, course_id, now, round(float(similarity), 4)) return True, oktry_checkin的分层判断顺序是有讲究的先判断相似度够不够再判断时间窗口最后判断重复。这样阈值最低的检查放最前面可以少两次数据库查询。has_checkin在 SQLite 里用SELECT 1 ... LIMIT 1避免把整张表查出来。相似度保留 4 位小数写入是为了答辩时展示“这个人识别得分是多少”时有据可查。3.4 建表 SQL特征库和签到记录分开数据库设计上我一般用 SQLite零配置单文件交源代码时直接把.db文件一并打包即可。两张核心表一张存签到记录一张存人脸特征。CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, student_no TEXT UNIQUE NOT NULL ); CREATE TABLE IF NOT EXISTS face_features ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, feature BLOB NOT NULL, created_at TEXT DEFAULT (datetime(now, localtime)), FOREIGN KEY (user_id) REFERENCES users(id) ); CREATE TABLE IF NOT EXISTS checkin_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, course_id INTEGER NOT NULL, checkin_time TEXT NOT NULL, similarity REAL NOT NULL, UNIQUE(user_id, course_id, date(checkin_time)) );face_features表里的feature字段存的是 512 维向量的二进制 pickle 数据而不是逐个字段存 512 个浮点数这样查询时一次性读出来用 numpy 直接还原。UNIQUE(user_id, course_id, date(checkin_time))是数据库层的最后一道防线就算业务代码里的has_checkin因为并发漏判数据库也会拒绝重复写入这个约束在答辩时值得重点讲。4. 使用指南从环境准备到跑通一次实时签到代码写得再清晰环境配不对也是白搭。这一章直接给最小可用的操作路径装什么、跑什么、参数怎么调、最常见的问题出在哪。4.1 环境与依赖按 CPU 和 GPU 两个方案给先给一份不区分显卡的requirements.txt再单独说明 CUDA 相关处理。numpy1.24 opencv-python4.8 onnxruntime1.17 scipy1.10 pandas2.0 requests2.31PyTorch 如果只是用来跑预训练模型可以不装。特征提取和 face 检测都用 ONNX Runtime 处理这也是为什么前面代码里用onnxruntime而不是torch。有 NVIDIA 显卡时需要额外安装onnxruntime-gpu版本要和 CUDA 版本匹配比如 CUDA 11.8 配onnxruntime-gpu1.17.*。装错版本最常见的报错是CUDAExecutionProvider初始化失败此时程序会自动回落到 CPU但观察日志能看到警告。4.2 实时签到最小可运行脚本下面这个脚本把检测、编码、检索、签到串成一条最简单的流水线。摄像头每 3 帧做一次检测检测到人脸后直接调用服务层。# main.py import cv2 from engine.detector import FaceDetector from engine.encoder import FaceEncoder from engine.matcher import FaceMatcher from app.checkin_service import CheckinService def main(): detector FaceDetector(models/face_det.caffemodel, models/face_det.prototxt) encoder FaceEncoder(models/mobilefacenet.onnx) matcher FaceMatcher(storage/face_db.npz) service CheckinService(matcher.face_repo, dbNone) cap cv2.VideoCapture(0) frame_id 0 while True: ret, frame cap.read() if not ret: break frame_id 1 if frame_id % 3 ! 0: continue faces detector.detect(frame) for (box, conf) in faces: x1, y1, x2, y2 box face_img frame[y1:y2, x1:x2] feat encoder.encode(face_img) user_id, score matcher.match(feat) if user_id is not None: ok, msg service.try_checkin(user_id, course_id1, similarityscore) # 把识别框和名字画到画面上 cv2.imshow(checkin, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()抽帧策略frame_id % 3 ! 0的意思是每 3 帧只处理 1 帧摄像头通常 30fps相当于每秒只做 10 次识别。这个参数是为了降低 CPU 占用。如果机器性能好可以改成每 1 帧处理一次如果画面里的人移动特别快抽帧太稀会导致角度变化大却漏检。4.3 必调参数表先调哪个、调到多少、以什么为依据参数调整不能靠感觉。下面是这套系统里最关键的 5 个参数以及我给出的调整建议参数初始推荐值调整依据人脸检测置信度conf_thresh0.7漏检多就降误检多就升签到相似度阈值min_score0.5用验证集算误识率后用最低正确分数打折检测框外扩像素10框太小扩展太大则抬手误判抽帧间隔3 帧CPU 占用高就调大人移动快就调小签到时间窗口15 分钟看课程开始时间窗口越小越能防代签相似度阈值是整套系统最重要的参数。调法不是拍脑袋设 0.5而是先收集 10 个人的照片各 5 张算组内相似度和组间相似度取两者分界线的中间值。比如同一人相似度最低是 0.62不同人最高是 0.38那阈值取 0.5 左右最安全。阈值设太高会把真实签到的人拦在外面设太低则可能把长得像的同班同学认成同一个人。4.4 新手最容易踩的三个坑CPU 推理太慢是第一个坑。症状是画面卡顿、签到按钮点了没反应解决方法是把检测输入从 300×300 换成更小的模型变体或者把抽帧间隔从 3 改成 5。第二个坑是多人同时出现在画面里这时检测器会返回多个框识别结果可能签到到错误的人。常见做法是只取画面中心、面积最大的框签入其余框只画框不处理。第三个坑是数据库写入失败却不打印原因。UNIQUE约束触发异常时程序会静默崩溃。建议在insert_checkin里捕获sqlite3.IntegrityError把它当成“重复签到”的正常返回而不是异常。这样代码里永远不会有未处理的数据库错误。提示开发时把config.py里的日志级别设为DEBUG把每帧的相似度分数打出来不要只看最终结果。分数日志能直接告诉你阈值是否设得太严省去反复试错的成本。5. 答辩前必做的验证评估识别精度与特征库热更新这一章不聊大框架只讲两件能在演示现场救命的事怎么证明模型在你的数据集上真的能用以及新增一个人进特征库时怎么不重启程序。5.1 写一个离线验证脚本统计正确接受率和误拒率收集一批真实签到照片和公开数据集混合样本按 person_id 组织好目录结构跑一遍全量比对输出置信度矩阵。下面这个脚本思路简洁适合直接嵌入源代码注释里。# tools/evaluate.py import numpy as np from engine.encoder import FaceEncoder encoder FaceEncoder(models/mobilefacenet.onnx) probe encoder.encode(data/probe/zhang_san.jpg) # 待识别样本 gallery np.load(storage/face_db.npz)[features] # 库里所有人的特征 scores gallery probe.T # 余弦相似度的矩阵乘法实现 top_idx np.argsort(scores, axis0)[::-1] for rank, idx in enumerate(top_idx[:3]): print(rank 1, idx, float(scores[idx]))gallery probe.T利用了“如果所有特征向量都是单位向量余弦相似度等于向量内积”这条性质写起来比循环算cosine_similarity快得多。验证时重点看两个数正确样本的最低分决定阈值上限和错误样本的最高分决定阈值下限。这两个值之间的间隔越大系统越稳。5.2 特征库热更新不重启进程也能加人演示现场最尴尬的场景是“老师我加一个人然后重启一下”。为了流畅把特征库的加载和写入做成两个独立操作。新特征写入时先写磁盘再更新内存里的 numpy 数组这样下一次匹配立刻生效。def upsert_feature(self, user_id: int, feature: np.ndarray): # 1. 把新特征拼到内存数组尾部 self._features np.vstack([self._features, feature]) self._user_ids np.append(self._user_ids, user_id) # 2. 持久化到 npz 文件下次启动也能加载 np.savez_compressed(storage/face_db.npz, featuresself._features, user_idsself._user_ids)注意np.savez_compressed每次全量写文件特征库到几千人时写盘会有几十毫秒延迟但签到场景完全够用。极端情况下可以把写盘放进后台线程内存数组更新是即时的不阻塞识别主流程。这套热更新机制的代码量只有 6 行却能把“新增成员”从一项操作变成一个随时可用的功能答辩演示时会是加分项。本文还有配套的精品资源点击获取
RELATED

相关推荐

DaoCloud public-image-mirror 镜像加速指南:3 种用法从单条命令到全局生效

DaoCloud public-image-mirror 镜像加速指南:3 种用法从单条命令到全局生效

DaoCloud public-image-mirror 镜像加速指南:3 种用法从单条命令到全局生效 【免费下载链接】public-image-mirror 很多镜像都在国外。比如 gcr 。国内下载很慢,需要加速。致力于提供连接全世界的稳定可靠安全的容器镜像服务。 项目地址: https://gitc…

📅 2026/9/11 18:40:42
Authelia SQLite3 本地存储配置详解:storage.local 路径、加密密钥与高可用限制

Authelia SQLite3 本地存储配置详解:storage.local 路径、加密密钥与高可用限制

Authelia SQLite3 本地存储配置详解:storage.local 路径、加密密钥与高可用限制 【免费下载链接】authelia The Single Sign-On Multi-Factor portal for web apps. OpenID Certified™ and Post-Quantum Cryptography Ready. 项目地址: https://gitcode.com/GitH…

📅 2026/9/11 18:40:42
基于MediaPipe与CNN的手势控制鼠标:从关键点到事件映射

基于MediaPipe与CNN的手势控制鼠标:从关键点到事件映射

简介:基于OpenCV、MediaPipe与CNN的手势识别鼠标操控项目,是一份面向计算机视觉、人机交互方向毕设、课程设计与入门进阶的完整资源。项目以指尖移动控制鼠标、手势点击、滚动页面、虚拟键盘输入及快捷按键触发为核心功能,覆盖手部关键点检测…

📅 2026/9/11 18:40:42
MORE NEWS

更多资讯

📰

CMSIS-FreeRTOS源码审计:从适配层到内核调度的嵌入式RTOS实践

做嵌入式开发这些年,我越来越觉得有一类源码值得每个 MCU 工程师静下心来从头到尾读一遍:CMSIS-FreeRTOS。尤其是用 STM32CubeMX、Keil RTE 或 ARM 官方仓库生成过工程的朋友,你们每天打开的就是这个东西,但它内部到底怎么跑起来的…

📰

运放同相与反相怎么选?从输入阻抗、偏置到噪声增益讲透

前阵子画一块光电检测板的信号调理电路,原理图出来后,同事指着一个运放级问我:这一级为什么用反相放大?信号相位在这里需要翻转吗?我当时愣了一下,然后诚实地说:其实不是因为相位,是…

📰

Duix.Avatar报SQLite3 can only bind错误?三步定位并解决

Duix.Avatar报SQLite3 can only bind错误?三步定位并解决 【免费下载链接】Duix-Avatar 🚀 Truly open-source AI avatar(digital human) toolkit for offline video generation and digital human cloning. 项目地址: https://gitcode.com/GitHub_Tre…

📰

OSI七层模型|各层级传输数据单位(详解)

很多人学不懂网络层级,核心问题就是:每层的“数据单位名词太多、容易混淆”。本文对照标准 OSI 七层表格,逐词拆解、通俗解释,不堆砌专业术语,零基础也能看懂。一、OSI七层模型标准对照表先把你看到的原版表格规整排版…

📰

Vben Admin 国际化(i18n)完全指南:从语言配置到远程语言包与自定义语言扩展

Vben Admin 国际化(i18n)完全指南:从语言配置到远程语言包与自定义语言扩展 【免费下载链接】vue-vben-admin A modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. Its fast! 项目地址: https://gitcod…

📰

基于深度学习的人脸识别签到系统开发指南

简介:基于深度学习的人脸识别签到系统毕业设计源码包,主要面向计算机相关专业正在完成课程设计、毕业设计或需要项目实战练习的学生,可帮助解决人脸识别与自动签到相结合的系统实现问题。项目在导师指导下完成并获评审98分,源码经…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬