ARTICLE DETAIL

资讯详情

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

VOC+YOLO双格式电塔数据集:244张小样本遥感检测实战

VOC+YOLO双格式电塔数据集:244张小样本遥感检测实战 简介面向遥感目标检测和电力设施巡检场景一份包含244张遥感图像的电塔检测数据集同时提供Pascal VOC与YOLO两种标准标注格式类别统一为dianta共包含504个矩形框。相较于仅有单一种类标注的数据集它省去了格式转换环节可直接用于YOLO系列、Faster R-CNN、SSD等常见检测模型的训练、验证与算法对比也适合目标检测初学者和遥感应用开发者用于模型微调与教学实验。资源共734个文件其中jpg原图、xml的VOC标签、txt的YOLO标签以同一文件名相互对应覆盖全部244个样本压缩包整体大小78.4MB解压后结构清晰便于按样本索引检索。标注工作通过labelImg完成矩形框定位统一、类别和坐标均经过合理整理可作为电力杆塔识别任务的基准数据使用。目前已有280人学习下载适合需要真实遥感样本完成课程设计、毕业设计或算法复现的读者。1. 244 张 VOCYOLO 双格式电塔数据集小样本遥感检测能做什么把 244 张遥感图像直接丢进目标检测模型训练第一次跑完的 mAP 大概率不会好看。但如果这批图像全是电力铁塔情况就不同了电塔在遥感视角下有极高的结构化特征——笔直的塔身、规则的横担、明显的长阴影类别单一且外观稳定。这类数据集的真正价值是让你用最小的标注成本把「遥感图像 → 目标检测」这条链路完整跑通。标题里同时给了 VOC 和 YOLO 两种格式意味着拿到压缩包后不需要做任何标注格式迁移一边是 XML 路径、一边是 txt 路径直接对接 labelImg 生态和 ultralytics 生态。适合的人群有两类一类是想在遥感场景里验证小目标检测方案的工程师另一类是刚接触 YOLO 训练、需要一个干净数据来理解整个 pipeline 的新手。244 张图确实撑不住大规模训练但用于微调、对比实验和数据增强策略验证已经足够暴露问题。2. 数据包内部结构VOC 与 YOLO 目录的对应关系2.1 一个包里两套标注体系为什么是双保险VOC 格式是计算机视觉的老牌标注标准核心是一张图像对应一个 XML 文件XML 里用绝对像素坐标描述每个目标的包围框。YOLO 格式则是 darknet 时期定下的归一化坐标体系每张图对应一个同名 txt 文件每行是「类别id cx cy w h」所有数值都是相对于图像宽高的比例。这两种格式的差别不是字段名不同而是坐标基准不同。VOC 的xmin/ymin/xmax/ymax是像素绝对值图像一旦 resize 就要全部重算YOLO 的归一化坐标天然与图像尺寸解耦训练时无论输入到 640 还是 1024标注都不需要重新生成。数据包同时提供两套格式常见做法是AnnotationsXML和labelstxt内容一一对应你可以任选一条技术栈直接用也可以用两者互相校验。2.2 打开 VOC 侧JPEGImages、Annotations 与 XML 字段解压后典型的 VOC 目录结构如下目录或文件作用JPEGImages/存放全部 244 张 JPG 图像Annotations/每张图对应一个 XML 标注文件ImageSets/Main/train.txt训练集图像名列表无扩展名ImageSets/Main/val.txt验证集图像名列表随机挑一个 XML 文件打开核心信息集中在object节点。类别基本只有一个name字段可能是tower、pylon或者powerline_tower取决于采集方的命名习惯。bndbox里的xmin/ymin/xmax/ymax描述的就是电塔主体在图像中的像素范围。有几个点要特别确认size节点里的 width 和 height 是否和实际 JPG 尺寸一致是否存在某个 XML 有object块、但文件名没有出现在train.txt或val.txt中是否有可能出现一张图里标注了两个电塔的情况——这在电塔检测里很常见因为 244 张图里部分场景确实有前后塔重叠或同框双塔。2.3 打开 YOLO 侧images、labels 与归一化坐标对应目录下是images/train、images/val、labels/train、labels/val。打开一个 txt 文件典型的行内容是0 0.492188 0.449219 0.156250 0.273438这行的含义是类别 id 为 0数据包只有一个类别理论上所有行的首位都是0建议确认classes.txt里是否只有一行目标中心点在图中 x 方向 49.2% 的位置y 方向 44.9% 的位置框宽占图像宽的 15.6%框高占图像高的 27.3%。这里有个检查点如果 XML 里的坐标除以图像宽高后和你手上的 txt 数值差超过 0.001说明转换过程中可能存在 resize 或坐标偏移问题。另一个常见问题是类别 id 漂移——数据包说是 1 类但labels里出现了1甚至2这种情况在整理数据集时偶尔出现训练前必须先清理。2.4 拿到数据后先做的四项检查不要急着训练多花 10 分钟做一次完整性校验。第一项是数量对照JPEGImages的数量要等于Annotations的数量同时也要等于labels下所有子目录图片数量之和。第二项是文件名字匹配用jdeps思路写个短脚本把三种后缀的文件名取交集找出那些有图无标注或者有标注无图的孤例。第三项是框有效性读取所有 XML 的 bndbox 和所有 txt 的 w/h找出宽高为 0、坐标为负、或者超出图像边界的标注框。第四项是 class 分布即使只有一个类别也要统计每个 train 和 val 子目录里标注框的数量确认没有出现 val 集里只有几十个框、train 集几百个框这种极端失衡。# 检查文件和标注数量是否对得上 ls JPEGImages | wc -l ls Annotations | wc -l ls labels/train/*.txt | wc -l ls labels/val/*.txt | wc -l # 找出有 txt 但没有对应 jpg 的文件名 for f in labels/train/*.txt; do base$(basename $f .txt) [ -f images/train/$base.jpg ] || echo missing: $base done刚解压完先不要动labels目录建议跑完上面的检查再决定是否直接训练。数据包往往还附一个classes.txt或obj.names里面大概率只有一行类名这个文件在训练前要保留好后面写数据配置会用到。3. 格式转换脚本手写一个可靠的 VOC 转 YOLO 转换器3.1 为什么不直接复用网上转格式脚本网上搜「VOC转YOLO」能出来一大堆脚本多数是处理 PASCAL VOC 公开数据集的默认类别是 person、car、dog 那 20 类你拿过来直接跑大概率会得到两个结果要么把所有类别都转成从 0 开始的连续 id要么因为你的 XML 里只有一个自定义类名而报错。更重要的问题是很多脚本把图片宽高写死在代码里而数据包中图像尺寸可能并不统一这时轻则标注偏移重则目标框整体漂移。所以与其用别人不透明的脚本不如自己写一个只在已知目录结构上运行、每一步都能打印结果的转换器。3.2 转换脚本读取 XML 输出归一化 txt下面是以 VOC 侧为基准、生成 YOLO 侧 txt 的最小 Python 脚本以Annotations为输入输出到labels_converted目录。假设 XML 中只有一个类别类名以classes.txt第一行为准。import os import glob import xml.etree.ElementTree as ET # 1. 读取类别这里只取第一行作为唯一类别 with open(classes.txt, r) as f: classes [line.strip() for line in f.readlines() if line.strip()] print(classes:, classes) class_to_idx {name: idx for idx, name in enumerate(classes)} # 2. 遍历所有 XML 文件 xml_list glob.glob(Annotations/*.xml) os.makedirs(labels_converted, exist_okTrue) for xml_path in xml_list: tree ET.parse(xml_path) root tree.getroot() # 3. 图像尺寸一定要从 XML 的 size 节点读不要从图片文件读 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) if img_w 0 or img_h 0: print(skip zero-size:, xml_path) continue lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_to_idx: print(unknown class:, name, in, xml_path) continue bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 4. VOC 的 xmax/ymax 是真实像素坐标直接计算宽高 w xmax - xmin h ymax - ymin if w 0 or h 0: print(invalid box in, xml_path, name) continue # 5. 转成 YOLO 的归一化中心点格式并做越界截断 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h nw w / img_w nh h / img_h cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) nw min(max(nw, 0.0), 1.0) nh min(max(nh, 0.0), 1.0) lines.append(f{class_to_idx[name]} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}) # 6. 输出 txt 文件名保持和 XML 同名 base os.path.splitext(os.path.basename(xml_path))[0] out_path os.path.join(labels_converted, base .txt) with open(out_path, w) as f: f.writelines(line \n for line in lines) print(converted:, base, f({len(lines)} boxes)) print(done. total files:, len(xml_list))3.3 脚本逻辑与参数说明脚本的关键决策有三处。第一图像宽高从 XML 的 size 节点读取而不是用 PIL 去打开 JPG因为标注流程中经常出现「原始图 resize 后才标注」的情况XML 里写的才是标注时的真实尺寸。第二归一化后做了min/max截断这能避免极端标注越界导致训练报错或 loss nan。第三框宽高直接用xmax - xmin不要用xmax - xmin 1因为 YOLO 的归一化坐标基准确认的是像素边界差 1 个像素在归一化后影响微乎其微但遇到小目标时w从 3 变成 4 会让目标尺寸统计失真。跑完后对比labels/自带的 txt 和labels_converted/的输出如果两者数值差异大说明数据包里的 VOC 和 YOLO 标注并非同源。这类双格式数据包最怕的就是其中一版标注后来被修改过而另一版没跟上。用diff -r labels labels_converted可以直接看到差异。4. 用 YOLOv8 训练自己的数据集目录组织与训练命令4.1 配置文件从解压包到 ultralytics 标准目录训练前把数据整理成 ultralytics 期望的结构。我一般会在项目根目录下建立一个datasets/tower/文件夹直接把数据包的images和labels两个目录按比例分配进去datasets/tower/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml这里最容易踩的坑是 images 和 labels 的目录数量不一致——images 里分成 train/val 两个子目录labels 里也要对应完全相同的子目录名。YOLOv8 是通过图像路径前缀把 images 替换为 labels 来找对应标注任何一个目录缺失都会在训练时打印警告并跳过对应图像。data.yaml 的写法如下path: ./datasets/tower train: images/train val: images/val names: 0: towernames的 id 顺序必须和 txt 里的首位数字严格对应。如果你数据包的classes.txt里只有一个名字上面这份配置直接可用如果发现 txt 里有类别数字 1而这里只定义了 0训练时会报 out of bounds 错误。另一个细节是path建议写成相对路径方便在多台机器间迁移但如果你的训练脚本要在不同工作目录下启动就改成绝对路径。4.2 训练命令与参数说明yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz1024 \ batch16 \ device0 \ project./runs \ nameelectric_tower_v1参数本次建议值说明modelyolov8n.pt244 张小数据量用小模型起步避免欠拟合epochs100初始值配合早停策略再决定是否增加imgsz1024遥感图像电塔是小目标640 会丢失细节batch16 或更低显存不够时优先降 batch而不是降 imgszdevice0GPU 编号CPU 训练会把 device 设为 cpuproject / name./runs / electric_tower_v1输出目录便于多次实验对比遥感图像和自然图像最本质的区别是目标尺度占比。244 张图里电塔的包围框可能只占整张图像的十分之一甚至二十分之一如果训练时把 imgsz 从原图尺寸强行缩到 640电塔的像素尺寸会被压缩到二三十个像素级别小目标检测器的特征层根本提不到有效信息。因此这里的 1024 不是随意写的大数字而是「尽可能贴近原始图像尺寸」的选择实践里也有人直接设 1280 来保细节。4.3 首次训练要知道的 3 个观察点训练跑起来之后先不要只盯 mAP。前 10 个 epoch 看train/box_loss是否稳定下降如果 loss 呈锯齿状反复、甚至发散大概率是学习率或 batch 的问题可以顺手把modelyolov8s.pt换掉看参数量的影响。第二个观察点是val/dataloader的进度条上是否有 skipped 图像提示有就是目录结构不对。第三个观察点是用yolo predict对 val 集画几张框落地检查电塔的框是不是在正确的位置——有工程师遇到模型把地面纹理当塔体框出来的情况这种错误在 val mAP 里可能看不出来但可视化一眼就暴露。5. 增强与验证基于损失函数和混淆矩阵的小样本调参5.1 增强策略围绕 244 张图怎么凑够训练量数据量只有 244 张时增强不是辅助手段而是核心手段。YOLOv8 默认开启 mosaic 增强它把 4 张图拼接成一张对电塔这种多尺度目标有显著帮助推荐保留。另外两个值得调整的增强参数是flipud0.5和degrees5.0遥感图像不存在「上下颠倒」的语义问题垂直翻转对电塔检测完全无害旋转角度不要设太大超过 15 度会让塔身长宽比严重变形。还有一个在遥感检测里被验证过的通用做法把原图切块tiling。把 1024×1024 的图按 512×512 切份再对每个 patch 做训练相当于把电塔的尺寸在原图上放大了两倍。切块时要注意框超过边界的情况要么丢弃越过边界的标注框要么做「框中心在 patch 内才保留」的判断。增强参数调整后用yolo detect train的augmentTrue重新训练不少小样本项目靠这一步就能把 mAP 提升 5 个点以上。5.2 用损失函数曲线判断该不该停小样本数据集容易过拟合epochs 设到 200 并不代表要跑满。关注results.csv里的val/box_loss曲线如果验证集 loss 连续 20 个 epoch 不再下降甚至回升就说明模型开始记住训练集的噪声了。这时可以停掉训练回退到验证 loss 最低的那个权重文件通常以best.pt命名。5.3 验证时不只看 mAP还要看单类混淆单类别检测的 mAP 只是一个数字真正反映模型问题的是预测框的分布。用验证集跑一次推理按置信度排序输出前几个误检样本重点观察是否有大量低置信度预测落在背景纹理复杂的区域比如农田、立交桥和重复结构的建筑区屋顶。另一个验证手段是统计所有预测框的宽高比分布电塔的高宽比通常在 1.5 到 3 之间如果模型输出了大量接近正方形的预测框优先怀疑是背景误检。对于 244 张电塔这种小样本遥感检测最后一招是交叉验证把 val 集重新打乱划分一次再训练如果两次的 mAP 波动超过 3 个点说明当前模型对数据划分方案太敏感部署时有必要收集更多标注图来压方差。本文还有配套的精品资源点击获取
返回列表