简介:这份火车轨道检测数据集面向计算机视觉开发者、轨道交通智能化研究者及深度学习实践者,用于训练和验证轨道区域与障碍物识别模型,可支撑列车前方障碍预警、轨道巡检自动化等场景。资源以COCO标注格式组织,包含3900张原始图片,官方给出的识别准确率达93.7%,适合作为目标检测模型的训练与评估基础。压缩包共2000个文件,其中1995个jpg图像为实际样本,3个json文件承载COCO标注信息,另有2个txt说明文件,整体约471.42MB,目录结构便于直接接入主流检测框架。目前已有2063人学习下载,说明其在轨道检测方向具备一定参考价值。读者可据此获得成规模的标注数据,用于模型训练、精度对比与算法调优,也能结合标注文件快速完成数据加载与类别映射,减少自行采集和标注的成本。
1. 火车轨道检测数据集:3900 张 COCO 标注图能撑起 93.7% 准确率吗
铁路巡检这个场景,真正难的不是把轨道框出来,而是把轨道和侵限障碍物同时框对。轨道是细长目标,障碍物可能是落石、异物、行人、倒伏树枝,尺度差异极大,背景又高度重复。你拿一个通用 COCO 预训练模型直接推,轨道框会断成一段一段,障碍物小目标基本漏检。这个标题里的数据集,3900 张原始图片、COCO 标注、可识别火车轨道和障碍物、准确率 93.7%,本质上是在回答一个很实际的问题:有没有一份标注质量够用、类别定义清晰、能直接喂给 YOLO 系列或 MMDetection 的轨道场景数据,让一线做巡检算法的人不用从零标几千张图。
它适合三类人:做铁路/地铁巡检视觉算法的工程师、想拿一个细长目标加小目标混合场景练手检测模型的人、以及需要评估「自建标注 vs 直接用现成数据集」成本的人。不适合指望开箱即用就能上线的场景,93.7% 这个数字背后一定绑定了特定验证集和 IoU 阈值,脱离评测协议谈准确率没有意义。下面按「数据长什么样 → 怎么转成训练格式 → 怎么训 → 坑在哪 → 怎么把指标做扎实」推一遍。
2. 拆开这份 COCO 标注:类别定义、标注粒度与 93.7% 的评测口径
2.1 轨道和障碍物在 COCO 里到底怎么标
COCO 格式的核心是三个 JSON:images、annotations、categories。轨道检测这类场景,类别通常不会只有两个。常见做法是把轨道拆成「轨道」一类,障碍物按来源拆成「落石/异物/人员/车辆」若干类,也可能只给一个粗粒度的「障碍物」类。3900 张图如果只标两个类,标注成本低但模型学不到障碍物内部差异;如果拆到五六类,每类样本量会被摊薄,小类别容易欠拟合。
判断这份数据能不能用,第一件事是统计每个类别的实例数,而不是看图片数。轨道是长条目标,一张图里可能只有 1 到 2 个轨道实例,但障碍物实例数波动很大。下面这段脚本直接读 COCO JSON 做类别分布统计,跑一遍就知道数据是否均衡。
import json from collections import Counter # 换成你的标注文件路径 ann_path = "annotations/instances_train.json" with open(ann_path, "r", encoding="utf-8") as f: coco = json.load(f) # 类别 id -> 名称 cat_map = {c["id"]: c["name"] for c in coco["categories"]} # 统计每个类别的标注框数量 inst_counter = Counter(ann["category_id"] for ann in coco["annotations"]) # 统计每个类别的图片覆盖数(去重) img_counter = Counter() for ann in coco["annotations"]: img_counter[ann["category_id"]] += 0 # 占位,下面用集合去重 cat_imgs = {} for ann in coco["annotations"]: cat_imgs.setdefault(ann["category_id"], set()).add(ann["image_id"]) print(f"图片总数: {len(coco['images'])}") print(f"标注框总数: {len(coco['annotations'])}") for cid, name in cat_map.items(): print(f"{name:12s} 框数={inst_counter.get(cid,0):6d} 覆盖图片={len(cat_imgs.get(cid,set())):5d}")逻辑说明:inst_counter统计的是标注框数量,反映模型能学到的正样本量;cat_imgs统计的是类别覆盖的图片数,反映类别在场景中的出现频率。参数上唯一要改的是ann_path,训练集和验证集各跑一次。如果某个障碍物类覆盖图片不到 100 张,训练时就要考虑过采样或类别加权,否则 93.7% 的准确率大概率是被轨道大类拉上去的。
2.2 93.7% 准确率对应的指标到底是什么
检测任务里说「准确率」是不严谨的。COCO 评测体系用的是 AP(Average Precision),按 IoU 从 0.5 到 0.95 步长 0.05 取平均得到 AP@[.5:.95],另外单独报 AP50、AP75。93.7% 如果指的是 AP50,那是一个偏乐观但常见的数字;如果指的是 AP@[.5:.95],在轨道这种细长目标上非常难,需要标注框贴合度极高。
轨道目标的 IoU 对框的偏移极其敏感。一条宽 20 像素的轨道,框上下各偏 5 像素,IoU 就掉到 0.6 以下。所以看这份数据的指标,必须确认三件事:IoU 阈值是多少、是按类别平均还是按全体平均、验证集是不是和训练集同分布。常见做法是要求提供方给出 per-class AP 表,而不是一个总数。下面这张表是我评估任何检测数据集时会填的核对表。
| 核对项 | 为什么重要 | 合格线参考 |
|---|---|---|
| IoU 阈值 | 决定指标乐观程度 | 至少给 AP50 和 AP75 |
| per-class AP | 防止大类掩盖小类 | 每个类都要有 |
| 验证集来源 | 判断是否同分布 | 最好独立于训练采集批次 |
| 小目标占比 | 轨道障碍物多为小目标 | 统计 area<32² 的比例 |
| 标注一致性 | 细长目标框贴合度 | 抽查 50 张看框是否贴边 |
提示:拿到任何声称高准确率的数据集,先跑一遍类别分布和 per-class AP,再决定要不要投入训练。一个总数说明不了问题。
3. 把 COCO 标注转成 YOLO 格式:转换脚本与四个边界坑
3.1 COCO 转 YOLO 的坐标换算
YOLO 格式要求每行class_id x_center y_center width height,全部归一化到 0 到 1。COCO 给的是[x_min, y_min, width, height]绝对像素。换算本身简单,但轨道这种细长目标在转换时最容易出问题的是框超出图像边界和宽高为 0。下面脚本做了边界裁剪和合法性校验。
import json import os from PIL import Image def coco_to_yolo(coco_json, img_dir, out_dir): with open(coco_json, "r", encoding="utf-8") as f: coco = json.load(f) # COCO category_id 可能不连续,重映射成 0 起始 cats = sorted(coco["categories"], key=lambda x: x["id"]) cat_id_map = {c["id"]: i for i, c in enumerate(cats)} img_info = {img["id"]: img for img in coco["images"]} # 按 image_id 聚合标注 ann_by_img = {} for ann in coco["annotations"]: ann_by_img.setdefault(ann["image_id"], []).append(ann) os.makedirs(out_dir, exist_ok=True) for img_id, anns in ann_by_img.items(): info = img_info[img_id] w, h = info["width"], info["height"] # 用真实图片尺寸兜底,防止 JSON 里尺寸写错 real_path = os.path.join(img_dir, info["file_name"]) if os.path.exists(real_path): with Image.open(real_path) as im: w, h = im.size lines = [] for ann in anns: x, y, bw, bh = ann["bbox"] # 裁剪到图像范围内,避免负坐标和越界 x1 = max(0, x) y1 = max(0, y) x2 = min(w, x + bw) y2 = min(h, y + bh) bw, bh = x2 - x1, y2 - y1 if bw <= 1 or bh <= 1: continue # 丢弃退化框 xc = (x1 + bw / 2) / w yc = (y1 + bh / 2) / h nw = bw / w nh = bh / h cls = cat_id_map[ann["category_id"]] lines.append(f"{cls} {xc:.6f} {yc:.6f} {nw:.6f} {nh:.6f}") txt_name = os.path.splitext(info["file_name"])[0] + ".txt" with open(os.path.join(out_dir, txt_name), "w") as f: f.write("\n".join(lines)) coco_to_yolo("annotations/instances_train.json", "images/train", "labels/train")逻辑说明:cat_id_map把 COCO 里可能跳号的 category_id 压成从 0 开始的连续整数,这是 YOLO 训练必须的,否则类别索引对不上。real_path那段用 PIL 重新读图片尺寸,是因为不少数据集 JSON 里的 width/height 和实际图片不一致,直接信 JSON 会导致归一化坐标整体偏移。bw <= 1 or bh <= 1过滤掉裁剪后退化的框,轨道细长目标偶尔会出现这种脏标注。
参数说明:coco_json是标注文件,img_dir是原图目录,out_dir是输出的 labels 目录。训练集和验证集分别调用一次。转换完必须抽查,不能直接开训。
3.2 转换后必须做的三项校验
转换脚本跑完不代表数据就对了。我一般会做三件事:一是用可视化脚本把 YOLO 框画回原图,看轨道框有没有整体偏移;二是统计每张图的标注行数,找出空标签文件;三是检查类别索引分布是否和 COCO 统计一致。空标签文件在 YOLO 训练里会被当成纯背景图,如果数量多,会明显拉低召回率。
# 统计空标签文件数量 find labels/train -name "*.txt" -empty | wc -l # 统计标签文件总数,和图片数对比 ls labels/train/*.txt | wc -l ls images/train/*.jpg | wc -l如果空标签数量超过图片总数的 5%,要么是转换漏了,要么是原数据里确实有大量无目标图。前者回去查ann_by_img聚合逻辑,后者考虑把这些图单独拿出来做背景负样本,而不是混在正样本里。
3.3 数据集划分:别让同一段轨道同时出现在训练和验证里
3900 张图如果是从连续视频抽帧来的,相邻帧高度相似。随机按 8:2 划分,训练集和验证集会存在大量近重复样本,验证指标虚高,93.7% 很可能就是这么来的。正确做法是按采集批次、线路区段或时间划分。常见做法是:同一段轨道、同一趟巡检的图片只进训练集或只进验证集,绝不跨集。
判断是否存在近重复,可以用感知哈希快速筛一遍。下面这段用 imagehash 找相似图,把相似度高的图归到同一组,再按组划分。
import imagehash from PIL import Image import os from collections import defaultdict groups = defaultdict(list) hashes = {} for fn in os.listdir("images/all"): if not fn.lower().endswith((".jpg", ".png")): continue with Image.open(os.path.join("images/all", fn)) as im: h = imagehash.phash(im) hashes[fn] = h # 汉明距离小于 5 视为近重复 files = list(hashes.keys()) visited = set() group_id = 0 for i, f1 in enumerate(files): if f1 in visited: continue group = [f1] visited.add(f1) for f2 in files[i+1:]: if f2 in visited: continue if hashes[f1] - hashes[f2] < 5: group.append(f2) visited.add(f2) groups[group_id] = group group_id += 1 print(f"总图片 {len(files)},聚成 {len(groups)} 组")逻辑说明:phash对缩放和轻微压缩鲁棒,适合找抽帧近重复。汉明距离阈值 5 是经验值,轨道场景背景重复度高,可以适当放宽到 8。划分时以组为单位整体进训练或验证,避免泄漏。这一步做完,验证指标会掉一些,但那个数字才是可信的。
4. 用 YOLOv8 训练这份数据:配置、参数与指标解读
4.1 数据配置文件与最小训练命令
YOLO 系列训练需要一份 YAML 描述数据路径和类别。轨道检测类别少,配置文件很短,但路径写错是最常见的翻车点。
# rail_data.yaml path: /data/rail_dataset # 数据集根目录 train: images/train val: images/val names: 0: rail 1: obstacleyolo detect train \ data=rail_data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=1024 \ batch=8 \ patience=30 \ project=runs/rail \ name=exp1逻辑说明:imgsz=1024是关键选择。轨道是细长目标,640 分辨率下轨道宽度可能只剩几个像素,特征提取会丢。1024 能明显改善细长目标召回,代价是显存和训练时间上升。batch=8是 1024 分辨率下 8G 显存的保守值,显存够可以往上加。patience=30表示 30 轮验证指标不升就早停,防止过拟合。
参数说明:model从 yolov8n 到 yolov8x 按精度递增,轨道场景建议至少 yolov8s,n 版对小目标偏弱。epochs=150是起点,看验证曲线决定是否加。如果障碍物小目标多,可以开mosaic=1.0增强,但轨道是长条目标,mosaic 拼接会切断轨道连续性,建议后期关掉 mosaic 再微调几十轮。
4.2 训练后必须看的四条曲线
训练完不能只看一个 mAP。我固定看四条:train/box_loss和val/box_loss是否同步下降、metrics/mAP50和metrics/mAP50-95的差距、每个类别的 AP、以及混淆矩阵。轨道和障碍物如果混淆矩阵里互相误判,说明类别定义本身有歧义,比如「轨道上的异物」到底算轨道还是障碍物。
# 用验证集跑一次详细评估,输出 per-class 指标 yolo detect val \ model=runs/rail/exp1/weights/best.pt \ data=rail_data.yaml \ imgsz=1024 \ plots=Trueplots=True会在输出目录生成混淆矩阵和 PR 曲线。重点看障碍物类的召回,轨道类通常 AP 很高,如果障碍物召回低于 0.7,说明小目标或样本量不够,需要针对性补数据或调 anchor。
4.3 93.7% 这个数字怎么复现和验证
要复现一个接近 93.7% 的指标,前提是评测协议一致。如果原数据报的是 AP50,你也要在 AP50 口径下比。如果原数据用了 TTA(测试时增强)或高分辨率推理,你也要对齐。下面命令开启 TTA 推理,通常能涨 1 到 2 个点,但推理速度会成倍下降。
yolo detect val \ model=runs/rail/exp1/weights/best.pt \ data=rail_data.yaml \ imgsz=1280 \ augment=True \ conf=0.001 \ iou=0.6逻辑说明:augment=True开启 TTA,conf=0.001是 COCO 评测的标准低置信度阈值,保证 PR 曲线完整,iou=0.6是 NMS 的 IoU 阈值。这三个参数和 COCO 官方评测对齐后,得到的数字才有可比性。如果这样跑出来和 93.7% 差很多,先怀疑验证集划分泄漏,再怀疑标注质量。
5. 避坑与排查:轨道检测数据集落地时最容易翻车的五件事
5.1 现象:训练 mAP 很高,实际推理轨道断断续续
原因:训练时用了 mosaic 增强,轨道被拼接切断,模型学到的是局部片段;或者验证集和训练集同源,指标虚高。解决:后期关闭 mosaic 微调,推理时降低 conf 阈值到 0.2 左右,让断开的轨道段连起来,再考虑后处理做线段拟合。
5.2 现象:障碍物小目标几乎全漏
原因:输入分辨率太低,小目标在特征图上不足一个像素;或者障碍物样本量太少,被轨道大类压制。解决:把 imgsz 提到 1024 以上,检查障碍物类覆盖图片数,低于 200 张就做过采样,并在 loss 里给障碍物类更高权重。
5.3 现象:COCO 转 YOLO 后框整体偏移
原因:JSON 里的 width/height 和实际图片尺寸不一致,归一化基准错了。解决:转换时用 PIL 重新读真实尺寸,不要信 JSON 字段,转换后可视化抽查至少 50 张。
5.4 现象:验证指标忽高忽低,每次划分结果差很多
原因:连续帧近重复导致数据泄漏,随机划分不稳定。解决:用感知哈希聚类,按组划分,保证同一段轨道的图不跨集。这一步做完指标会降,但稳定。
5.5 现象:轨道和障碍物互相误判
原因:类别定义边界模糊,比如轨道上的小异物标注时一会儿标轨道一会儿标障碍物。解决:回到标注规范,明确「障碍物」只标侵限且独立于轨道的目标,轨道上的附着物归轨道类,重新清洗标注后再训。
6. 把 93.7% 变成可复现的工程指标:验证集构建与推理后处理技巧
数据集的价值不在于那个数字,而在于你能不能在自己的场景里复现并超过它。我一般会做两件事:一是自建一个「困难验证集」,专门收集漏检和误检的图,每次模型迭代都在这上面跑;二是针对轨道细长特性做推理后处理,把断开的检测框合并成完整轨道线。
困难验证集的构建不需要重新标注,从现有验证集里挑出模型置信度在 0.3 到 0.6 之间的样本,人工复核一遍,这些就是边界样本。每次训练完先在这个子集上看指标,比看全体 mAP 更能反映真实进步。下面这段脚本从验证结果里筛出低置信度样本,方便你快速定位困难图。
import json # YOLO val 保存的预测结果 JSON with open("runs/rail/exp1/predictions.json", "r") as f: preds = json.load(f) # 按图片聚合,找平均置信度低的图 from collections import defaultdict img_confs = defaultdict(list) for p in preds: img_confs[p["image_id"]].append(p["score"]) hard = [(img, sum(c)/len(c)) for img, c in img_confs.items() if c] hard.sort(key=lambda x: x[1]) print("最难的 20 张图:") for img, avg in hard[:20]: print(f"image_id={img} avg_conf={avg:.3f}")逻辑说明:平均置信度低的图,要么是目标模糊,要么是模型没学好。把这些图挑出来人工看一遍,能快速判断是数据问题还是模型问题。参数上predictions.json是 YOLO val 保存的预测文件,不同版本路径略有差异,找不到就开save_json=True。
推理后处理方面,轨道检测可以加一步线段拟合:把同一类别的检测框按长宽比筛出细长框,用最小二乘拟合直线,再把断开的段按距离和角度合并。这一步能把轨道连续性指标拉高不少,但要注意别把弯道拟合直了。我的习惯是弯道区段单独处理,直线段才做合并。
最后说个血泪经验:任何声称 93.7% 的数据集,拿到手第一件事不是训模型,而是花半天做数据审计——类别分布、近重复、标注贴合度、验证集独立性。这半天能省掉后面两周的玄学调参。希望帮到你。
本文还有配套的精品资源,点击获取