简介:本资源是一项基于YOLOv8目标检测模型的智能健康监测实践项目,面向计算机、人工智能、电子信息等专业本科生及初学者,解决睡眠过程中打鼾行为的自动识别与频率量化统计问题,适用于毕业设计、课程设计或大作业开发。压缩包共8个文件(3个Python主程序、3个PyTorch模型文件.pt、2个说明文档),总大小15.91MB,涵盖训练、检测、可视化全流程:包含可直接运行的Detection_video.py与Visual_interface.py界面程序、预训练及微调后的yolov8n.pt与best.pt模型、完整标注数据集及详细部署教程。项目已通过实测验证,输出F1分数曲线、精确率-召回率曲线、混淆矩阵、标签分布图及验证集预测结果等核心评估图表,结构清晰、注释完备,开箱即用。目前已有46人学习下载,适合零基础快速上手,也支持进阶修改以拓展至其他生物行为识别场景。
1. 项目概述:从“听见”到“看见”的睡眠健康监测
打鼾,这个看似寻常的夜间现象,背后可能隐藏着睡眠呼吸暂停等健康风险。传统的监测手段要么依赖昂贵的专业设备,要么需要伴侣的“人工记录”,既不经济也不客观。作为一名长期关注计算机视觉落地的开发者,我一直在寻找能用技术解决生活小痛点的方式。这个“基于YOLOv8的智能枕头打鼾频率统计”项目,就是一次将前沿的目标检测模型YOLOv8,应用于非接触式、低成本睡眠健康监测的尝试。
简单来说,这个项目通过一个放置在床头的普通摄像头(如USB摄像头或树莓派相机),实时捕捉睡眠者的头部区域视频。然后,利用训练好的YOLOv8模型,精准识别视频帧中人的嘴巴是否处于“张开”状态——这是打鼾时一个非常典型且稳定的视觉特征。通过统计单位时间内嘴巴张开的频率和持续时间,我们就能量化“打鼾频率”,并以图表等形式直观展示。项目打包了完整的源码、一个用户友好的可视化操作界面、我精心整理和标注的数据集,以及详尽的部署教程,真正做到解压即用,非常适合计算机视觉、人工智能相关专业的同学作为毕业设计或课程设计的实战项目,也适合任何对AI应用开发感兴趣的爱好者入门实践。
2. 项目核心设计思路与技术选型
2.1 为什么选择“嘴巴状态”作为检测目标?
在构思初期,我们面临几个备选方案:检测鼾声的音频分析、检测身体震动的传感器方案,以及我们最终采用的视觉方案。音频分析容易受到环境噪音干扰,且涉及隐私问题;传感器方案需要接触人体,可能影响睡眠舒适度。视觉方案的优势在于非接触、无感、成本低。而在视觉特征中,打鼾时气流通过狭窄的上呼吸道,通常会导致嘴巴不自觉地张开以增加通气量。经过对公开睡眠视频资料和我自己录制的一些样本观察,“嘴巴张开”是一个在多数打鼾场景下都存在的、相对容易捕捉的视觉线索,其稳定性远高于试图去检测“喉咙震动”或“面部肌肉微动”等更细微的特征。
因此,我们将一个复杂的“打鼾检测”问题,转化为了一个更具体的计算机视觉任务:实时视频流中的嘴巴状态(张开/闭合)分类。这大大降低了问题的复杂度,提高了方案的可行性。
2.2 为什么是YOLOv8?
目标检测模型众多,从两阶段的Faster R-CNN到单阶段的SSD、YOLO系列。选择YOLOv8主要基于以下几点考量:
- 卓越的精度与速度平衡:YOLOv8在保持YOLO系列一贯高速推理的同时,通过新的骨干网络和检测头设计,在COCO等标准数据集上达到了SOTA(State Of The Art)级别的精度。对于需要实时处理视频流(通常25-30 FPS)的我们的应用,推理速度至关重要。
- 开发者友好:Ultralytics公司维护的YOLOv8开源库,提供了极其简洁易用的API。从安装、训练到验证、导出,几乎一行命令就能完成,大大降低了开发门槛,让我们能把精力集中在业务逻辑而非模型调试上。
- 完善的生态与部署支持:YOLOv8支持导出为多种格式,如ONNX、TensorRT、CoreML等,可以轻松部署到从云端服务器到边缘设备(如Jetson Nano、树莓派)乃至移动端,为项目的后续扩展提供了可能。
- 对小目标检测友好:在我们的应用场景中,“嘴巴”在整张床铺图像中属于典型的小目标。YOLOv8在模型结构上加强了对小目标的检测能力,这对于保证检测的准确率非常关键。
2.3 系统架构总览
整个项目的运行流程可以概括为以下几步,形成了一个完整的闭环:
- 视频采集:通过OpenCV调用摄像头,获取实时视频流。
- 目标检测:将每一帧图像送入YOLOv8模型。模型首先检测出“人”(Person)这个大类,然后在人的边界框内,进一步检测“嘴巴”(Mouth)的位置及其状态(张开/闭合)。这里我们实际上训练了一个自定义的YOLOv8模型,其类别是
[‘mouth_open’, ‘mouth_close’]。 - 逻辑判断与统计:程序根据检测到的嘴巴状态,进行打鼾事件判断。例如,连续N帧检测到“mouth_open”,且张开幅度超过阈值,则记为一次有效的打鼾事件。同时,统计每分钟、每小时打鼾事件的次数、总时长等。
- 数据可视化与存储:通过PyQt5或Gradio等库构建的图形界面,实时显示视频画面、检测结果(用方框标出嘴巴)、以及打鼾频率的历史曲线图。所有统计数据会同步保存到本地数据库(如SQLite)或CSV文件中,供后续查看分析。
- 报警与反馈(可选扩展):可以设置阈值,当打鼾频率过高或单次打鼾持续时间过长时,通过界面闪烁、轻微震动(连接智能硬件)等方式进行提醒。
3. 数据集构建与模型训练实战
3.1 数据收集与标注:项目的基石
“巧妇难为无米之炊”,高质量的数据集是模型成功的核心。我构建这个数据集的过程,或许能给你一些启发。
数据来源:
- 公开数据集:筛选了部分包含睡眠、谈话、打哈欠等场景的人脸数据集,如CelebA、WIDER FACE,但需要仔细清洗,因为其中嘴巴张开不一定代表打鼾。
- 网络爬虫(合法合规):使用搜索引擎的图片高级搜索功能,以“sleeping snoring”、“person sleeping mouth open”等为关键词,并严格筛选“允许商业使用”的图片。注意版权,本项目数据集仅供学习研究。
- 模拟拍摄:这是最重要的部分。邀请朋友或家人(已征得同意)在类似睡眠的姿势下,模拟打鼾时的呼吸状态(口呼吸),以及平静睡眠时的闭口状态。使用不同的光线(侧光、顶光、昏暗)、不同的枕头、不同的拍摄角度(侧面、斜上方)进行采集,以增强模型的鲁棒性。
标注工具与过程: 我使用的是Roboflow在线平台和LabelImg本地工具相结合的方式。
- Roboflow:非常适合团队协作和数据处理。它提供了强大的数据增强功能(旋转、裁剪、亮度调整、模糊等),能有效扩充数据集规模,防止过拟合。我将收集的图片上传后,统一预处理为640x640的尺寸(YOLOv8的常用输入尺寸)。
- LabelImg:用于精细标注。标注时,关键点在于:
- 类别:只定义两个类别——
mouth_open和mouth_close。 - 边界框(Bounding Box):框选范围要紧密贴合嘴唇外缘,不宜过大或过小。对于侧脸时嘴巴呈一条缝的情况,也需要根据可见部分合理框选。
- 质量:确保每个样本标注准确。对于难以判断的状态(如微张),宁可舍弃也不要模糊标注。
- 类别:只定义两个类别——
最终,我整理了一个包含约2500张图像的数据集,按照8:1:1的比例划分为训练集、验证集和测试集。数据集的多样性是模型能否在实际复杂卧室环境中表现良好的关键。
实操心得:标注是最耗时但最不能偷懒的环节。一个常见的坑是“标注不一致”,比如今天觉得嘴角微张算“open”,明天又觉得不算。最好在开始前明确标注规范,或者由同一个人完成大部分标注工作。使用Roboflow的“自动标注”预标注功能可以大幅提升效率,但完成后必须人工逐一检查修正。
3.2 YOLOv8模型训练详解
有了数据集,训练就变得非常 straightforward。以下是核心步骤和参数解析:
环境配置:
pip install ultralytics # 安装YOLOv8官方库建议使用Python 3.8+和PyTorch 1.7+。本项目代码已在包含常见依赖的requirements.txt中。
数据准备: YOLOv8要求特定的数据目录结构。使用Roboflow可以直接导出为YOLOv8格式,它会生成一个data.yaml文件,内容如下:
path: ../datasets/snoring_mouth # 数据集根目录 train: images/train # 训练集图片路径 val: images/val # 验证集图片路径 test: images/test # 测试集图片路径 nc: 2 # 类别数量 names: ['mouth_open', 'mouth_close'] # 类别名称训练命令与关键参数:
yolo task=detect mode=train model=yolov8n.pt data=data.yaml epochs=100 imgsz=640 batch=16 patience=20model=yolov8n.pt: 这里选择YOLOv8n(nano)模型,它是体积最小、速度最快的版本。对于我们的二分类小目标任务,其精度已经足够,且能在树莓派等低算力设备上实时运行。如果追求更高精度,可选用yolov8s.pt或yolov8m.pt。epochs=100: 迭代轮数。可以通过观察训练曲线(如损失loss、精度mAP)提前停止。imgsz=640: 输入图像尺寸。与数据预处理尺寸保持一致。batch=16: 批大小。根据你的GPU显存调整(如8G显存可用16,4G显存可用8)。如果使用CPU训练,batch应设为1或一个很小的数。patience=20: 早停耐心值。如果验证集指标在连续20个epoch没有提升,则自动停止训练,防止过拟合。
训练过程监控: 训练开始后,Ultralytics会实时打印日志,并在runs/detect/train/目录下生成一系列重要文件:
results.csv和results.png: 训练过程的所有指标曲线,包括损失函数(box_loss, cls_loss)、精度(mAP50, mAP50-95)等。务必重点观察验证集损失(val/loss)是否在持续下降,验证集精度(metrics/mAP50)是否在上升。如果训练集损失下降但验证集损失上升,就是过拟合的典型信号。confusion_matrix.png: 混淆矩阵,清晰展示模型在测试集上对两个类别的分类情况。理想情况下,对角线(正确分类)的值应该很高。weights/best.pt: 训练过程中在验证集上表现最好的模型权重,这是我们最终要使用的模型。
注意事项:训练时如果遇到“CUDA out of memory”错误,首要降低
batch大小,其次可以尝试减小imgsz(如从640降到320),但这可能会影响小目标检测精度。另外,使用预训练权重(yolov8n.pt)进行迁移学习,比从零开始训练收敛快得多,效果也更好。
4. 工程实现与可视化界面搭建
4.1 核心检测循环代码解析
模型训练好后,接下来就是编写推理代码。核心逻辑在一个循环中,不断从摄像头抓取帧并进行处理。
import cv2 from ultralytics import YOLO # 加载训练好的最佳模型 model = YOLO(‘runs/detect/train/weights/best.pt’) # 打开摄像头(0代表默认摄像头) cap = cv2.VideoCapture(0) snore_event = False # 当前是否处于打鼾事件中 snore_count = 0 # 打鼾事件计数 frame_buffer = [] # 用于状态判定的帧缓冲区 while cap.isOpened(): success, frame = cap.read() if not success: break # 使用YOLOv8进行推理 results = model(frame, verbose=False) # verbose=False关闭冗余输出 # 初始化状态 current_state = ‘mouth_close‘ # 遍历本帧所有检测结果 for r in results: boxes = r.boxes if boxes is not None: for box in boxes: cls_id = int(box.cls) # 类别ID conf = float(box.conf) # 置信度 # 只处理高置信度的检测结果 if conf > 0.7: if cls_id == 0: # 假设0是‘mouth_open‘ current_state = ‘mouth_open‘ # 在图像上画框和标签 x1, y1, x2, y2 = map(int, box.xyxy[0]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, f‘Open {conf:.2f}‘, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,0,255), 2) # 打鼾事件判定逻辑(示例:连续5帧检测到张嘴,则认为开始打鼾) frame_buffer.append(current_state) if len(frame_buffer) > 5: frame_buffer.pop(0) if all(state == ‘mouth_open‘ for state in frame_buffer): if not snore_event: snore_event = True snore_count += 1 print(f“开始打鼾事件 #{snore_count}“) else: snore_event = False # 在画面上显示统计信息 cv2.putText(frame, f‘State: {current_state}‘, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2) cv2.putText(frame, f‘Snore Events: {snore_count}‘, (10, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2) # 显示画面 cv2.imshow(‘Smart Pillow Snore Detection‘, frame) # 按‘q‘退出 if cv2.waitKey(1) & 0xFF == ord(‘q‘): break cap.release() cv2.destroyAllWindows()这段代码是核心骨架,实现了视频读取、模型推理、结果解析、简单状态逻辑和OpenCV显示。在实际项目中,我们还需要将统计信息(如每分钟打鼾次数、张嘴总时长)记录下来,并定时(如每秒)更新到图表中。
4.2 使用PyQt5构建功能完善的桌面GUI
为了提升用户体验,我们使用PyQt5来构建一个功能更全面的桌面应用程序。界面主要包含以下区域:
- 视频显示区:实时显示摄像头画面和YOLOv8的检测框。
- 控制面板:包含“开始/停止”检测按钮、“选择摄像头”下拉框、“模型置信度”滑块、“事件判定阈值(连续帧数)”设置框。
- 数据统计区:以数字和仪表盘形式显示当前打鼾频率、本次监测总时长、总打鼾事件数。
- 图表可视化区:使用
matplotlib或PyQtGraph嵌入一个动态图表,横轴为时间,纵轴为打鼾频率(次/分钟),曲线实时更新。 - 历史记录区:一个表格,展示每次打鼾事件的具体开始时间、持续时间。
- 菜单栏:提供“开始记录”、“停止记录”、“导出数据(CSV/Excel)”、“查看历史报告”等功能。
PyQt5多线程编程要点: GUI界面必须保持响应,而视频检测循环是阻塞的。因此,必须将检测循环放在一个独立的QThread(线程)中运行。主线程(GUI线程)负责更新界面,子线程负责处理视频和推理,两者通过信号(Signal)和槽(Slot)机制通信。例如,子线程每处理完一帧,就发出一个包含检测结果和统计数据的信号,主线程接收到信号后,再去更新图像显示和图表。
踩坑实录:直接在PyQt5的主线程里跑OpenCV的
while循环,会导致界面“卡死”,无法操作按钮。这是GUI编程新手最常见的坑。务必牢记“耗时操作放子线程”。
4.3 数据持久化与报告生成
监测数据需要保存下来。我选择轻量级的SQLite数据库,因为它无需安装单独的数据库服务,一个.db文件搞定。 数据库表设计可以很简单:
CREATE TABLE snore_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, start_time TIMESTAMP, -- 打鼾事件开始时间 duration REAL, -- 持续时间(秒) intensity INTEGER -- 强度(可用张嘴的置信度平均值表示) ); CREATE TABLE session_summary ( session_id INTEGER PRIMARY KEY AUTOINCREMENT, date DATE, total_duration INTEGER, total_events INTEGER, avg_frequency REAL );每次监测会话结束后,程序可以自动生成一个简单的HTML或PDF报告,使用Jinja2模板引擎将数据库中的数据填充到HTML模板中,然后利用weasyprint库转换为PDF。报告内容可以包括:监测时间段、打鼾事件折线图、统计数据汇总(最频繁时段、最长单次打鼾时间等)以及一些简单的健康建议。
5. 完整部署教程与优化指南
5.1 一键部署与运行
为了让项目真正做到“开箱即用”,我提供了两种主要的部署方式:
方式一:本地Python环境部署(适合开发者)
- 解压项目包。
- 打开终端,进入项目目录。
- 创建并激活Python虚拟环境(推荐):
python -m venv venv # Windows: venv\Scripts\activate # Linux/Mac: source venv/bin/activate - 安装依赖:
pip install -r requirements.txt。这个文件已经包含了ultralytics,opencv-python,PyQt5,sqlite3等所有必要库。 - 运行主程序:
python main.py。首次运行会自动下载YOLOv8n的预训练权重(如果不用自己的模型)。
方式二:使用Docker容器化部署(适合快速体验和避免环境冲突)项目包含了Dockerfile和docker-compose.yml。
- 确保系统已安装Docker和Docker Compose。
- 在项目根目录下执行:
docker-compose up -d。 - Docker会自动构建镜像,并启动容器。可以通过访问容器的GUI(需要X11转发或VNC)或提供的Web接口(如果集成了Gradio)来使用应用。
部署提示:对于Windows用户,如果遇到PyQt5相关DLL错误,可以尝试安装特定版本的
PyQt5,或者使用pip install pyqt5-tools。对于Mac用户,需要注意OpenCV的摄像头权限。
5.2 模型优化与加速技巧
如果发现推理速度跟不上摄像头帧率(导致界面卡顿),可以尝试以下优化:
模型轻量化:
- 使用更小的模型:我们已经用了YOLOv8n,这是最快的。如果还嫌慢,可以考虑专门为移动端优化的模型,如YOLOv8的PaddleOCR版本或使用模型剪枝、量化技术。
- 模型量化:将训练好的
best.pt模型转换为INT8量化模型,可以大幅减小模型体积并提升推理速度,精度损失很小。yolo export model=best.pt format=onnx imgsz=640 half=True # 导出为半精度FP16的ONNX # 然后可以使用TensorRT或ONNX Runtime进行INT8量化推理
推理优化:
- 降低输入分辨率:在模型推理时,将
imgsz参数从640降低到320或416,速度会成倍提升,但需要重新评估精度是否可接受。 - 使用TensorRT部署:对于NVIDIA GPU用户,将模型转换为TensorRT引擎是终极加速方案,能极大发挥GPU性能。Ultralytics官方支持直接导出为TensorRT格式。
- 帧采样:如果不是非要每帧都检测,可以每两帧或三帧检测一次(
frame skipping),用上一帧的结果来填充跳过的帧,这对频率统计影响不大,但能显著降低计算负载。
- 降低输入分辨率:在模型推理时,将
代码层面优化:
- ROI(Region of Interest)检测:先用一个非常轻量的模型或传统视觉方法(如人脸检测器)快速定位人脸区域,然后只将人脸区域裁剪出来送给YOLOv8做嘴巴状态检测。这样YOLOv8处理的图像尺寸就小了很多。
- 异步处理:将图像预处理、模型推理、后处理(画框、统计)放在不同的线程或队列中,形成流水线,充分利用多核CPU。
5.3 常见问题排查(FAQ)
Q1: 运行程序后,摄像头黑屏/打不开。
- 检查1:摄像头索引号是否正确?
cv2.VideoCapture(0)中的0通常是内置摄像头,外接USB摄像头可能是1或2。可以在代码里遍历尝试。 - 检查2:摄像头是否被其他程序(微信、Zoom)占用?关闭所有可能使用摄像头的软件。
- 检查3:在Linux系统上,可能需要将用户加入
video组:sudo usermod -aG video $USER,然后注销重新登录。
Q2: 检测框乱飞,或者完全检测不到嘴巴。
- 检查1:模型置信度阈值是否合适?默认0.25可能太低,在复杂背景下容易产生误检。可以在推理时调高
conf参数:results = model(frame, conf=0.5)。 - 检查2:环境光线是否太暗?YOLOv8虽然有一定抗干扰能力,但极暗环境下效果会大打折扣。建议保证床头有微弱、稳定的光源(避免直射人脸)。
- 检查3:拍摄角度是否太偏?模型在正侧脸和微俯视角度下效果最好。摄像头应放置在床头柜,略高于枕头,斜向下对准人脸。
- 检查4:你的数据集是否缺少当前场景(如戴眼罩、被子遮住半张脸)的样本?考虑扩充数据集重新训练。
Q3: 程序运行一段时间后越来越卡,最后崩溃。
- 检查1:内存泄漏。确保在循环中释放不再使用的变量,特别是大图像张量。使用
del语句或确保它们离开作用域。 - 检查2:图形界面更新过于频繁。确保图表的更新不是每帧都进行,可以设置一个定时器,每100毫秒或1秒更新一次数据点和界面。
- 检查3:数据库操作频繁。不要每检测到一次状态变化就写入数据库,可以缓存起来,每隔一段时间(如10秒)批量写入一次。
Q4: 如何将项目部署到树莓派上?
- 步骤1:在树莓派上安装64位 Raspberry Pi OS。
- 步骤2:由于树莓派算力有限,必须使用量化后的模型(如ONNX格式),并使用
onnxruntime库进行CPU推理,或者尝试使用libtorch(PyTorch C++)。Ultralytics官方对ARM架构的支持正在完善中。 - 步骤3:使用更轻量的GUI框架,如Tkinter,或者直接使用无界面的“服务器模式”,通过局域网在另一台电脑的浏览器上访问数据看板。
- 步骤4:考虑使用
picamera2库来获取树莓派专用摄像头的图像,效率比OpenCV通用接口更高。
这个项目从构思到实现,最深的体会是,一个成功的AI应用不仅仅是模型精度高,更是工程化、用户体验和鲁棒性的结合。选择嘴巴状态作为切入点,用YOLOv8快速实现原型,再通过PyQt5封装成易用的工具,最后在部署和优化上打磨,每一步都充满了权衡与抉择。希望这份详细的拆解,能帮你不仅复现这个项目,更能理解其背后的设计逻辑,并激发你做出更有趣的改进。比如,结合音频分析进行多模态验证,或者加入心率、血氧(需要其他传感器)数据,打造一个更全面的非接触式睡眠健康监测原型。
本文还有配套的精品资源,点击获取