ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLOV5的水域游泳者危险检测:从数据集处理到部署避坑

基于YOLOV5的水域游泳者危险检测:从数据集处理到部署避坑 简介本资源为基于YOLOv5的水域中游泳者危险检测识别系统完整项目包面向计算机视觉方向的高校学生、期末大作业或毕业设计开发者以及需要水域安全监控方案的工程人员。项目已获导师指导并通过取得96分高分代码完整下载即可运行可直接用于课程设计、竞赛或二次开发。压缩包共1878个文件约235.38MB包含880张jpg与4张jpeg图像、811个txt标注文件、60个yaml配置、34张png、27个py源码、11个pt权重文件以及sh脚本、Dockerfile、ipynb笔记本和docx说明文档覆盖数据集、训练配置、模型权重与部署脚本。已有287人学习下载。读者可获得完整的水域游泳者检测识别方案包括数据集标注、YOLOv5训练流程、模型推理代码与可视化结果便于快速复现实验、理解检测思路并完成项目报告撰写。1. 水域游泳者危险检测为什么通用 YOLOV5 模型直接拿来用会翻车夏天一到泳池、水库、河道边的监控画面里全是人头但真正让救生员和安防团队头疼的不是有没有人下水而是这个人是不是正在出事。基于 YOLOV5 的水域中游泳者危险检测识别系统本质上就是把这件靠人眼盯屏幕的活儿交给一个能实时框出人体、判断姿态异常并触发告警的视觉模型。它要解决的核心问题是在开放水域或泳池场景下把正常游泳和溺水挣扎、长时间静止、头部没入水面这几类危险状态区分开而不是简单数人头。这套东西适合谁做期末大作业的学生、想入门目标检测落地的工程师、以及需要给现有监控加一层智能告警的集成方。很多人拿到一份源码数据集的压缩包第一反应是解压、装环境、跑detect.py然后发现模型在 COCO 上能识别人到了泳池画面里却把水花、泳帽、反光全框成目标或者干脆漏掉半个身子在水下的人。这不是模型不行是通用权重和你的场景之间隔着一整个数据分布。所以这篇笔记不聊虚的从数据集怎么处理、YOLOV5 怎么改、训练参数怎么调一路讲到部署时那些血泪踩坑让你拿到这套源码后能真正跑出可用的危险检测效果。2. 数据集处理从原始标注到 YOLOV5 能吃的格式2.1 游泳者危险检测的数据集长什么样水域场景的数据集和常规行人检测数据集差别很大。常见做法是采集泳池俯拍、岸边斜拍、水库远景三类视角标注类别通常分两种粒度粗粒度只标person细粒度标swimmer、drowning、floating漂浮静止等。如果你拿到的数据集是后者恭喜危险检测的语义信息已经嵌在标签里了如果是前者就得靠后续的姿态或时序逻辑补判断。数据集目录一般长这样这是 YOLOV5 标准结构拿到压缩包后先核对dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamlimages下放 jpg/pnglabels下放同名 txt每行格式是class_id x_center y_center width height坐标全部归一化到 0~1。这里第一个坑就来了很多网上流传的游泳者数据集标注是 VOC 的 xml 格式或者 LabelMe 的 json直接丢给 YOLOV5 是读不进去的。2.2 把 VOC 和 LabelMe 标注转成 YOLO 格式先看 VOC xml 转 YOLO txt 的脚本这是最常遇到的情况import xml.etree.ElementTree as ET import os # 类别映射按你的 data.yaml 顺序改 classes [swimmer, drowning, floating] def convert_voc_to_yolo(xml_dir, out_dir, img_w, img_h): if not os.path.exists(out_dir): os.makedirs(out_dir) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 有些数据集 size 写在 xml 里优先用它避免传参错误 size root.find(size) w int(size.find(width).text) if size else img_w h int(size.find(height).text) if size else img_h lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 归一化并转中心点格式 x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) with open(os.path.join(out_dir, xml_file.replace(.xml, .txt)), w) as f: f.write(\n.join(lines)) convert_voc_to_yolo(annotations_xml, labels/train, 1920, 1080)逻辑说明脚本遍历 xml读取每个 object 的类别和边界框把左上右下坐标转成 YOLO 要求的中心点加宽高并除以图像宽高做归一化。参数上classes列表的顺序必须和data.yaml里的names完全一致否则类别会错位训练出来的模型会把溺水标成正常游泳。img_w、img_h是兜底参数当 xml 里没有 size 节点时才用建议优先信任 xml 自带尺寸。LabelMe 的 json 转换思路类似读shapes数组里的points和label两点就是矩形框多点则是多边形多边形需要取外接矩形或者用分割标注另走 YOLOv5-seg 分支。转换完务必抽查几张用labelImg打开图片和 txt看框有没有偏移、有没有越界。2.3 data.yaml 的路径与类别配置转换完标注写data.yamlpath: /home/user/dataset train: images/train val: images/val test: images/test nc: 3 names: [swimmer, drowning, floating]path是数据集根目录train/val/test是相对路径。这里第二个高频坑是路径写成绝对路径后换机器就崩建议用相对路径配合path。nc是类别数必须和 names 长度一致。如果你的数据集只有person一类nc: 1names 写[person]危险判断交给后面的逻辑层。提示转换脚本跑完后统计一下每个类别的框数量。如果drowning类只有几十个框而swimmer有几千个这就是典型的类别不平衡直接训练模型会倾向于全预测成 swimmer后面第 4 章会讲怎么处理。3. YOLOV5 模型训练环境、参数与危险类别调优3.1 环境配置与源码目录结构拿到源码包先别急着pip install -r requirements.txt。YOLOV5 对 torch 版本敏感常见做法是用 conda 建独立环境conda create -n yolo5_water python3.8 -y conda activate yolo5_water # 先装 torch按你的 CUDA 版本选这里以 cu118 为例 pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cu118 pip install -r requirements.txt装完验证python -c import torch; print(torch.cuda.is_available(), torch.__version__)输出True才说明 GPU 可用。源码目录里几个关键文件要认识train.py训练入口、detect.py推理入口、models/yolov5s.yaml网络结构、data/下放你的 data.yaml、utils/里是数据加载和指标计算。第三个坑requirements.txt 里有时会锁死numpy或opencv版本和你系统里已有的冲突报错就单独降级别硬扛。3.2 训练命令与关键参数怎么设单卡训练的最小命令python train.py \ --data data/water.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 100 \ --device 0 \ --workers 8 \ --project runs/train \ --name water_v1逐参数说--weights yolov5s.pt是 COCO 预训练权重迁移学习能大幅缩短收敛时间水域场景强烈建议用预训练而不是从零训。--img 640是输入分辨率泳池俯拍小目标多的话可以提到 960 或 1280但显存占用会翻倍。--batch-size 16在 8G 显存上跑 640 分辨率比较稳爆显存就降到 8。--epochs 100是上限实际看mAP0.5什么时候不再涨通常 60~80 轮就够。--workers是数据加载线程设成 CPU 核数的 0.7 倍左右设太高反而抢资源。训练过程中盯runs/train/water_v1/results.csv重点看三个指标metrics/mAP_0.5、metrics/precision、metrics/recall。危险检测场景里 recall 比 precision 更重要漏检一个溺水者比多报几次误报代价大得多。如果 recall 上不去优先查标注质量而不是调模型。3.3 针对危险类别的超参数调整YOLOV5 的超参数在data/hyp.scratch.yaml里几个和本场景强相关的参数默认值水域场景建议原因lr00.010.005~0.01预训练微调太大容易震荡mosaic1.00.5~1.0增强小目标但水域拼接可能造出不真实画面fl_gamma0.01.0~1.5焦点损失缓解类别不平衡anchor_t4.03.0~4.0锚框匹配阈值小目标多可放宽hsv_v0.40.3~0.5水面反光强亮度增强有用类别不平衡的另一个手段是复制粘贴增强把drowning类的样本框抠出来随机贴到其他水域背景上扩充少数类。YOLOV5 自带copy_paste超参但只对分割任务生效检测任务得自己写增强脚本或者用--oversample思路在数据集层面重复采样少数类图片。注意调超参前先把 baseline 跑出来记录 mAP 和 recall每次只改一到两个参数否则你根本不知道是哪个起了作用。这是玄学调参和工程调参的分界线。4. 危险行为判定从检测框到告警逻辑4.1 为什么单帧检测不够需要时序判断YOLOV5 输出的是单帧的框和类别。如果数据集里直接标了drowning模型能学到一些视觉特征比如手臂上扬、头部后仰。但真实溺水往往是安静的一个人可能只是垂直立在水中、头半没、几十秒不动单帧看和正常踩水几乎一样。所以工程上常见做法是YOLOV5 负责稳定地检测人体框危险判定交给一个轻量的时序逻辑层。最简单的时序逻辑对每个跟踪 ID统计连续 N 帧内框的中心点位移和框高宽比变化。正常游泳位移大、姿态变化频繁溺水或静止危险状态位移接近零、框高宽比持续偏小人下沉。用sort或deep_sort做跟踪把 YOLOV5 的检测结果喂进去。4.2 用检测框做静止与下沉判定的代码import numpy as np from collections import deque class DangerTracker: def __init__(self, track_id, window30, move_thresh5.0, ratio_thresh1.2): self.track_id track_id self.window window self.move_thresh move_thresh # 像素位移阈值 self.ratio_thresh ratio_thresh # 高宽比阈值 self.history deque(maxlenwindow) # 存 (cx, cy, w, h) def update(self, box): x1, y1, x2, y2 box cx, cy (x1 x2) / 2, (y1 y2) / 2 w, h x2 - x1, y2 - y1 self.history.append((cx, cy, w, h)) if len(self.history) self.window: return False arr np.array(self.history) # 位移窗口内中心点标准差 move np.std(arr[:, 0]) np.std(arr[:, 1]) # 高宽比均值人下沉时框变扁 ratio np.mean(arr[:, 3] / (arr[:, 2] 1e-6)) is_danger (move self.move_thresh) and (ratio self.ratio_thresh) return is_danger逻辑说明每个跟踪 ID 维护一个滑动窗口存最近 30 帧的框信息。move用中心点坐标标准差衡量活动量ratio用高宽比均值衡量姿态。两个条件同时满足才判危险避免把正常踩水误报。参数move_thresh和ratio_thresh必须按你的摄像头分辨率和俯拍角度实测标定没有万能值。window越大越稳但告警越滞后实时场景 25~35 帧比较平衡。4.3 告警触发与误报抑制判定为危险后别立刻弹告警。加一个持续帧数计数器连续 M 次判定为危险才触发M 取 3~5。同时设一个冷却时间同一个 track_id 触发后 30 秒内不重复告警。这两条能砍掉大部分因为水花、遮挡造成的瞬时误报。告警输出可以走三种本地弹窗加声音、写日志文件、推 HTTP 接口给上层平台。期末大作业一般本地弹窗就够工程部署建议走接口方便和现有安防系统对接。输出内容至少包含时间戳、摄像头编号、框坐标、危险类型方便事后回溯。5. 避坑与排查训练和部署中最容易翻车的五个点5.1 损失不下降mAP 一直卡在 0.1 以下现象训练几十轮box_loss和cls_loss几乎不动mAP 接近零。原因通常是标注格式错了或者路径没对上。先检查labels里的 txt 是不是归一化坐标有没有出现大于 1 的值再检查data.yaml的path和train拼起来能不能找到图片。还有一个隐蔽原因图片和标签文件名不一致比如图片叫IMG_001.jpg标签叫IMG_001.txt但实际是img_001.txtLinux 下大小写敏感直接读不到。解决写个脚本遍历 images 和 labels打印没有配对的文件名一一修正。5.2 模型把水面反光、泳帽、水花框成目标现象推理时画面里出现大量无意义框置信度还不低。原因是训练集里这类负样本太少模型没学会这些不是人。解决收集一批纯水面、纯反光的负样本图片不放任何标签加入训练集。YOLOV5 对空标签图片是支持的会当作背景负样本。比例控制在正样本的 10%~20%。另外把conf-thres推理阈值从默认 0.25 提到 0.4~0.5也能压掉一批低质量框。5.3 小目标漏检严重远处游泳者完全检测不到现象近处的人能框住画面远处的游泳者一个都检测不到。原因是 640 分辨率下远处人体只有十几个像素特征太弱。解决有三条路一是提高推理分辨率到 1280代价是速度下降二是用 YOLOV5m 或 YOLOV5l 更大的模型特征提取能力更强三是在data.yaml里把img训练尺寸调大同时用--rect矩形推理减少 padding。实测 1280 加 YOLOV5m 对远处小目标提升最明显但帧率可能从 30 掉到 12得按你的硬件权衡。5.4 训练时显存爆掉报 CUDA out of memory现象跑几轮就 OOM或者一开始就崩。原因是 batch-size 太大、img 太大、或者 workers 太多导致内存泄漏。解决先把--batch-size减半还不行就减--img。如果用的是多卡但只指定了一张检查--device参数。另外--workers设太高在部分系统上会累积内存设成 4 或 8 试试。实在不行开--cache ram改成--cache disk用磁盘缓存换内存。5.5 部署到树莓派或边缘设备后帧率极低现象PC 上跑得好好的部署到树莓派 5 或 Jetson 上只有 2~3 帧。原因是 PyTorch 模型没做推理优化。解决导出 ONNX 或 TensorRT 引擎用export.pypython export.py --weights runs/train/water_v1/weights/best.pt --include onnx --img 640 --batch 1导出后用 onnxruntime 或 TensorRT 加载树莓派上还能进一步用 NCNN。注意导出时的--img要和训练一致否则精度会掉。边缘设备上建议用 YOLOV5n 或 YOLOV5s别上 m 和 l。6. 进阶技巧用半自动标注和模型蒸馏把迭代速度提上来真正做过几个水域项目后你会发现最耗时间的从来不是调模型而是标数据。第一版模型哪怕只有 0.5 的 mAP也能拿来当预标注工具把新视频抽帧后自动生成框人工只需要修正漏检和错标标注效率能翻三倍。具体做法用训练好的best.pt跑detect.py加--save-txt --save-conf输出 YOLO 格式的 txt导入 labelImg 微调。置信度阈值设低一点比如 0.15宁可多框也别漏人工删比人工画快得多。另一个提效手段是模型蒸馏。如果你有算力训了一个 YOLOV5l 的大模型可以拿它的输出当软标签去蒸馏一个 YOLOV5s 小模型让小模型在边缘设备上也能接近大模型的 recall。蒸馏损失用 KL 散度对齐两者的类别输出分布框回归还是用原损失。这套在危险检测里特别值因为边缘部署是刚需而大模型只用来离线生成伪标签和蒸馏目标。验证模型有没有真的学到危险特征别只看 mAP。构造一个对抗测试集把正常游泳视频逐帧做水平翻转、调暗、加水面波纹噪声看模型输出是否稳定。如果翻转后溺水框全没了说明模型学的是位置先验而不是姿态特征得回去补数据增强。我一般会在val.py跑完后手动抽 20 张漏检和误检的图一张张看比盯指标有用得多。最后说个习惯每次训练完把best.pt、data.yaml、hyp配置、训练命令和 mAP/recall 结果记在一个experiments.md里。水域场景的数据集版本一多没有这个后悔药两周后你根本想不起来哪个权重对应哪批数据。这套流程跑顺了从拿到压缩包到能演示的危险检测系统一周内能出第一版。希望帮到你。本文还有配套的精品资源点击获取
返回列表