简介面向跌倒检测任务的数据集资源适用于YOLOv5等主流目标检测框架的训练、验证与部署目标用户为算法工程师、科研人员以及有监督学习需求的开发者。压缩包大小约236.1MB文件总数约2000个主要包含5228张jpg图像和4572个xml标注文件采用VOC格式记录跌倒人员位置可快速转换为YOLOv5所需的txt标签文件自行完成数据划分、模型微调与推理验证。已有1545人学习下载数据集中每张图片对应一个xml标注覆盖室内外多种场景和不同角度下的跌倒姿态实例命名清晰便于筛选与扩展。使用时可结合图像增强、调整anchor和训练轮数来提升检测精度并参考VOC格式进行二次标注。相比从零采集标注该资源能显著节省人力与时间拿到后即可开展训练迭代适合快速搭建跌倒检测原型、验证算法效果。1. 4500 张已标注的「跌到检测」数据集先想清楚它到底能训出什么项目标题写的是「跌到检测数据集」行业规范里叫「跌倒检测」检索两个写法都有人用。做监控告警、独居老人看护这类场景的工程师拿到一份 4500 张左右、已经标注的数据集后往往想直接开训但我的经验是这个规模最尴尬比论文公共集大比工业数据小能不能用、效果如何全看标注格式、类别定义和里面的脏数据。见过太多人拿 yolov8 训练自己的数据集跑一半才发现类名对不上、验证集和训练集是同一段视频抽帧只能返工。下面按这个顺序展开先判格式再做体检、转格式、划划分、定规范最后验证。2. 先分清标注格式再动手VOC、COCO 与 YOLO txt 的差别和选型拿到「已经标注」的跌倒检测数据集第一件事不是打开图片看画质而是打开标注文件看格式。不同标注工具导出的东西差别很大用错解析脚本会让你在第一步就翻车。这里先说结论绝大多数跌倒检测数据集的标注文件逃不出 VOC XML、COCO JSON、YOLO txt 三种形态判断格式用命令 30 秒就能完成不需要装任何图形工具。格式文件组织坐标含义常用工具跌倒检测场景常见度VOC XML每张图一个 .xmlxmin, ymin, xmax, ymax 绝对像素labelimgPascalVOC 模式很常见COCO JSON整个数据集一个 .jsonbbox 为 x, y, w, h 绝对像素另含 area 等字段labelme、CVAT学术复现常见YOLO txt每张图一个 .txtcls cx cy w h归一化到 01labelimg、CVAT最常用yolov8 直接吃判断现有标注是哪一种直接在数据集根目录跑三条命令cd 数据集目录 find . -name *.xml | head -3 # 有输出 → VOC 格式 find . -name *.txt | head -3 # 有输出 → 可能是 YOLO txt ls *.json # 单文件且含 annotations 字段 → COCO 结构逻辑说明VOC 和 YOLO txt 都是「一张图对应一个标注文件」所以能用 find 快速定位COCO 则是「整个数据集打包成一个 json」所以必须用 ls 看单文件。这里最容易被坑的是 txtlabelme 的 JSON 转出来的 txt 和 YOLO 训练用的 txt 长得不一样前者可能还有逗号或非数字内容后者每行必须严格是「cls cx cy w h」五个数字判断时别看到 txt 就以为是 YOLO 格式随便打开一个看内容再下结论。2.1 标注内容不只是框跌倒检测的类别设计与关键点格式之外更要紧的是标注内容的结构。跌倒检测数据集的标注内容分两类一类是纯目标检测框每张图里是「person」和「fall」的矩形框另一类是带人体关键点的姿态标注每个框里还带左右肩、左右膝、脚踝等十几个点。4500 张图如果是纯框标注只能支撑检测方案想做 pose-based 的二次判断就得另外找带关键点的数据这两条技术路线的数据需求完全不同第一步就要想清楚。类别设计方面两类做法最常见。两分类「person fall」用跌倒框代表跌倒事件起步阶段最稳类别少意味着标注一致性容易保证三分类「normal fall lying」把倒地未起单独成类能区分「跌倒」和「倒地休息」但代价是标注员对 lying 和 fall 的边界容易产生分歧返工率高。我一般建议先训两分类等模型能稳定检出跌倒框之后再看误报集中在哪决定要不要拆类。另一个容易被忽略的细节是命名项目标题里写「跌到」写论文和项目文档时规范叫法是「跌倒」学术检索里只有「跌倒」能查到东西但搜数据集时两个词都得搜数据标注平台上的标题经常有错别字这是实际检索经验。2.2 4500 张在这个领域是什么水位4500 张已标注图片在跌倒检测这个细分领域算什么水平公开的跌倒检测数据集规模大多在几千到一万张之间4500 张属于偏小但能起步的量级。关键指标不是图片总数而是「跌倒类」的实例总数如果 4500 张图里跌倒框只有 300 个剩下的全是正常行走的人类别不平衡会让训练默认偏向样本多的类。一个健康的起步数据集跌倒类实例占比至少在 30% 以上低于这个数要么补标要么在训练时对跌倒类做重采样。不管标注是哪种格式数据集目录结构基本都是这个骨架dataset/ ├── annotations/ # 标注文件xml/json/txt 之一 ├── images/ # 原图jpg/png └── README.md # 类名表 标注规则先跑一次图片与标注的数量核对ls images | wc -l ls annotations | wc -l两个数字不一致直接说明有问题标注了但图片缺失或者图片比标注多都是数据集交付时常见的情况。前者训练到一半报「image not found」后者等于白跑算力。这一步做完格式、类别、规模都清楚了下一步给标注做体检。3. 用脚本给数据集做体检类别统计、异常框与小目标筛查「已经标注」不等于「标得对」。标注工具导出的数据里最常见的脏数据是类名不一致、坐标越界、空框、重复标注。这些问题训练时未必报错但会悄悄污染 loss 曲线让你花大量时间调参还以为是模型不行。我的做法是训练前先写一个体检脚本把数据集的问题全部暴露出来。3.1 体检脚本第一步按类统计实例数与图片数下面这个脚本只依赖 Python 标准库解析 VOC XML。如果标注是 COCO JSON 或 YOLO txt解析方式换成对应的 json 或逐行读 txt 即可统计逻辑完全一样。#!/usr/bin/env python3 # inspect_dataset.py - 体检 VOC 格式标注 import xml.etree.ElementTree as ET import glob, os, sys from collections import Counter ann_dir sys.argv[1] if len(sys.argv) 1 else annotations class_instances Counter() # 每个类别的实例数 class_images Counter() # 包含每个类别的图片数 total_images 0 for xml_path in sorted(glob.glob(os.path.join(ann_dir, *.xml))): total_images 1 seen set() root ET.parse(xml_path).getroot() for obj in root.findall(object): name obj.find(name).text.strip() class_instances[name] 1 if name not in seen: seen.add(name) class_images[name] 1 print(f图片总数: {total_images}) print(类别实例数:) for name, cnt in class_instances.most_common(): print(f {name}: {cnt} 个框 / 出现在 {class_images[name]} 张图)逻辑说明用 xml.etree.ElementTree 解析每个 XML对每个 object 节点取 name 字段统计实例数seen 集合保证同一张图中相同类别只计一次得到的是「包含该类的图片数」。这个统计能立刻看出三件事一是类名是否统一——如果同时出现 Fall 和 fall 两个名字说明标注过程中改过类名且没有全局替换后面转格式时会丢框二是跌倒类实例占比低不低三是某些图里是否只有背景没有标注这类图对检测训练没有贡献。参数说明ann_dir 是标注目录路径解析 COCO 时把 ET 循环换成 json.load 后遍历 annotations 数组统计逻辑不变。YOLO txt 更简单每行第一个数字就是类别 id统计起来更快但 id 没法直接看出类名还得靠 4.1 的类别映射表翻译。3.2 异常框筛查越界、空框与「矮宽异常」接下来检查每个框的几何合法性。跌倒检测的场景特殊性在这里开始体现站立的人框通常是瘦高的宽高比在 0.20.5 之间跌倒的人框通常是矮宽的宽高比能达到 1.54 甚至更高。如果标注导出的「fall」框全是瘦高的说明标注规范出了大问题。下面这段是核心检查函数需要嵌到 3.1 的循环里使用def check_boxes(root, img_w, img_h): problems [] for obj in root.findall(object): name obj.find(name).text.strip() b obj.find(bndbox) xmin float(b.find(xmin).text) ymin float(b.find(ymin).text) xmax float(b.find(xmax).text) ymax float(b.find(ymax).text) if xmin 0 or ymin 0 or xmax img_w or ymax img_h: problems.append(f{name} 越界: ({xmin:.0f},{ymin:.0f},{xmax:.0f},{ymax:.0f}) vs 图 {img_w}x{img_h}) if xmax xmin or ymax ymin: problems.append(f{name} 空框: xmaxxmin 或 ymaxymin) else: w, h xmax - xmin, ymax - ymin ratio w / h if ratio 5 or ratio 0.2: problems.append(f{name} 宽高比异常: {ratio:.2f}) return problems逻辑说明三个检查项对应三类典型错误。越界是硬错误坐标超出图片范围标注工具在图片缩放后导出时常出现必须修空框是导出时坐标调整产生的必须删宽高比阈值在三类里最需要人工判断——超过 5:1 或小于 1:5 的框几乎可以断定是手滑拉了个大框把背景也框进去了这类框会让模型学到「比人还宽五倍的跌倒者」验证指标偶尔异常而且极难定位到具体是哪张图出的问题。参数说明ratio 的上下界我默认 5 和 0.2俯视镜头下跌倒的人框宽高比会低一些可以放宽到 3 和 0.15但超过 5 的框在跌倒检测里没有合理场景基本可以放心标记为异常。3.3 小目标框分布先知道有多少框是模型看不清的跌倒检测落地时最大的漏检来源是小目标。监控画面里远角摄像头下的跌倒者往往只有三四十像素高缩放到 640 输入后只剩十几个像素模型根本分不清是蹲下还是摔倒。体检脚本还要统计框的面积分布import glob, os import xml.etree.ElementTree as ET areas [] ann_dir annotations for xml_path in glob.glob(os.path.join(ann_dir, *.xml)): root ET.parse(xml_path).getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) for obj in root.findall(object): name obj.find(name).text.strip() if name ! fall: continue b obj.find(bndbox) w float(b.find(xmax).text) - float(b.find(xmin).text) h float(b.find(ymax).text) - float(b.find(ymin).text) area_ratio (w * h) / (img_w * img_h) areas.append((area_ratio, os.path.basename(xml_path))) print(跌倒框面积占比分布:) for ratio in (0.001, 0.01, 0.05, 0.1): cnt sum(1 for a, _ in areas if a ratio) print(f 小于全图 {ratio:.1%}: {cnt} 个框)逻辑说明把每个跌倒框的面积除以整图面积得到面积占比再看有多少框小于 1%、5%、10% 这三个典型阈值。面积占比小于 1% 的框在 640 分辨率下大约只有 50×50 像素以内yolov8 默认配置下必然难检解决办法要么把训练输入分辨率提高到 1280要么把大图切块训练也就是 SAHI 的思路实在不行就放弃检测远角把报警区域约束在画面中近景。这一步拿到的数据决定你后面要不要为小目标付出额外工程成本。如果这类框只有十几个不值得专门做切图如果有几百个不做切图上线后漏检会很惨。这些数据是后面所有决策的依据比任何调参技巧都重要。4. 把标注转成 YOLO 训练格式转换脚本、划分策略与可视化复核体检通过后进入训练前最后一道工程把标注统一成 YOLO txt并且保证 train/val 划分不泄漏。yolov8 训练自己的数据集时yaml 里指定的 names 顺序必须和 txt 里的类别 id 完全一致这是最容易埋雷的环节错一个数字全部框都错位。4.1 VOC 转 YOLO一个能直接跑的转换脚本假设现有标注是 labelimg 导出的 VOC XML下面这个脚本把整个 Annotations 目录转成 YOLO labels 目录。#!/usr/bin/env python3 # voc2yolo.py - 将 VOC XML 转为 YOLO txt import xml.etree.ElementTree as ET import glob, os, sys # 类名顺序就是类别 id训练 yaml 的 names 必须和这里完全一致 class_names [person, fall] class_map {name: i for i, name in enumerate(class_names)} ann_dir sys.argv[1] if len(sys.argv) 1 else annotations out_dir sys.argv[2] if len(sys.argv) 2 else labels os.makedirs(out_dir, exist_okTrue) for xml_path in sorted(glob.glob(os.path.join(ann_dir, *.xml))): root ET.parse(xml_path).getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_map: print(f[跳过] {xml_path} 里的未知类别: {name}) continue b obj.find(bndbox) xmin float(b.find(xmin).text) ymin float(b.find(ymin).text) xmax float(b.find(xmax).text) ymax float(b.find(ymax).text) w, h xmax - xmin, ymax - ymin if w 0 or h 0: continue xc, yc xmin w / 2, ymin h / 2 lines.append(f{class_map[name]} {xc/img_w:.6f} {yc/img_h:.6f} {w/img_w:.6f} {h/img_h:.6f}) stem os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, stem .txt), w) as f: f.write(\n.join(lines))逻辑说明VOC 的 bndbox 是绝对的左上右下四值YOLO 要的是归一化后的中心 x、中心 y、宽度 w、高度 h这是两种格式最本质的差异。转换公式三步走先求宽高再求中心点最后全部除以图片宽高。脚本里对未知类别打日志而不是静默丢弃是为了让你在转换后去核对如果日志里出现了 fall_person 这种类名说明标注阶段用过别的命名先统一类名再转换否则这个框会直接消失。参数说明class_names 列表是类别 id 的「合同」顺序不能随便排之后训练 yaml 里写 names: [person, fall]顺序一错所有框全乱。labelme 标注的 JSON 想用于 yolov8路径也一样把 JSON 解析出来先转成 VOC 或 COCO 再走这个脚本不要试图直接改后缀名硬塞给模型。4.2 按视频或场景划分 train/val别让验证集虚高划分数据集是 4500 张跌倒检测数据里最容易被低估的一步。如果这些图是从视频里抽帧得到的相邻帧之间高度相似随机划分 train/val 等于让模型「开卷考试」验证集里全是训练集同段视频的下一帧val mAP 虚高到 0.8 以上一上线立刻打回原形。我一般按视频或场景维度划分。#!/usr/bin/env python3 # split_by_group.py - 按文件名前缀分组后划分 train/val import glob, os, random from collections import defaultdict files sorted(glob.glob(images/*.jpg)) groups defaultdict(list) for f in files: stem os.path.splitext(os.path.basename(f))[0] # 假设命名形如 cam01_000123.jpg前缀是 cam01没有下划线就退回用完整文件名 prefix stem.rsplit(_, 1)[0] if _ in stem else stem groups[prefix].append(os.path.abspath(f)) all_prefixes list(groups.keys()) random.seed(42) random.shuffle(all_prefixes) val_prefixes set(all_prefixes[: max(1, round(len(all_prefixes) * 0.15))]) with open(train.txt, w) as ft, open(val.txt, w) as fv: for prefix, paths in groups.items(): out fv if prefix in val_prefixes else ft for p in paths: out.write(p \n)逻辑说明先按文件名前缀把图分成组同一摄像头或同一段视频的帧只进一个集合从根上杜绝相邻帧泄漏。随机种子固定为 42保证每次复现同一份划分这对之后对比不同训练配置非常关键否则你没法判断指标变化是模型改的还是数据划分改的。参数说明val 比例 0.15 是默认值如果数据来自 5 个摄像头最好保证每个摄像头的帧都按比例进 val否则会出现训练集见过某个机位、验证集没见过的「假泛化」。文件里写的是绝对路径换机器训练前记得改成相对路径或重新生成。提示如果文件名没有清晰前缀无法按视频分组就用摄像头编号或采集时间段重新命名后再划分不要拿着一张张孤立的帧硬分。4.3 转完别急着训先画框复核转换脚本跑完后我强烈建议先画框看一眼再进训练。这个步骤十分钟能省掉后面好几轮「loss 不降、mAP 不动」的排错时间。#!/usr/bin/env python3 # draw_boxes.py - 把 YOLO txt 画回原图输出预览图 import cv2, os, glob label_dir labels img_dir images out_dir preview os.makedirs(out_dir, exist_okTrue) class_names [person, fall] colors [(0, 255, 0), (0, 0, 255)] # person 绿色fall 红色 for txt_path in sorted(glob.glob(os.path.join(label_dir, *.txt)))[:50]: stem os.path.splitext(os.path.basename(txt_path))[0] img cv2.imread(os.path.join(img_dir, stem .jpg)) if img is None: continue h, w img.shape[:2] for line in open(txt_path): parts line.strip().split() if len(parts) ! 5: continue cls_id, xc, yc, bw, bh int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), colors[cls_id], 2) cv2.putText(img, class_names[cls_id], (x1, max(0, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, colors[cls_id], 1) cv2.imwrite(os.path.join(out_dir, stem _preview.jpg), img)逻辑说明把归一化坐标换算回像素坐标后直接画框。重点看两类图一类是跌倒框的宽高比是否符合想象一类是同时出现多个框的图框之间是否明显不合理。预览目录里随机抽不同摄像头的图看不要只看前 50 张——前 50 张往往来自同一段视频看不出分布问题。复核通过之后才轮到训练。如果你用的是 CVAT 这类在线标注工具也可以把转好的 YOLO 标注重新导入 CVAT 做人工抽检但脚本画框更快不需要来回导数据。5. 跌倒检测标注避坑5 个让我返工过的坑与处置这些坑我基本都踩过一遍有些是把标注员叫回来重新标有些是自己写脚本批量修。按现象、原因、解决的顺序写方便对照排查。5.1 类名不统一Fall、fall、fall_person 混在一份标注里现象训练不报错但 loss 曲线震荡明显同一个目标被模型输出多个框验证 mAP 上不去怎么调学习率都没用。原因标注过程中多人协作有人用大写有人加了 person 后缀类名在工具里被视为完全不同的类别等于模型同时学三个长得一样的类。解决3.1 的 Counter 一眼就能看到全量类名把不一致的类名统一成小写英文单数写脚本批量替换 XML 或 txt 里的类名再重建类别映射表。类名这事没有捷径定好标准写进 README后续补标直接照抄不要每次新加数据都重新发明一遍命名。5.2 过渡帧到底算不算跌倒没定规范一整批返工现象模型把「正在下蹲」的人误检成跌倒把「已经倒地起身一半」的人漏检。原因标注规范里没有定义「跌倒」的起点和终点同一个动作不同标注员理解不同有的从身体倾斜 30 度就开始标有的等完全倒地才标。解决我后来用一条硬规则——躯干与地面夹角小于 45 度且该状态持续 3 帧以上才算跌倒倒地后只要还没完全站直框仍标 fall。把这条规则写进 README模型的目标就清晰了val 指标也会跟着稳定。这类规范问题靠脚本修不了只能回炉重标所以越早定规则越省事。5.3 遮挡场景的框怎么画可见部分还是全身现象多人遮挡场景下模型把两个紧挨的人并成一个框或者把被遮挡的人完全忽略。原因标注员有的画可见部分有的按记忆补全身模型学到两种矛盾的目标定义输出自然不稳定。解决规定「被遮挡超过一半才允许跳过该目标否则标可见部分」。跌倒在桌后只有头脸露出时至少要标一个头部框宁可小也不要跳过——小框训练时会被增强但漏标会让模型学会忽略这个位置下次真的有人在那个位置跌倒也不会报警。这个规则也要写进 README并且抽检时专门看遮挡多的帧。5.4 小目标框扎堆在远角验证集漏检一多半现象val mAP 看着正常逐帧检查发现漏检全集中在画面远端角落。原因远角的人只有三四十像素高下采样后特征几乎消失如果 3.3 体检显示面积占比小于 1% 的框扎堆这就是漏检的预警信号。解决按严重程度三选一——把训练输入分辨率从 640 提到 1280对远角区域做切图训练SAHI 思路或者直接缩小检测区域把远角排除在报警区外。选哪种取决于部署硬件边缘盒子跑不动 1280 输入切图又增加推理次数有时限制报警区域才是性价比最高的方案。前提是先用体检数据知道小目标占比别靠肉眼猜。5.5 视频抽帧的「假兄弟」训练集和验证集长得一样现象训练时 val mAP 飙到 0.85一上真实场景直接掉到 0.4 以下。原因随机划分把同一段视频的相邻帧分到两个集合模型相当于把帧序号背下来了验证时遇见的全是训练集里同场景的下一帧属于典型的划分泄漏。解决回到 4.2 的按视频前缀分组划分val 里只放模型没见过的摄像机位或时间段。一个土办法判断是否泄漏拿 val 里的一张图到训练集里找同场景兄弟帧如果对应位置的像素差很小就是漏了。注意第 4.2 的脚本先写进所有新数据集的预处理流程里不要等训练完了才发现 val 虚高那个阶段返工成本最高。6. 用一轮快速训练验证数据集mAP 走势与失败样本回看数据集的最终验证不能靠肉眼抽查要跑一轮小模型快速训练。做法是用 yolov8n 配 640 输入、batch 16训 30 个 epoch观察 val mAP0.5 的曲线形态。6.1 看 mAP 走势判断数据是否可用如果 30 epoch 内 mAP0.5 能爬到 0.6 以上说明类别定义和标注一致性基本达标这个数据集值得继续投入如果低于 0.4先不要盲目加 epoch 或换大模型回头做三件事复查类名统计、复查异常框、复查过渡帧标注是否统一。4500 张跌倒检测数据类别平衡的前提下yolov8n 30 epoch 达不到 0.6问题大概率在标注而不在模型。6.2 把置信度阈值拉低回看失败样本第二招是拿训练好的模型去跑 val 集把置信度阈值降到 0.25把 0.30.7 之间的预测结果全部保存成图。这批「不确定样本」是数据集质量最真实的镜子有的框标漏了模型其实检出来了只是标注里没有有的框标错了模型把站立的人当跌倒有的纯属漏检。人工过一遍把值得补的样本挑出来这才是 4500 张数据继续滚大的正确方向。踩了这么多次坑之后我现在每拿到一批新数据第一件事永远是让旧模型跑一遍把置信度中段的样本挑出来人工看再决定是补标还是修标注。这个习惯帮我省掉大量返工也让我对数据集的真实水平心里有数。希望帮到你。本文还有配套的精品资源点击获取