简介铁路轨道缺陷数据集面向目标检测、缺陷识别方向的研究者与算法工程师聚焦铁轨裂缝、间隙等常见表面缺陷的标注数据。压缩包内共包含2000个XML文件均采用PASCAL VOC格式记录缺陷类别与边界框信息可用于YOLO、Faster R-CNN等模型的训练前格式转换资源包整体约273.84MB文件命名规律清晰便于按图像维度索引对应标注。数据集已有1688人学习使用适合作为铁路巡检视觉算法的数据补充与评测基准。通过该标注集读者可快速获得批量VOC格式缺陷标签减少手动标注成本同时可依据标签分布划分训练集与验证集并结合描述中提供的原始图与标注示例开展进一步实验。1. 铁路轨道缺陷数据集4278张原始图的裂缝与间隙检测起点铁路轨道缺陷数据集一直是目标检测练手和工业视觉落地绕不开的素材来源。我接过一个轨道巡检验证项目最头疼的倒不是模型选型而是找不到带干净标注的轨道图片网上散落的图片要么分辨率参差要么根本没有标注文件补标一张轨道缺陷图的成本远高于普通场景。这份数据集打包了4278张原始图片配的是PASCAL VOC的XML标注标注内容覆盖轨道表面裂缝、间隙缺陷两类情况拿来做训练集可以直接跑通YOLO系列或者Faster R-CNN。适合两类人第一类是刚接触目标检测、需要一个现成标注集来入门VOC转YOLO流程的第二类是真正在搞轨道缺陷识别、想先看一个标准数据集的标注粒度和坐标分布再决定要不要自己采集数据的。如果你正在找能下载、带标注、能直接训练的数据集这份值得先看目录结构和标注文件再动手。2. 数据集的构成与PASCAL VOC标注格式先读XML再谈训练标题里写的PASICAL VOC其实就是目标检测里最常见的PASCAL VOC标注格式拼写上多一个字母少一个字母都有人写内容上都是同一套东西每个图片对应一个XML文件XML里记录图片尺寸、文件名以及每一个目标物体的类别和边界框坐标。拿到这份数据集第一步不是急着训练而是把目录结构和单个XML文件看懂。我一般会先花十分钟把这层搞明白后面转格式、补标注、排查数据问题都有底。2.1 目录结构与文件约定数据集解压后典型布局是PASCAL VOC风格目录/文件作用JPEGImages/原始图片JPG格式文件名是编号Annotations/每张图对应的XML标注文件ImageSets/Main/划分训练集和验证集的txt文件label.txt / classes.txt类别列表如果有的话一般是两行crack、gap4278张图片对应4278个XML文件一对一关系是这套数据集默认的约定。文件名一般带序号比如rail_0001.jpg和rail_0001.xml。命名对不上是后面最容易翻车的地方比如某个XML里写的是rail_1001.jpg但实际图库里的文件是rail_1001.JPG大小写不同在Windows上没事在Linux训练容器里直接报找不到图。拿到手先跑一个文件对比把JPEGImages和Annotations两个目录的文件名逐个比对缺任何一个都先补齐再谈训练。2.2 读一个XML标注文件字段逐个拆用VS Code、Notepad或者直接浏览器打开任意一个XML都可以内容是这样实际宽高取决于拍摄设备我这里写的是示例值annotation folderJPEGImages/folder filenamerail_0021.jpg/filename path/datasets/rail/JPEGImages/rail_0021.jpg/path source databaseRail Defect Dataset/database /source size width1920/width height1080/height depth3/depth /size object namecrack/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin636/xmin ymin412/ymin xmax723/xmax ymax467/ymax /bndbox /object /annotation这段标注表达的是图片宽高为1920x1080图中有一个目标类别叫crack目标边界框左上角在(636, 412)右下角在(723, 467)。bndbox里存的是绝对像素坐标x轴向右增长、y轴向下增长这四个值共同围出一个矩形。一个XML文件里可能有多个object每个object对应一个缺陷实例。difficult为1表示这个目标难以辨认训练时一般直接跳过这类样本我习惯在转换脚本里保留它但单独标注不默认删除。如果你拿到这份数据集之后想补标新图片labelimg默认导出的就是一模一样的PASCAL VOC格式只需要提前配一个classes.txt用CVAT在线标注导出的VOC压缩包结构和这个也几乎一致。所以看懂这个XML之后无论补标还是换工具都不慌。自己写脚本做XML解析时我建议只用ElementTree不要引lxml标准库足够应对这种量级的标注文件少一个依赖就少一个环境坑。2.3 用脚本统计类别与图片数量先看分布再决定策略拿到数据集后我会先跑一个统计脚本看两种缺陷的数量分布。这一步可以顺手完成数据标注质量的初检如果crack有3800个实例、gap只有500个训练时的类别不均衡问题从第一天就要想对策。import xml.etree.ElementTree as ET from collections import Counter import glob xml_files glob.glob(Annotations/*.xml) counter Counter() for xml_file in xml_files: tree ET.parse(xml_file) root tree.getroot() for obj in root.findall(object): counter[obj.findtext(name)] 1 print(标注文件总数:, len(xml_files)) for name, count in counter.most_common(): print(f{name}: {count})这个脚本用ElementTree逐个解析XML找出所有object节点再把name字段塞进Counter里统计。核心只有几行但能反映出类别个数、是否有多余的类名、以及有没有空标注文件。跑完如果发现类别名不止两个比如出现了Crack和crack各占一半那就是标注时命名不统一训练前必须合并。建议把统计结果截图保留训练完了对比mAP时有个参照。另外一个细节统计出来的实例总数大于4278是正常的因为一张图里可以有多个缺陷框我们统计的是实例数不是图片数。3. 把VOC XML转成YOLO格式归一化坐标的转换脚本与三个边界坑YOLO系列的训练代码不认识PASCAL VOC的XML它只认挂在label目录下的txt文件每行格式是“类别ID x_center y_center width height”。这就是为什么每次拿到VOC数据集第一件事永远是写转换脚本。我以前贪省事直接拿别人博客的脚本跑结果碰到坐标越界、类别顺序错乱两个问题后来老老实实自己写顺便理清了边界条件。3.1 VOC坐标和YOLO坐标的本质差别VOC用绝对像素值描述边界框左上角、右下角单位是像素。YOLO用相对值描述边界框中心点坐标和宽高数值范围理论上在0到1之间。转换本质就是做两个除法中心点x等于(xmin加xmax)除以2再除以width中心点y等于(ymin加ymax)除以2再除以height宽度等于(xmax减xmin)除以width高度等于(ymax减ymin)除以height。这里有一个常见的误用计算过程必须用浮点数用整数除法会把所有小于1的结果直接截成0整个标注对位全错而且这种错误在mAP上看不出来因为框都在图片左上角缩成了一条线。3.2 转换脚本直接从XML生成YOLO格式txtimport os import glob import xml.etree.ElementTree as ET import numpy as np class_mapping {crack: 0, gap: 1} # 类别ID从0开始顺序决定data.yaml里的names xml_dir Annotations/ txt_dir labels/ os.makedirs(txt_dir, exist_okTrue) for xml_file in glob.glob(os.path.join(xml_dir, *.xml)): tree ET.parse(xml_file) root tree.getroot() w int(root.findtext(size/width)) h int(root.findtext(size/height)) lines [] for obj in root.findall(object): name obj.findtext(name) if name not in class_mapping: continue cls_id class_mapping[name] bnd obj.find(bndbox) xmin float(bnd.findtext(xmin)) ymin float(bnd.findtext(ymin)) xmax float(bnd.findtext(xmax)) ymax float(bnd.findtext(ymax)) x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) txt_name os.path.basename(xml_file).replace(.xml, .txt) with open(os.path.join(txt_dir, txt_name), w) as f: f.write(\n.join(lines))逻辑上就三步读XML拿原图宽高、按物体解析边界框、把像素坐标换算成相对坐标写进txt。size/width这种写法是ElementTree的路径语法直接定位到嵌套节点比先find(size)再find(width)少一层报错点。class_mapping字典的顺序决定输出的类别ID这一步必须和后面data.yaml里的names完全一致否则训练出来的模型会把裂缝和间隙认反。f{cls_id} {x_center:.6f}里的.6f保留6位小数对640分辨率来说足够精确写少了框会偏写多了文件变大6位是经验值。这里有一个边界坑要提前说当缺陷正好贴着图片边缘时很多标注工具会给出xmax等于width的坐标换算出来宽度恰好是1.0这合法。更危险的是xmax大于width的情况那是标注时不小心拖出了画布换算出来宽度大于1。YOLO训练遇到这种标注一般不会直接报错但会降低那一批样本的拟合质量。我建议在bw和bh计算之后加一行bw min(bw, 1.0)把越界值截断回合法区间。第二个坑是类别ID顺序错乱。有人喜欢把background占成0号但YOLO的txt里不会出现背景框类别ID必须从0开始连续编号如果写成{gap: 0, crack: 2}训练会直接报索引越界。第三个坑是空XML有些图片没有缺陷XML里一个object都没有转换脚本会生成一个0字节的txt这是对的YOLO把这种文件当背景样本千万别为了省事把空txt删掉否则图片会被跳过。3.3 组织目录结构并生成data.yamlYOLOv8要求图片和label严格配对常见目录组织长这样mkdir -p datasets/rail/images datasets/rail/labels cp JPEGImages/*.jpg datasets/rail/images/ cp labels/*.txt datasets/rail/labels/把所有图片拷进images、所有txt拷进labels就够了。data.yaml是训练入口内容如下path: datasets/rail # 数据集根目录 train: images/train val: images/val names: 0: crack 1: gap注意names的顺序必须和转换脚本里的class_mapping一致。划分训练集和验证集时不要用random.shuffle直接打乱然后把最后20%当验证集——如果某个类别的样本本来就少很容易出现验证集里一个裂缝都没有的情况。我一般把文件名按类别做了分层抽样保证train和val里都有两类样本比例控制在8:2附近这个比例对4278张图来说验证集约850张足够让mAP的波动落在合理范围内。4. YOLOv8训练轨道缺陷检测模型从data.yaml到验证指标数据集准备好以后训练本身反而是最不费脑的一步。这里用YOLOv8做例子因为这个框架把训练、验证、导出都封装成了统一接口而且它对这种几千张的小数据集很友好预训练权重能帮模型少走很多弯路。如果你手头的显卡显存不大选nano版本完全够用4278张图的轨道缺陷识别任务用不着large模型。4.1 环境准备pip install ultralytics这个包会自动装好torch和torchvision的对应版本。装完建议随手跑一下yolo checks看CUDA是否可用否则后面训练报的CUDA error会被误以为是标注问题实际上是模型跑在CPU上太慢。我在Windows和Linux服务器上都跑过Linux容器里注意别用root裸装容易把系统自带的Python搞乱我一般建一个venv再装。如果公司内网不能直接连pip源离线装的话先下载ultralytics的whl再pip install本地文件依赖项同样要提前打包。4.2 训练命令与参数yolo detect train \ datadatasets/rail/data.yaml \ modelyolov8n.pt \ epochs120 \ imgsz640 \ batch16 \ workers4 \ device0 \ projectrail_runmodelyolov8n.pt会自动下载COCO预训练权重第一次跑会花点时间预训练模型认识通用特征对轨道场景帮助有限但迁移学习仍然比随机初始化快得多。关键参数里imgsz640是训练时的输入分辨率轨道裂缝这种细长目标如果原图是1920宽的高清图片缩到640会损失不少细节我建议试一下imgsz960代价是显存占用明显上升batch不得不降到8。workers4是数据加载线程数Windows上建议改成0否则多进程会反复报错。参数建议值说明epochs120小数据集早停设为50防止空跑batch16显存不够就降到8imgsz640 / 960高清原图优先960patience50100个epoch没提升就停device0多卡用0,1CPU别试训练日志里重点看三个量P是精确率R是召回率mAP50是IoU阈值0.5下的平均精度。轨道缺陷检测这种场景我更看重召回率因为漏检一个裂缝比误检一个裂缝后果严重得多。4.3 训练后验证训练跑完结果目录里会生成weights/best.pt和last.pt别急着拿best.pt去推理先跑一次验证from ultralytics import YOLO model YOLO(rail_run/weights/best.pt) metrics model.val(datadatasets/rail/data.yaml, imgsz640) print(metrics.box.map) # mAP50-95 print(metrics.box.map50) # mAP50 print(metrics.box.maps) # 每个类别的mAP50-95列表验证结果里最值得盯的是box.maps它按names顺序给出每个类别的mAP。如果crack很高、gap很低说明类别不均衡问题直接反映到了指标上这时候回去调整loss权重或者补样本比继续加大epochs有效。YOLOv8会在验证时把预测框和真实框画到图上存在rail_run/val_batch0_pred.jpg里强烈建议打开看一遍轨道缺陷里有些标注框只框住了裂缝的一半模型预测反而更全这种标注比模型差的情况仅靠mAP数字是看不出来的。5. 避坑排查越界标注、类别名混乱与小样本类别的五个坑这一章写的是我在这类VOC标注数据集上翻过的车每一条都对应一个具体的现象、原因和解决方式。你如果按前面的章节走大概率会撞上其中一两个知道坑在哪能省下大半天排查时间。5.1 标注框越界导致loss变成nan现象训练到中途loss突然变成nan或者在数据加载阶段报AssertionError: bbox width 1。原因XML里xmax大于图片width换算成YOLO坐标后宽度大于1模型在损失函数里对超出边界的框计算出了非有限值。这种标注多半是标注时鼠标拖出了画布边界工具没有自动截断。解决在转换脚本里对bw、bh、x_center、y_center统一做np.clip把结果限制在0到1之间。别只截断宽高中心点坐标越界同样会导致nan四个值一起截才安全。5.2 类别名大小写不一致被当成两类现象统计脚本跑出来有四个类名比如crack、Crack、gap、Gap训练时模型输出层突然多了两个类别。原因多位标注员或者不同标注工具自动补全时首字母大小写混用。XML解析是按字符串精确匹配的crack和Crack在字典里是两个完全不同的键。解决统计完类别先做一次归一化把name.lower()统一之后再写进class_mapping。更稳妥的做法是把所有XML里的name字段直接替换成小写再重新生成标注避免后面每次训练都要处理大小写。5.3 验证集里没分到某种缺陷现象训练日志里mAP50看起来有0.85但box.maps输出发现gap那一列的召回率是0。原因随机划分数据集时少数类样本只有几百个被全部分进了训练集验证集里一张gap图片都没有。模型在验证集上没见到这个类指标直接归零。解决按类别做分层抽样先按类别把所有文件分组再从每个组里取20%组成验证集保证两个类别在验证集里都存在。这个逻辑写成一个独立脚本存好换数据集的时候改一下类别列表就能复用。5.4 图片和XML对不上导致训练跳过样本现象训练日志里不断出现skipping corrupt JPEG的警告而且训练集图片数比预期少了几十张。原因XML的filename字段和JPEGImages目录里的实际文件名不匹配可能是大小写差异也可能是扩展名从.jpg写成了.jpeg。YOLO按XML的filename去索引图片找不到或者无法解码就直接跳过。解决写一个校验脚本遍历Annotations目录检查每个XML里写的filename在JPEGImages里是否存在不存在就打印出来。我一般会在转换脚本里加同样的检查把断掉的配对关系在源头暴露。5.5 增强参数开太大导致模型学到假纹理现象训练集mAP很高但拿到现场拍摄的新图片上一测掉了一半还多。原因YOLOv8默认开启HSV颜色扰动和随机透视变换对轨道这种纹理敏感的场景来说颜色扰动太大会让模型把光照变化当成缺陷特征尤其裂缝这种线性纹理很容易被透视变换扭曲成假样本。解决训练时显式关闭或调低增强参数hsv_h0.0、hsv_s0.0、hsv_v0.1、degrees0.0、translate0.05、scale0.2。做工业检测的数据集增强参数宁可保守也不要让模型在合成变形的图片上建立错误的判别逻辑。6. 训练闭环用标注回显脚本检查数据和模型格式转换和训练流程都跑通之后我最推荐你做的一件事是写一个标注回显脚本把XML里的边界框画回原图人工快速核对。这一步看起来原始但它是同时检查数据质量和模型学习效果的最快手段。import cv2 import xml.etree.ElementTree as ET img_path datasets/rail/images/rail_0021.jpg xml_path Annotations/rail_0021.xml img cv2.imread(img_path) tree ET.parse(xml_path) root tree.getroot() for obj in root.findall(object): name obj.findtext(name) bnd obj.find(bndbox) xmin int(bnd.findtext(xmin)) ymin int(bnd.findtext(ymin)) xmax int(bnd.findtext(xmax)) ymax int(bnd.findtext(ymax)) color (0, 0, 255) if name crack else (0, 255, 255) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), color, 2) cv2.putText(img, name, (xmin, ymin - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2) cv2.imwrite(inspect_out/rail_0021.jpg, img)脚本逻辑很简单读图、解析XML、画框、写回。值得强调的是输出目录inspect_out要单独建不要覆盖原始图片。批量检查时把这段逻辑包成一个遍历函数随机抽200张图输出回显图快速翻看时重点找三类问题框偏了、框太小、图片过曝到缺陷看不见。第一次跑的时候我很惊讶有些轨道缝隙的框只框住了缝隙的一段模型要学全整条缝隙全靠数据增强硬撑这种样本不清理mAP会被顶在某个瓶颈上不去。训练完之后我用同样的回显逻辑把best.pt的预测框也画上去和GT框叠在一起对比。叠加图里能看到两类典型情况预测框比GT框更全面说明模型学到了标注没标全的缺陷此时可以把预测结果导出人工确认后做二次标注预测框漂到轨道边缘的石渣上说明现场背景干扰严重需要增加负样本。从那以后我每次拿到新的缺陷数据集第一件事就是跑标注回显强制看30张图再进训练流程检查脚本和数据落在同一个目录里随手能跑不跑不训。希望帮到你。本文还有配套的精品资源点击获取
