☰
万张遥感电力塔目标检测数据集:三格式标注与YOLOv8训练全攻略
2026/9/26 6:59:43 网站建设 项目流程

简介:面向遥感目标检测与电力设施巡检场景的YOLO电力塔目标检测数据集,包含10000张真实场景高清图片,由LabelImg标注,标注框质量较高。数据提供voc、coco、yolo三种标签格式,分别存放于不同文件夹,可直接用于YOLO系列模型训练,适合目标检测学习者、算法工程师及电力巡检智能化项目开发人员。图像覆盖平原、山地、城市等不同地貌,可有效提升模型泛化能力。压缩包共2000个文件,以1985个xml标签为主,另有yolo/txt标签文件、6个html训练教程和3个Python脚本,整体大小约764.62MB,目录清晰便于快速定位数据与脚本。目前已有655人浏览学习。通过该资源能快速拿到标准化数据,并参照附赠教程完成环境配置、模型训练与自定义数据集的改造,还能按需划分训练集、验证集和测试集,大幅节省数据准备时间。

1. 遥感电力塔目标检测数据集:为什么说三格式齐全比图片数量更重要

做遥感目标检测的人都有一个共识:图片好找,标注难凑。尤其是电力塔这种单一类别目标,公开数据集里几乎找不到像样的,自己标注一万张遥感图,标注成本高到能劝退大部分个人开发者。这份YOLO遥感电力塔目标检测数据集包含10000张遥感影像,全部标注完成,并同时提供VOC、COCO、YOLO三种格式的标签文件,外加划分脚本和训练教程,正好补上这个缺口。

我拆解这份资源时最在意的不是图片数量,而是三格式齐全这一点。实际工程里,你换一个框架就要换一种标签格式,VOC的XML、COCO的JSON、YOLO的TXT三种格式的坐标体系完全不同,手工转换极易出错。有了现成的三格式标签和划分脚本,从数据集到训练只差一条命令。适合谁用?正在做遥感目标检测、电力巡检相关项目的研究生和工程师,以及想用现成数据快速熟悉YOLO训练流程的入门者。这篇笔记我把数据组织结构、格式坐标关系、划分脚本逻辑、训练配置和踩坑记录全部拆开讲。

2. 数据形态与三种标注格式:先搞懂坐标体系再动手

2.1 遥感电力塔图片长什么样

这份数据集的图片是遥感影像切片,常见的做法是把原始大图切成512×512或1024×1024的小图,方便直接送入检测网络。电力塔在遥感影像里的特征是目标小、背景杂,铁塔结构呈细长线状,和道路、建筑物混在一起时肉眼都容易看漏。这意味着训练时如果直接套用COCO上常用的anchor参数,小目标召回率会很难看。

图片格式一般是JPG,文件名按编号排列。拿到底包后先不要急着训练,花十分钟把图片随机抽几十张出来看一遍,重点观察目标的尺度分布和背景复杂度。我的习惯是统计一下目标框的宽高比分布,如果大多集中在1:1到1:3之间,说明数据分布正常;如果出现大量极端长宽比的目标框,后面选anchor时要格外小心。

2.2 VOC格式:XML里存的是绝对像素坐标

VOC格式的核心是每个图片对应一个同名XML文件,存放在Annotations目录下。XML里最关键的是<bndbox>节点,保存目标的左上角和右下角绝对像素坐标。

<annotation> <folder>JPEGImages</folder> <filename>image_00001.jpg</filename> <size> <width>1024</width> <height>1024</height> <depth>3</depth> </size> <object> <name>electric_tower</name> <bndbox> <xmin>256</xmin> <ymin>300</ymin> <xmax>312</xmax> <ymax>420</ymax> </bndbox> </object> </annotation>

这里有个很容易被忽略的细节:VOC的坐标是绝对的像素值,而且是以图片左上角为原点,x轴向右,y轴向下。xmin和ymin是目标框左上角,xmax和ymax是右下角。如果从其他地方拿到VOC格式数据,第一步要想办法确认坐标是否真的符合这个约定,我遇到过有人把中心点坐标写进去的情况,训练时loss直接下不来。

2.3 COCO格式:单个JSON文件承载全部标注

COCO格式把所有图片信息和标注信息塞进一个JSON文件里,一份完整的COCO标注包含images、annotations和categories三个核心字段。每张图片在images里有一条记录,每个目标框在annotations里有一条记录,通过image_id关联。

{ "images": [ { "id": 1, "file_name": "image_00001.jpg", "width": 1024, "height": 1024 } ], "annotations": [ { "id": 1, "image_id": 1, "category_id": 1, "bbox": [256, 300, 56, 120], "area": 6720, "iscrowd": 0 } ], "categories": [ { "id": 1, "name": "electric_tower" } ] }

COCO的bbox格式是[x, y, width, height],这是和VOC最大的区别。VOC存的是两个点,COCO存的是左上角加宽高。类别ID从1开始,这个细节很关键,很多框架里如果背景也算作0号类别,类别ID从0开始,转换时错一位后面全是问题。

2.4 YOLO格式:归一化坐标和三者的换算关系

YOLO格式每个图片对应一个TXT文本文件,每行一个目标,格式是类别ID 中心点x 中心点y 宽 高,全部是相对于图片宽高的归一化值,范围在0到1之间。

0 0.27734375 0.3515625 0.0546875 0.1171875

对应前面VOC例子里的坐标,中心点x=(256+312)/2/1024=0.27734375,宽=(312-256)/1024=0.0546875。三种格式的换算关系用一张表格可以看得很清楚。

格式坐标表示坐标原点数值范围
VOCxmin, ymin, xmax, ymax左上角绝对像素
COCOx, y, width, height左上角绝对像素
YOLOx_center, y_center, width, height左上角归一化0~1

从VOC转到COCO:x取xmin,y取ymin,width取xmax-xmin,height取ymax-ymin。从VOC转到YOLO:x_center=(xmin+xmax)/2再除以图片宽,y_center同理。坐标转换的坑一般在除法和取整上,YOLO格式要求浮点数,保留六位小数足够,不要四舍五入成整数,否则小目标框会偏掉。

3. 划分脚本与格式转换:先防泄漏再谈训练

3.1 数据集划分的三个原则

拿到数据和标签之后,第一步不是转格式,而是划分训练集、验证集、测试集。一个科学的划分要满足三个条件:比例合理、分布随机、场景隔离。

比例上一般按7:2:1或者8:1:1划分。遥感数据有个特殊问题:同一个区域拍出来的切片背景高度相似,如果这些切片同时出现在训练集和验证集里,验证分数会虚高。最靠谱的做法是按场景或者按原始大图来分——把同一张原始大图切出来的切片全部放进同一个集合,而不是逐张随机分。这一点在训练教程里如果没提到,自己划分时要格外留意。

划分脚本做的事很直接:扫描所有图片文件名,按比例切分,把归属信息写入TXT列表文件。VOC格式的划分文件放在ImageSets/Main/下,文件名约定是train.txt、val.txt、test.txt。

import os import random random.seed(42) image_dir = 'JPEGImages' all_images = [f for f in os.listdir(image_dir) if f.endswith('.jpg')] all_images.sort() # 固定随机种子,保证每次运行获得相同划分结果 random.shuffle(all_images) # 按7:2:1划分 train_ratio, val_ratio = 0.7, 0.2 train_cnt = int(len(all_images) * train_ratio) val_cnt = int(len(all_images) * val_ratio) train_set = all_images[:train_cnt] val_set = all_images[train_cnt:train_cnt + val_cnt] test_set = all_images[train_cnt + val_cnt:] def write_split(file_list, filename): # 只写文件名不写扩展名是VOC的约定 with open(filename, 'w') as f: for img in file_list: f.write(os.path.splitext(img)[0] + '\n') os.makedirs('ImageSets/Main', exist_ok=True) write_split(train_set, 'ImageSets/Main/train.txt') write_split(val_set, 'ImageSets/Main/val.txt') write_split(test_set, 'ImageSets/Main/test.txt') print(f'train: {len(train_set)}, val: {len(val_set)}, test: {len(test_set)}')

这里固定random.seed(42)很关键。不固定种子的话,每次跑脚本划分结果都不一样,前期调参时前后两次实验的验证集不统一,指标没法对比。数据量小的时候可以一次划分终身使用,数据量大或者要反复做交叉验证时,再把划分逻辑封装成函数。

3.2 从VOC到YOLO的批量转换脚本

Ultralytics YOLOv8训练时只需要YOLO格式的TXT标签,所以拿到VOC格式后第一件事是批量转换。转换脚本的核心是解析XML,提取坐标,按图片宽高归一化,然后写入TXT文件。

import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_dir, class_names): tree = ET.parse(xml_path) root = tree.getroot() img_width = int(root.find('size/width').text) img_height = int(root.find('size/height').text) out_lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in class_names: continue class_id = class_names.index(name) 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) # 防止边界值越界 xmin = max(0, min(xmin, img_width)) xmax = max(0, min(xmax, img_width)) x_center = (xmin + xmax) / 2.0 / img_width y_center = (ymin + ymax) / 2.0 / img_height w = (xmax - xmin) / img_width h = (ymax - ymin) / img_height out_lines.append(f'{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}') img_name = os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, img_name + '.txt'), 'w') as f: f.write('\n'.join(out_lines)) class_names = ['electric_tower'] xml_dir = 'Annotations' yolo_label_dir = 'labels' os.makedirs(yolo_label_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if xml_file.endswith('.xml'): voc_to_yolo(os.path.join(xml_dir, xml_file), yolo_label_dir, class_names)

这段代码里class_names.index(name)这个操作要确保XML里标注的类别名和列表里的完全一致,大小写都不能差。我碰到过标注的人把类别写成带空格的,脚本跑完labels目录里全是空文件,此时很难发现,因为一张两张空的还能混过去,多了训练时每个batch都没有正样本。

3.3 转换后的验证步骤不能省

转换完成后不要急着训练,先写两行代码抽查验证。随机抽三五个TXT文件,看看类别ID是否在合理范围,坐标值是否都在0到1之间,目标框面积是否和原图对应。我之前吃过一次亏,转换脚本里忘记处理坐标越界的边界值,个别标注框右边界超出了图片宽度,归一化后大于1,训练时损失直接朝上走。

可视化的验证方法是把YOLO格式的坐标换算回像素坐标,用OpenCV画框叠加到原图上,人眼扫一遍就能发现坐标偏移问题。这一步花不了五分钟,但能省下后面几个小时的调试时间。

4. 基于Ultralytics YOLOv8的训练流程:环境、配置、一条命令跑通

4.1 环境配置与目录结构整理

Ultralytics YOLOv8是目前最主流的训练框架,pip直接安装即可,不过Anaconda环境里需要留意包版本的兼容性。我一般用Python 3.9配PyTorch 2.0以上版本,装完ultralytics之后跑一遍自带的检测demo确认环境没问题。

# 创建虚拟环境并安装必要依赖 conda create -n yolo python=3.9 conda activate yolo pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118

装完后把数据集整理成YOLO格式的标准目录结构。注意这里的images和labels目录需要分别存放,训练和验证的图片路径要与标签路径一一对应。

datasets/ electric_tower/ images/ train/ val/ test/ labels/ train/ val/ test/

训练前检查的一个关键点:labels/train/下每个TXT文件名要和images/train/下的JPG文件名完全匹配,包括扩展名处理方式。Ultralytics框架里如果图片缺少对应标签,默认当作背景图参与训练,1万张图里混进几十张缺标签的,损失曲线看不出大问题,但精度会莫名低一截。

4.2 数据集YAML配置与训练参数选择

在工程目录下新建一个electric_tower.yaml文件,指定数据路径和类别名称,这是训练的前置条件。

path: /path/to/datasets/electric_tower train: images/train val: images/val test: images/test names: 0: electric_tower

类别ID必须从0开始连续排列。如果数据集的YOLO标签里类别ID是1,而YAML里只有0号类别,训练时会直接报错或者把标签当噪声滤掉。这个和COCO格式从1开始不同,很多从COCO转过来的人会在这里翻车。

训练命令本身不复杂,复杂的是参数选择。

yolo train data=electric_tower.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16

从yolov8s预训练权重开始是性价比最高的选择,模型尺寸适中,单卡能跑,精度也比nano好一截。遥感电力塔属于小目标检测,imgsz设640时目标可能只有十几个像素,此时可以考虑设成1024或1280来提高小目标分辨率,但显存占用会同步上涨,batch要相应调小。

参数推荐值说明
modelyolov8s.pt从预训练权重开始,收敛更快
imgsz640~1024目标小时用大分辨率
batch16~32按显存调整,OOM时减半
epochs100~300遥感数据收敛慢,100起步
patience20验证集长期不涨就早停
lr00.01数据量1万张可以稍高

4.3 训练过程中的监控与常见现象

训练启动后,终端会实时刷新每次epoch的loss、精度和召回率。前10个epoch大概率看到loss震荡,这是正常的,等warmup结束后会稳定下降。遥感数据有个特点:背景复杂导致收敛比常规数据集慢,训练到第50个epoch左右,精度可能还在缓慢爬升,不要急着早停。

验证集指标要看mAP50和mAP50-95两个数字。前者衡量粗略定位能力,后者衡量精细定位能力。电力塔这种目标细长的检测任务,mAP50能到0.9以上才算合格。如果mAP50到0.9附近卡住不动,先从数据层面找问题,看标注框有没有大量错位,再考虑调anchor参数。我在这个数据集上训练时看到过一种现象:训练loss下降很漂亮,但验证mAP很低,一查发现是标签里混入了大量空文件,模型一直在学背景。

5. 避坑记录:五条从数据到训练的真实翻车经验

5.1 训练时loss变成NaN,前面的功夫全白费

现象:训练到第20个epoch左右,终端输出的loss突然变成nan,验证集精度直接清零。

原因:最常见的是学习率设置过大或者数据里出现了极端值。遥感切片如果存在全黑的图片,归一化后的像素值会异常,个别标注框坐标越界也会导致loss计算出现NaN。

解决:先把lr0从默认0.01调低到0.005重新训练。同时检查labels目录下所有TXT文件,用脚本扫描坐标值是否在0到1之外。如果确认有越界坐标,重新跑转换脚本,并且在转换时加上边界裁剪逻辑。

5.2 YOLO标签类别ID从1开始,训练时所有目标被当背景

现象:训练能跑通,loss在下降,但训练集的PR曲线完全没有正样本,val精度一直为0。

原因:VOC格式转YOLO格式时,类名映射表写错了。COCO格式的类别ID从1开始,直接拿COCO的JSON转YOLO,没有把类别ID减1,导致所有标签的类别ID变成1,而数据集的类别配置只有0号类别。

解决:转换脚本里把class_id = class_names.index(name)改成category_id - 1,转完再抽查一个TXT文件确认第一列全部是0。

5.3 划分脚本没有固定随机种子,调参实验指标对不上

现象:同一个数据集同一条训练命令,第一次跑mAP50是0.85,第二次跑变成0.82,两次验证集不一致导致无法判断改参数是否有效。

原因:划分脚本每次运行时重新打乱图片顺序,训练集和验证集的组成每次都不同。

解决:在划分脚本里固定random.seed(42),划分一次后把生成的TXT文件或者目录结构打包保存,后续所有实验共用同一份划分。

5.4 COCO格式面积字段算错,评估阶段AP异常偏低

现象:训练正常,但跑COCO评估脚本时mAP比用YOLO格式评估低一大截。

原因:COCO标注里的area字段用于小目标、中目标、大目标的分类统计,如果area算的是宽高之和而不是宽乘高,评估结果会乱。标注转换工具一般不会主动校验这个字段。

解决:转换时单独计算area = width * height并写入。从原始XML转COCO时,这个字段必须显式计算,不能默认0或者留空。

5.5 验证集和训练集切片来自同一张原始大图,精度虚高

现象:训练时验证集loss一直在降,mAP50超过0.95,但拿到新的遥感影像上测试,效果差得离谱。

原因:遥感大图切片的切片之间重叠度高,同一张大图切出的几十张小图被随机分到了训练集和验证集,验证集实际上参与了训练。

解决:划分时以原始大图为单位分桶,整张大图的所有切片只能进入同一个集合。如果你的划分脚本只拿到了切片后的文件名,无法回溯原始大图关系,那就要重新规划划分逻辑,或者在训练前手动检查相邻文件名是否同时出现在train.txt和val.txt里。

6. 验证与真实工程落地:用三类指标确认模型真的能用

模型训练完只是第一步,真正要确认的是它能不能扛住实际场景。我的习惯是训练结束后马上做三件事:跑验证集看指标、跑测试集对比、可视化推理结果确认目标位置准不准。

先跑验证集得到最终的mAP50和mAP50-95,再跑测试集。测试集在整个训练过程中没有参与过任何决策,指标会有一定回落,如果回落幅度超过5个百分点,说明模型过拟合了,此时回去加数据增强或者调大正则化系数。我见过有人把训练集指标当成模型真实水平汇报到项目周报里,然后被测试集打脸,这个教训在工程上不能犯。

可视化这步最容易被跳掉但最有价值。随机抽几十张测试集图片,把推理结果画框保存出来,对着看电力塔的检测框有没有偏移。遥感影像上电力塔经常在复杂背景里,比如田地、山体、房屋密集区,人眼能看出模型在哪些场景下漏检,在哪些场景下误检。这一步能帮你判断模型部署后的适用边界。

写一段简单的推理脚本检查检测框位置:

from ultralytics import YOLO model = YOLO('runs/detect/train/weights/best.pt') results = model.predict(source='test_images/image_00042.jpg', conf=0.25, save=True) for r in results: boxes = r.boxes.xyxy.cpu().numpy() # xyxy像素坐标 scores = r.boxes.conf.cpu().numpy() for box, score in zip(boxes, scores): x1, y1, x2, y2 = [int(v) for v in box] print(f'box: ({x1}, {y1}) -> ({x2}, {y2}), conf: {score:.3f}')

置信度阈值设置有个经验值:遥感小目标场景下默认的conf=0.25会漏掉很多低置信度但真实存在的目标,我的习惯是先调到0.1跑一遍,把输出框全看一遍再决定最终阈值。电力塔的纹理特征在遥感影像上不算特别显著,有些塔在阴影里置信度只有0.2左右,但从工程角度依然值得检测出来。

从那以后我每次拿到遥感检测数据集,都会在训练前先跑一遍数据完整性检查脚本:核对每个集合的图片和标签数量、扫描越界坐标、抽查五个TXT可视化确认坐标没有偏移,把这套流程强制走一遍再开始训练。坏数据带偏模型这种事,防比修便宜得多。这套检查步骤配合这份数据集的划分脚本做下来,第一轮训练基本不会翻车,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询