☰
YOLOv8口罩检测系统实战:从数据标注到实时部署全流程
2026/10/18 18:49:27 网站建设 项目流程

基于 YOLOv8 的口罩检测系统

最近帮朋友做了一个实时的口罩佩戴检测系统:摄像头对着入口,画面里每个人有没有戴口罩、口罩戴得规不规范,屏幕上直接框出来并给出提示。用的是 YOLOv8,从数据准备、模型训练到部署落地,前后大概花了两周时间。这篇文章把这个项目的完整做法、踩过的坑和最终能直接用的经验整理出来,给正在做同类目标检测项目的同学一个参考。无论是毕业设计、比赛练手,还是想做成有报警能力的完整系统,这套思路都能直接复用。

1. 项目整体设计与思路拆解

1.1 这个系统到底要解决什么问题

表面上看,口罩检测是“判断画面里的人有没有戴口罩”,但实际需求比这个复杂。真实场景里,常遇到三类情况:正确佩戴口罩、未佩戴口罩、以及口罩戴在下巴上或者遮住嘴但露出鼻子等不规范佩戴。

关键误区是把问题想简单。做一个能“框出人脸并打上标签”的 demo 确实不难,难的是应对真实场景的多样性和复杂度:人离摄像头远近不同导致目标大小变化、逆光或暗光下成像质量差、低头或侧脸导致特征不全、多人密集通行。这些在 demo 里不出现,但一旦放到门禁、食堂、医院入口,全是必须面对的“硬骨头”。

1.2 为什么选 YOLOv8,而不是其他目标检测方案

先想清楚这个问题的前提:口罩检测属于单阶段目标检测任务,输入一张图片,输出每个目标的类别和位置框。可选的方案其实不少,传统视觉方法比如 HOG 特征加 SVM、Haar 特征加 Adaboost,这些早年也有用来做类似检测的,但对姿态变化、光照变化、遮挡都非常敏感,实际场景基本不可用。深度学习方案里,两阶段的 Faster R-CNN 精度高但推理慢,不太适合实时摄像头场景。

剩下的是一阶段系列,当时我把常用选项排了一遍:

  • SSD:老牌单阶段,但小目标性能弱,在口罩这种密集小目标场景下不占优势。
  • EfficientDet:结构复杂,调参和部署成本高。
  • YOLOv5:生态成熟、资料多,但 2023 年之后维护节奏放慢。
  • YOLOv8:继承了 YOLO 系列一脉相承的易用性,同时引入了 anchor-free 检测头、C2f 特征模块、任务对齐分配策略这些改进,在相同精度水平下推理速度更快,而且官方库集成了训练、验证、导出和推理的完整链路。

选择 YOLOv8 更实际的理由是:它的接口设计非常亲民,给一个标注格式正确的数据集和一份数据配置文件,一条命令就能开始训练,导出 ONNX 或 TensorRT 模型也非常顺手。对于“想快速做出可用的系统”来说,这一点特别重要——开发时间比模型那百分之一的精度差距值钱得多。

1.3 整体架构设计

整个系统按模块划分,思路非常清晰:

  1. 数据层:收集并标注口罩人脸图片,做成 YOLO 格式的数据集。
  2. 训练层:用预训练权重在自定义数据集上微调,做训练监控和效果评估。
  3. 推理层:加载训练好的模型权重,处理图片或视频流,输出检测框、类别和置信度。
  4. 业务层:结合报警逻辑、日志记录和界面展示,形成完整闭环。

架构上每个模块尽量解耦。比如数据层只负责产出干净的、格式统一的数据集,不关心后续怎么训练;推理层只输出原始检测结果,至于要不要报警、报警怎么展示,由业务层负责。这样后期替换模型、调参数、改业务逻辑都不会牵一发动全身。

2. 数据集准备:这一步的质量决定了模型的天花板

2.1 数据从哪里来

口罩检测任务的数据来源主要有三个渠道:

  • 公开数据集:常见的口罩人脸公开数据集合计有接近万张图片,覆盖了不同光照、不同角度、不同人种的场景。但从公开数据集拿数据要注意两点:一是图片分辨率参差不齐,需要做统一化处理;二是部分数据集以人脸检测为主,口罩是附加标注,可能存在标注边界不紧、类别混乱的问题,拿过来之前要人工抽查清洗。
  • 自制数据:比如在校园、办公室门口用手机或摄像头拍摄短视频,再抽帧成图片。这个渠道最大优势是场景贴合,图片中的光线、摄像头角度、人群特征都和你最终部署的环境一致,对实际效果提升最明显。需要注意的是拍视频涉及他人隐私,必须遵守相关规定,在不侵犯他人权益的前提下进行。
  • 爬虫抓取:可以从图片搜索网站下载,但存在版权和合规风险,而且下载回来的图片五花八门,清洗成本高。个人更推荐前两种方式组合。

2.2 标注规范和格式

YOLO 系列使用 txt 文本文件存放标注,每行一个目标,格式是:

class x_center y_center width height

所有坐标都是相对于图片宽高的归一化比例值,取值在 0 到 1 之间。比如一张 1920×1080 的图片中,某个人脸框左上角位于 (480, 270),宽 480、高 540,那么归一化后就是:

x_center = (480 + 480/2) / 1920 = 0.375 y_center = (270 + 540/2) / 1080 = 0.5 width = 480 / 1920 = 0.25 height = 540 / 1080 = 0.5

标注结果对应一行:0 0.375 0.5 0.25 0.5。

标注工具我用的是 LabelImg,图形界面操作,画框后直接选择类别保存,支持自动生成 YOLO 格式的 txt 文件。也出现过标注完成后类别编号对不上的情况,所以我在训练前写了一个脚本统一校验:检查每个 txt 是否与对应图片匹配、类别编号是否在合法范围、框的坐标是否越界。这一步别省,数据质量出问题,训练的模型大概率在推理阶段出现莫名其妙的行为。

关于类别定义,这个项目的实际需求是“判断戴口罩是否合规”,所以我建议用三类:

  • well_mask:正确佩戴口罩
  • no_mask:完全没有佩戴口罩
  • wrong_mask:口罩戴了但方式不对(悬挂在下巴、露出口鼻等)

如果只是做一个“有没有戴”的简单检测,两类就够了,但“戴了下巴”很可能被误判为“正确佩戴”,这在真实场景中远比“完全没戴”更隐蔽。用三类标注可以让模型强制区分这些容易混淆的情况,精度上更可靠。

2.3 数据增强不是“加滤镜”,而是模拟真实环境

YOLOv8 内置了丰富的在线数据增强策略,训练过程中自动对图片做变换。我用到的典型增强方式包括:

  • Mosaic 增强:把 4 张图片拼接成一张,模型被迫在更低分辨率、更复杂的背景下学习目标特征。对提升小目标检测能力很有效。
  • 随机仿射变换:包括旋转、缩放、平移。尤其重要,因为摄像头角度和距离会导致人脸的尺度和旋转各不相同。
  • HSV 色彩抖动:随机调整亮度、对比度、饱和度。拍到的监控视频经常是逆光和阴阳光线交替,这步能增强模型对光照变化的鲁棒性。
  • 随机遮挡和模糊:模拟人脸被手遮挡、摄像头失焦的情况。

初期我的数据集只有约 1500 张图片,训练出来的模型对侧脸和远处小目标的检测一塌糊涂。后来补充了 500 张不同角度、不同距离的自制图片,配合增强策略,指标立刻上来了。我的体会是:数据增强是“让模型在有限数据下变得更强”的有效手段,永远比反复调模型结构更省力。

2.4 训练集和验证集的切分技巧

把数据集按约 8:1:1 的比例分为训练集、验证集、测试集。有个分区原则值得注意:同一场景或同一段视频的图片集中放同一分区,不能让来自同一视频相邻帧的图片既出现在训练集又出现在验证集。否则验证的 mAP 看起来很高,但实际上模型只是“记住”了相近帧的内容,部署到真实场景时效果会大打折扣。

另外,类别均衡也需要关注。公共通道的场景里“未佩戴口罩”的人可能远多于“佩戴口罩”的人,如果训练集里两类数量差距过大,模型会偏向预测多数类。我用了一个简单方法处理:在标注完成后统计各类别目标数量,对偏少的类别可以通过重复采样或对局部区域做增强来补齐。

3. 模型训练核心环节

3.1 环境配置:显存不够时的现实选择

环境准备的关键是依赖版本匹配。项目用的技术栈如下:

  • Python 3.10
  • PyTorch 2.0 系列
  • CUDA 11.8(配合 6GB 显存入门显卡足够)
  • ultralytics 库(YOLOv8 官方实现)

安装核心依赖:

pip install ultralytics

如果你的机器没有独立显卡,可以用 CPU 训练,但速度慢很多。训练 100 epoch 的小模型在入门级显卡上大约需要 3 到 4 小时,CPU 可能要十几个小时。所以个人建议哪怕租云服务器也要有 GPU。显存大小决定能跑多大的 batch size,模型大、batch 大,显存占用就高。显存不足时的几个调整手段:降低 batch size、把图片分辨率从 640 降至 512、或是换更小的模型变体。

3.2 预训练权重选型:n、s、m、l、x 怎么挑

YOLOv8 提供从 n 到 x 的多种模型变体,参数规模和精度依次上升。官方也发布了在大型数据集上预训练好的权重,直接用这些权重初始化,再在口罩数据集上微调,比从头训练收敛更快、精度更高。各版本的核心差异大致如下:

变体参数量特点适合场景
YOLOv8n约 3.2M轻量、速度快,精度相对偏低边缘设备、实时高帧率场景
YOLOv8s约 11.2M速度和精度的较好平衡一般项目首选
YOLOv8m约 25.9M中等体积,精度明显提升算力充足、精度优先
YOLOv8l/x约 43.7M/68.2M高精度,但推理慢离线批量检测或专一服务器部署

我个人推荐直接以yolov8n或yolov8s起步。在口罩检测这种单类目标且目标尺度不算极端的任务上,小模型在足够数据下完全能达到可用精度。等流程跑通了,再根据实际精度需求尝试更大的模型,避免一上来就训练大模型导致时间成本居高不下。

3.3 训练数据配置文件与启动训练

ultralytics 框架通过一个 YAML 文件描述数据集的信息。口罩检测数据配置文件如下:

path: ./datasets/mask_dataset # 数据集根目录 train: images/train # 训练集图片目录 val: images/val # 验证集图片目录 nc: 3 # 类别数 names: ['well_mask', 'no_mask', 'wrong_mask']

训练命令:

yolo train model=yolov8s.pt data=mask.yaml epochs=100 imgsz=640 batch=16

几个关键参数的选择依据:

  • epochs:建议 100 起。口罩检测任务不算复杂,100 轮的训练在入门级显卡上大约数小时,能覆盖从欠拟合到过拟合的完整过程,配合早停机制自动保存最优模型。
  • imgsz:默认 640×640 是精度和速度的良好折中。如果摄像头画面中人脸普遍较小,可以尝试 800 或 960,但训练和推理速度会相应下降。
  • batch:能在显存允许范围内尽量大。批量大小的选择会影响收敛稳定性,过小时梯度噪声大,过大时内存压力高。入门级 6GB 显存下,yolov8s配batch=16是比较稳妥的组合。
  • optimizer:我常用AdamW,默认初始学习率 0.01 配余弦退火。相比 SGD,AdamW 在大多数下游任务上收敛更稳定,对超参数不那么敏感,对新手更友好。

3.4 训练过程中的监控与管理

训练时屏幕上会实时打印每个 epoch 的 loss 以及检测指标。需要重点关注的几个指标:

  • box_loss、cls_loss、dfl_loss:分别是定位损失、分类损失和分布焦点损失。三者在训练初期快速下降,中后期趋于平缓,这属于正常现象。如果某个 loss 在后期反弹上升,大概率是过拟合信号。
  • mAP50、mAP50-95:mAP50 是 IoU 阈值 0.5 时的平均精度,mAP50-95 是 0.5 到 0.95 每隔 0.05 取一个阈值的平均。口罩检测一般以 mAP50 为主参考,但 mAP50-95 能反映边框定位的精细程度。如果 mAP50 很高但 mAP50-95 偏低,说明模型能大致找到目标但框贴得不紧。
  • Precision、Recall:直接反映漏检和误检的取舍。两者本来冲突,需要根据场景选择侧重的指标。安全监控更在意漏检,宁可误报也不能漏掉未佩戴口罩的人,优先保证 Recall。

训练结束后,框架会自动在运行目录下生成results.png(各指标训练曲线)、confusion_matrix.png(混淆矩阵)和val_batch*.jpg(验证集的推理可视化结果)。这些图值得仔细看,尤其是混淆矩阵:如果模型大量把no_mask预测成wrong_mask,先别急着调模型,回头检查标注和数据集更实际。

4. 推理部署与系统集成

4.1 使用最佳权重进行推理

训练完成后,runs/train/exp/weights/下会生成best.pt和last.pt。前者是验证集上表现最好的权重,部署时用这个。

单张图片的推理:

from ultralytics import YOLO model = YOLO("runs/train/exp/weights/best.pt") results = model("test_images/street.jpg", conf=0.5, iou=0.45) for r in results: for box in r.boxes: cls = int(box.cls.item()) conf = float(box.conf.item()) xyxy = box.xyxy[0].tolist() print(f"类别: {model.names[cls]}, 置信度: {conf:.2f}, 框: {xyxy}")

conf参数是置信度阈值,低于该值的检测结果会被丢弃;iou是 NMS 去重的 IoU 阈值。实际部署时这两个参数值得反复试:conf=0.25和conf=0.5的检测结果在复杂场景下差异很大,调参时要结合场景的漏检和误报容忍度来判断。

4.2 摄像头实时检测的基本脚本

实时检测和静态图片检测的核心逻辑没有区别,只是输入源变成视频流。一个可直接用的摄像头推理脚本:

import cv2 from ultralytics import YOLO model = YOLO("best.pt") cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) while True: ret, frame = cap.read() if not ret: break results = model(frame, conf=0.5, iou=0.45, verbose=False) for r in results: boxes = r.boxes for box in boxes: x1, y1, x2, y2 = map(int, box.xyxy[0].tolist()) cls = int(box.cls.item()) conf = float(box.conf.item()) label = f"{model.names[cls]} {conf:.2f}" # 不同类别用不同颜色区分 color = (0, 0, 255) # no_mask 红色 if model.names[cls] == "well_mask": color = (0, 255, 0) # 正确佩戴口罩绿色 elif model.names[cls] == "wrong_mask": color = (0, 255, 255) # 不规范佩戴黄色 cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imshow("mask detection", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()

实测在入门级显卡上,yolov8s处理 720p 视频流能跑到 35 帧/秒以上,足够流畅。如果需要在无 GPU 的环境下部署,可以先导出成 ONNX 再用 ONNX Runtime 加速,或者直接用 OpenCV 的 DNN 模块加载。

4.3 模型导出:从 PyTorch 到通用部署格式

想把模型交给 C++ 服务端或手机端使用,通常需要导出成标准格式:

yolo export model=best.pt format=onnx opset=12 yolo export model=best.pt format=tensorrt imgsz=640

导出的模型可以用 ONNX Runtime 加载推理,推理速度快于直接使用 PyTorch:

import onnxruntime as ort import cv2 import numpy as np session = ort.InferenceSession("best.onnx") input_name = session.get_inputs()[0].name img = cv2.imread("street.jpg") img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (640, 640)) img = img.astype(np.float32) / 255.0 img = np.transpose(img, (2, 0, 1)) img = np.expand_dims(img, axis=0) outputs = session.run(None, {input_name: img})

这里要提醒一句:ONNX 导出后默认的输出格式和原始 PyTorch 略有不同,会输出一个解码前的张量,需要自己解析边界框和类别,所以建议在导出前先测试一遍基础推理脚本,确认输出能正确解析。早期我直接拿着 ONNX 模型上线,结果因为输出解析没做好,画面上的检测框全部偏移,排查了半天才发现是导出后处理逻辑没跟上。

4.4 完整系统的业务层怎么做

模型只是检测引擎,要让系统真正可用,业务层还需要做几件事:

  1. 报警热区与防抖机制:摄像头画面里可能存在重复检测同一人的情况。简单但有效的做法是做一个连续帧计数:如果同一位置的检测框连续 N 帧(比如 5 帧)都判定为no_mask或wrong_mask,才触发报警。这样避免了人只是路过或快速进出时一帧误检导致的误报。

  2. 目标追踪:入门方案直接按坐标位置做帧间匹配;更正规的做法是用 ByteTrack 或 DeepSort 做目标追踪,给每个经过的人一个唯一 ID,统计他未佩戴口罩的时长。加入了追踪后,还能做“此人未戴口罩进入”的整段视频回放,而不是只报警一个瞬间。

  3. 跨设备部署:同一个模型能非常方便地在不同设备间迁移。训练好的权重文件,拷贝到另一台机器,安装好依赖即可运行。也适合部署在树莓派或 Jetson 这类板卡上,只需根据设备算力选择合适模型变体。

  4. 与门禁闸机联动:如果检测到未佩戴口罩,输出一个 IO 信号控制闸机保持关闭,同时通过界面或扬声器提醒操作人员。这类联动逻辑别写进推理循环里,用独立线程处理硬件 IO,避免摄像头帧率被硬件通信卡住。

5. 常见问题与排查技巧实录

5.1 训练不收敛或 loss 变成 NaN

训练过程中 loss 剧烈震荡甚至变成 NaN,是比较常见的问题。大概率原因及处理办法:

  • 学习率过高:默认 0.01 一般来说没问题,但如果用了更大的 batch,学习率上限也随之变化。把lr0调低到 0.005 试试。
  • 数据中有 NaN 标注:坐标或类别字段里混入异常值。写一个脚本遍历所有标注文件,校验坐标范围、类别编号合法性,非常有效。
  • 混合精度训练不兼容:关闭 AMP,设置amp=False。
  • 模型权重损坏:重新下载预训练权重再试。

5.2 精度尚可,但小目标检测不到

摄像头画面里的人脸像素面积很小,多目标密集,对检测造成困难。处理思路:

  • 把训练和推理的imgsz增大到 960 或以上,相当于放大小目标的有效信息。
  • 在推理阶段借助上一帧的检测结果约束搜索区域,把大图切块分别检测。
  • 数据层面:要注意标注框是否把整张脸包括完整。如果标注框框住的是人脸下半部分而忽略额头,模型要学到的特征就不完整。
  • 实在不行再考虑换yolov8m或更大模型。

5.3 误检:把口罩形状的物体误判为口罩

一次现场测试中,背景墙上的圆形装饰被多次识别为well_mask。原因很好理解:模型学到的是圆形加纹理,没学到人脸结构。排查时我查看了训练集中负样本和硬负样本数量,发现几乎全是人脸相关图片,缺少背景干扰样本。改进方法:

  1. 从部署场景中采集大量不包含任何检测目标的背景帧,作为负样本加入训练集,标注为空。
  2. 引入在线难例挖掘机制(虽然 YOLOv8 默认的 Mosaic 增强会在一定程度上增加背景多样性,但不够系统性)。
  3. 提高置信度阈值到 0.35 甚至 0.5,过滤低置信度误检框。

免费的午餐是没有的,背景回归验证集,反复调一轮,误检率能降一个数量级。

5.4 推理慢,FPS 达不到实时

从部署角度,推理性能优化路径按优先级排列:

  1. 导出 ONNX 并开启优化,或直接导出 TensorRT,推理速度提升往往是一倍起步。
  2. 检查图像预处理耗时:使用 OpenCV 的imread和直接传给模型之间要做缩放,别小看这个耗时,过大图片会浪费大量时间。
  3. 降低输入分辨率:从 640 降到 416 或 320,帧率能显著提升。在入口通道这种近距离场景中,小分辨率带来的精度损失很多时候可以接受。
  4. 把后处理放到独立线程:主线程只做推理,绘制、日志、报警放异步线程,避免绘图阻塞判断循环。

5.5 “训练集很好、现场很惨”的困境

最常见的部署翻车情况是:在自己采集的测试集上 mAP 很高,一到现场就连续漏检。出现在这个问题,说明训练集和真实部署场景之间存在分布偏差。比如训练数据基本都是白天光线,而现场是晚上黄光灯;训练数据都是正面脸,而现场摄像头角度偏低只能拍到斜脸。解决办法是重新采集现场数据并增量式地补入训练集,用真实分布数据出发,重新微调模型。模型不是一拍脑袋就一劳永逸的东西,它需要跟着场景变化持续迭代。

6. 一些实用心得与扩展方向

口罩检测这个项目做完后,我最大的感受是:代码层面 YOLOv8 已经做到极简,真正花时间的地方全在数据质量和场景适配。如果能把数据的坑填平,模型效果不会让人失望。

有个技巧跟大家分享:训练结束后,把验证集的预测结果跑一遍,随机抽取一些图片用 OpenCV 拼成网格图人工过目,很多问题会比看指标更直观地暴露出来。比如我自己抽检时就发现“no_mask”的框把人脸照得特别红,整个框的边缘贴得很紧,而“wrong_mask”的框普遍偏大。这类现象通过指标看不出,但通过可视化一眼就能识别。

这个系统后续的扩展方向我梳理了三条:

  • 同类安全检测:安全帽检测、反光衣检测、作业区域人员闯入检测,思路和代码结构一模一样,只需换数据集和类别定义。
  • 实时流批一体:把检测结果同时写入数据库,配合大屏统计当前通道通过人数、未佩戴口罩比例,可以形成更完整的管理看板。
  • 边缘设备部署:把模型导出后放到低成本嵌入式设备上,用摄像头直接接设备,做到本地实时推理,不依赖服务器网络,这在很多现场场景里是刚需。

口罩检测现在看起来是个已经被做“烂”的题目,但认真把一个项目从数据集做到部署、把遇到的所有问题都记录下来,收获的能力是可以平移到其他检测任务上的。希望这篇文章能帮你少走一些弯路。

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

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

立即咨询