
简介面向计算机视觉与智能铁路安全场景这份火车轨道检测数据集提供基于3900张原始图片制作的目标检测样本图片内容覆盖不同光线、角度和远近环境下的轨道与障碍物采用COCO格式完成标注并报告93.7%的识别准确率。压缩包共2000个文件涵盖其中1995张jpg图像及对应标注配套3个json标注文件和2个txt说明文件整体大小约471.42MB目录结构清晰便于直接接入主流检测框架适合不同规模的训练与验证需求。目前已有2063人学习下载。使用者可获得带精确框标注的轨道与障碍物样本适合用于训练YOLO、Faster R-CNN等模型也可作为铁路异物入侵检测、轨道障碍物预警等项目的初始数据。json文件包含类别、边界框等关键信息txt文件提供类别或路径说明显著降低了数据预处理成本。1. 火车轨道检测数据集93.7%准确率背后的COCO标记逻辑一个只包含3900张原始图片的火车轨道检测数据集能把识别准确率推到93.7%说明瓶颈不是数据量而是标注方式。轨道检测和通用目标检测的差异在于场景高度固定背景、轨道线、障碍物之间有着清晰的几何关系。COCO标记在这里不只是“画框”它让每个目标同时拥有类别、边界框和分割区域给后续训练和评估提供了统一接口。这个数据集的价值在于它的COCO标记规范以及围绕它训练一个可部署的检测模型。下面要做的不是报一个数字而是把标注、训练、评估、扩大数据四个环节的细节全部展开。适合想复现或迁移这个准确率的算法工程师也适合帮轨道检测业务搭建数据规范的数据集生产者。2. 用COCO标记的3900张原始图片搭建轨道检测数据集拿到别人标好的COCO数据集第一步不是直接训练而是把标注格式吃透。COCO标记的原始图片往往没有固定的目录结构真正定义数据的是那个annotations.json。下面先把COCO数据集结构拆开再给出一套从原始图片到COCO JSON的落地脚本最后用统计检查把标注质量卡住。2.1 COCO数据格式里火车轨道和障碍物应该怎么定义COCO标记有两层要理解一层是数据集结构一层是目标定义。coco2017数据集结构是常见的参照它把内容分成info、licenses、images、annotations、categories五个数组。对轨道检测来说categories里只需要两个类track和obstacle。注意类别的顺序会直接影响后续训练时的类别IDtrack排0obstacle排1一旦定下来就不要再变。annotations里的每个标注记录对应一个目标实例。障碍物用bbox和area就能表达但火车轨道这种细长结构只给一个横跨整张图的bbox会把大量背景带入正样本让模型学到错误特征。更可靠的做法是给轨道线加segmentation多边形即使后续模型不做实例分割训练时也可以用分割掩膜做裁剪或引导注意力。下面是一个典型标注项{ id: 1, image_id: 0, category_id: 1, bbox: [182, 130, 66, 254], area: 16764, segmentation: [[185, 130, 192, 384, 210, 384, 200, 130]], iscrowd: 0 }这段JSON里category_id为1对应obstaclebbox是[x, y, width, height]格式segmentation是单个多边形。轨道线的标注可以只写segmentationbbox用多边形的最小外接矩形自行生成避免手工画倾斜框。iscrowd必须填0否则在COCO评估中会被当成密集人群目标剔除。理解了COCO标记的结构还要确认3900张原始图片里每一张都有对应标注。常见做法是打开annotations.json检查images数组长度和图片文件数量是否一致。不一致时优先以annotations为准删掉没有标注的图片而不是去补标因为后续训练框架遇到缺失标注会直接报错。2.2 从原始图片到COCO JSON的最小Python脚本如果手里的3900张图是从标注工具导出的导出格式可能是每张图片一个JSON或一个CSV。最省事的办法是写一个最小转换脚本把零散结果合并成标准COCO标记。下面这个脚本按“一行一个目标”的CSV格式输入列名为image_file, category, x, y, w, h输出coco_annotations.json。import json, os, csv from collections import defaultdict def convert_csv_to_coco(csv_path, image_dir): images, annotations [], [] ann_id 1 current_image_id -1 image_to_id {} cat_to_id {track: 0, obstacle: 1} with open(csv_path) as f: reader csv.DictReader(f) for row in reader: if row[image_file] not in image_to_id: current_image_id 1 image_to_id[row[image_file]] current_image_id # 读取图片宽高COCO标记必须显式写入 from PIL import Image img Image.open(os.path.join(image_dir, row[image_file])) w, h img.size images.append({ id: current_image_id, file_name: row[image_file], width: w, height: h }) bbox [float(row[x]), float(row[y]), float(row[w]), float(row[h])] annotations.append({ id: ann_id, image_id: image_to_id[row[image_file]], category_id: cat_to_id[row[category]], bbox: bbox, area: bbox[2] * bbox[3], segmentation: [], iscrowd: 0 }) ann_id 1 coco { info: {description: railway track detection}, licenses: [], images: images, annotations: annotations, categories: [{id: 0, name: track}, {id: 1, name: obstacle}] } with open(coco_annotations.json, w) as f: json.dump(coco, f) if __name__ __main__: convert_csv_to_coco(labels.csv, images)脚本的关键点是给图片分配连续ID并用字典缓存图片文件到ID的映射避免每读一行都重复打开图片。PIL读取宽高是必须的因为COCO的images字段要求每个实例都带width和height很多训练框架会用它过滤超出图片边界的框。area直接用bbox宽高相乘如果后续要用mask评估最好用多边形面积。这个脚本里segmentation为空对轨道线不够友好建议单独解析标注工具导出的多边形顶点再把顶点写入segmentation字段bbox仍然可以用最小外接矩形。转换完成后用下面的命令检查生成文件的合法性和类别统计python -c import json; djson.load(open(coco_annotations.json)); print(len(d[images]), len(d[annotations]))如果输出的图片数不等于3900就要回到CSV检查是否有重复或缺失的image_file。2.3 标注质量检查类别不平衡和框尺寸分布COCO格式正确不代表标注质量过关。轨道场景里track和obstacle的数量天然不平衡一张图通常只有一条轨道但可能有五六个障碍物。我一般会先统计类别数量再画一个框尺寸分布找出面积过小或宽高比极端的异常目标。下面这段代码可以快速输出类别数量和框的尺寸分位数import json import numpy as np with open(coco_annotations.json) as f: coco json.load(f) cat_count {} areas [] for ann in coco[annotations]: cat_count[ann[category_id]] cat_count.get(ann[category_id], 0) 1 areas.append(ann[area]) print(类别统计:, cat_count) print(面积分位:, np.percentile(areas, [10, 50, 90]))正常轨道数据集的面积分位会有明显长尾10%的框可能小于500像素90%的框大于几万像素。如果最小框面积连几十像素都没有说明标注时把远处的小障碍物也框了进去。这类小目标在训练时容易被忽略需要靠后续的Mosaic增强来缓解。拿一个3900张图规模的轨道数据集来说下面这张表的分布很典型类别标注数量平均面积面积中位数track约3900约32000 px²约8100 px²obstacle约5800约15000 px²约2200 px²如果track的标注数量明显少于图片数说明有不少图里轨道被漏标了。轨道被漏标比障碍物漏标对训练影响更大因为模型会把漏标的轨道区域当成背景训练出的特征响应会变得不稳定。此时宁可删掉漏标的图片也不要保留不完整的标注。3. 用YOLOv8在轨道数据集上训练障碍物识别模型COCO标记的数据集不能直接喂给YOLOv8Ultralytics框架默认读取YOLO txt格式。所以要把COCO JSON转成每张图片一个txt并写一个data.yaml。转换完成后训练命令只有一行但决定93.7%准确率的是imgsz和epochs这几个参数。3.1 从COCO JSON转YOLO txt格式并定义data.yamlYOLO格式的核心是类别ID后跟归一化坐标的center_x, center_y, width, height。转换COCO标记时注意把bbox的x,y原点是左上角转换成中心点并除以图片宽高。下面是转换脚本的关键部分import json, os from pathlib import Path def coco_to_yolo(coco_path, output_dir): with open(coco_path) as f: coco json.load(f) img_map {img[id]: img for img in coco[images]} Path(output_dir).mkdir(exist_okTrue, parentsTrue) 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(): img img_map[img_id] w, h img[width], img[height] txt_path Path(output_dir) / (Path(img[file_name]).stem .txt) lines [] for ann in anns: x, y, bw, bh ann[bbox] cx x bw / 2 cy y bh / 2 cls ann[category_id] lines.append(f{cls} {cx/w:.6f} {cy/h:.6f} {bw/w:.6f} {bh/h:.6f}) txt_path.write_text(\n.join(lines))注意category_id在COCO里如果是0和1正好和YOLO的类别ID一致。如果不是就要维护一个映射表。转换后图片文件要按txt同名的规则放进训练和验证目录然后写data.yamlpath: /data/railway train: images/train val: images/val names: 0: track 1: obstacle路径里不要写中文Ultralytics在部分版本下对非英文路径支持有问题。val集合建议从3900张里抽10%15%而且要确保val里track和obstacle的比例和训练集一致。3.2 训练命令与关键超参数转换完成后用下面的命令开始训练yolo train modelyolov8n.pt datarailway.yaml imgsz640 batch16 epochs300 patience50 lr00.01参数说明我用表列出来这几个参数对轨道检测的影响最大参数推荐值说明imgsz640轨道图片通常很长640能平衡速度和小目标召回batch163090及以上可用32小卡用8epochs3003900张图足够收敛早期可设150patience50连续50轮val指标不涨就停止lr00.01预训练权重建议保持默认weight_decay0.0005防止障碍物类别过拟合到纹理上模型选择上如果复现93.7%准确率yolov8n或yolov8s就够了。轨道检测的目标类别少不需要用l模型。准确率这个数字要分清是mAP50还是mAP50-95通常COCO评估下mAP50-95达到93.7%对二分类数据集是合理的mAP50会更高。3.3 评估阶段看哪些指标训练结束后运行验证命令yolo val modelruns/detect/train/weights/best.pt datarailway.yaml输出会给出mAP50、mAP50-95和每个类别的AP。我一般还会生成混淆矩阵重点看obstacle是否被大量预测成track。轨道检测的典型误判是远处行人或石块被标成track因为轨道线的边缘纹理和障碍物相似。如果混淆矩阵里track和obstacle的互相混淆超过5%就要回头检查标注框是不是把轨道和障碍物重叠区域重复标注了。4. 轨道检测落地的三个坑小目标、遮挡和光照数据集上跑出93.7%不代表现场部署没问题。轨道场景有三类问题必须在数据层面提前处理远处障碍物只有几十像素、杂草遮挡轨道边界、逆光使轨道线和背景的对比度急剧下降。4.1 轨道线目标与障碍物目标的标注差异COCO标记如果只提供bbox轨道线检测就别硬套通用框。轨道线是长直线一个bbox会同时包含两侧的碎石和枕木模型难以确定到底学的是哪条边。我见过一个训练结果模型在弯道处把两条铁轨中间的路基全部当成track就是因为训练时bbox框进了太多背景。缓解办法是在标注时给轨道线补polygon或者在转换脚本里把track的包围框收缩到轨道宽度的80%。收缩操作可以用原框中心不变、宽高乘以0.8来实现代价是损失轨道两端的信息但能显著减少背景干扰。障碍物则保持完整bbox不要收缩因为障碍物边界就是检测边界。4.2 数据增强与难例挖掘对于小障碍物YOLOv8自带Mosaic是够用的但轨道场景的独特性值得再加两项增强随机Gamma校正和轨道视角的仿射变换。Gamma能模拟不同时间段的曝光仿射变换中的横向拉伸则对应列车视角变化。用Albumentations包可以这样配置import albumentations as A train_transform A.Compose([ A.RandomGamma(gamma_limit(80, 120), p0.5), A.Affine(scale(0.8, 1.2), translate_px(-20, 20), shear(-10, 10), p0.3), A.RandomBrightnessContrast(brightness_limit0.2, p0.5), ])如果不想改YOLOv8的增强管道可以把这段配置离线跑一遍把增强后的图片写入新目录再和原始3900张图合并训练。哪些场景需要难例挖掘如果val集里最差的图片集中在夜晚或逆光就把这些图片从val里拿出来放到训练集里重新标注或者用当前模型对它们做伪标注后再人工修正。轨道检测的数据集扩充优先级永远是把“模型最容易看错的光照条件”先补齐而不是随机下载更多图片。4.3 推理阶段的后处理模型训练完成后部署时还要调整NMS和置信度阈值。COCO评估默认置信度阈值是0.001这适合评测但不适合现场。轨道检测的现场推理我通常这样配置yolo predict modelbest.pt sourcetest_imgs conf0.25 iou0.6conf设0.25可以把误报压下来iou设0.6对轨道和障碍物都不算激进。如果发现轨道线被切成一截一截的碎片把conf降到0.15同时把max_det调大再用后处理把同一法线上的多个track框拼接成完整轨道线段。拼接时按框底边中心点进行聚类距离小于20像素的归为同一段轨道。5. 扩充轨道数据集的思路未标记轨道图像的伪标签自训练手动标注轨道障碍物成本太高3900张图复现到93.7%后要再往上走常规做法不是继续标新图而是把相机在真实线路上录制的未标记视频抽帧用当前模型生成伪标签。这就是常说的半监督自训练实现成本比再标3900张低得多。具体步骤是把视频抽帧成图片用best.pt跑一次批量预测保留conf大于0.5的预测结果转成COCO标记格式后人工抽查其中10%的图把错标和漏标的删掉再合并进原训练集重训。伪标签里track类保留的条件要比obstacle更严格因为track漏检会直接让背景变正样本。如果需要引入公开数据coco数据集里的traffic light、person等类别可以在去掉极端光照样本后迁移一部分过来但poi数据集这类以兴趣点为主的资源对轨道场景几乎没用标注风格和类别定义差异太大混入反而会把模型带偏。验证伪标签效果有一个简单办法固定训练参数只对比加入伪标签前后的val mAP50-95。如果提升不足0.5%说明伪标签质量差或分布偏移优先检查漏标track的比例。整个流程跑通后3900张原始图配合伪标签就能支撑下一轮迭代准确率会朝着94%以上走但每一步数据变更都要重新跑一遍验证别信单一指标的跳变。本文还有配套的精品资源点击获取