吸烟与跌倒行为检测实战:基于YOLO格式数据集从训练到部署
发布时间:2026/9/1 10:25:06来源:尧图网络
简介这套吸烟与跌倒行为目标检测数据集专为YOLO系列目标检测模型设计面向有安全监控或行为识别需求的开发者与算法学习者可直接用于训练、验证吸烟和跌倒两类常见异常行为。资源包共2000个文件主体为1999个txt格式的标注文件配套1个yaml配置文件图片与标签按trainset、testset分类存放images与labels目录对应清晰压缩包整体约98.47MB便于快速解压并接入训练流程。目前已有413人学习查看适合作为入门行为检测或扩充训练数据的实用选择。数据集的样本标签覆盖跌倒与吸烟场景可直接用于YOLOv5、YOLOv7、YOLOv8等框架的数据加载yaml文件简化了类别与路径配置帮助读者省去繁琐的格式转换更快完成模型训练与效果验证。1. 场景先行先搞清楚这两类检测难在哪在正式碰数据集之前我得先说你一句别一看是“吸烟、跌倒行为目标检测数据集YOLO格式”就觉得把图喂进去、把模型训出来就完事了。真做过这两个任务的人都知道它们和普通的目标检测比如检测行人、检测车辆完全是两码事。先看吸烟检测。吸烟的目标不是“烟”这个东西而是“人手烟”的组合状态。烟本身很小最细的烟支也就几个像素宽而且形态多变——有人叼着有人夹着有人正在点烟火光一闪而过。检测模型真正要学的是“手在嘴附近、手里有细长物体”这一抽象组合。如果你只是去标“烟”这个框模型大概率会学歪训完在监控画面上一测试手指头、笔杆子全给你框出来。再看跌倒检测。跌倒不是“一个静态物体”而是一个“人从直立到躺平的瞬间过程”。单帧画面里人躺在地上和正常睡觉、趴着休息有什么区别几乎没有。所以做跌倒检测业内主流做法从来不是靠单帧硬扛而是“目标检测姿态/位置时域分析”的组合拳。先用检测模型输出人的位置框再根据框的宽高比、中心点位移速度、人的朝向变化去判断是不是真的摔了。这就是这份数据集真正值钱的地方它不是一个普通的分类标注包而是围绕这两个高风险动作专门设计的行为检测数据。你拿它训练出来的模型不是为了看懂“画面里有什么”而是为了看懂“画面里的人正在发生什么”。应用的场景也非常清晰工厂/建筑工地吸烟属于明火违规行为跌倒属于安全生产事故养老院/独居老人住所跌倒检测是刚需中的刚需学校/宿舍楼道吸烟行为管控防火灾于未然卫生间/浴室等高隐私场景只出不进用雷达或热成像替代不在本数据集讨论范围。下面我会从数据结构、标签格式、训练配置到推理部署一条线说清楚所有内容都围绕这份YOLO格式数据集展开并且会把我在实际项目里踩过的坑一并交代。2. 核心细节解析YOLO格式数据集的真实构造这份数据集既然叫“YOLO格式”那么它的核心就是标注文件的格式。很多新手在这里栽过跟头我先讲明白最底层的规则再带你看这套数据集的具体目录结构。2.1 标签格式一行一个目标五个数字别搞错YOLO格式的标注文件是纯文本文件名和图片名一一对应后缀从.jpg变成.txt。每一行代表一个目标框格式是类别ID 中心点x 中心点y 框宽 框高注意这里所有的坐标值都是归一化的而不是像素坐标。归一化公式是x 目标框中心点的像素x坐标 / 图片宽度y 目标框中心点的像素y坐标 / 图片高度w 目标框的像素宽度 / 图片宽度h 目标框的像素高度 / 图片高度我见过不少人直接把LabelImg导出的VOC格式左上角x、左上角y、右下角x、右下角y硬塞给YOLO训练结果损失函数一路乱跳根本收敛不了。如果你手里的标注是VOC格式需要先转成YOLO格式。下面我贴一个转换脚本的模板实测可直接用import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in class_names: continue cls_id class_names.index(cls) bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) center_x ((x1 x2) / 2) / img_w center_y ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {center_x:.6f} {center_y:.6f} {w:.6f} {h:.6f}) out_path os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) with open(out_path, w) as f: f.write(\n.join(lines)) # 使用示例 class_names [smoking, falling] for xml_file in os.listdir(annotations_xml): voc_to_yoto(os.path.join(annotations_xml, xml_file), annotations_yolo, class_names)提示转换完成后一定要随机挑几张图把生成的txt标注画回图片上看一遍。坐标算错是最常见的低级错误画框验证能直接发现问题。2.2 目录结构标准YOLO训练集的组织方式这套数据集的目录结构是标准的YOLO组织方式打开后你会看到dataset/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片可选 ├── labels/ │ ├── train/ # 训练集标注txt │ ├── val/ # 验证集标注txt │ └── test/ # 测试集标注txt ├── data.yaml # 数据集配置文件 ├── train.txt # 训练集图片路径列表部分教程用 └── val.txt # 验证集图片路径列表这个组织结构是Ultralytics YOLOv5/v8系列直接支持的。只要你提供data.yaml训练器会自动按images和labels目录下的train/val子文件夹去配对加载。data.yaml的内容是这样的train: dataset/images/train val: dataset/images/val # test: dataset/images/test nc: 2 names: [smoking, falling]nc是类别数量names是类别名列表顺序必须和标注文件里的类别ID一一对应。这个文件看似简单但特别容易出幺蛾子——最常见的错误就是train路径写错导致训练时加载不到任何图片程序报“Found 0 images in train folder”之类的错误。强烈建议你把路径写成绝对路径或者相对项目根目录的路径别用~这种缩写。3. 实操过程与核心环节实现数据集拿到手、格式确认无误之后接下来就是要让模型真正跑起来。这一节我按照完整的流程来走一遍包含环境准备、数据检查、训练配置和结果验证。全程使用YOLOv8作为基准因为它的API设计最友好对新手和老手都适用。3.1 环境准备与数据校验训练YOLO模型离不开PyTorch。建议你直接用conda建一个干净的环境避免把系统Python环境搞乱conda create -n yolo python3.10 conda activate yolo pip install ultralytics torch torchvision装ultralytics包的时候它会自动把YOLOv8的CLI和Python API都带进来。装完后先跑一句yolo predict modelyolo11n.pt sourcehttps://ultralytics.com/images/bus.jpg如果这句能正常输出检测结果说明环境基本没问题。这一步特别值得做因为很多训练报错根本不是数据和代码的问题而是环境里缺了某个依赖提前验证能省很多时间。在正式训练之前必须先做一次全量数据校验。我写了一个快速脚本检查所有标注文件是否存在、格式是否合法、坐标是否越界import os def validate_labels(label_dir, img_dir): label_files os.listdir(label_dir) img_exts {.jpg, .jpeg, .png, .bmp} issue_count 0 for lf in label_files: if not lf.endswith(.txt): continue base os.path.splitext(lf)[0] img_path None for ext in img_exts: candidate os.path.join(img_dir, base ext) if os.path.exists(candidate): img_path candidate break if img_path is None: print(f警告: 标注 {lf} 对应的图片不存在) issue_count 1 continue with open(os.path.join(label_dir, lf), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f错误: {lf} 某行格式错误: {line.strip()}) issue_count 1 continue cls_id, x, y, w, h parts if not (0 float(x) 1 and 0 float(y) 1): print(f错误: {lf} 中心点坐标越界: {line.strip()}) issue_count 1 if float(w) 0 or float(h) 0: print(f错误: {lf} 框宽高为负: {line.strip()}) issue_count 1 print(f校验完成共发现问题 {issue_count} 个) validate_labels(dataset/labels/train, dataset/images/train)这个脚本看起来不起眼但真的能救命。我试过一份第三方数据集里面有一张图片的标注框x坐标写成了1.7显然超出边界。如果不检查直接训练轻则该样本的梯度异常重则影响整个收敛过程。花5分钟跑一遍校验是训练前最值的投入。3.2 训练配置与参数选择环境没问题、数据也校验过了下一步就是写训练命令。YOLOv8的好处是默认参数已经比较合理新手用小数据集可以直接用默认配置跑但针对吸烟和跌倒这两个任务有几个参数我建议你手动调整。先看我的训练命令yolo train \ modelyolo11m.pt \ datadataset/data.yaml \ epochs150 \ batch16 \ imgsz640 \ patience30 \ projectruns/detect \ namesmoke_fall_exp1 \ lr00.005 \ cos_lrTrue \ workers4 \ device0逐项解释一下我为什么这么配modelyolo11m.pt我选了m版本不是s也不是n。吸烟和跌倒的检测目标都属于中小目标n模型轻量但特征提取能力弱m模型在精度和速度之间比较平衡。如果你的算力很紧张可以降级到s。epochs150吸烟检测的难点在于“烟支小手部动作多样”模型需要更多epoch才能学到微妙的特征。150轮在大多数场景下够用配合早停也不会过拟合。imgsz640这个分辨率是默认值。如果你的监控画面是1080p甚至更高建议训练和推理都保留在640。分辨率不是越高越好高分辨率会显著增加推理延迟而YOLO模型本来就是设计在640分辨率下工作的。lr00.005和cos_lrTrue这是一个组合拳。初始学习率设低一点同时用余弦退火策略训练后期学习率平滑下降比固定学习率更容易收敛到更好的局部最优解。训练启动后你会看到终端里滚动输出每轮的mAP50、mAP50-95、precision、recall这些指标。这里我想多说一句不要只看mAP50mAP50-95更关键。mAP50只要求预测框和真实框的IoU超过0.5就算你预测对这个标准在跌倒检测这种“框动得很快”的场景下偏宽松。mAP50-95衡量的是IoU从0.5到0.95的均值能更真实反映框的定位精度——而这正是跌倒检测时判断“人处于什么姿势”的关键。3.3 训练完成后的评估与导出训练结束后项目目录的runs/detect/smoke_fall_exp1/weights/下会生成best.pt和last.pt两个权重文件。best.pt是验证集上表现最好的模型导出和部署都用它。先用验证集看看模型表现yolo predict modelruns/detect/smoke_fall_exp1/weights/best.pt \ sourcedataset/images/test \ save_txtTrue \ save_confTrue \ conf0.4在test目录上跑完建议你挨个翻一遍保存的预测图片重点观察三类现象吸烟场景里模型是否把“手拿笔”“手拿筷子”“手指放到嘴边”误判为吸烟。这种误判很常见因为烟支太细模型很容易学习到“手接近嘴”这个最粗粒度的特征。跌倒场景里模型是否漏检了“正在下坠过程中”的人。注意很多人跌倒时的动作幅度大、姿态奇怪训练集如果没有覆盖足够多的跌倒中间状态模型很可能会认为“这个姿势不像人”直接漏检。是否有大量的重叠框叠加在一个目标上。如果出现说明NMS后处理参数可能不合适需要调整。确认模型效果可以接受后导出成ONNX格式方便部署yolo export modelruns/detect/smoke_fall_exp1/weights/best.pt formatonnx dynamicFalse imgsz640导出的ONNX文件可以直接用来做C推理也可以放到OpenVINO、TensorRT这类加速框架里优化。如果你后续要部署到边缘设备比如RK3588ONNX是中间必需的格式。4. 部署细节从训练机到实际场景的最后一公里模型训好了只是一个开始。真正决定项目成败的是部署环节——你训练的时候用的是GPU跑得飞快但到了实际场景可能在CPU上推理或者在边缘盒子上推理要保证速度的同时还得保证检测效果。4.1 端侧推理C与ONNX Runtime的快速实现训练是Python生态的天下但部署到工业设备的时候C才是主力。这里我给出一个基于ONNX Runtime的C推理核心代码片段把YOLO模型落地到端侧的关键逻辑都包含在注释里了#include onnxruntime_cxx_api.h #include opencv2/opencv.hpp #include vector struct Detection { cv::Rect box; float score; int class_id; }; // 预处理YOLO要求 letterbox 缩放保持宽高比并填充灰边 cv::Mat letterbox(cv::Mat src, int target_size 640) { int w src.cols, h src.rows; float scale std::min(1.0f * target_size / w, 1.0f * target_size / h); int nw static_castint(w * scale); int nh static_castint(h * scale); cv::Mat resized; cv::resize(src, resized, cv::Size(nw, nh)); cv::Mat canvas cv::Mat::zeros(cv::Size(target_size, target_size), CV_8UC3); canvas.setTo(cv::Scalar(114, 114, 114)); resized.copyTo(canvas(cv::Rect((target_size - nw) / 2, (target_size - nh) / 2, nw, nh))); return canvas; } // 核心推理函数 void infer(cv::Mat frame, Ort::Session session, float conf_thres 0.4, float iou_thres 0.45) { // 1. Letterbox 预处理 cv::Mat input_img letterbox(frame); cv::Mat rgb; cv::cvtColor(input_img, rgb, cv::COLOR_BGR2RGB); // 2. HWC 转 CHW归一化到0-1 // 3. 创建输入tensor维度 [1, 3, 640, 640] // 4. 运行session.Run(...) 得到输出 // 5. 输出解析YOLOv8 输出为 [1, 84, 8400] // 84 4个框坐标 80个类别概率可裁剪为 [1, 4num_classes, 8400] // 6. 解码坐标乘以缩放比例并减去letterbox偏移 // 7. 按class-wise做 NMS过滤重复框 }这里最核心的注意点是letterbox。如果你直接对整张图做resize会破坏目标的宽高比导致框的位置偏移尤其是在跌倒检测这种对框位置精度要求高的场景里误差会被放大。所以业界统一用letterbox先把图等比例缩放到长边为640然后在短边两侧用灰色像素填充到640x640。推理之后要把输出框的坐标减去填充区域的偏移再除以缩放比例才能映射回原图坐标。4.2 行为判定跌倒检测不能只看单帧如果你把训练好的模型直接接上摄像头对着画面一帧一帧做检测然后“检测到人躺在地上”就报警那误报率会高到让人崩溃。因为有人蹲下系鞋带、有人睡觉翻身、有人做拉伸单帧看起来都像跌倒。实际项目中跌倒检测的正确打开方式是“目标检测帧序列判定”。用你的YOLO模型先持续输出人的检测框然后维护一个“人的姿态随时间的演变序列”根据以下指标做决策框的宽高比变化率站立时人的框宽高比大约在0.3~0.5倒地后变成0.8~2.0。宽高比突变且持续保持在低位超过N帧是一个强信号。框中心点垂直速度人在跌倒时头部中心点会在很短时间内通常0.3~0.5秒快速下移垂直速度远超正常弯腰动作。帧间框重叠率人在站立时相邻帧的框重叠率很高跌倒过程中框的位置移动极快重叠率会急剧下降。一个简单的判定逻辑伪代码如下# 维护一个队列保存最近30帧的检测结果 # fall_threshold: 连续4帧以上满足以下条件即触发 if frame_idx 3: prev_box q[-2] curr_box q[-1] h_over_w_ratio curr_box.h / curr_box.w # 条件1: 宽高比大于0.9说明接近躺平 # 条件2: 中心点y坐标在短时间内下移超过身高的30% # 条件3: 最近4帧中至少3帧检测到人 if h_over_w_ratio 0.9 and center_y_drop height * 0.3: fall_count 1 else: fall_count 0 if fall_count 4: trigger_alarm()这个思路不是我拍脑袋想的是实际项目里验证过的方案。只做单帧检测的跌倒识别误报率居高不下加上时间维度之后误报率能下降一个数量级。这也解释了为什么这份数据集是“行为目标检测”而不是“单纯的目标检测”——行为检测天然需要时序信息的配合。5. 常见问题与排查技巧实录这一节我把自己在训练这类数据集时遇到过的、以及身边同行踩过的典型问题整理出来按“现象-原因-解决”的逻辑写方便你当速查手册用。5.1 模型把笔、手指、棒棒糖误判为吸烟这是吸烟检测最经典的误报。原因显而易见烟支的目标太小网络能提取的纹理信息有限训练时如果正样本不足模型只能退而求其次学习“手靠近嘴”这个最粗糙的判别特征。解决方案分两层。数据层面增加“手拿笔写字的监控画面”“手指夹着其他细长物体的画面”作为负样本并在训练配置里显式加入负样本图片即没有任何标注目标的图片。模型层面提高输入分辨率到960甚至1280给小目标更多像素同时考虑在训练时使用mosaic增强让模型看到更多“手小物体”的组合。5.2 跌倒检测漏检“正在倒地的人”训练数据里如果只包含“已经躺在地上”的样本模型对这个动作过程的中间状态一无所知。跌倒过程的姿态变化极大从直立到弯曲到倒地每一帧的样子都不同一个只有静态倒地样本的模型遇到“正在倒下”的画面时检测置信度会很低。解决思路是补数据。我建议你去网上专门找“摔倒瞬间”的视频比如体育比赛中的摔倒合集、老年人踉跄的监控片段然后逐帧提取做标注。这个操作确实耗时但对跌倒检测效果的提升是决定性的。如果实在没有人工标注的精力也可以先用现有模型做伪标注再人工审核修正能省一半时间。5.3 训练集mAP指标很高但部署到实际监控上效果很差这是典型的“过拟合训练集分布”现象。训练集里的画面可能都来自固定的监控机位、固定的角度和光线而真实场景的机位、高度、角度、光线完全不同模型见过的新鲜画面太少泛化能力自然差。业界叫这个“数据集漂移”。我的建议是一套用于行为检测的数据集必须包含多机位、多时间段白天/夜晚/逆光、多距离近景/中景/远景的画面否则模型出了训练环境就废。如果你手里的数据集没有这类多样性拿到后第一件事不是训练而是自己补拍一批真实场景画面混合进去一起训练。5.4 导出ONNX后C推理结果与Python不一致这个坑我踩了好多次最后定位到根因训练时开启的某些预处理和后处理与导出ONNX时默认集成的逻辑不一致。YOLOv8导出ONNX时会自动嵌入一部分预处理归一化和后处理如NMS但细节和你在Python里直接调用模型时用的不完全一样。所以导出一个干净的推理模型是关键。常见做法是导出时不带NMSyolo export modelbest.pt formatonnx dynamicFalse imgsz640 simplifyTrue然后在C端手动实现NMS虽然多写几十行代码但逻辑完全可控。另一个常见坑是输入输出的数据布局搞错——ONNX Runtime要求的输入是CHW格式而OpenCV默认是HWC。不转换直接塞进去推理结果必然错乱。检查的时候先打印一下输入输出的shape和内存数据通常一眼就能看出问题。下表是这几个问题的速查总结问题现象主要原因快速对策手指、笔被误判为吸烟烟支目标太小模型学到的是“手靠近嘴”的粗粒度特征补充负样本提高输入分辨率倒地中的人漏检训练集缺乏跌倒过渡帧补拍/截取视频帧标注跌倒中间状态训练集指标好现场效果差数据集场景单一泛化不足混合多机位、多时段的真实场景数据再训练ONNX推理结果与Python不一致预处理/后处理逻辑不一致或HWC与CHW布局错误导出时simplifyTrueC端手动实现NMS6. 实操心得与扩展建议这套“吸烟、跌倒行为目标检测数据集YOLO格式”的完整流程走到这里模型能跑起来、能部署、能报警一个可用的原型系统已经成型。不过我在实际项目中还有一个体会想多说几句行为检测类项目永远不要把全部赌注押在模型上。摄像头安装的角度、高度现场的光线环境报警后的响应机制这些“非模型因素”对最终效果的影响往往比模型本身还大。模型再准如果摄像头装在逆光位置晚上全是噪点什么算法都白搭。所以做这类项目现场勘察和工程部署的精力投入至少要和调参的精力持平。最后再说一个针对这份数据集的扩展方向。如果你想把系统做深可以考虑在YOLO检测的基础上叠加一个关键点提取或轨迹跟踪的模块。检测完每个人用ByteTrack之类的跟踪算法把同一个人的跨帧检测框串起来这样跌倒判定就有一个连续、可靠的“人-帧”对应关系了。没有跟踪你的报警逻辑就难以判断是“同一个人躺了5秒”还是“五个人接连倒地”这在安防和医疗场景里是大忌。这个项目做完之后我自己最大的感受是数据集的质量决定了模型的实际表现上限。YOLO的训练代码是固定的损失函数是固定的唯一能改的就是你的数据。多花时间清理数据、补充分界场景、校验标注质量比多试几种训练参数带来的收益大得多。这份数据集给你打了个不错的基础接下来能把这口井挖多深就看你的数据工程能力了。本文还有配套的精品资源点击获取
网站建设高端定制企业官网