news 2026/9/15 6:14:20

YOLO航空卫星图像目标检测:从数据准备到调优实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLO航空卫星图像目标检测:从数据准备到调优实战

简介:面向yolo系列算法目标检测任务的航空卫星图像数据集,涵盖森林、公路、作物、河流、住宅、工业、果园、牧场及贫瘠土地等多类地物目标,共1366张带标签图像。资源已按训练、验证、测试划分完成,并附带data.yaml配置文件,可直接用于yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等主流版本训练与验证。包内同时提供YOLO格式与VOC格式两种标注文件:前者的txt文件逐行记录类别索引与归一化后的中心点坐标、宽高信息,后者的xml文件按Pascal VOC结构保存目标位置,便于不同工具链切换使用。全部文件共2000个,其中xml标注1230个、txt标注770个,压缩包大小约60.51MB,结构紧凑,下载后即可展开训练。适合目标检测初学者快速上手,也方便遥感领域研究者在多类别地物识别中直接复用标注数据;目前已有42人学习下载,可用于地物分类、农业监测、区域规划等应用研究。

1. 1366张带标签航空卫星图,YOLO训练的第一步不是写训练代码

用YOLO做航空卫星图像的目标检测,很多人第一反应是拿到数据后立刻解压、改个yaml、敲训练命令。但遥感图与自然图像差异很大:1366张带标签的航空卫星数据,类别覆盖森林、公路、作物、河流、住宅、工业、果园、牧场和贫瘠土地,这9类地物在图像上的形态完全不同,不是简单套用COCO预训练模型就能直接收敛的。常见的问题包括mAP虚高但实际漏检严重、后几类样本太少导致模型直接学不到、长条形地物如公路和河流被框切成多段。所以这篇直接把数据准备、格式转换、训练参数、评估排查和遥感特有的调优手段按一条线走通,适合已经把YOLO基本流程跑通、但不熟悉遥感数据的人。

2. 从zip到YOLO训练集:目录结构、标注格式与类别检查

拿到zip包,先别急着解压训练。先扫目录,确认标注格式,再做可视化核对。遥感数据集的标注规范差别巨大,有些是从公开遥感数据集转化的VOC格式,有些是Labelme的JSON输出,还有些是别人处理好的YOLO txt。三种格式的后续处理路径完全不同,第一步是花十分钟看清楚,而不是直接假设它是标准YOLO格式。

2.1 解压后先扫清目录结构与标注类型

我一般在Linux环境下先用find命令把目录结构拉出来,同时统计文件后缀:

mkdir -p ~/aerial_dataset cd ~/aerial_dataset unzip ~/downloads/yolo算法-航空卫星图像数据集-1366张图像带标签-森林-公路-作物-河流-住宅的-工业-果园-牧场-贫瘠的土地.zip find . -maxdepth 2 -type d | sort find . -maxdepth 2 -type f | sed 's/.*\.//' | sort | uniq -c

第一行是解压,第二行看目录层级,第三行统计文件后缀。-maxdepth 2把扫描范围控制在两层以内,避免嵌套太深时输出刷屏;uniq -c直接给出jpg、txt、xml、json各自的数量,一眼就能判断标注格式。如果发现还有嵌套的压缩包,先一并解压出来再继续。

接着抽一个标注文件看内容。YOLO格式的txt每行是class_id x_center y_center width height,全部是相对图像的归一化坐标;VOC格式的xml则包含<object><name><bndbox>节点;Labelme的json里有shapesimageWidth/imageHeight。三种格式决定了后续要不要写转换脚本。

find . -name "*.txt" | head -3 cat "$(find . -name '*.txt' | head -1)"

如果txt文件是空的,说明要么是背景图,要么是标注遗漏。对于1366张图像的中小规模数据集,空标签图是否保留要看你的任务:想检测所有地物就删掉,想做包含背景的分类就保留。我倾向于保留并单独建一个backgrounds目录,后面训练时可以作为负样本加入。

2.2 抽样可视化标签,逐类核对标注质量

格式确认后不要直接统计数量,而是先做可视化。标注文件与图像是否对齐、框是否框在目标上、有没有整张图只标了一个点的情况,这些问题只有人眼看过才能发现。我一般每类抽5张图,把YOLO标签画出来检查:

import cv2 img_path = "images/0.jpg" label_path = "labels/0.txt" class_names = ["forest", "highway", "crop", "river", "residential", "industrial", "orchard", "pasture", "barren_land"] img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path) as fp: for line in fp: parts = line.strip().split() if len(parts) < 5: continue cid, xc, yc, bw, bh = map(float, parts) x1 = int((xc - bw / 2) * w) y1 = int((yc - bh / 2) * h) x2 = int((xc + bw / 2) * w) y2 = int((yc + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[int(cid)], (x1, max(0, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.imwrite("check_0.jpg", img)

这段代码把归一化坐标乘回原图宽高,画出矩形框和类别名。注意YOLO里xc, yc是中心点坐标,绘制时要换算成左上角与右下角;putText里的max(0, y1 - 6)防止类别名画到图像外。如果发现某些图标注框偏移半个身位,说明标注质量不稳定,后续训练时要看着办,要么手动修正,要么在loss权重上做处理。

可视化要重点看三件事:长条形目标是不是被一个巨大框包住、密集小目标是不是只标了部分、类别名称有没有和实际地物对不上。这些在mAP里很难暴露,但在图上非常直观。

2.3 统计类别分布,先看类别ID映射是否连续

接下来统计每个类别出现的实例数。遥感地物的共性是类别极不平衡:森林、作物这类大块连片地物通常标注数量多,河流可能只有几十条,牧场和贫瘠土地面积大但实例少。这个统计直接决定后面训练时的采样策略和增强策略。

import os from collections import Counter label_dir = "labels" counter = Counter() img_covered = Counter() for f in os.listdir(label_dir): if not f.endswith(".txt"): continue cls_in_img = set() with open(os.path.join(label_dir, f)) as fp: for line in fp: parts = line.strip().split() if len(parts) < 5: continue cid = int(parts[0]) counter[cid] += 1 cls_in_img.add(cid) for c in cls_in_img: img_covered[c] += 1 for cid in sorted(counter.keys()): print(f"class {cid}: instances={counter[cid]}, images={img_covered[cid]}")

counter统计实例总数,img_covered统计每个类别至少出现一次的图像数。这两个数字分开看很重要:某个类实例数多但只集中在少数几张图里,和分散在几百张图里,训练行为完全不同。

结合标题中的9类地物,类别映射关系大致如下:

class_id类别常见形态对YOLO的挑战
0森林大块连片框内包含大量非目标区域
1公路长条弯曲宽高比极端,单框横跨多段
2作物条块纹理与果园、牧场边界模糊
3河流细长蜿蜒一个框无法覆盖连续体
4住宅密集小方块小目标聚集,漏检率高
5工业大尺度规则建筑与住宅容易混淆
6果园均匀纹理区与森林、作物边界交叠
7牧场大片草地与贫瘠土地难以区分
8贫瘠的土地低纹理裸土语义界线本身不清晰

这张表后面会反复用到。所有类别ID必须从0开始连续编号,如果原始数据里类别跳号或顺序不一致,要先做一个重映射字典再写训练配置。

2.4 VOC/JSON转YOLO txt的坐标换算与过滤规则

如果标注是VOC格式,转换成YOLO txt是绕不开的一步。VOC里的坐标是像素绝对值,YOLO需要归一化,换算公式是:

x_center = ((xmin + xmax) / 2) / img_width y_center = ((ymin + ymax) / 2) / img_height width = (xmax - xmin) / img_width height = (ymax - ymin) / img_height

对应脚本:

import xml.etree.ElementTree as ET class_mapping = { "forest": 0, "highway": 1, "crop": 2, "river": 3, "residential": 4, "industrial": 5, "orchard": 6, "pasture": 7, "barren_land": 8 } def voc_to_yolo(xml_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): name = obj.find("name").text cid = class_mapping.get(name) if cid is None: continue box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) if xmax <= xmin or ymax <= ymin: continue xc = (xmin + xmax) / 2 / img_w yc = (ymin + ymax) / 2 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cid} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}") return lines

脚本里先过滤掉宽高非正的框,防止某些标注工具导出时出现坐标颠倒。转换后建议再跑一遍可视化,确认ID没有因为类别名拼写不同而掉进cid is None分支。为什么非要在转换后确认一遍,因为遥感标注里的类别名经常带有空格、下划线或中文,比如barren landbarren_land会被解析成两个键,导致这一类别实例数直接归零。

3. 基于这份1366张遥感数据训练YOLO:配置文件、数据划分与超参

数据准备好了,才轮到训练。这里要强调一个前提:1366张图、9类地物,这个规模在遥感任务里属于中小型数据集,选模型、设超参的思路和用几万张自然图像训练完全不一样。模型选大了会过拟合,数据增强开猛了会破坏长条形目标,patience设太短又会错过晚来的收敛点。

3.1 模型选型:YOLOv8s起步,YOLO11留给对比实验

对这种规模的数据集,起步模型建议用YOLOv8s。为什么不选n?n的参数量小、训练快,但遥感小目标的语义特征提取能力偏弱;m或l在这个数据量下容易把训练时间拉长且收益不稳定。YOLO11比v8在主干和训练策略上有改动,但1366张图不足以把这部分优势发挥出来,可以先跑通v8s的baseline,再拿v8m做对比就够了。

3.2 写data.yaml并做按类别分布的数据划分

ultralytics的训练入口是yolo detect train,数据侧只需要一个yaml文件:

path: /data/aerial train: images/train val: images/val names: 0: forest 1: highway 2: crop 3: river 4: residential 5: industrial 6: orchard 7: pasture 8: barren_land

path是数据集根目录,trainval是相对路径,names必须与2.3节的class_id一一对应。注意类别名里不要带空格,barren land写成barren_land,否则后面评估指标打印时排版会乱。

数据划分不能简单random shuffle。遥感数据经常出现同一区域的多张图,随机切分会让验证集和训练集包含同一片地物的不同视角,导致mAP虚高。更稳妥的做法是先按文件名前缀或图像编号分组,再把组切分。以纯随机划分为例:

import random, shutil from pathlib import Path root = Path("/data/aerial") all_imgs = sorted((root / "images/all").glob("*.jpg")) random.seed(42) random.shuffle(all_imgs) split_idx = int(len(all_imgs) * 0.8) for split, imgs in [("train", all_imgs[:split_idx]), ("val", all_imgs[split_idx:])]: out_img_dir = root / "images" / split out_lbl_dir = root / "labels" / split out_img_dir.mkdir(parents=True, exist_ok=True) out_lbl_dir.mkdir(parents=True, exist_ok=True) for img in imgs: label = root / "labels/all" / (img.stem + ".txt") if not label.exists(): continue shutil.copy(img, out_img_dir / img.name) shutil.copy(label, out_lbl_dir / label.name)

random.seed(42)固定随机种子,保证每次划分结果一致。if not label.exists()跳过没有对应标签的图像,避免空标签进入训练。更细化的做法是按2.3节统计出的类别分布做分层抽样,保证河流、牧场、贫瘠土地这些少样本类别在验证集里也有出现,否则验证集的mAP会非常不稳定。

3.3 训练命令与核心超参

baseline训练命令:

cd /data/aerial yolo detect train \ data=aerial.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=8 \ patience=20 \ mosaic=0.8 \ fliplr=0.5 \ scale=0.3

model=yolov8s.pt是COCO预训练权重,遥感图和自然图像差异大,但底层的边缘、纹理特征可以迁移,1366张图的情况下从零训练收敛慢且容易陷入局部最优。patience=20表示验证集mAP连续20个epoch不增长就早停,遥感数据集小,过拟合来得快,没必要死等100个epoch。mosaic=0.8是YOLOv8默认的mosaic增强概率,对遥感数据要适当下调。mosaic把4张图拼成一张,大面积的森林、作物会被切得支离破碎,长条形公路和河流在拼接边界处语义断裂,模型容易学到错误的上下文。scale=0.3控制随机缩放的幅度,遥感地物尺寸相对稳定,不需要像自然图像那样做大幅缩放。

各项建议参数如下:

参数默认值建议值原因
imgsz640640起步,显存充裕可试960小目标依赖分辨率,但显存占用随分辨率平方上升
batch168~16受显存限制,batch太大会让BN统计不稳定
epochs100100数据量小,再长就过拟合
patience5020遥感数据收敛窗口短,早停更实际
mosaic1.00.8降低大面积地物被切碎的概率
fliplr0.50.5遥感图左右镜像语义保持不变
hsv_h0.0150.005地物颜色有领域一致性,过大的色彩扰动会让模型学错

3.4 类别不平衡的应对:复制少样本图而不是调loss权重

直接调loss权重在YOLO里不容易生效,因为YOLO训练是按图像采样的,不是按实例采样。更直观的做法是让少样本类别对应的图像在训练集里多出现几次。操作上把包含河流、贫瘠土地的图像路径复制到训练列表中,配合mosaic增强,相当于提高了这些图像的采样概率。复制后图的总量增加了但模型输入尺寸不变,训练代价增加有限。

代价是要小心过拟合,复制不是无限复制。我一般会把最少的类别复制到接近中位类别图像数的1.5倍就停,同时注意验证集不要因为复制而被污染。另一个辅助手段是把少样本类别的实例区域裁剪出来,通过Copy-Paste增强粘贴到不含该类别的图像上,这个在遥感地物上效果比通用目标明显,因为地物纹理与原图背景融合度天然较高。

4. 训练完先别信mAP:混淆矩阵、按类指标与遥感漏检排查

训练结束后,很多人直接看终端打印的总mAP就收工。遥感数据上这远远不够。9类地物形态差异大,总mAP会被森林、作物这些好分的类别拉高,河流、贫瘠土地的低分被掩盖。要按类检查,结合混淆矩阵判断到底哪些地物在互相打架。

4.1 从results.png与训练日志确认收敛状态

训练完成后进runs/detect/train目录,先看results.png。这张图包含box_loss、cls_loss、dfl_loss,以及验证集mAP50和mAP50-95。注意区分两类现象:如果cls_loss一直在降但val mAP不涨,通常是验证集和训练集分布不一致,或者验证集样本太少导致指标抖动;如果box_loss降到0.8左右就下不去,常见原因是标注框本身有偏差,模型在学习一个不稳定的回归目标。

遥感场景里值得额外留意的现象是mAP50高但mAP50-95低。这说明模型能把目标大致框出来,但框的边界不够准。小目标在640分辨率下可能只占十几个像素,IOU稍微偏移一点mAP50-95就掉得厉害。

4.2 跑validation并直接读混淆矩阵

用下面命令验证:

yolo detect val \ model=runs/detect/train/weights/best.pt \ data=aerial.yaml

跑完后runs/detect/val目录下会生成confusion_matrix.png。这张图比mAP更直接。森林和果园往往整片混淆,牧场和贫瘠土地互相误判,河流经常被漏检到背景里。如果看到某两类横向纵向都有明显响应,先别急着调模型,回去看这两类的标注是否真的可分。牧场和贫瘠土地在视觉上确实没有明确边界,标的人可能自己都拿不准,这种情况模型学不好不是模型的错。

4.3 用低置信度预测区分漏检和误检

验证集上漏检怎么排查?把置信度阈值调低重新预测,看模型到底给不给框。如果给了框但置信度低,说明模型学到了特征,只是打分不够高;如果低置信度下依然完全没有框,才是真的漏学。

from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") results = model.predict( source="images/val", conf=0.05, imgsz=640, save=True, project="debug_vis" )

conf=0.05把阈值压得很低,目的是把模型的所有预测都释放出来。逐个看保存下来的预测图,重点看三类现象:低置信度框是否刚好压在目标上,判断是置信度校准问题还是特征学习不足;同一目标是否出现多个互相重叠的半截框,说明NMS参数或目标形态有问题;长条形河流是否被切成几段框,这是单框检测长条目标的固有缺陷。

4.4 按类看AP,找到真正拖后腿的类别

验证集命令行输出的末尾会打印每一类的P、R和mAP:

Class Images Instances Box(P R mAP50 all 273 ... ... ... ... forest 273 ... ... ... ... highway 273 ... ... ... ...

重点看R(召回率)低的类别。召回低意味着有大量目标没被检测出来,在遥感地物里通常对应三类原因:目标太小、训练样本太少、目标形态长宽比极端。河流和公路召回落典型,原因是单个框很难覆盖整条连续地物,训练时框的宽高比分布和自然图像差异太大。此时如果只是想提升这类指标,可以在训练参数里开启rect=True,按宽高比分组batch,减少padding带来的无效计算:

yolo detect train \ data=aerial.yaml \ model=yolov8s.pt \ epochs=30 \ rect=True

rect=True会按图像的宽高比排序并分桶,同一batch内的图pad到相近尺寸,长条形遥感图的特征保留更好。注意它要求cache=True或允许预处理阶段重排数据,部分旧版本下与mosaic增强不兼容。

5. 遥感场景的YOLO调优手段:瓦片切分、多尺度推理与ONNX导出

遥感图像本身通常很大,1366张图如果原图是几千像素见方,全局resize到640会让小目标缩小到几乎不可见。解决这个问题最有效的手段是瓦片切分。

5.1 瓦片切分与标签同步偏移

把原始大图切成1024×1024的tile,切分时保留重叠区域,避免目标恰好落在切缝上。切图的同时必须把标签坐标同步偏移,否则标注位置全部错位。以下函数处理标注偏移与过滤:

def filter_tile_boxes(boxes, x0, y0, tile_w, tile_h, min_cover=0.6): keep = [] for x1, y1, x2, y2 in boxes: inter_w = max(0, min(x2, x0 + tile_w) - max(x1, x0)) inter_h = max(0, min(y2, y0 + tile_h) - max(y1, y0)) inter_area = inter_w * inter_h box_area = (x2 - x1) * (y2 - y1) if box_area <= 0: continue if inter_area / box_area >= min_cover: nx1 = max(x1, x0) - x0 ny1 = max(y1, y0) - y0 nx2 = min(x2, x0 + tile_w) - x0 ny2 = min(y2, y0 + tile_h) - y0 keep.append((nx1, ny1, nx2, ny2)) return keep

min_cover=0.6表示只有超过60%面积落在tile内的目标才保留,避免大量半截框污染训练。推理时每个tile独立预测,最后把所有框的坐标加上对应tile的偏移量,再做一次全局NMS合并重叠区域。重叠区建议设10%~20%,太少会让切缝两边的检测不稳定。

5.2 多尺度与TTA

训练完模型后,用原始图像直接推理往往不是最佳方案。ultralytics的predict支持多尺度测试时增强:

yolo detect predict \ model=runs/detect/train/weights/best.pt \ source=test_images \ imgsz=960 \ tta=True \ conf=0.15

tta=True会对输入做翻转和尺度变换后融合预测框,对小目标召回有明显收益,但推理时间会增加到原来的3倍左右。放在batch推理服务前要评估延迟预算。单独设imgsz=960而不开TTA是一个性价比更高的中间档位。

5.3 导出ONNX并固定输入尺寸

模型验证没问题后一般要导出部署。先用ONNX导出:

yolo export \ model=runs/detect/train/weights/best.pt \ format=onnx \ dynamic=True \ opset=12

如果部署端是固定尺寸输入,dynamic=True建议关闭,固定比如640或960,动态尺寸在TensorRT和OpenVINO上的兼容性开销不值得。导出后用onnxruntime验证一遍:

import onnxruntime as ort import numpy as np session = ort.InferenceSession("best.onnx", providers=["CPUExecutionProvider"]) input_name = session.get_inputs()[0].name img = np.random.randn(1, 3, 640, 640).astype(np.float32) outputs = session.run(None, {input_name: img}) for o in outputs: print(o.shape)

如果输出shape与预期一致,再把瓦片切分的tile推理、坐标偏移、NMS合并这三段串起来,跑一遍完整测试图,确认切到1024或960尺寸下漏检情况最轻。对这份1366张带标签的遥感数据,把瓦片大小设成tile_size=1024, overlap=128,配合imgsz=960的TTA推理,一般能同时兼顾小目标召回率和漏检边界切碎的问题。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 6:12:49

金融App支付漏洞攻防实战与安全架构设计

1. 金融App支付漏洞的现状与挑战金融App支付漏洞已经成为移动互联网时代最严峻的安全威胁之一。根据我过去三年参与金融安全审计的经验&#xff0c;超过60%的金融类App至少存在一种高危支付漏洞。这些漏洞轻则导致用户资金损失&#xff0c;重则引发系统性金融风险。支付漏洞主要…

作者头像 李华
网站建设 2026/9/15 6:12:34

DANN实现轴承跨工况故障诊断:Python实战与调优

简介&#xff1a;本资源是一套基于迁移学习DANN&#xff08;Domain-Adversarial Neural Network&#xff09;模型的轴承故障诊断完整实践方案&#xff0c;面向计算机、机械、自动化等专业本科生及初阶AI学习者&#xff0c;解决跨工况下振动信号特征分布差异导致的诊断泛化性差问…

作者头像 李华
网站建设 2026/9/15 6:12:13

YOLO+MobileFaceNet会议签到系统:真实场景落地实践

1. 项目概述&#xff1a;一个能真正落地的会议签到系统长什么样&#xff1f;我带过六届计算机专业毕业设计&#xff0c;每年都会遇到十几份“人脸识别签到系统”选题。但90%的项目停留在PPT里——摄像头一拍&#xff0c;框出人脸&#xff0c;打个名字标签&#xff0c;就叫“完成…

作者头像 李华
网站建设 2026/9/15 6:12:07

YOLOv8扶梯逆行检测系统:方向建模与实时告警落地实践

简介&#xff1a;本资源是一套基于YOLOv8实现的商场扶梯逆行行为智能预警系统&#xff0c;面向计算机科学、人工智能、自动化等专业的在校学生及初学者&#xff0c;解决公共场所安全监管中的关键视觉识别问题&#xff0c;特别适合作为毕业设计、课程设计或项目原型快速验证。压…

作者头像 李华
网站建设 2026/9/15 6:12:01

Solidigm SSD如何通过MLPerf Storage v3.0七项严苛IO测试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 6:11:52

YOLOv8扶梯逆行行为识别系统:实时方向判别与边缘部署

简介&#xff1a;本资源是一套基于YOLOv8实现的商场扶梯逆行行为智能预警系统&#xff0c;面向计算机、人工智能、自动化等专业的在校学生与初学者&#xff0c;解决公共场所安全监管中关键异常行为识别与实时告警的实际问题&#xff0c;特别适合作为毕业设计、课程设计或项目原…

作者头像 李华