尧图精选

矿井安全帽检测:COCO JSON数据集解析与YOLO训练实战

🕒 发布时间:2026/10/2 3:21:10 📁 来源:尧图网络
简介面向煤矿井下作业环境的目标检测数据集采用COCO标准标注格式覆盖安全帽、指示器、人员、自救器四类关键目标模型识别率可达96.3%适用于智慧矿山安全监控、井下违规行为识别、人员防护装备检测等场景。资源共2000个文件包含1997张JPG原始图像与3个JSON标注文件整体压缩包约294.93MB解压后可直接用于模型训练与验证。图片均取自真实井下监控视角场景涵盖不同光照、角度与遮挡情况命名含唯一标识便于进行训练集与测试集划分JSON文件严格遵循COCO数据格式对应每张图的类别、边界框等标注信息可无缝接入YOLO、Faster R-CNN、MMDetection等主流检测框架大幅节省数据准备时间。目前已有492人浏览学习适合目标检测初学者、算法工程师及煤矿安全研究人员用于算法验证、模型调优与工程落地。1. 矿井下安全帽检测为什么偏偏用 COCO JSON 做数据集煤矿井下的视觉识别和地面安防完全是两码事。井下光照昏暗、粉尘附着镜头、设备反光、人员穿戴复杂安全帽、自救器这类小目标经常被遮挡。很多团队一开始拿公开的 COCO 数据集训练检测精度看起来不错一到井下测试就翻车。原因很简单公开数据集里的“人”和“安全帽”来自地面场景背景、光照、遮挡分布和矿井差太远。带标注的矿井下数据集就是把模型拉回真实场景的“后悔药”。这个数据集以 COCO JSON 格式存储标注信息包含安全帽、指示器通常是信号灯或标识牌、人、自救器四类目标官方给出的识别率 96.3% 是在特定测试集上的 mAP 或准确率。对于做矿山智能化、人员安全管控、合规检查的团队来说这是一个可以直接拿来训练 YOLO、Faster R-CNN 等检测模型的数据基础。下文会把这个数据集的格式、标注结构、训练复现步骤、参数调优和踩坑点完整拆开让你拿到手就能用而不是只看到一个 JSON 文件发呆。需要先说清楚COCO JSON 不是一种文件格式而是一种标注组织方式。它定义了 images、annotations、categories 三个核心字段检测任务只需要这几块。后面所有操作都围绕这三个字段展开。理解了它你就能把任何标注工具导出的结果转换成 COCO JSON也能把 COCO JSON 转成 YOLO 训练需要的 txt 格式。这才是这个数据集真正的价值。2. 拆解 COCO JSON 标注结构四类目标如何编码2.1 COCO JSON 的核心字段与矿井场景的特殊约定COCO 格式的标注文件是一个 JSON 对象顶层包含 info、images、annotations、categories 四个主要字段。对于目标检测任务最关键是 images 和 annotations。下面是一份典型的矿井数据集标注文件骨架{ images: [ { id: 1, file_name: mine_001.jpg, width: 1920, height: 1080 } ], annotations: [ { id: 1, image_id: 1, category_id: 1, bbox: [x, y, w, h], area: w*h, iscrowd: 0 } ], categories: [ {id: 1, name: helmet}, {id: 2, name: indicator}, {id: 3, name: person}, {id: 4, name: self-rescuer} ] }images 数组里每条记录对应一张原始图片必须包含唯一的 id、文件名、宽和高。矿井数据集的图片通常来自井下监控摄像头分辨率可能是 1280x720 或 1920x1080。annotations 数组是核心每个目标用一个字典描述image_id 关联到图片category_id 对应类别bbox 是 [左上角x, 左上角y, 宽度, 高度] 的列表area 是 bbox 面积iscrowd 为 0 表示这是独立实例。categories 数组定义了模型要学的类别。在这个数据集里四类目标的顺序可能不同但常见约定是0 安全帽、1 指示器、2 人、3 自救器。训练前务必检查 category_id 映射很多坑就出在这里。另外注意矿井场景里“指示器”可能指巷道内的信号灯、设备指示灯或标识牌不同数据集定义可能不一致。拿到文件后先统计类别分布不要凭猜测训练。2.2 用 Python 快速验证标注是否合法很多人在训练前根本不检查 JSON直接丢给训练脚本结果 Loss 异常或 mAP 为 0。正确的第一步是用脚本读一遍 JSON检查字段完整性和坐标是否越界。下面是我常用的检查脚本import json with open(annotations/instances_mine.json, r) as f: data json.load(f) print(images:, len(data[images])) print(annotations:, len(data[annotations])) print(categories:, data[categories]) # 检查 bbox 是否越界 for ann in data[annotations]: img None for im in data[images]: if im[id] ann[image_id]: img im break if img is None: print(Missing image for annotation, ann[id]) continue x, y, w, h ann[bbox] if x 0 or y 0 or x w img[width] or y h img[height]: print(bbox out of range:, ann[id])这段代码先统计图片和标注数量然后逐个检查 bbox 是否超出图片边界。矿井数据集偶尔会有标注工具导出的坐标精度问题比如左上角为负值或宽高为 0这些都要在训练前清理。如果 bbox 越界训练时目标框会被裁掉或产生 NaN Loss。另一个常见问题是 area 与 bbox 不一致虽然 COCO 评测时会重新计算但你自己的工具可能依赖 area 字段最好统一修正。2.3 类别不平衡与标注噪声矿井下数据集的一个显著特点是类别极不平衡。安全帽出现频率最高自救器很少而“人”往往被安全帽遮挡下半身。用 COCO 格式直接训练模型会对安全帽过拟合自救器召回率很低。这就需要在训练时做类别权重调整或者对自救器做数据增强。另外标注噪声也很常见有些标注框把自救器画在人的背上但框得过大包含手臂这会让模型学到错误特征。从 COCO JSON 里统计每个类别的实例数量很简单from collections import Counter cat_counter Counter() for ann in data[annotations]: cat_counter[ann[category_id]] 1 print(cat_counter)我一般会把这个统计结果打印出来如果发现某个类别少于总数量的 5%就要考虑是否做过采样或合成数据。这个数据集声称 96.3% 的识别率大概率是在一个类别相对均衡的测试集上测的。你自己复现时如果直接按默认设置训练很可能只有安全帽的 AP 高其他类别无法见人。3. 把 COCO JSON 转成 YOLO 格式转换脚本与四个边界坑3.1 为什么一定要转成 YOLO 格式当前做目标检测的主流框架是 YOLOv5、YOLOv8、YOLOv9 甚至 YOLO11。YOLO 系列原生的训练格式不是 COCO JSON而是每张图片对应一个同名的 txt 文件每行内容是class_id x_center y_center width height其中坐标是归一化到 0~1 的相对值。COCO JSON 的 bbox 是绝对像素值且格式为x, y, w, h。因此转换是必须的。另外很多开源训练仓库如 ultralytics虽然支持 COCO 格式但底层仍会将其转为 YOLO txt 缓存。自己转换的好处是能彻底检查数据质量避免训练中途报错。如果你后续还想用 Detectron2 或 MMDetectionCOCO JSON 又是标准输入所以保留原始 COCO JSON 作为主数据生成 YOLO txt 作为训练副本是最稳妥的做法。3.2 转换脚本与参数说明下面是我常用的 COCO JSON 转 YOLO txt 脚本支持四类目标的矿井数据集。关键点在于归一化时用图片宽高做分母并且确保类别 ID 从 0 开始import json import os def convert_coco_to_yolo(coco_json_path, output_dir): with open(coco_json_path, r) as f: data json.load(f) # 建立 image_id - file_name 映射 img_map {} for img in data[images]: img_id img[id] file_name img[file_name] width img[width] height img[height] img_map[img_id] (file_name, width, height) # 建立 category_id - 连续id 映射防止原id不是0开头 cat_map {} for idx, cat in enumerate(data[categories]): cat_map[cat[id]] idx os.makedirs(output_dir, exist_okTrue) # 按图片聚合 bbox anns_by_img {} for ann in data[annotations]: img_id ann[image_id] anns_by_img.setdefault(img_id, []).append(ann) for img_id, anns in anns_by_img.items(): file_name, width, height img_map[img_id] txt_path os.path.join(output_dir, file_name.replace(.jpg, .txt).replace(.png, .txt)) with open(txt_path, w) as f: for ann in anns: cat_id ann[category_id] x, y, w, h ann[bbox] # 转中心点坐标并归一化 x_center (x w / 2) / width y_center (y h / 2) / height w_norm w / width h_norm h / height f.write(f{cat_map[cat_id]} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}\n)逻辑说明脚本首先建立 image_id 到文件名和尺寸的映射因为标注里的 image_id 不一定是按顺序排列的。接着把 categories 里的原始 id 映射到从 0 开始的连续索引这一步非常关键。如果原始 id 是 1、2、3、4而 YOLO 期望 0、1、2、3不做映射就会把类别错位。然后按 image_id 聚合标注最后逐个写入 txt 文件。这里假设图片文件后缀是 jpg 或 png如果你的数据集是 .jpeg 或 .bmp需要扩展替换逻辑。注意YOLO txt 文件和图片文件必须放在同一个目录下或按 ultralytics 的约定放在 images 和 labels 两个子目录。转换完后建议随机抽查 5~10 个 txt把坐标反算回像素值画框验证不要盲目开训练。3.3 边界坑一文件名匹配问题很多矿井数据集的图片文件名带有特殊字符比如cam01_20231011_153200.jpg。如果你用file_name.replace(.jpg, .txt)没问题。但如果图片和标注文件不在同一层目录或者图片路径带子目录简单替换会失效。常见做法是只取file_name的 basename然后与 labels 目录下的文件名对齐。否则会出现 “label not found” 或图片与标签错位。3.4 边界坑二bbox 坐标越界与宽高为零上面检查脚本提到过这里再说详细点。有的标注工具在裁剪边缘物体时会生成超出图片边界的 bbox例如 x1900 而图片宽 1920 没问题但 xw1930 就越界了。YOLO 归一化后坐标可能大于 1训练时 OpenCV 读取会裁掉多余部分但框位置偏移。我的处理方式有两种一是直接 clipping把 x,y 限制在 [0, width-1] 内w,h 限制为不超出边界二是如果框面积小于原图 0.3%直接删除。具体用哪种取决于你对召回率的容忍度。3.5 边界坑三类别 id 从 1 开始的陷阱COCO 官方数据集的 categories 是从 1 开始的例如 person1。但很多矿井数据集是自定义的可能从 0 或 1 开始。YOLO 要求类别 id 从 0 开始。如果你不映射而直接使用原始 id那么原本 4 类目标会被当成 5 类且第 0 类永远为空训练时 loss 会异常。务必将原始 categories 按遍历顺序重新编号并保存一个classes.txt文件供后续推理映射。3.6 边界坑四area 字段与 COCO 评测转换到 YOLO 时不需要 area。但如果你之后要回到 COCO 评测指标mAP要确保 area 与 bbox 一致。一些标注工具导出时 area 是手动填的可能出错。建议在转换前重新计算每个 ann 的 area w * h并更新到 JSON 中。这样以后不管用哪个框架评测指标才可信。4. 用 Ultralytics YOLO 训练矿井检测模型参数与配置4.1 准备数据配置文件假设你已经把 COCO JSON 转成了 YOLO txt图片放在mine_dataset/images下标签放在mine_dataset/labels下同时划分 train 和 val 子目录。接下来需要写一个 data.yaml 给 ultralytics 用path: /path/to/mine_dataset # 数据集根目录 train: images/train val: images/val nc: 4 names: 0: helmet 1: indicator 2: person 3: self-rescuer注意path 字段如果是相对路径ultralytics 会相对当前工作目录解析。建议写绝对路径避免在 IDE 或 YOLO 命令行中路径解析分歧。names 顺序必须与转换脚本里的 cat_map 一致否则类别错位。nc 是类别数这里是 4。4.2 训练命令与关键参数常见做法是直接用yolo命令训练。如果你是 YOLOv8 或 v11命令如下yolo detect train datamine_dataset/data.yaml modelyolov8s.pt epochs150 imgsz640 batch16 device0,1说明model 选择预训练权重yolov8s.pt是小型模型适合井下小目标训练快如果追求更高精度可用yolov8m.pt或yolov8l.pt。epochs 设为 150但实际要看早停。imgsz 640 是默认但井下监控图常是 1280x1080直接缩放到 640 可能会丢失小目标可以试 960 或 1280但显存和训练时间会上升。batch 按显存调整单卡 16G 建议 8~16双卡可以 16 到 32。device 指定 GPU如果只有单卡就device0。训练时建议开启以下增强参数针对井下光照yolo detect train ... hsv_h0.02 hsv_s0.6 hsv_v0.4 degrees10 translate0.1 scale0.5 fliplr0.5 mosaic1.0井下光照不稳定HSV 增强可以帮助模型适应不同色温degrees 旋转 10 度用于处理摄像头安装角度scale 缩放到 0.5 倍相当于模拟较远的物体。但不要用太大的 translate因为救援设备位置相对固定。4.3 遇到精度不足的调优顺序当你训练完发现 96.3% 这个目标没达到不要盲目堆 epoch。我的调优顺序是先确认验证集和训练集是同一批摄像头拍的。如果训练集和验证集来自不同矿井或不同角度精度低是正常的。此时应该按摄像头划分而不是随机划分。检查每个类别的 AP。打开 runs/detect/train/confusion_matrix.png 和 results.png看哪一类拉胯。自救器通常拉胯因为样本少。对自救器做针对性增强复制样本、增加亮度扰动、或者用 mosaic 让自救器与大目标混合。提高输入分辨率。如果显存允许从 640 提到 768 或 896小目标 AP 会明显上升。换更强的 backbone。yolov8s 换 yolov8m 或 yolo11m同时降低 lr 或增加 weight decay。4.4 自定义锚定框不是必需的很多人一上来就调 anchor其实 YOLO 8 以后是 anchor-free不需要手动设置。但如果你用 YOLOv5可以针对井下目标的宽高比重新聚类 anchor。安全帽接近正方形指示器是长条人瘦高所以聚类后 anchor 应该包含长宽比 1:1、1:3、3:1 等。用以下命令查看数据集的 bbox 分布import os labels_dir mine_dataset/labels/train all_wh [] for f in os.listdir(labels_dir): with open(os.path.join(labels_dir, f)) as fp: for line in fp: parts line.strip().split() if len(parts) 5: w float(parts[3]) # 已经是归一化 h float(parts[4]) all_wh.append((w, h)) print(all_wh[:10])你可以自己算平均宽高比如果极端比例多才考虑改 anchor。5. 识别率 96.3% 的复现陷阱验证指标与数据集划分5.1 96.3% 是怎么算出来的识别率这个说法很模糊。可能是 mAP0.5、mAP0.5:0.95、也可能只是准确率正确检测数/总目标数。在复现时必须定义清楚。对于目标检测业界更常用 mAP0.5IoU0.5 时的平均精度作为可比较指标。如果你看到一份报告说“识别率 96.3%”大概率是 mAP0.5 或所有类别准确率的加权平均。这种数字在测试集分布简单时很容易达到比如只检测带安全帽的正面人物。因此你复现时不要直接拿“96.3%”当目标而是用自己的验证集重测。建议把数据按 70% train、15% val、15% test 划分且划分时以视频序列或摄像头为组单位防止同帧图片泄漏。很多矿井数据集会包含连续帧监控随机划分会让相似帧同时出现在训练集和验证集模型记忆能力被误报为高识别率。5.2 验证脚本计算 mAP 和各类别 AP用 ultralytics 训练完可以用下面的 Python 脚本加载验证集并计算指标from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) metrics model.val(datamine_dataset/data.yaml) print(metrics.box.map) # mAP0.5:0.95 print(metrics.box.map50) # mAP0.5 print(metrics.box.maps) # 每个类别的 mAP如果metrics.box.maps输出[0.98, 0.90, 0.95, 0.85]对应的类别顺序就是 data.yaml names 的顺序。你会发现自救器的 AP 最低。这时候不要慌这是常态。井下自救器体积小且在人体上当人弯腰或遮挡时很难检测。你可以考虑用更高的分辨率或增加自救器的训练样本。5.3 避坑类别名与类别 id 不一致导致推理混乱这个问题特别隐蔽。比如你训练时 names 是 helmet, indicator, person, self-rescuer但因为转换脚本的错误类别 id 映射成了 person, helmet, indicator, self-rescuer。训练不会报错但推理结果标签全错。预防办法是在训练前打印一张训练集图片的标签信息或者推理时画框查看类别。也可以直接看runs/detect/train/confusion_matrix.png类别轴标签对得上才算对。5.4 模型过拟合到某种光线条件的处理矿井下不同巷道光照差异很大。有的巷道是暖黄灯有的是冷白 LED还有的是微光。如果模型在某个摄像头精度高换一个巷道就下降说明过拟合到特定光照。这时需要做色彩增强或把图像转为灰度概率导入训练。我一般会在训练配置里增加hsv_v0.5和hsv_s0.7并加入degrees15。另外也可以用 MixUp 增强让模型学到更鲁邦的特征。如果实在不行就用测试时增强TTA来提高推理稳定性yolo detect predict sourcetest_imgs/ modelbest.pt ttaTrueTTA 会对同一张图做多尺度变换和翻转推理时间变长但精度提升明显。对于井下小目标TTA 通常能带来 1~2 个点的 mAP 提升。6. 常见问题与避坑清单5 个真实训练跑偏案例6.1 训练途中 Loss 变成 NaN现象训练到第 20 轮时 Loss 变成 nan然后一直无法恢复。原因最常见的因素是 bbox 归一化坐标出现非数值例如宽或高为 0或图片文件缺失导致读取到空数组。第二个因素是学习率过大尤其是用yolov8l但 batch 较小导致梯度爆炸。解决先检查转换后的 txt 文件是否有纯空标签或坐标大于 1 的情况。用脚本扫描所有 txt 文件如果发现坐标值超过 1.0 或小于 0.0找出对应图片检查原始标注。如果空标签过多考虑过滤掉完全没有目标的图片或关闭drop_last。学习率方面初始 lr 保持默认 0.01若出现 NaN 可降为 0.001。6.2 验证集 mAP 高但实际监控效果很差现象模型在验证集上 mAP0.5 达到 0.97但接到真实井下监控视频误检和漏检严重。原因验证集与训练集来自同一批静态图片缺少视频流中的模糊、运动、遮挡变化。这是典型的评估域不匹配。解决建立视频验证集。从监控视频中抽帧不要只选清晰帧要保留运动模糊、逆光、粉尘帧。用这些视频帧重新测试模型以这个数字作为真实可用性指标。另外检测结果后处理可以设置更高的 conf_threshold比如从默认 0.25 提高到 0.4减少误检但召回率会下降。6.3 自救器完全检测不到现象安全帽和人的 AP 都在 90% 以上自救器 AP 只有 20%。原因样本太少且目标太小。自救器通常挂在人腰间在 640 分辨率下只有十几个像素检测器难以提取特征。解决把训练分辨率提高到 960或对自救器类别做 x2 过采样。还可以用数据增强把自救器区域放大贴到不同背景上。如果标注允许一种做法是把自救器作为“人”的一部分先检测人再用关键点或者 ROI 分类器判断腰间是否有自救器。这是一个更实用的工程方案。6.4 训练速度极慢一次推理耗时 80ms 以上现象模型在 GPU 上推理 1000 张图片每张需要 80ms部署到边缘盒子Jetson需要 300ms。原因输入图像分辨率太高或模型太大。井下监控摄像头一般 25 fps实时检测要求单帧 40ms 以内。解决对于边缘盒子输入 imgsz 降到 512 或 416模型换为 yolo11n 或 yolov8n。剪枝掉不常用通道。如果还嫌慢用 TensorRT 导出 int8 量化模型。精度损失一般在 1~3 个点但速度提升 2 倍以上。6.5 标注里有的“指示器”其实是“信号灯”模型总是误检现象indicator 类别的 AP 还不错但实际输出时把巷道墙壁上的反光贴纸也当成 indicator。原因数据集中的 indicator 定义不统一。有的标注框的是信号灯有的框的是指示牌模型学到的是各种“亮块”。解决重看标注统计将 indicator 类别拆分或者对验证集采用更严格的 IoU。如果只是要判断矿井是否有信号灯异常可以用帧差法或颜色阈值做前处理再分类。如果必须用检测模型目标是让模型只学到固定的信号灯外观需要清洗标注。7. 进阶技巧用 COCO JSON 做数据质量审计与模型迭代闭环7.1 利用 COCO JSON 的 iscrowd 字段处理重叠目标井下拥挤时安全帽之间可能互相重叠。COCO 格式支持iscrowd1表示密集群体标注此时 bbox 可以是一整块区域。但 YOLO 不支持 crowd。如果你打算用 Detectron2 的 Mask R-CNN 做实例分割crowd 字段有价值。如果只做检测建议在转换时忽略 iscrowd1 的框直接用每个独立实例框。7.2 半自动标注并回流数据集96.3% 的模型训练出来后最大的价值不只是部署而是用它标注新的视频数据。做法先用模型推理未标注的矿井图片保存带 conf 的检测结果然后将 conf 高于 0.9 的框自动写入 COCO JSON 作为伪标签人工只审核低置信度的框。这样可以把标注成本降到三分之一。具体操作用模型跑一批图片输出 JSON 格式的预测结果然后写一个小脚本合并到现有 COCO JSON。注意合并前要检查类别 id 映射、图片 id 冲突、以及时间戳是否一致。这样可以持续扩大数据集特别是补充自救器的样本。7.3 用混淆矩阵定位难例针对性补充拍摄训练完查看confusion_matrix.png如果 person 和 helmet 互混把安全帽下方的人头误检为帽子说明模型没有很好地区分头和帽子。这需要收集一些穿戴不规范帽子歪、不系帽带的图片。这种硬例挖掘比盲目增加数据量更高效。我通常会在每轮模型迭代后把 train 和 val 中预测错误的样本导出拼成一张大图人工快速浏览再决定是否采集新数据或修正标注。7.4 部署时的标签映射与推理框架集成最后落地上无论你用 ONNX、TensorRT 还是 OpenVINO推理输出的 class id 都是从 0 开始的连续整数。要维护一个 JSON 或文本映射文件让上层平台显示中文名“安全帽”“指示器”“人员”“自救器”。这个映射与训练时的 data.yaml names 顺序保持一致。最好在模型导出后把映射写入一个label_map.json与模型放一起避免跨团队传递时信息丢失。我的习惯是每次训练完固定写一份 JSON{ 0: helmet, 1: indicator, 2: person, 3: self-rescuer }推理时把这个映射加载进内存输出时自然转换成业务标签。这样即使后续新增类别也只是在文件和配置中增加一行不会改动推理代码主链路。实战中我踩过不少次坑最深的教训是不要在拿到数据集后立刻启动训练先花两小时做格式校验和类别分布分析比后期调参弥补损失的时间划算得多。这个流程适用于任何 COCO JSON 格式的行业数据集矿井、电力、工地、化工厂都类似。把这个基础打牢识别率 96.3% 虽然是个参考但你自己构建的数据闭环里模型会持续进步。希望这篇笔记能帮你少走一段弯路。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →