ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

煤矸石目标检测实战:从COCO JSON到YOLOv8训练全流程

煤矸石目标检测实战:从COCO JSON到YOLOv8训练全流程 简介这份煤矸石识别数据集面向从事矿物识别、目标检测的算法工程师与科研人员基于现场采集的102张原始图片构建可支持煤炭、煤矸石与高岭石的多类别识别适用于煤矿矸石分选、矿物自动分类等工业视觉场景。压缩包共105个文件包括102个JPG现场图像、1个COCO JSON标注文件和2个TXT文件整体大小仅2.27MB结构紧凑便于快速下载和部署。COCO标注格式是目标检测领域的通用标准读者无需自行标注可直接用于训练YOLO、MMDetection、Detectron2等常见检测框架极大节省数据准备时间。图像均来自实地采集包含不同角度、光照条件和煤矸石粒度能帮助模型更好地适应复杂工况虽然样本规模不大但针对性强适合小样本学习、数据增强策略验证和迁移学习实验。目前已有426人学习适合需要积累煤矸石图像样本、开展小样本目标检测实验或验证识别算法的工程技术人员。1. 102张现场图像标注的煤矸石数据集为什么值得复现在选煤厂的皮带廊里煤流、矸石和伴生高岭石混在一起依靠人工盯监控做分选二十分钟后就会疲劳漏判。做智能化识别选矸的第一步不是调算法而是先有一份能把目标说清楚的标注数据。102张现场采集原始图片、COCO JSON格式、煤炭/煤矸石/高岭石三类这三个信息组合起来定义的正是从皮带视觉识别到固废分拣最常遇到的小样本起步场景。对选煤智能化、视觉识别选矸相关从业者来说这份数据集的价值不在数量而在样本来自现场有真实的皮带纹理、粉尘遮挡和光照波动。用它可以完整走通COCO JSON解析、json转换、yolov8训练到误检排查的链路也可以作为后续扩大标注的种子集。十万张合成图不如一千张现场图可靠这102张的意义是把识别任务从PPT拉回现场。2. COCO JSON标注结构拆解三个核心字段决定识别边界COCO JSON不是一种文件后缀而是一套组织图像与标注信息的约定常见于目标检测与实例分割任务的数据交换。数据集中每一张原始图片对应images数组里的一条记录每一个框好的目标对应annotations里的一条记录每一类物体在categories里登记。训练框架读数据时先把这三块加载成索引表再按image_id去关联标注字段名写错一个轻则加载慢重则整批训练跑不起来。2.1 一张现场图在COCO JSON中的完整记录COCO格式的实例文件打开后通常长这样{ images: [ { id: 1, file_name: site_0021.jpg, width: 1280, height: 960 } ], annotations: [ { id: 1, image_id: 1, category_id: 2, bbox: [218, 137, 306, 244], area: 74664, segmentation: [ [218, 137, 320, 150, 356, 210, 330, 290, 244, 381, 208, 300, 190, 230] ], iscrowd: 0 }, { id: 2, image_id: 1, category_id: 1, bbox: [712, 460, 288, 172], area: 49536, segmentation: [], iscrowd: 0 } ], categories: [ { id: 1, name: coal }, { id: 2, name: gangue }, { id: 3, name: kaolinite } ] }bbox字段顺序固定是[x, y, width, height]x和y是目标框左上角在原始图片上的像素坐标。area在只有矩形框时可以直接用width乘height求近似值如果后续还要做细粒度分割area最好由segmentation多边形真实面积决定。segmentation在纯检测标注里可能是一个多边形坐标数组也可能为空数组空的框不影响目标检测训练。iscrowd表示该目标是否属于密集人群、堆叠煤块这类难以逐框区分的目标一般设置成0。categories的id从1开始而不是0这是COCO和YOLO两者之间最经典的差一问题来源。切换到YOLO训练前记得为category_id统一减一映射由数据加载脚本完成还是由标注工具导出时完成要提前在项目里定好避免出现类别整体偏移一个数字的隐蔽错误。2.2 选择COCO格式而非纯txt的工程理由这个数据集用COCO格式存目标检测框对工程链路的直接好处主要体现在四个位置字段路径 作用 影响训练的位置 images[].id 与annotations.image_id对应 数据加载时关联图片与标注 annotations[].category_id 与categories.id对应 类别映射与损失计算 annotations[].bbox 检测框坐标 回归分支的监督信号 annotations[].iscrowd 是否密集目标 推理时NMS阈值过滤策略images里带上了宽高尺寸模型预处理时不用为了取宽高把每张图重新读一遍IO开销小很多。segmentation字段先占位后续从检测升级成实例分割时用cvat或labelstudio补画掩码仍能复用原来的JSON外壳。纯txt格式虽然简单但面对三分类、多边形标注、多版本类别名变更这类需求时缺少结构化的地方放元数据每次改都要写额外映射文件。2.3 用一段Python验证标注是否读得进来拿到数据集后第一步不是急着转YOLO而是先加载JSON统计类别分布和引用完整性import json from collections import Counter with open(annotations/instances_102.json, r, encodingutf-8) as f: coco json.load(f) cat_id2name {c[id]: c[name] for c in coco[categories]} cnt Counter() for ann in coco[annotations]: cnt[cat_id2name[ann[category_id]]] 1 print(实例数量按类别分布:, cnt.most_common()) imgs {im[id]: im for im in coco[images]} missing [a[image_id] for a in coco[annotations] if a[image_id] not in imgs] print(引用不存在image_id的标注数量:, len(missing)) invalid_box [ a[id] for a in coco[annotations] if a[bbox][2] 0 or a[bbox][3] 0 ] print(宽或高非正的标注id:, invalid_box)这段代码首先建立类别id到名称的映射用Counter统计三个类别的实例数量差异再检查annotations里是否存在悬空的image_id最后筛选出宽度或高度非正的非法框。前两项通过后才能进入训练环节若有类别只有个位数实例就要在第4章增强阶段重点照顾若invalid_box不为空说明json转换或人工标注时产生了脏数据这类坏标注会在训练时让回归loss剧烈跳动。2.4 不同标注工具导出COCO的差异点从cvat、labelimg、labelstudio这类数据标注工具导出COCO JSON字段结构大体一致细节上却各有脾气。cvat导出时categories的顺序可能按标注时间排列而不是按类别名字排序换工具前要重新打印一遍id对照。labelimg本身不直接导COCO通常先用VOC XML标注再用脚本转脚本容易丢弃segmentation里的多边形数据。labelstudio导出为COCO时要选对任务的标注配置它会把labels列表结构映射到categories配置不对就会出现所有目标类别id都是同一个值的现象。这些差异都发生在进入训练之前的准备阶段花点时间逐个核对比跑出模型后才发现类别错位要划算。3. 用YOLOv8训练自己的数据集煤矸石三类识别最小命令COCO JSON更适合做数据交换和审计训练时多数检测框架更习惯直接读目录结构下的归一化txt。Ultralytics YOLOv8支持直接读取COCO格式但实际项目中更可控的做法是先转成YOLO目录结构因为划分训练集和验证集时按图片文件路径操作比操纵JSON里的大数组直观得多。转换过程只做一件事把COCO的bbox坐标换算成归一化中心点坐标同时把类别id从1起始改成0起始。3.1 从COCO JSON到YOLO数据集的转换脚本目录结构约定为下面的形式dataset/ images/train/site_0001.jpg ... images/val/ ... labels/train/site_0001.txt ... labels/val/ ... data.yaml转换脚本核心部分如下import json from pathlib import Path with open(annotations/instances_102.json, r) as f: coco json.load(f) out Path(dataset/labels/train) out.mkdir(parentsTrue, exist_okTrue) imgs {im[id]: im for im in coco[images]} anns_by_img {} for ann in coco[annotations]: anns_by_img.setdefault(ann[image_id], []).append(ann) for img_id, anns in anns_by_img.items(): im imgs[img_id] w, h im[width], im[height] lines [] for ann in anns: x, y, bw, bh ann[bbox] cx, cy x bw / 2, y bh / 2 cat_id ann[category_id] - 1 if bw 0 or bh 0: continue lines.append(f{cat_id} {cx / w:.6f} {cy / h:.6f} {bw / w:.6f} {bh / h:.6f}) stem Path(im[file_name]).stem (out / f{stem}.txt).write_text(\n.join(lines)) print(转换完成涉及图像数量:, len(anns_by_img))脚本用dict按image_id分组标注省去两重循环的O(n²)匹配开销。逐行写入时保留6位小数精度对640像素输入足够。代码里保留了对非法宽高的过滤正常标注不会触发但批量转换时能挡住手误。注意cat_id ann[category_id] - 1这行COCO的categories从1计数YOLO的行首类别号从0计数。如果训练时出现class index out of range优先检查这一处是否漏减。3.2 train/val划分与样本均衡102张图规模不大但划分方式直接影响验证指标的可信度。建议按7:3切分即约70张训练、30张验证。划分前先按类别做统计如果某个类别只出现在少量图片里要保证这些图片同时进入训练集否则验证集会留下一个完全没见过的类别。另外注意连续拍摄的现场图片往往背景高度相似例如同一皮带段落的前后帧不能让它们一张落在train、一张落在val那会造成数据泄漏验证loss会虚低、换上真实场景后误检率反弹。按现场点位或拍摄时间分组划分比随机shuffle更可靠。3.3 data.yaml与训练命令的关键参数data.yaml写入类别名称映射path: dataset train: images/train val: images/val names: 0: coal 1: gangue 2: kaolinite训练用最小命令yolo detect train \ modelyolov8n.pt \ datadataset/data.yaml \ epochs120 \ imgsz640 \ batch16 \ patience30 \ projectruns/gangue \ nametry1参数 推荐值 作用与调参方向 imgsz 640 现场图按1280x960采集缩到640训练可在显存和特征粒度之间平衡 batch 16 6GB以下显存降为8batch过小会让BN统计量抖动 patience 30 mAP连续30个epoch不涨就早停102张小样本需要这个机制防过拟合 epochs 120 小数据集收敛快后半程主要由早停决定实际训练轮数 model yolov8n 基础版本参数少先验证链路通不通再考虑s/m训练过程中日志会显示每一类的mAP50-95。如果发现煤、矸石两类指标普遍高而高岭石抬不起来不要急着加epoch先回到第2章的类别统计脚本确认实例数量。数量差距太大时后面的数据增强章节就是重点。4. 102张太少不可怕数据增强与扩充组合方案小样本训练目标检测模型瓶颈不在模型复杂度而在类别内特征多样性。现场图里的煤矸石会随皮带速度产生运动模糊、随环境光产生色偏还会出现煤块堆叠遮挡。只靠原始102张图训练loss压得再低验证集里稍微出现一张光照怪异的图就崩。解决办法分两路离线增强扩大落盘样本量在线增强用训练时的随机扰动提高泛化性。4.1 离线增强与在线增强的分工离线增强先产生新图片和对应标注结果落盘后作为训练文件。它的好处是效果确定、能逐张检查增强结果避免训练过程里出现“增强后框丢了”的隐蔽问题缺点是会放大重复采样增加相似背景在batch里出现的频率。在线增强由训练框架在每个iteration随机施加不额外占磁盘。对这份数据集建议先做一轮离线增强把有效样本数量提到300张以上再让YOLOv8内部增强在每轮继续扰动两层搭配比分层堆叠更稳。4.2 基于albumentations的针对性增强代码import albumentations as A import cv2 train_transform A.Compose([ A.RandomBrightnessContrast(brightness_limit0.2, contrast_limit0.2, p0.6), A.HueSaturationValue(hue_shift_limit10, sat_shift_limit25, val_shift_limit20, p0.5), A.MotionBlur(blur_limit(3, 7), p0.3), A.HorizontalFlip(p0.5), A.RandomSizedBBoxSafeCrop(height640, width640, erosion_rate0.2, p0.3), ], bbox_paramsA.BboxParams(formatcoco, min_visibility0.3)) image cv2.imread(dataset/images/train/site_0021.jpg) boxes [[218, 137, 306, 244], [712, 460, 288, 172]] labels [1, 0] # 减一后1矸石0煤炭 for idx in range(5): aug train_transform(imageimage, bboxesboxes, labelslabels) out_name fdataset/images/train/site_0021_aug{idx}.jpg cv2.imwrite(out_name, aug[image])这段增强针对现场场景选了三类变换。MotionBlur用3到7像素的核模拟皮带运动拖影RandomBrightnessContrast模拟车间照明波动RandomSizedBBoxSafeCrop随机裁掉部分背景同时保证目标框不出界等价于让模型在更丰富的背景下看到同一个矸石。BboxParams里的format设为coco对应输入bbox是x,y,w,h格式min_visibility0.3表示增强后与原始框的交并比低于30%的目标会被丢弃避免模型学习半张目标。4.3 小样本场景下的增强参数区间增强类型 推荐区间 与现场图片的关系 水平翻转 0.4 ~ 0.6 皮带方向固定时不要开垂直翻转 HSV色偏 sat_shift_limit 20~40 煤与矸石颜色差异是关键判据 强度过大会破坏类别区分度 运动模糊 blur_limit 3~7 匹配皮带运输中相机曝光造成的拖影 随机裁剪缩放 0.8 ~ 1.2 模拟不同安装高度与焦距的相机 MixUp 0.1 ~ 0.2 小数据集的额外正则降低过拟合一个常见误用是开了随机旋转90度或垂直翻转。选煤现场的料流方向由皮带决定翻转后的样本违背物理规律模型会学到错误的方向先验。用平移、缩放、轻微错切组合成的仿射扰动比旋转更安全这也是适合皮带视觉识别的增强策略。4.4 在线增强参数写在训练命令里YOLOv8把在线增强参数直接放在训练命令上不需要改代码yolo detect train \ modelyolov8n.pt \ datadataset/data.yaml \ epochs120 \ imgsz640 \ batch16 \ hsv_h0.015 \ hsv_s0.5 \ hsv_v0.3 \ fliplr0.5 \ flipud0.0 \ scale0.3 \ translate0.1hsv_h控制色相偏移幅度现场粉尘会让画面偏灰0.015的小幅扰动有助于抵抗这类色偏hsv_s饱和度偏移0.5可以增加煤矸石边缘的反差变化scale0.3允许目标缩放30%模拟相机到皮带的距离波动flipud保持0.0是刻意设置垂直翻转的矸石在重力方向上没有物理意义这条参数与离线增强时的禁忌保持一致。5. 验证阶段的mAP分析与误检排查技巧训练结束不等于识别可用。扩充数据集后最该盯的是混淆矩阵和三分类各自的precision、recall而不是总体mAP。煤矸石识别里最容易把表面带煤粉的矸石判成煤炭这种错误在总体指标里可能被另外两类的高分掩盖只有分类别看才暴露得出来。5.1 输出混淆矩阵与每个类别的指标yolo detect val \ modelruns/gangue/try1/weights/best.pt \ datadataset/data.yaml验证完成后在runs/gangue/try1/路径下找confusion_matrix.png和results.csv。重点看党gangue与coal两个类别之间的交叉格子若数值超过0.15说明类别边界纠缠严重回到第4章把HueSaturationValue的饱和度扰动继续收窄。再看results.csv里mAP50-95(B)的三个类别值通常煤炭最高、高岭石因实例数最少最不稳定。如果验证阶段出现“高岭石完全没框出来”的情况优先回看训练集中该类别是否被切分后只剩几张小尺寸目标。5.2 用置信度过滤定位高误检图片from ultralytics import YOLO model YOLO(runs/gangue/try1/weights/best.pt) results model.predict( sourcedataset/images/val, conf0.25, save_txtTrue, save_confTrue, projectruns/debug, ) for r in results: for box, cls, conf in zip(r.boxes.xyxy, r.boxes.cls, r.boxes.conf): if int(cls) 2: print(f高岭石检出置信度: {conf.item():.2f})把置信度阈值从0.25提高到0.45再跑一遍对比两次输出的漏检图片。高置信度的误检比低置信度误检更值得人工复查若得分很高却框错目标多半是训练数据标注本身有问题若阴影和水洼反光被当成煤块说明场景覆盖不够应该回现场补拍而不是继续堆增强。最后一招是把误检图片按置信度排序后存成小图拼版交给现场工艺人员做人工复核他们对“这块矸石为什么像煤”的判断往往比算法工程师调整损失函数权重来得直观也更接近实际分选工位上的判断逻辑。本文还有配套的精品资源点击获取
返回列表