
简介这是一份面向目标检测学习者的游泳与溺水图像数据集适用于YOLO全系列网络训练可支撑水域安全监控、溺水预警等场景的模型开发与实验。数据已统一处理为YOLO格式标注采用classes、x_centre、y_centre、w、h的相对坐标类别涵盖游泳、溺水等3类具体可参考class类别文本文件。资源包共2000个文件以1999个txt标签文件和1个show.py可视化脚本为主压缩包约300.66MB按YOLOv5目录结构组织可直接投入检测训练。数据划分为训练集约5000张、验证集约1400张、测试集约700张均含对应标签。show.py可将box目标绘制在图像上便于快速核验标注质量。目前已有1096人学习下载适合需要现成水域检测数据、希望省去标注与格式转换环节的开发者与研究者。1. 游泳溺水检测数据集7000 张图背后的真实需求与选型判断溺水检测这个方向很多人第一次接触是因为泳池安防项目。真实场景里摄像头架在池边画面里几十号人在水里扑腾救生员盯久了必然疲劳等发现异常再跳下去黄金救援时间往往已经过去。用目标检测做辅助预警核心诉求就一个把「正常游泳」和「溺水挣扎」这两类目标框出来给后端报警逻辑提供触发信号。这个标题讲的是一份超过 7000 张图片带标注的游泳、溺水图像检测数据集属于目标检测数据集里比较垂直的一类。它适合三类人做智慧场馆安防的算法工程师、想拿它练手 yolov8 训练自己数据集的学生、以及需要快速验证溺水识别可行性的产品团队。数据集本身不解决全部问题但它是这条链路里最容易被低估、也最容易翻车的一环。2. 溺水检测数据集到底长什么样类别设计与标注边界2.1 类别体系为什么多数方案只分两类拿到一份溺水数据集第一件事不是急着跑 yolov5 训练自己的数据集而是看它的类别定义。游泳溺水场景里常见做法是分两类swimming正常游泳和 drowning溺水。也有更细的把「水中站立」「岸边休息」单独拆出来但类别一多标注一致性就崩。我一般建议先用两类跑通基线因为溺水检测的最终目标是二分类预警中间类别对报警逻辑没有直接贡献反而增加标注噪声。两类体系下标注框的边界要统一。正常游泳的人框住头部和肩部即可因为身体大部分在水下框全身会引入大量水面反光区域。溺水目标则要框住可见的挣扎肢体和头部标注时以「人眼能判断这是一个人」为准。这个规则必须在标注文档里写死否则 7000 张图里会出现三种框法训练时模型直接学懵。提示如果数据集没有附带标注规范文档先抽 50 张图人工核对框的松紧程度再决定是否直接用于训练。2.2 图像来源与场景分布决定模型泛化上限7000 张图的来源通常分几类公开泳池监控截图、网络视频抽帧、以及部分模拟拍摄。来源越杂场景分布越广模型泛化越好但标注质量越难保证。你需要关注几个维度室内泳池 vs 室外泳池、白天 vs 夜间、水面平静 vs 水花飞溅、单人 vs 多人。如果数据集里 80% 是室内白天平静水面那模型到了室外夜间场景基本报废。常见做法是先用脚本统计图像尺寸、亮度均值、人数分布再决定要不要做数据增强。下面这段代码可以快速摸清数据集的底细import os import cv2 import numpy as np from collections import Counter img_dir dataset/images sizes [] brightness [] for name in os.listdir(img_dir): path os.path.join(img_dir, name) img cv2.imread(path) if img is None: continue h, w img.shape[:2] sizes.append((w, h)) # 转灰度算平均亮度判断白天/夜间分布 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) brightness.append(np.mean(gray)) print(尺寸分布:, Counter(sizes).most_common(5)) print(亮度均值:, np.mean(brightness)) print(亮度低于60的图片占比:, np.sum(np.array(brightness) 60) / len(brightness))这段代码的逻辑很直接遍历图像目录记录每张图的宽高和灰度均值。尺寸分布告诉你需不需要统一 resize亮度分布告诉你夜间样本够不够。参数上亮度阈值 60 是我在泳池场景里常用的经验值低于这个值基本算弱光。如果弱光占比超过 20%训练时就要加亮度扰动增强否则模型在夜间直接失效。2.3 标注格式VOC 与 YOLO 的转换坑数据集常见的标注格式有两种VOC 的 XML 和 YOLO 的 txt。如果你要用 yolov8 训练自己的数据集必须转成 YOLO 格式。转换本身不难坑在坐标归一化和类别映射。VOC 的 xmin/ymin/xmax/ymax 是绝对像素坐标YOLO 需要归一化到 0-1 之间的中心点加宽高。下面是一个转换脚本import xml.etree.ElementTree as ET import os classes [swimming, drowning] xml_dir dataset/annotations out_dir dataset/labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) 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) # 归一化并转中心点格式 cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) txt_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines))逻辑说明先读 XML 里的图像宽高再对每个目标框做归一化。参数上classes列表的顺序必须和训练时的 data.yaml 完全一致否则类别全错。:.6f保留六位小数是 YOLO 官方推荐的精度少了会导致小目标框偏移。转换完一定要抽几张图用可视化脚本核对我见过太多人转完直接训练结果 mAP 一直是 0查了半天发现是宽高写反了。3. 用 YOLOv8 跑通溺水检测基线从 data.yaml 到第一轮训练3.1 环境与目录结构别在路径上翻车训练之前先把目录理清楚。YOLOv8 对目录结构有固定要求常见做法是dataset/ images/ train/ val/ labels/ train/ val/ data.yaml图片和标签文件名必须一一对应只是扩展名不同。划分比例一般 8:2 或 7:3溺水场景样本少的话验证集不要低于 15%否则评估指标波动太大。data.yaml 内容如下path: ./dataset train: images/train val: images/val nc: 2 names: [swimming, drowning]nc是类别数names顺序必须和转换脚本里的 classes 一致。路径用相对路径绝对路径换机器就废。3.2 训练命令与关键参数batch 和 imgsz 怎么定环境装好后一条命令就能起训yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/drown \ namebaseline参数逐个说modelyolov8n.pt是最小的预训练权重适合先跑通流程显存不够就换 n够就上 s。imgsz640是默认输入尺寸泳池场景里溺水目标通常占画面比例不大640 够用但如果你的摄像头分辨率是 4K建议切到 1280 再试一轮。batch16是 8G 显存的安全值爆显存就降到 8。lr00.01是初始学习率溺水数据集如果只有 7000 张这个值偏大可以降到 0.005。patience20是早停轮数验证集指标 20 轮不升就停省时间。训练过程中重点看三个指标box_loss、cls_loss、mAP50。box_loss 降不下去说明框回归有问题常见原因是标注框太松或太紧。cls_loss 震荡说明类别不平衡溺水样本远少于游泳样本时可以加cls0.8调高分类损失权重。mAP50 到 0.7 以上算可用到 0.85 以上算不错但溺水检测更看召回率漏报比误报代价大得多。3.3 推理与阈值调整把 recall 拉上来训练完拿验证集跑推理yolo detect predict \ modelruns/drown/baseline/weights/best.pt \ sourcedataset/images/val \ conf0.25 \ iou0.5 \ saveTrueconf0.25是置信度阈值默认值。溺水检测里我一般会降到 0.15宁可多报几个假阳性也别漏掉真溺水。iou0.5是 NMS 的 IoU 阈值多人重叠场景可以调到 0.6减少框被误删。推理结果重点看漏检把 false negative 的图挑出来看是标注漏了还是模型没学到。如果是标注漏了补标如果是模型没学到加同类样本或做 mosaic 增强。4. 溺水检测训练避坑5 个血泪踩坑记录4.1 水面反光被当成溺水目标现象模型在平静水面场景下频繁误报把阳光反射区域框成 drowning。 原因训练集里溺水样本多伴随水花模型学到了「亮色区域溺水」的伪特征。 解决在增强里加随机亮度扰动和对比度扰动同时补一批平静水面无目标的负样本让模型学会区分反光和人。4.2 小目标漏检严重现象远处泳池角落的溺水目标几乎全漏。 原因溺水目标在 640 输入下可能只有 20x20 像素YOLOv8 的 P3 特征图感受野不够。 解决把 imgsz 提到 1280或者在 data.yaml 里开启rectTrue保持长宽比避免 resize 后小目标进一步缩小。也可以换 yolov8s 或加 P2 检测头。4.3 类别不平衡导致 drowning 召回率低现象swimming 的 mAP 0.9drowning 只有 0.5。 原因正常游泳样本远多于溺水样本模型偏向多数类。 解决用 copy-paste 增强把溺水样本复制到不同背景或者训练时给 drowning 类更高损失权重。YOLOv8 不直接支持类权重可以通过过采样实现。4.4 验证集和训练集场景重叠现象验证集 mAP 很高实际部署一塌糊涂。 原因划分时随机分同一段视频的相邻帧同时进了训练和验证模型只是记住了场景。 解决按视频源或时间段划分确保验证集的泳池、光照、角度和训练集不重叠。这是最容易被忽略的坑也是模型上线翻车的主要原因。4.5 标注框把水面波纹框进去现象模型输出的框比实际人体大一圈IoU 低。 原因标注时把水花和波纹一起框了模型学到的是「大框」。 解决重新核对标注规范框只包人体可见部分。已经训完的模型可以用高 IoU 阈值筛一遍预测框反推标注问题。5. 把溺水检测做到可用的进阶技巧时序投票与误报压制单帧检测永远有误报溺水是一个持续过程不是某一帧的姿态。我一般会在检测后面加一层时序投票对同一摄像头连续 15 帧的检测结果做统计如果 drowning 类在超过 60% 的帧里出现才触发报警。这个逻辑用 Python 写起来很简单from collections import deque class DrownVoter: def __init__(self, window15, ratio0.6): self.window window self.ratio ratio self.buffer deque(maxlenwindow) def update(self, detections): # detections 是当前帧的类别列表如 [swimming, drowning] has_drown 1 if drowning in detections else 0 self.buffer.append(has_drown) if len(self.buffer) self.window: return False return sum(self.buffer) / self.window self.ratiowindow15对应大约 0.5 秒的视频30fpsratio0.6是触发比例。这两个参数按实际帧率和场景调整泳池人多时调高 ratio 减少误报人少时调低 ratio 提高灵敏度。这个投票器不依赖模型结构任何检测器输出都能接。另一个技巧是负样本挖掘。模型上线后把误报的图存下来人工确认后加入训练集重新训一轮。我习惯每两周做一次三轮之后误报率通常能降一半。溺水检测没有一劳永逸的模型只有持续迭代的流程。这套方案值不值得做取决于你的场景有没有真实报警需求如果有7000 张图起步是够的但别指望一次训练就上线。希望帮到你。本文还有配套的精品资源点击获取