☰
基于PyramidBox与FaceNet的口罩检测+人脸识别毕设源码解析
2026/10/12 22:54:51 网站建设 项目流程

简介:这份资源是面向计算机、电子信息等专业学生及深度学习入门者的口罩佩戴检测与人脸识别实战项目,可作为课程设计、期末大作业或毕业设计的参考方案。项目基于Python与深度学习框架实现,涵盖人脸检测、口罩佩戴状态判别、图像与视频推理等核心功能,适合希望理解检测模型落地流程、积累工程经验的读者。压缩包共86个文件,约120.16MB,包含21个py源码文件、19个pyc编译文件、17张jpg与2张jpeg测试图片、4个pth权重文件、4个mp4演示视频,以及npy数据、ui界面文件、ttf字体和md说明文档,覆盖从模型权重到界面交互的完整结构。目前已有437人学习下载。读者可获取完整可运行源码、预训练模型、测试素材与项目说明,通过requirements.txt快速配置环境并运行main.py,直观体验检测效果,同时借助模块化目录理解人脸检测与口罩识别的代码组织方式,为二次开发或功能扩展提供参考。

1. 从一份能跑起来的口罩检测+人脸识别源码说起

前阵子帮学弟看毕设,他发来一个压缩包,说跑不起来,报错一堆。我解压一看,目录结构其实挺规整:main.py、mask、facenet_retinaface、nets_retinaface、model_data、requirements.txt,典型的 Python 深度学习桌面端项目。这类「口罩佩戴检测 + 人脸识别」的毕设源码网上不少,但真正能做到开箱即用、依赖清晰、UI 能起来的并不多。这份资源的核心价值在于:它把两个模型串成了一条完整链路——先用 PyramidBox Lite 做人脸检测和口罩状态判断,再用 FaceNet + RetinaFace 做人脸特征提取与比对识别,最后套了一个 PyQt 的图形界面,支持图片、视频、摄像头三种输入方式。适合谁?计算机、电子信息类专业的同学做课程设计或毕设参考,也适合刚入门深度学习、想找一个能跑通的多模型串联项目练手的开发者。下面我按实际拆包和调试的顺序,把这份源码讲透。

2. 拆开目录看架构:PyramidBox 与 FaceNet 是怎么串起来的

2.1 两个模型各管什么

这份源码里其实藏着两条独立的技术线,很多人第一次看容易混。

第一条线是口罩佩戴检测,用的是 PyramidBox Lite。PyramidBox 是百度提出的人脸检测算法,Lite 版本是轻量化后的移动端模型,放在mask/pyramidbox_lite_mobile_mask和mask/pyramidbox_lite_server_mask两个目录下。前者适合 CPU 推理,后者精度更高但吃资源。模型输出的是人脸框坐标 + 是否佩戴口罩的分类结果。

第二条线是人脸识别,用的是 RetinaFace 做人脸检测、FaceNet 做特征嵌入。facenet_retinaface目录下是推理入口,nets_retinaface是网络结构定义,model_data里放着预训练权重。RetinaFace 负责把人脸框出来,FaceNet 把每张脸映射成一个 128 维或 512 维的向量,然后通过计算向量之间的欧氏距离来判断是不是同一个人。

两条线的关系是:口罩检测负责「状态判断」,人脸识别负责「身份确认」。在main.py里,它们被 UI 事件驱动,用户点哪个按钮就走哪条推理路径。

2.2 关键文件与职责对照

文件/目录职责备注
main.py程序入口,启动 PyQt 主窗口先跑这个
mainwindow.py主窗口逻辑,绑定按钮与推理函数UI 与业务耦合处
image_infer.py图片推理入口单张图片检测
video_infer.py视频文件推理逐帧处理
camera_infer.py摄像头实时推理注意帧率
mask/PyramidBox Lite 口罩检测模型mobile 与 server 两版
facenet_retinaface/FaceNet + RetinaFace 人脸识别含predict.py
nets_retinaface/RetinaFace 网络定义含retinaface.py
model_data/预训练权重不要随意删
face_dataset/人脸底库识别比对的目标库
requirements.txt依赖清单版本敏感

这张表建议对照实际目录看一遍,心里有个地图,后面调代码不会迷路。

2.3 环境搭建:从 requirements.txt 到第一次运行

先别急着pip install -r requirements.txt,这份依赖清单里有些包版本比较老,直接装容易和现有环境冲突。我一般会先建虚拟环境,再逐条装。

# 创建虚拟环境,Python 版本建议 3.7 或 3.8 python -m venv venv_mask # 激活环境 # Windows: venv_mask\Scripts\activate # Linux/Mac: source venv_mask/bin/activate # 升级 pip,避免旧版解析依赖出问题 pip install --upgrade pip # 安装依赖,建议逐条看一遍 requirements.txt pip install -r requirements.txt

逻辑说明:虚拟环境是为了隔离,因为这份源码依赖的 PyQt5、opencv-python、numpy 版本和系统全局环境大概率冲突。参数上,Python 版本不要选 3.9 以上,PyQt5 和老版 paddle 相关包在 3.9+ 上编译容易翻车。

装完后先别跑main.py,用一个小脚本验证核心依赖是否就位:

# check_env.py import cv2 import numpy as np import PyQt5 import paddle # 如果口罩检测走 Paddle 推理 print("opencv:", cv2.__version__) print("numpy:", np.__version__) print("PyQt5 ok") print("paddle:", paddle.__version__)

如果paddle导入报错,说明口罩检测那条线可能用的是 ONNX 或其它推理后端,具体看mask/目录下的推理脚本用的是哪个框架。这一步是排雷,别跳过。

2.4 跑通图片推理:从 image_infer.py 入手

main.py带 UI,出问题不好定位。我习惯先单独跑image_infer.py,把推理链路验证通。

# 简化后的调用逻辑,实际以源码为准 from mask.infer import MaskDetector from facenet_retinaface.predict import FaceRecognizer # 初始化口罩检测器 mask_detector = MaskDetector(model_path="mask/pyramidbox_lite_mobile_mask") # 初始化人脸识别器 face_recognizer = FaceRecognizer(model_path="model_data/facenet_retinaface") # 读图 img = cv2.imread("test.jpg") # 第一步:口罩检测 mask_result = mask_detector.predict(img) # 返回:人脸框列表 + 每张脸的口罩状态 # 第二步:对未戴口罩或需要识别的脸做人脸识别 for face_box in mask_result: face_img = img[face_box[1]:face_box[3], face_box[0]:face_box[2]] identity = face_recognizer.predict(face_img) print("身份:", identity)

逻辑说明:先检测再识别,顺序不能反。参数上,model_path指向的目录里必须有模型权重文件,如果源码里权重是分开下载的,需要先确认model_data和mask下的权重是否完整。常见坑是权重文件缺失但代码不报错,只是结果全为空。

2.5 人脸底库与识别阈值怎么设

face_dataset/目录是人脸识别的底库。FaceNet 的输出是特征向量,识别时计算待测脸和底库中每张脸的欧氏距离,小于阈值判定为同一人。

# encoding.py 里通常会有底库编码生成逻辑 import os import pickle from facenet_retinaface.predict import get_embedding database = {} for person_name in os.listdir("face_dataset"): person_dir = os.path.join("face_dataset", person_name) for img_name in os.listdir(person_dir): img_path = os.path.join(person_dir, img_name) embedding = get_embedding(img_path) database[person_name] = embedding # 保存编码,避免每次启动重新计算 with open("encodings.pkl", "wb") as f: pickle.dump(database, f)

阈值建议:FaceNet 在 LFW 数据集上 0.6 左右是常见经验值,但实际项目里要根据底库质量和光照条件调。我一般从 0.5 开始试,误识多就降到 0.45,漏识多就升到 0.55。这个参数没有绝对标准,得拿实际数据试。

3. 把 UI 跑起来:main.py 启动链路与三种推理模式

3.1 main.py 到底做了什么

main.py本身很短,核心就几行:创建 QApplication、实例化主窗口、进入事件循环。

# main.py 典型结构 import sys from PyQt5.QtWidgets import QApplication from mainwindow import MainWindow if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())

真正的逻辑在mainwindow.py里。它把按钮点击事件绑定到image_infer.py、video_infer.py、camera_infer.py三个模块的推理函数上。UI 文件是mainwindow.ui,用 Qt Designer 打开可以改界面,改完用pyuic5转成 Python 代码。

# 如果改了 .ui 文件,需要重新生成 .py pyuic5 mainwindow.ui -o mainwindow.py

注意:直接改mainwindow.py再重新生成会被覆盖,正确做法是改.ui再转换,或者把自定义逻辑写在一个单独的类里继承 UI 类。

3.2 图片推理模式:单张检测的完整流程

图片模式是最容易验证的。点击「选择图片」按钮后,程序读取图片路径,调用推理函数,把结果画在图上再显示到 QLabel 里。

# image_infer.py 核心逻辑示意 def infer_image(image_path): img = cv2.imread(image_path) # 口罩检测 faces = mask_detector.predict(img) for (x1, y1, x2, y2, label) in faces: color = (0, 255, 0) if label == "mask" else (0, 0, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2) # 转 RGB 给 Qt 显示 img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) return img_rgb

逻辑说明:OpenCV 读进来是 BGR,Qt 显示需要 RGB,转换这一步漏了会颜色错乱。参数上,矩形框颜色按口罩状态区分,绿框表示戴了,红框表示没戴,这是常见做法。

3.3 视频与摄像头模式:帧率与线程的坑

视频文件和摄像头推理的逻辑类似,都是逐帧读、逐帧推理、逐帧显示。但这里有两个坑。

第一,摄像头推理如果放在主线程,UI 会卡死。常见做法是开一个 QThread,把推理循环放进去,通过信号槽把帧传回 UI 线程显示。

# camera_infer.py 线程化示意 from PyQt5.QtCore import QThread, pyqtSignal class CameraThread(QThread): frame_signal = pyqtSignal(object) def run(self): cap = cv2.VideoCapture(0) while self.running: ret, frame = cap.read() if not ret: break result = mask_detector.predict(frame) # 画框... self.frame_signal.emit(frame) cap.release()

第二,推理速度跟不上摄像头帧率时,画面会延迟累积。解决办法是跳帧推理,比如每 3 帧检测一次,中间帧复用上一次的结果。

frame_count = 0 last_faces = [] while True: ret, frame = cap.read() frame_count += 1 if frame_count % 3 == 0: last_faces = mask_detector.predict(frame) # 用 last_faces 画框

参数上,跳帧间隔根据机器性能调。GPU 机器可以每帧都跑,CPU 机器建议 3 到 5 帧一次。

3.4 人脸识别与口罩检测的联动逻辑

这份源码的一个亮点是把两个功能联动了:先判断有没有戴口罩,没戴的再做人脸识别确认身份。这个逻辑在mainwindow.py里体现为条件分支。

# 联动逻辑示意 faces = mask_detector.predict(frame) for face in faces: if face.label == "no_mask": # 没戴口罩,触发人脸识别 identity = face_recognizer.predict(face.crop) cv2.putText(frame, f"NO MASK: {identity}", ...) else: cv2.putText(frame, "MASK OK", ...)

这种设计在实际场景里对应的是门禁或考勤系统:戴口罩的人放行,没戴的人记录身份。如果要做成「没戴就报警」,把识别结果写日志或触发声音即可。

4. 避坑与排查:这份源码最容易翻车的五个地方

4.1 现象:pip install 到一半报编译错误

原因:requirements.txt里某些包(如paddlepaddle、PyQt5)在特定 Python 版本或系统上需要编译,缺少系统级依赖。

解决:先确认 Python 版本是 3.7 或 3.8。Windows 上装 PyQt5 一般有 wheel,Linux 上可能需要sudo apt install python3-pyqt5。Paddle 相关包建议去官网找对应版本的预编译 wheel,不要直接 pip 装最新版。

4.2 现象:程序启动后 UI 空白或闪退

原因:mainwindow.ui和mainwindow.py不匹配,或者 Qt 平台插件缺失。

解决:重新用pyuic5生成一次。Linux 上如果报could not load the Qt platform plugin "xcb",装libxcb-xinerama0等依赖。另外确认icon.icns和h.png这些资源文件路径没被改动。

4.3 现象:推理结果全是空,没有人脸框

原因:模型权重文件缺失或路径不对。mask/和model_data/下的权重文件可能因为压缩包分卷或下载不完整而丢失。

解决:检查mask/pyramidbox_lite_mobile_mask和model_data目录下是否有.pdmodel、.pdiparams或.onnx等权重文件。如果源码里权重是单独提供的,确认是否已放入正确位置。路径建议用绝对路径或基于os.path.dirname(__file__)拼接,避免工作目录变化导致找不到。

4.4 现象:摄像头打开失败或画面卡顿

原因:摄像头被其他程序占用,或者推理线程阻塞了 UI。

解决:先确认没有其他程序(如 Zoom、腾讯会议)占用摄像头。卡顿问题按 3.3 节的跳帧方案处理。另外cv2.VideoCapture(0)里的 0 是摄像头索引,笔记本外接摄像头可能需要改成 1。

4.5 现象:人脸识别认错人,或者同一个人识别不出来

原因:底库编码质量差,或者阈值设置不合理。

解决:底库照片要选正脸、光照均匀、无遮挡的。每个人至少放 3 到 5 张不同角度的照片。阈值从 0.5 开始调,用实际测试数据画一个距离分布图,找误识率和漏识率的平衡点。如果底库只有一张照片,识别率会明显下降。

5. 进阶技巧:把识别结果落库与批量验证

5.1 识别记录写入 SQLite

跑通之后,很多人会想加一个「识别记录」功能。用 SQLite 最省事,不用额外装数据库。

import sqlite3 from datetime import datetime def init_db(): conn = sqlite3.connect("recognition.db") cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS records ( id INTEGER PRIMARY KEY AUTOINCREMENT, person_name TEXT, mask_status TEXT, timestamp TEXT, image_path TEXT ) """) conn.commit() return conn def log_record(conn, name, mask_status, image_path): cursor = conn.cursor() cursor.execute( "INSERT INTO records (person_name, mask_status, timestamp, image_path) VALUES (?, ?, ?, ?)", (name, mask_status, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), image_path) ) conn.commit()

逻辑说明:每次识别后调用log_record,把姓名、口罩状态、时间、截图路径存进去。参数上,image_path可以存截图文件路径,方便后续追溯。这个表结构简单,但足够支撑一个毕设的「数据管理」模块。

5.2 批量验证与阈值调优

单张测试不够,要批量跑一批图看准确率。写个脚本遍历测试集,统计口罩检测的准确率和人脸识别的通过率。

import os import cv2 def batch_test(test_dir, mask_detector, face_recognizer, threshold=0.5): total = 0 mask_correct = 0 face_correct = 0 for img_name in os.listdir(test_dir): img_path = os.path.join(test_dir, img_name) img = cv2.imread(img_path) total += 1 # 口罩检测验证 faces = mask_detector.predict(img) # 假设文件名格式为 "name_mask.jpg" 或 "name_nomask.jpg" true_label = "mask" if "mask" in img_name and "nomask" not in img_name else "no_mask" if faces and faces[0].label == true_label: mask_correct += 1 # 人脸识别验证 if faces: identity, distance = face_recognizer.predict_with_distance(faces[0].crop) if distance < threshold: face_correct += 1 print(f"口罩检测准确率: {mask_correct / total:.2%}") print(f"人脸识别通过率: {face_correct / total:.2%}")

参数说明:threshold是识别阈值,批量测试时可以从 0.4 到 0.7 每隔 0.05 跑一遍,看哪个值下通过率和误识率最平衡。这个脚本跑完,毕设里的「实验分析」章节数据就有了。

5.3 模型切换:mobile 与 server 的取舍

mask/下有两个模型目录,pyramidbox_lite_mobile_mask和pyramidbox_lite_server_mask。mobile 版速度快、精度略低,server 版精度高、速度慢。切换方式通常是改推理脚本里的模型路径。

# 切换模型只需改这一行 MODEL_PATH = "mask/pyramidbox_lite_server_mask" # 或 mobile

我一般会在 UI 上加一个下拉框,让用户自己选。CPU 机器默认 mobile,有 GPU 的可以切 server。实测下来,server 版在遮挡和侧脸场景下召回率明显更高,但单帧推理时间可能是 mobile 的 2 到 3 倍。

5.4 一个我踩过的坑

第一次跑这份源码时,摄像头模式一直报NoneType错误,查了半天发现是cv2.VideoCapture(0)返回了空,原因是笔记本的摄像头被系统隐私设置禁用了。从那以后我每次调摄像头相关的代码,都强制先跑一遍cap.isOpened()检查,再往下走。这个习惯帮我省了很多无谓的排查时间。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询