ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

绳子检测数据集VOC与YOLO双格式解析:训练避坑与工程实践

绳子检测数据集VOC与YOLO双格式解析:训练避坑与工程实践 简介绳子检测数据集精选322张包含绳索目标的图片采用Pascal VOC与YOLO两种格式同步标注适合正在学习目标检测、需要单类别数据集的开发者与研究者直接使用。资源包共968个文件内含322张jpg原图、322个xml标注文件、324个txt标签文件整体大小约24.6MB目录按图片-标注对应排列下载后无需额外转换格式即可接入常见训练流程。所有标注均由labelImg工具绘制矩形框完成仅包含rope一个类别累计375个目标框覆盖多张不同场景下的绳索图像便于模型学习该类目标的共性特征同时每张图片均配有同名VOC与YOLO标注文件适合用于YOLO系列、SSD、Faster R-CNN等算法训练。目前已有176人学习获取。该数据可作为绳索检测、线缆识别、物料捆扎等任务的基础训练集标注规范且合理但不附带任何训练权重及精度保证适合用于自行调参、算法对比或课程设计。1. 绳子检测数据集VOCYOLO格式322张1类别先看清单再定方案拿到“绳子检测数据集VOCYOLO格式322张1类别”这个 7z 压缩包时我先不急着解压训练而是把它的定位看清楚322 张图、1 个类别双格式标注VOC 的 XML 和 YOLO 的 TXT。这个规模不是发布级数据集而是帮你在钢丝绳、架空线、缆绳这类条状物检测上快速跑通流程的冷启动包。它省掉的是从零标框、格式互转的时间换回来的是半天内跑出第一版模型、看清检测下限。适合谁手上有绳子类检测需求、没时间标数据、想先验证可行性的工程师。开箱前要接受一个现实样本量小后面必须补自己的数据。2. 绳子数据集的 VOC 与 YOLO 双格式从解压到双标注一致性校验拿到数据集先做的是解压和清点不是打开训练脚本。7z 打包的好处只有一个压缩率比 zip 高单文件传输方便坏处也只有一个你机器上不一定有解压工具。先把这个解决掉再谈后面的格式问题。2.1 7z 解压的两个常用姿势Windows 右键与 Linux 命令行在 Windows 上最省事的是装 7-Zip右键压缩包选“解压到当前文件夹”。在 Linux 服务器上我一般用 p7zip 提供的命令行工具。Debian/Ubuntu 系先装包sudo apt install p7zip-full 7za x rope_dataset.7z -orope_data7za是 7-Zip 的独立命令行版本x表示保留完整目录结构解压-o指定输出目录注意-o后面紧接着路径、不能有空格写成-o rope_data会解压到错误的相对路径。解压完成后先清点数量再动手改文件find rope_data -name *.xml | wc -l find rope_data -name *.txt -path *labels* | wc -l find rope_data -name *.jpg | wc -l如果三个数字对不上说明压缩包里的标注有缺漏后文 4.2 会专门讲这个问题。我习惯在这里就把路径记下来后续写 dataset.yaml 要原样引用。绳索类数据集的图片大概率是现场拍的文件名可能带中文或空格解压后建议统一改成rope_0001.jpg这类无空格的命名否则后面 YOLO 读路径时很容易踩“路径带空格”的坑。2.2 VOC 的 XML 里真正影响训练的字段只有四个VOC 格式的核心是每个图片对应一个 XML 文件里面用bndbox记录像素坐标的真实框。一个典型的单框标注长这样annotation filenamerope_0001.jpg/filename size width1280/width height720/height depth3/depth /size object namerope/name truncated0/truncated difficult0/difficult bndbox xmin120/xmin ymin98/ymin xmax1031/xmax ymax192/ymax /bndbox /object /annotationfilename用来和图片对应size里的宽高在转 YOLO 格式时是必用的分母name是类别名bndbox是坐标。truncated和difficult这两个字段要留意difficult1表示目标本身难辨认很多转格式脚本会直接丢弃这类框如果数据集里混了 difficult 框训练前要么过滤要么把 difficult 改成 0否则你看到的图片数和标注框数会对不上。这个数据集的类别只有一个理论上name全是 rope 或 rope 的变体拼写。我曾经遇到过一个声称单类别的数据集解压后发现 XML 里同时存在 rope 和 rope_tail 两种名字YOLO 训练时自动把它们当成两个类输出类别数量和你预期不一致。所以拿到手不要只看文件名用 grep 把name字段全部扫一遍grep -h name rope_data/Annotations/*.xml | sort | uniq -c这一步花不了十秒能省掉后面整个训练白跑一轮的后悔药。2.3 YOLO 的 TXT 里坐标全部是归一化后的比例值YOLO 格式每行是一个目标格式是class_id x_center y_center width height前一项是类别编号从 0 开始后四项都是相对图片宽高的比例值范围在 0 到 1 之间。把 XML 里的像素坐标换算成 YOLO 坐标核心是四行除法x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height box_width (xmax - xmin) / width box_height (ymax - ymin) / height对应到 2.2 的例子上1280x720 的图、框 xmin120、ymin98、xmax1031、ymax192换算后是0.4492 0.2014 0.7117 0.1306在 TXT 里写成0 0.4492 0.2014 0.7117 0.1306注意这里0是类别编号不是坐标。如果你用 labelimg 打标完 yolo 格式的标界面里显示的框是像素坐标存到 TXT 里才是归一化值很多人拿 labelimg 改完图尺寸后不重新打标只把 TXT 里的数字等比缩放这是最常见的翻车点原图裁剪过、分辨率换过TXT 里的比例就不再对应当前图片必须回到图片上重新框一次。这个类别编号还有一个隐含约定编号顺序和训练时用的names列表位置绑定。数据集作者给的 TXT 里类别编号是 0对应唯一类别名是 rope那你在 dataset.yaml 里写names: [rope]就没问题如果你擅自改成names: [背景, rope]等于把编号 1 当成绳子模型从头错到尾。2.4 双格式一致性校验脚本让 XML 和 TXT 对着算一遍VOC 和 YOLO 两套标注并存最大的隐患是两套标注各自演化作者改了几张图的 XML忘了同步 TXT或者原来就有一批图漏标。我不信任肉眼抽查尤其是 322 张这个量级正好处在一张张看嫌多、抽样又容易漏的尴尬位置。最靠谱的做法是写一个校验脚本把两套标注的框换算成同一坐标系后比 IOU。import argparse import xml.etree.ElementTree as ET from pathlib import Path def xml_to_box(xml_path): root ET.parse(xml_path).getroot() size root.find(size) W, H int(size.find(width).text), int(size.find(height).text) obj root.find(object) bb obj.find(bndbox) x1 float(bb.find(xmin).text) y1 float(bb.find(ymin).text) x2 float(bb.find(xmax).text) y2 float(bb.find(ymax).text) return [(x1 / W, y1 / H, x2 / W, y2 / H)] def txt_to_box(txt_path): boxes [] for line in txt_path.read_text().strip().splitlines(): parts list(map(float, line.split())) cx, cy, w, h parts[1], parts[2], parts[3], parts[4] boxes.append((cx - w / 2, cy - h / 2, cx w / 2, cy h / 2)) return boxes def iou(b1, b2): x1, y1 max(b1[0], b2[0]), max(b1[1], b2[1]) x2, y2 min(b1[2], b2[2]), min(b1[3], b2[3]) inter max(0.0, x2 - x1) * max(0.0, y2 - y1) area1 (b1[2] - b1[0]) * (b1[3] - b1[1]) area2 (b2[2] - b2[0]) * (b2[3] - b2[1]) return inter / (area1 area2 - inter 1e-6) # 用法示例python check_dual.py --xml_dir Annotations --txt_dir labels --img_dir images if __name__ __main__: ap argparse.ArgumentParser() ap.add_argument(--xml_dir, requiredTrue) ap.add_argument(--txt_dir, requiredTrue) ap.add_argument(--img_dir, requiredTrue) args ap.parse_args() for xml_path in Path(args.xml_dir).glob(*.xml): stem xml_path.stem txt_path Path(args.txt_dir) / f{stem}.txt img_path Path(args.img_dir) / f{stem}.jpg if not txt_path.exists(): print(f[MISSING TXT] {stem}) continue if not img_path.exists(): print(f[MISSING IMG] {stem}) continue xml_boxes xml_to_box(xml_path) txt_boxes txt_to_box(txt_path) if len(xml_boxes) ! len(txt_boxes): print(f[COUNT MISMATCH] {stem}: xml{len(xml_boxes)} txt{len(txt_boxes)}) continue for bx, bt in zip(xml_boxes, txt_boxes): if iou(bx, bt) 0.99: print(f[BOX MISMATCH] {stem}: iou{iou(bx, bt):.4f}) break print(check done)脚本的校验逻辑是以 XML 为基准找到同名 TXT 和同名图片把 XML 的像素框除以图片宽高得到归一化坐标把 TXT 的归一化中心点宽高还原成归一化左上角右下角再算两组框的 IOU。这里我没有要求坐标完全相等而是用 IOU 大于 0.99 作为阈值因为 7 位小数的归一化坐标在来回换算时有舍入误差完全相等会误报。跑完之后看到COUNT MISMATCH的图片优先怀疑是 difficult 框被过滤或者某一套标注漏标回到 2.2 的 grep 命令去查。3. 用绳子检测数据集训练 YOLO划分、配置与第一轮跑通数据集校验通过后训练链路反而没有太多玄学把数据划分、配置文件、训练命令三件事做对第一轮模型就出来了。3.1 322 张图的划分要先固定随机种子322 张图片做目标检测最怕的是随手random.shuffle今天跑一次和明天跑一次结果对不上你很难判断精度的变化来自代码改动还是来自数据划分。我一般会把划分脚本固定成以下结构import random, shutil from pathlib import Path random.seed(42) # 固定种子保证每次划分结果一致 images sorted(Path(images).glob(*.jpg)) random.shuffle(images) train, val, test images[:270], images[270:312], images[312:] for split, files in [(train, train), (val, val), (test, test)]: dst Path(split) / split dst.mkdir(parentsTrue, exist_okTrue) for f in files: shutil.copy(f, dst / f.name) lbl Path(labels) / f{f.stem}.txt if lbl.exists(): shutil.copy(lbl, dst / lbl.name) print(f{split}: {len(files)} images)固定 seeds42 的意义在于可复现换任何机器跑同一份数据划分出来的是同一批图。270/42/10 的划分比例是 322 张里比较保守的做法训练集保证有足够样本验证集 42 张足够画出稳定的 PR 曲线测试集 10 张只做最终确认。如果图片本身就是连续视频抽帧同一个场景的相邻帧会被拆到三份里检验时会被相同背景骗到此时不能按文件列表随机打散要先按文件名前缀分组再切分比如同一机位拍的一组图只进 train 或只进 val。这一节做完数据集目录就固定下来了后面不管怎么改超参数对比的都是从同一批数据训练出来的模型指标变化才敢归因到超参数上。3.2 dataset.yaml两行路径、一个类别名别给 YOLO 出难题YOLO 系列读数据集的方式是统一的一个 YAML 文件描述数据集位置、类别数、类别名。拿绳子检测数据集来说写完是这样的path: rope_data train: split/train val: split/val test: split/test nc: 1 names: [rope]path是数据集根目录train和val是相对 path 的训练、验证图片目录。YOLO 训练时会自动在图片目录旁边找同级labels目录所以 labels 目录必须跟 images 目录在同一级且同名这是官方约定改不了。三个常见坑我在实际跑的时候都遇到过第一path写绝对路径后换机器就要改我统一用相对路径训练时 cd 到数据集上一级目录再执行命令第二names里的类名必须是英文中文名在画结果图时显示乱码还会影响后续导出成 ONNX 时的后处理第三YAML 只能用空格缩进不能用 Tab否则解析直接报错这个错误太小排查起来反而最费时间。如果你解压出来的目录结构和 3.1 里 split 出来的结构不一致不用慌只要你能在 YAML 里把train、val指到对应图片目录即可YOLO 不关心你的目录叫 train 还是叫 other只关心它能不能在图片目录同级找到 labels 目录。3.3 最小可用训练命令先跑通再谈调优环境层面用 conda 建一个独立的 Python 环境是最省心的方式。yolo 环境配置这个环节劝退了很多人其实核心就三条命令conda create -n yolo python3.10 -y conda activate yolo pip install ultralytics然后执行训练。我给出的最小可用命令如下yolo detect train \ datarope.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ patience30 \ workers4modelyolov8s.pt我一般写预训练权重路径而不是从头初始化。322 张图从头训练一个检测模型前几十轮 loss 根本压不下来用 COCO 预训练权重做迁移学习相当于把对“边缘、纹理、形状”的底层特征直接搬过来只在绳子这一个类上做微调这是小样本检测能跑通的关键。epochs150对小数据集偏够用配合patience30做早停连续 30 轮验证集指标不涨就自动停不会浪费算力也不会过拟合过头。imgsz640是速度和精度的折中绳子这种条状物如果现场图片很宽可以提到 1280代价是显存占用翻倍。batch16在 24G 显存以下建议降到 8否则 OOM 信息会来得比你想的快。训练日志里你主要盯一个数val_box_mAP50也就是 IOU 阈值 0.5 下的验证集 mAP。它比 loss 更直观loss 下降但 mAP 震荡说明模型开始过拟合了此时早停会兜底mAP50 一直趴在 0.1 以下多半是数据划分或标注坐标有问题回第 2 章校验。3.4 损失函数三个分量与绳子检测的对应关系YOLO 的损失函数大体上由三块组成分类损失负责把“是不是绳子”判对边界框回归损失负责把“框得准不准”优化到位此外还有一个分布焦点损失DFL在较新版本里用来细化框的定位。看训练日志时cls_loss和box_loss分开打印是有原因的绳子这类目标的标注框长宽比极度不均衡宽的框可能比例为十几比一回归损失对细微偏差非常敏感。一个 1280 宽的图中横向坐标偏 5 个像素对 mAP 的影响不大但细长框的纵向高度如果偏 5 个像素框就完全错位。所以当你发现 mAP50 还行、mAP50-95 很低时别急着加数据先去检查是不是框的上下边界标注得太随意。这类目标的标注容错率比方形目标低得多标注时少一个像素训练时回归损失就多一分震荡。这也是我上面反复强调要校验坐标换算的原因双格式转换中如果归一化坐标的舍入误差被放大长条框受到的伤害比方形框大得多。4. 绳子数据集训练常见问题与避坑5 条踩坑记录这一章直接把我在小数据集训练中遇到最多的问题列出来每条都是现象在前、原因居中、解决在后你可以把它当排查手册用。4.1 7z 解压报“CRC 失败”或“密码错误”现象解压到一半报CRC Failed或者明明密码是对的却一直提示错误。原因有两个一是压缩包在传输中损坏二是本地 7z 版本太老不支持压缩时用的新算法。解决先不急着重新下载用7za t rope_dataset.7z测试压缩包完整性会报告每个文件的 CRC 校验如果确认损坏就重新下载如果完整性没问题把 p7zip 更新到最新版或换 Windows 上的 7-Zip 新版本再解压。这个报错和密码无关很多人卡在“密码是正确的但一直报错”上其实是压缩工具版本问题。4.2 训练启动时报Assertion num_images 0或 “No labels found”现象训练命令执行后秒退或报找不到图片。原因dataset.yaml 里的路径写错或者 YOLO 没能在图片目录的同级目录找到 labels。解决先按 2.1 的做法用 find 数一遍图片和 TXT 的数量确认目录结构是images/xxx.jpg与labels/xxx.txt同级再用 python 打开 yaml 确认 path 拼写。不要凭感觉猜YOLO 的报错信息很明确顺着路径打印目录树半分钟就能定位。322 张的量级下图片名和标注文件名不一致是最常见的原因我习惯在训练前跑一遍 2.4 的脚本把 MISSNING TXT 全部查出来。4.3 验证集 mAP 很高实拍视频里绳子却频繁漏检现象训练完的模型在验证图片上表现不错一放到新拍视频里就漏。原因322 张图的场景单一模型把背景纹理和“绳子”绑定在一起另一个原因是默认置信度门限 0.25 对旧场景太严真实绳子和训练集里长得不太一样得分被压低。解决先在验证集上调 yolo 检测的置信度门限把门限降到 0.05 重新跑一次 val看 mAP50 是否明显回升如果回升明显说明模型其实学到了目标特征只是分数不够高部署时把 conf 调到 0.1 左右即可。如果降门限也没用就是数据覆盖不足得补拍。4.4 绳子交叉缠绕时模型把两条绳子标成一个框现象图中两条绳子交叉模型输出一个大框把两条都框进去。原因标注阶段用一个大框包住了交叉区域模型学到的模式就是“成片区域绳子”回归损失在交叉处被平均化反而学不出两条的边界。解决把交叉段拆开标注两条绳子各标各的框哪怕框部分重叠也没有关系如果交叉太密标注成本过高就要接受目标检测的边界——这种场景更适合转到实例分割或线段检测方向不要硬让检测模型猜重叠关系。4.5 labelimg 导出的 YOLO 标注和数据集自带标注混用导致类别错位现象自己补标了一批图和数据集自带的 TXT 混在一起训练结果模型把绳子识别成编号 1 的另一个类别或者训练时类别数和预想不一致。原因labelimg 导出 YOLO 格式时类别编号按你当时列表里的顺序赋值不同工具、不同列表顺序导出的编号意义不一样。解决所有标注统一归一到数据集原始格式编号 0 对应 rope补标前先建一个classes.txt内容只有一行 rope让 labelimg 读取这个类表再打标。补完后用 2.4 的校验脚本统一查一遍别让新增标注里的编号打架。5. 训练完别急着部署置信度门限、混淆矩阵与 322 张之后的扩充路径模型训练完我先不急着导出而是先跑一次带混淆矩阵的验证把模型的失败方式看清楚yolo detect val \ modelruns/detect/train/weights/best.pt \ datarope.yaml \ conf0.05 \ plotsTrueplotsTrue会生成混淆矩阵和 PR 曲线你要看的是“交叉场景下到底有没有检出绳子”而不是 mAP 数字。绳子这类细长目标漏检远比比错检更致命。实测中 322 张训出来的模型往往在规整背景上表现尚可一到真实现场就要面临背景、光照、角度三重变化。我的习惯是先用这个数据集把训练链路和部署链路全部跑通把标注工具、校验脚本、训练命令沉淀下来再集中精力补数据绳子检测最常见的扩充方式有三条——第一把单根绳子做旋转和长宽比扰动模拟缠绕姿态第二采集真实现场背景把绳子素材贴图合成新样本这比大幅增强更贴近真实分布第三用训练好的模型在未标注的新视频上做粗筛把高置信度帧挑出来人工复核作为下一轮训练素材。我自己跑这类小数据集时的经验是先承认 322 张是“跑通流水线”的规模不是“发布模型”的规模。把校验、训练、评估这串流程跑熟后面每补一次数据都能稳定地看到 mAP 变化比憋大招一次标几千张靠谱得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表