简介:一份基于YOLOv8的实验室防护服穿戴规范检测项目资源,面向需要完成毕业设计、课程设计或目标检测相关实训的计算机视觉学习者,也适合想快速上手YOLO系列模型的小白进阶。压缩包共8个文件,以Python脚本、模型权重和说明文档为主:py文件承担训练、视频检测及可视化界面交互,pt文件包含预训练权重与最优权重,txt则提供部署说明与标签规范。整体约15.91MB,结构清晰,无需额外整理即可运行。项目代码均经过测试,功能完整,支持生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果和标签分布图,能够覆盖算法效果展示的常见需求。目前已有45人学习使用,适合作为毕设答辩的保底方案,也可在此基础上修改拓展,快速完成实验室安全检测相关演示。
1. 让 YOLOv8 去盯实验室的防护服,比人眼巡检靠谱在哪儿
实验室安全巡检最容易被挑战的就是“着装规范”四个字怎么落地:靠安全员定时巡逻,漏检率高,夜间和节假日基本是盲区;靠摄像头让人盯着,看十分钟就疲劳。把 YOLOv8 部署到实验室出入口的监控上,用目标检测自动判定“有没有人没穿防护服”,是这类场景里性价比最高的方案。它只需要一台普通电脑或小盒子,模型用 COCO 预训练权重微调,不需要重新设计网络,也不需要昂贵的 AI 加速卡。
这个标题指向的是一套完整的毕设或课程设计交付物:YOLOv8 检测源码、可视化界面、标注好的数据集和部署教程。它适合两类人:一类是计算机视觉方向的学生,需要把检测模型、界面和数据集串成一个能答辩的作品;另一类是实验室或企业安全管理部门的技术人员,想用现成模型搭一个防护服穿戴检测的原型。后者尤其要注意,真正的难点不在模型训练——YOLOv8 的训练链路已经足够成熟——而在于数据的采集标注、穿戴合规的判定逻辑,以及从训练环境到部署环境的迁移。下文按照“数据 → 训练 → 界面 → 部署 → 验证改进”这条主线展开,每一节都可以直接照做。
2. 把防护服数据做成 YOLOv8 能吃的格式,训练参数一次调对
2.1 数据集的类别设计直接决定项目能不能答辩过关
防护服检测最常见的错误是一上来就训练“防护服”单类别,结果模型把白色墙面、白大褂、一次性鞋套全识别成防护服。更稳妥的做法是把类别拆成person和protective_clothing两类,让模型先学会“一个人”和一个“防护服区域”,穿戴是否合规交给后处理逻辑判断:检测到person时,如果其边界框内没有protective_clothing或二者 IoU 过低,就认为未穿戴。顺带说一句,如果你想做安全帽场景,完全可以沿用这个两类的思路,只是把后一类换成helmet,这也是 YOLOv5/YOLOv8 训练自己的数据集时社区最常见的标签设计方式。
位姿无所谓,关键是样本分布。数据里要包含穿白大褂、蓝色防尘服、连体洁净服的完整外观;负样本除了正常没穿衣服的人,还要考虑防护服穿到一半、只穿上衣、衣服敞开这类容易和正样本混淆的边界状态。标注时用 LabelImg 或 labelme,输出 YOLO 格式的 txt,每行是class x_center y_center width height,四个坐标都是除以图片宽高后的归一化数值。目录结构固定为:
dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml里要注意一行容易错的内容:
path: dataset train: images/train val: images/val names: 0: person 1: protective_clothingpath如果写相对路径,ultralytics 会以当前运行目录为基准解析,建议直接改成绝对路径或者把 yaml 文件和 dataset 放在同一个工程根目录下,否则训练时会报找不到图片。另外标签文件中类别编号必须从 0 开始连续编号,class 数量在标注阶段和names列表里要保持一致,这是 YOLOv8 环境配置完最常见的报错原因。
2.2 用 ultralytics 库跑通 YOLOv8 训练的最小命令
毕设项目里训练端不需要碰源码,直接用官方包就能完成整个闭环。环境建议用 Python 3.10 + CUDA 11.8 的组合,PyTorch 用pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118安装对应版本,再装 ultralytics。数据集切分好、yaml 写好之后,一行命令开始训练:
yolo detect train \ model=yolov8s.pt \ data=dataset/data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ project=runs \ name=lab_coat_exp关键参数按实际资源调整。batch在 8GB 显存(GTX 1660 Ti 就是典型代表)上跑yolov8s可以用 16,如果再大就报 CUDA out of memory,此时把batch降到 8,或者把model换成yolov8n.pt。imgsz不建议小于 640,监控画面里的人往往占画面比例不大,分辨率太低会把中远距离的小目标直接丢掉。训练过程中每天盯两个指标:一个是box_loss有没有稳定下降,另一个是val/ cls_loss在训练中后期有没有反弹。
训练完看runs/lab_coat_exp/weights/目录,里面会有best.pt和last.pt。best.pt是验证集指标最好的权重,直接拿它做推理;last.pt是最后一个 epoch 的权重,如果训练后期过拟合明显,last.pt反而可能比best.pt泛化更好,可以两个都测一遍。
2.3 验证集指标怎么读,mAP 50 和 mAP 50-95 分别代表什么
训练结束终端会打印一个指标表格,重点看mAP50和mAP50-95。mAP50是 IoU 阈值固定为 0.5 时的平均精度,适合快速整体判断模型有没有学起来;mAP50-95是 IoU 从 0.5 到 0.95 每 0.05 取一个阈值算出的平均,对边界框精度的要求更严格。防护服检测里不必追求mAP50-95过高,因为合规判定本质上是“有没有防护服覆盖人体”,0.6 左右的框精度配合后处理逻辑就够用,这也让 GTX 1660 Ti 这类入门卡完全足够完成整套训练。
还有一个容易被忽视的指标是每个类别的单独 AP。打开runs/lab_coat_exp/下的results.csv,你会看到每一行对应一个 epoch 的完整指标,其中metrics/precision(B)、metrics/recall(B)和metrics/mAP50(B)是整体值,但person和protective_clothing的差异需要看混淆矩阵。如果protective_clothing的召回率低,说明漏检多,增加穿防护服的样本比重;如果person的精确率低,说明误检多,往往是负样本不够或标注框不贴合身体。
3. 可视化界面如何接上 YOLOv8 检测结果,用 PyQt5 搭一套可操作的前端
3.1 界面选型:为什么毕设和原型都用 PyQt5 而不是 Web 前端
做可视化界面有三条路:PyQt/PySide 桌面端、Flask/FastAPI + Web 前端、直接用 ultralytics 自带的yolo predict命令行。第三条不算可视化界面,前两条里 PyQt5 是毕设和课程设计最常见的选择,原因很实际:不需要单独开一个前端服务,直接把视频解码、模型推理、结果显示放进同一个进程,虽然多线程上容易踩坑,但代码量最少;而 Web 方案的优势是能远程访问,适合部署到实验室闸机上。
真正决定选择的是硬件形态。如果界面跑在连接摄像头的本地电脑上,PyQt5 体验最好,配合 OpenCV 的VideoCapture读流,读取、推理、显示三个环节在一个循环里完成;如果摄像头画面要通过局域网从别的机器拉取,那 Web 方案更顺手。下文按 PyQt5 展开,它也是这个标题的源码包里最常见的界面实现方式。
3.2 把检测函数封装成一个独立的推理类
界面代码里最忌讳把模型加载和推理逻辑直接写进按钮回调里,否则画面一卡界面就假死。常规做法是封装一个Detector类,只保留predict(frame)一个入口,返回标注后的画面和人员统计信息:
from ultralytics import YOLO import cv2 import numpy as np class Detector: def __init__(self, weights_path, conf_thres=0.35): self.model = YOLO(weights_path) self.conf_thres = conf_thres def predict(self, frame): results = self.model.predict( frame, conf=self.conf_thres, imgsz=640, verbose=False ) boxes = results[0].boxes annotated = results[0].plot() persons, coats = 0, 0 if boxes is not None: cls = boxes.cls.cpu().numpy() persons = int((cls == 0).sum()) coats = int((cls == 1).sum()) return annotated, persons, coatsconf阈值在界面里做成一个滑块,范围 0.2 到 0.7 比较合理。设太低会出现大量低置信度框,后续合规判定被干扰;设太高会把远距离或遮挡下的人漏掉。results[0].plot()是 ultralytics 提供的可视化方法,会直接在原图上画框和标签,省去自己拼接类别名的工作。注意每次predict都会对单帧做一次前向推理,如果检测速度跟不上视频帧率,可以在循环里做跳帧,只对每 3 帧执行一次检测,中间帧直接复用上一帧的框。
3.3 界面布局和控制逻辑,以及最容易卡死的 QImage 转换
界面布局用QVBoxLayout从上到下排三个区域:顶部是操作栏,包括启动、停止、选择视频源和置信度滑块;中间占据最大面积的是显示画面的QLabel;底部是一个状态栏,显示当前帧率和人员统计结果。侧边或用分组框展示二进制状态,如“未穿戴防护服人数”。窗口标题和按钮文案直接用中文没有编码问题,但保存日志时务必用 UTF-8 编码写文件。
视频循环和界面刷新是两个线程的事,建议用QTimer驱动视频帧读取,它的超时事件会在主线程执行,不需要额外加锁。读取到帧后转换为QImage再放到QLabel上,这是最常出问题的地方,OpenCV 读出来的是 BGR 顺序,直接转QImage会红蓝互换:
def cv2_to_qimage(self, frame): rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch = rgb.shape bytes_per_line = ch * w return QImage(rgb.data, w, h, bytes_per_line, QImage.Format_RGB888).copy().copy()必须加,否则rgb局部变量在函数返回后被释放,QImage持有的指针会变成野指针,界面上会时不时出现花屏或闪退。按钮的回调里启动QTimer后,用timer.start(30)控制帧率,30 毫秒约 33 帧,足够流畅。
4. 从训练机到部署机的路径:模型导出、CPU 推理和告警数据落盘
4.1 工程目录怎么组织才能做到“简单部署即可运行”
标题里提到的源码包如果整理得清晰,部署时只需要解压、装依赖、改一条路径。拿到手先看目录结构,标准做法是这样:
lab_safety_detector/ ├── models/ │ └── best.pt ├── data/ │ └── data.yaml ├── utils/ │ └── detector.py ├── ui/ │ └── main_window.py ├── main.py ├── requirements.txt └── README.mdmain.py是整个程序入口,里面做好三件事:解析命令行参数(--source指定摄像头编号或视频文件)、实例化Detector、启动MainWindow。requirements.txt里固定好版本号,最保险的是ultralytics>=8.2.0,<9.0加PyQt5>=5.15加opencv-python>=4.8。部署教程里如果写的是先装 Anaconda 再建虚拟环境,那相当于是给 GPU 环境用的;纯 CPU 部署可以精简很多。
部署机如果只有 CPU,不要在requirements.txt里装 CUDA 版的 torch,直接在 PyPI 安装即可。torch 的 CPU 版和 GPU 版在推理速度上差很多,但 YOLOv8n 模型在纯 CPU 上也能跑出每秒 10 到 15 帧,够用于实验室出入口这种非高速场景。
4.2 导出 ONNX 用 onnxruntime 推理,比 torch 推理更快更稳定
训练好的best.pt是 PyTorch 权重,在部署机上直接推理会有两个问题:一是需要完整安装 torch,占用几个 GB 磁盘;二是推理接口偶尔会受 PyTorch 版本变化影响。导出为 ONNX 后用 onnxruntime 推理是目前最稳的落地路径,导出命令一行:
yolo export model=models/best.pt format=onnx opset=12 imgsz=640 simplify=Trueopset=12保证兼容性;simplify=True会调用 onnx-simplifier 对计算图做一次精简,去掉推理时用不到的节点,模型体积变小同时推理速度更快。导出完成后在models/目录下多出一个best.onnx,然后改造Detector让它支持两种后端:
class Detector: def __init__(self, weights_path, use_onnx=False): if use_onnx: import onnxruntime as ort self.session = ort.InferenceSession( weights_path, providers=["CPUExecutionProvider"] ) self.model = None else: self.model = YOLO(weights_path)onnxruntime 的输入预处理比 torch 麻烦一点:需要把 BGR 帧转为 RGB、resize 到 640 并归一化到 0 到 1,最后组织成1x3x640x640的数组。输出是一个1x84x8400的张量含义是每个候选框的 xywh、置信度和两个类别的分数,这部分解析建议直接用 ultralytics 提供的YOLO(best.onnx)接口处理,它会自动兼容,不需要自己手写 NMS 解析。这样做的意义是推理性能明显提升:同一台 CPU 机器上,onnxruntime 通常比 PyTorch 推理快 20% 到 40%。
4.3 违规事件落盘:保存截图和 CSV 记录,避免“能检测但没证据”
可视化界面跑通之后,真正让项目变得完整的功能是违规事件记录。检测到有人且其框内没有防护服时,自动保存当前帧到violations/目录,同时把时间、违规类型、置信度追加写入records.csv,这个功能用一段简单的回调就能实现:
import csv from datetime import datetime def save_violation(frame, boxes, records_path="records.csv"): if len(boxes) == 0: return ts = datetime.now().strftime("%Y%m%d_%H%M%S") cv2.imwrite(f"violations/{ts}.jpg", frame) with open(records_path, "a", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow([ts, len(boxes), "person_without_coat"])截图命名带上时间戳可以避免重名覆盖,编码用 utf-8 加newline=""防止在 Windows 上出现空行。这个功能同时解决了两个问题:给了用户可信的检测证据,也给答辩时演示“系统有效性”积累素材。如果部署在无人值守环境,可以在save_violation里再补一条logging日志,把事件写入系统日志而不是只落在控制台。
4.4 部署机最常见的 4 个坑,都是从源码包解压后立刻会撞上的
第一个坑是中文路径。程序放在D:\实验项目\防护服检测\下面运行时,OpenCV 的imread和imwrite在 Windows 上会读不出中文路径文件,界面显示黑屏但代码不报错。处理方法有两个:要么把工程放到纯英文路径,要么所有文件操作改用cv2.imdecode读入字节流。第二个坑是 CUDA 版本和 ultralytics 的编译版本不匹配,训练机上跑得好好的,换一台部署机就提示核心库加载失败。解决办法是把训练机和部署机的 PyTorch 版本统一,或者部署机直接走 ONNX Runtime 路线绕开 torch。
第三个坑是摄像头画面旋转。实验室出入口经常用吊装摄像头,画面是倒的,OpenCV 读流后需要做cv2.rotate(frame, cv2.ROTATE_180),这个要在进入Detector.predict之前处理,否则检测结果会整体错位。第四个坑是内存泄漏。界面长时间运行时,如果predict返回的annotated没有正确释放,内存占用会缓慢上涨,一般跑几小时后会卡死。排查方法是每 30 分钟打一次psutil.Process().memory_info().rss,确认变化幅度,泄漏点大多出在 onnxruntime 的 session 反复创建上。
5. 不重训也能提升效果:用曲线判断欠拟合,用后处理校正穿戴判定
5.1 损失函数曲线和 mAP 曲线的读法,三条线一起看
训练完成后不要只看最终结果,把runs/lab_coat_exp/results.csv用 pandas 读出来画成曲线,是最直观的改进依据。YOLOv8 画损失函数曲线图可以用 matplotlib 直接实现:
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/lab_coat_exp/results.csv") cols = df.columns plt.plot(df[" epoch"], df[" train/box_loss"], label="train") plt.plot(df[" epoch"], df[" val/box_loss"], label="val") plt.legend() plt.xlabel("epoch") plt.ylabel("box_loss") plt.title("YOLOv8 Box Loss Curve") plt.savefig("loss_curve.png", dpi=200)注意列名的空格,ultralytics 导出的 CSV 列名带有大量前导空格,直接复制列名时容易报 KeyError。三条线应该一起观察:train/box_loss持续下降说明模型在收敛;val/box_loss在前 30 个 epoch 下降、之后平稳说明正常;如果val曲线在中段掉头向上而train还在降,就是典型的过拟合信号,此时应减少epochs到 60 左右,或者调低模型的复杂度,把yolov8s换成yolov8n。
5.2 在 head 输出上做穿戴合规判定,是性价比最高的改进
训练层面的 head 改进难度大、周期长,改坏了还得重新训练。更聪明的做法是在模型输出端做后处理:既然模型已经给出person和protective_clothing两类框,就利用它们的空间关系判定合规性,这相当于在推理端做了一个轻量的规则层。最常用的逻辑是判断防护服框是否与人体框的中下部重叠,因为半身防护服与全身防护服的合规程度不同:
def check_compliance(person_box, coat_boxes, iou_thres=0.1): px1, py1, px2, py2 = person_box p_area = (px2 - px1) * (py2 - py1) for box in coat_boxes: cx1, cy1, cx2, cy2 = box inter_w = max(0, min(px2, cx2) - max(px1, cx1)) inter_h = max(0, min(py2, cy2) - max(py1, cy1)) inter_area = inter_w * inter_h if inter_area / p_area > iou_thres: return True return False这里的iou_thres在 0.05 到 0.15 之间调节。设太高会把“只遮住胸口”的半穿戴判为合规,设太低会把防护服从身后飘过的误算作遮挡。对检测结果做一些统计过滤:如果连续超过 5 帧判定为违规,再触发告警,可以过滤掉人转身时防护服短暂离开画面造成的误报,这个技巧在真实监控场景里非常实用。
5.3 数据层面最简单的提升:光照和尺度扰动
如果验证结果表明模型对暗光或远距离目标识别差,不必急着换模型,优先在数据层面做增强。ultralytics 自带增强参数里,hsv_h、hsv_s和hsv_v控制颜色扰动,其中hsv_v=0.4可以模拟亮度变化,对实验室常见的低照度场景很有效;scale=0.5会在训练时随机缩放目标框,增强模型对远处小目标的适应能力。这两个参数直接在训练命令里生效:
yolo detect train \ model=yolov8s.pt \ data=dataset/data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ hsv_v=0.4 \ scale=0.5跑完对比上一版结果的mAP50-95,如果提升了 1 到 2 个点,就可以保留该配置。相比改网络结构,这种数据层面的调优不需要有能力阅读ultralytics底层的 head 代码,也更好向答辩老师解释清楚:模型不变,只是让训练数据更接近真实部署环境的成像条件。
本文还有配套的精品资源,点击获取