
简介面向毕业设计、课程设计、课程大作业及遥感目标检测入门进阶的Python实现源码与模型包基于FAIR1M2.0遥感监测数据集训练完整覆盖环境安装、数据集整理、模型推理、结果分析与项目说明可用于项目复现、二次开发或初期方案演示。压缩包共404个文件以350个Python脚本为主体包含检测、训练、推理和工具脚本22个YAML配置文件负责模型结构、训练超参与数据路径等参数管理22个Markdown文档提供项目说明、配置说明与模型说明另有CSV、IPYNB、TXT等文件支撑结果分析与实验复现整体约7.54MB。已有729人浏览学习。特别适合计算机、人工智能、通信工程、自动化、电子信息等专业学生用于毕设、课设或初期项目演示代码经测试可运行并附有FAIR1M2.0数据集的train_color、train_gray预处理与目录组织说明方便快速迁移到自有数据。基础较好的读者还可基于此代码修改扩展实现自定义遥感目标检测功能。1. 遥感图像物体目标检测这份源码模型项目说明的zip该怎么用把这份基于python实现遥感图像物体目标检测的zip解压之后你面前大概率是三类东西一整套能跑的python源码、训练好的模型权重文件、一份项目说明文档。遥感目标检测和普通目标检测最大的差别在于目标小、方向任意、背景复杂直接把自然场景的检测流程搬过来mAP往往惨不忍睹。这份zip的价值不在于代码本身有多惊艳而在于它把数据准备—模型训练—推理评估这条链路预先串好了。你要做的不是从零搭环境而是理解这条链路、替换成自己的数据再跑起来。这篇笔记适合两类人遥感专业的硕士生以及想把检测能力接进GIS工作流的工程师。前者需要快速出结果后者需要稳定可交付的产出。我会按拆包、数据准备、训练、推理避坑、结果落地的顺序把这份项目包从里到外讲透并附上可以直接抄走的脚本和参数。文中提到的文件路径和命令都按最常见的YOLO系遥感项目写法来具体命名以你手里的zip为准。2. 把zip包拆开看源码、模型权重与项目说明的分工拿到zip先别急着train先花十分钟把家底盘清楚。这一步决定你后面是顺畅复现还是反复翻车。项目包里每个文件都有它的用途混着用会出大问题。2.1 源码目录结构train、detect、val三剑客以及辅助模块怎么认一个典型的遥感目标检测项目代码量通常不大核心入口只有三个train.py负责训练detect.py负责推理val.py负责评估。剩下的models、utils、data目录是辅助模块。我拿到zip的第一步不是读代码而是先看项目说明确认它基于哪个检测框架——YOLO系还是Faster R-CNN标注格式是VOC、COCO还是YOLO的txt。这个判断直接决定后面所有数据准备工作往哪个方向走。常见目录结构大概是这样的project_root/ ├── train.py # 训练入口 ├── detect.py # 推理入口 ├── val.py # 评估入口 ├── models/ # 网络结构定义 ├── utils/ # 数据加载、loss、指标工具 ├── data/ │ ├── images/ # 原始遥感影像 │ └── labels/ # 标注txt/xml ├── weights/ │ ├── best.pt # 验证集最优权重 │ └── last.pt # 最后一轮权重 └── 项目说明.md注意观察train.py和detect.py是否独立。如果zip里只有detect.py没有train.py那它大概率只是一个推理demo训练脚本得自己补反过来如果models目录下缺了网络定义文件训练一启动就会报ModuleNotFoundError。这种结构性检查五到十分钟就能做完省掉的是后面一整天排错时间。数据目录里如果images和labels各有一份且数量对得上说明作者至少跑通过一遍全流程这种包的可信度会高很多。如果只有images没有labels那数据准备工作要从头做起别指望训练脚本能自动标注。2.2 模型权重怎么认pt、pth、onnx三种格式的适用场景权重文件是项目里最容易被误用的一块。同样是模型后缀不一样使用方式完全不能混。遥感目标检测项目里最常见的三种格式差别很大后缀来自哪个生态能不能直接推理典型用途.ptPyTorch/YOLO能但依赖原项目网络定义用best.pt做迁移学习起点.pth纯PyTorch保存取决于保存的是state_dict还是整模型配合原模型类加载.onnx跨平台中间格式能且不依赖训练框架部署到CPU/边缘设备判断权重能不能用的土办法看项目说明里写的是预训练权重还是微调权重。预训练权重一般是在COCO或ImageNet上训出来的用途是当迁移学习的起点微调权重是用遥感数据训出来的可以直接拿来推理。如果说明文档只写下载权重没写来源先用一张带明显目标的遥感大图跑一次detect.py验证输出的框靠谱再当正式权重用。很多坑出在格式上。.pt和.pth看起来都是PyTorch文件但.pt权重在加载时必须能找到与训练时一致的网络结构定义models/yolo.py版本对不上就报尺寸不匹配。.onnx则不需要原始网络定义只要输入尺寸对得上就能跑这也是为什么我看到很多人做生产环境部署时都会额外导出一份onnx权重。2.3 项目说明文档先看哪三节环境依赖、数据格式、训练命令项目说明文档很多人解压后直接跳过这是坏习惯。它有明确的阅读顺序先看环境依赖再看数据格式最后看训练命令。环境依赖决定你能不能把程序跑起来数据格式决定你的标注要不要重做训练命令决定你用多大代价复现结果。环境依赖这块最稳妥的是项目里带了requirements.txtpip install -r requirements.txt装之前先确认两件事Python版本和PyTorch版本。遥感目标检测项目常用Python 3.8到3.11PyTorch 1.8到2.x。如果项目要求的torch版本和你本机CUDA版本不匹配会出现torch.cuda.is_available()返回False这种典型问题。这时候不要盲目重装先确认python安装时的版本适配再按CUDA版本选对应的安装命令。python环境配置是这里最常见的翻车点很多人卡在这一步一整天。数据格式部分要看清楚标注是水平框还是旋转框。水平框的标签文件里每行是类别id 中心x 中心y 宽度 高度旋转框会在后面多一个角度值。格式看错整个训练白跑。2.4 最小运行验证一条命令把detect.py跑通正式训练之前一定先把推理跑通。这一步能验证环境、权重、数据读取链路是否正常也让你直观看到模型的检测效果。最小推理命令通常长这样python detect.py --weights weights/best.pt --source data/images/0001.tif --img-size 640参数说明--weights指定权重路径--source是输入影像路径单张图、文件夹都行--img-size是推理尺寸遥感影像建议640起步太小会丢小目标。如果项目用YOLOv5系列命令基本一致但注意YOLOv8里img-size改成了imgsz传参名称对不上会直接报unexpected argument。如果这一步报错优先看三点权重路径是否存在、detect.py里import的模型模块是否能找到、输入图片路径是否含中文。中文路径会让OpenCV读图失败这是国内开发者踩得最多的坑把data目录全部改成英文路径90%的文件读取问题直接消失。跑通之后output目录会生成带检测框的标注图。看到框的位置基本正确说明整个项目的链路已经打通这时候研究训练和调参才有意义。3. 遥感图像数据准备从原始影像到可训练的标注集数据准备是遥感目标检测里最枯燥但最重要的环节。模型结构决定精度上限数据质量决定能不能逼近这个上限。这一章把数据集选型、标注工具、切图、格式转换一次讲清楚。3.1 遥感数据集选型DOTA、RSOD、HRSC2016怎么选遥感目标检测起步阶段最好先别用自己的数据用成熟的开源数据集把流程跑通最划算。行业里常用的三个数据集各有偏向选哪个取决于你的项目目标。数据集类别数标注形式适合场景DOTA15旋转四边形通用多类检测、算法对比RSOD4水平矩形框流程验证、小样本调参HRSC20161旋转矩形框船只专项、方向框研究DOTA是航空影像中规模最大的检测基准包含飞机、车辆、船只、储油罐、桥梁等15个类别标注是带旋转角度的四边形框适合做通用遥感目标检测的起点。RSOD规模小但类别干净只有飞机、操场、立交桥、船舶四类适合快速验证流程。HRSC2016专注船只检测图像来自不同分辨率适合做单一类别精细化检测。选数据集时先看自己需求如果做多类目标普查DOTA最合适但它单张图尺寸大必须切图训练如果只是验证源码能否跑通RSOD最快下载后基本不用清洗如果项目要检测船只且要方向信息HRSC2016的格式和DOTA相近但类别单一训练容易收敛。多数新手用RSOD入门跑通后再换DOTA提精度这条路线最省时间。3.2 遥感图像标注工具roLabelImg处理旋转框LabelImg兜底水平框标注工具的选择完全取决于模型支持哪种框。YOLO系列默认输出水平矩形框训练数据只要四个坐标值。但遥感场景里船只、飞机往往斜着排列水平框会框进大量背景模型学起来很吃力。如果项目说明里写了支持旋转框标注工具选roLabelImg它能画带角度的矩形框并输出旋转坐标。如果项目只支持水平框LabelImg就够了它导出的VOC xml或YOLO txt是通用格式。这里要特别提醒不要把旋转框和水平框混在一个数据集里标。同一批数据里既有旋转框又有水平框标注解析脚本会读取到不一致的字段训练时loss异常你根本查不出原因。我在这上面吃过亏——用roLabelImg标了200张图结果发现模型只支持水平框全部重标白白浪费两天。所以标注前一定先确认模型能力而不是先动手标。工具本身都是免费开源的重点不在工具在与模型匹配。3.3 滑窗切图参数怎么设小目标检测的前提操作遥感影像动辄几千乘几千像素直接整图喂给GPU不现实目标在整图里占比也小得可怜。滑窗切图是遥感目标检测的基础操作把大图切成模型输入尺寸的子图目标在子图里所占比例变大模型才能学得到。import cv2 from pathlib import Path def sliding_window_crop(image_path, output_dir, crop_size608, stride304): img cv2.imread(str(image_path)) h, w img.shape[:2] img_name Path(image_path).stem count 0 for y in range(0, h - crop_size 1, stride): for x in range(0, w - crop_size 1, stride): crop img[y:ycrop_size, x:xcrop_size] # 子图命名里带上左上角坐标方便后续坐标还原 cv2.imwrite(f{output_dir}/{img_name}_{x}_{y}.jpg, crop) count 1 # 处理右边缘和下边缘的剩余区域避免目标被切没 if w % crop_size ! 0: cv2.imwrite(f{output_dir}/{img_name}_{w-crop_size}_{0}.jpg, img[0:h, w-crop_size:w]) if h % crop_size ! 0: cv2.imwrite(f{output_dir}/{img_name}_{0}_{h-crop_size}.jpg, img[h-crop_size:h, 0:w]) print(f切出 {count} 张子图)crop_size和stride是切图两个核心参数。crop_size取608或640和模型输入尺寸对齐stride取crop_size的一半也就是重叠率50%这样目标落在切图边缘时至少有一张子图完整包含它。切图后目标相对变大小目标漏检问题能缓解一半这比调损失函数直接得多。关键点在于切图的同时必须同步切标注框不能只切图不切标注。标注框被切边界截断时要么丢弃要么做边界截断处理。如果忽略这一步训练时会出现大量gt框超出子图区域loss直接跑飞。3.4 标注格式转换VOC转YOLO的脚本逻辑与边界坑VOC格式xml和YOLO格式txt之间的转换是最高频操作也是数据准备里最容易出暗病的一步。YOLO的txt每行是一个目标类别id、中心点x、中心点y、宽度w、高度h全部归一化到0到1之间。import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, class_names, output_path, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() 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) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 归一化中心点坐标除以图像宽高 x_center ((xmin xmax) / 2) / img_width y_center ((ymin ymax) / 2) / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height # 截断到[0,1]防止坐标越界导致训练异常 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(output_path, w) as f: f.write(\n.join(lines) \n)这里的三个边界坑必须注意。第一如果xml里的坐标是切图前的绝对坐标而图像已经切过必须先换算到切图子图的局部坐标再归一化直接用原图宽高计算会得到完全错误的标签。第二类别名必须和class_names列表的顺序完全一致顺序错了模型训练出来类别全错而且这种错误在loss曲线上看不出来只能在验证集上发现。第三没有目标的图片也要生成空txt文件放在同目录很多训练脚本按图片名索引标签文件缺了会报KeyError。转换完一定要抽几张图可视化验证把txt标签画回图上对比原标注。不要只看文件生成了就觉得没问题坐标偏差几个像素在家用场景无所谓在遥感尺度下可能意味着几十米的偏差。4. 模型训练YOLOv8在遥感场景下的配置与调参数据准备好之后进入训练阶段。模型选型和参数配置直接决定训练效率和最终精度。这一章讲清楚为什么遥感场景默认选YOLOv8以及训练时最关键的参数怎么调。4.1 为什么遥感场景默认选YOLOv8而不是Faster R-CNN遥感目标检测项目里出现过的模型骨架很多Faster R-CNN、SSD、YOLOv5、YOLOv8最近还有DETR系。但在工程落地层面我一般首选YOLOv8。原因不是它精度最高而是生态最完整。Ultralytics框架把数据加载、训练、验证、导出ONNX全封装好了网上踩坑记录多改起来阻力最小。Faster R-CNN的两阶段结构在遥感小目标上确实有精度优势但训练速度慢、显存占用高、调参敏感对新手不友好。YOLO单阶段检测速度快配合合理的切图策略精度差距已经缩小到可以接受的程度。遥感目标检测的瓶颈往往在数据质量而非模型结构把YOLOv8训好比纠结用哪个SOTA结构收益高得多。如果你拿到的zip是YOLOv5写的不用急着换v5和v8的数据格式、训练流程基本一致迁移成本很低。版本不是第一位的能跑通才是第一位的。先把手里的项目跑起来再考虑升级框架。4.2 训练命令与五个必调参数batch、img-size、epochs怎么配训练命令通常长这样python train.py --data dataset.yaml --weights yolov8n.pt --epochs 150 --batch-size 16 --img-size 640 --device 0dataset.yaml是数据集的配置文件里面写训练/验证图片路径和类别名列表。五个必调参数是epochs、batch-size、img-size、device和workers。epochs遥感场景一般100起步数据量大到几千张图时150到300不夸张关键是看val loss是否还在下降。batch-size受显存限制12G显存跑YOLOv8n可以到32跑YOLOv8m建议降到8到16。低显存运行模型时优先降低batch而不是降低图片尺寸batch太小会引入噪声但总比显存溢出强。img-size是训练分辨率遥感小目标建议640到1024但注意分辨率和显存占用是平方关系1024的显存开销是640的2.56倍。device 0是第一块GPU没有GPU就写cpu训练速度会慢两个数量级。workers是数据加载线程数Windows下超过4容易报DataLoader worker崩溃Linux可以设到CPU核心数的一半。dataset.yaml是关键文件给一个经过实践验证的模板# dataset.yaml train: data/images/train val: data/images/val nc: 5 names: [airplane, ship, vehicle, storage_tank, bridge]nc是类别数names顺序必须和第3章里提到的class_names顺序保持一致。这个一致性是训练不出错的前提顺序错了模型学到了正确的特征却输出到错误的类别id上。注意dataset.yaml里的类别顺序一旦确定后续所有标注转换脚本里的class_names都要跟着它走。这个顺序问题在训练时不报错只在评估时发现mAP异常排查起来很费时。4.3 小目标漏检的针对性措施浅层特征图与切图互补遥感影像里的目标常只有几十个像素模型下采样四次后小目标在特征图上可能只剩一两个像素漏检几乎必然。这里有两个主流做法启用更浅层的检测头或者进一步切图放大目标。浅层检测头常说的P2层分辨率更高保留更多小目标细节但会显著增加计算量而且对新手来说修改模型配置偏硬核。更务实的做法是回到第3章的滑窗切图——把图像切成640子图训练目标在子图里占的比例变大模型更容易学。两者可以叠加切图加浅层检测头时间充裕就都上。另一个常用技巧是mosaic增强Ultralytics默认开启把四张图拼成一张训练图对小目标有利。但要注意遥感影像的特征和大自然图像差异大mosaic过度可能导致模型学到拼接边界纹理。如果训练中mAP迟迟不涨可以尝试关闭mosaic换成简单增强。4.4 训练日志监控loss曲线和mAP曲线哪些信号说明模型学歪了训练起来后不要只盯着进度条要保存并观察两条曲线box_loss和mAP50。box_loss持续下降是正常信号如果loss震荡不降多半是学习率过大或数据标注有问题。mAP50在前30个epoch不涨是正常的50个epoch还在0.1附近徘徊就要考虑类别不平衡——某些类别样本极少模型把所有框都预测成了多数类。用现成的日志文件就能观察tail -f runs/train/exp/results.csvresults.csv里每一行是一个epoch的指标包括train/box_loss、val/mAP50等。我一般用pandas直接读csv画曲线比自己从终端复制数字可靠。如果val/mAP50涨到某个值后开始下降而train/loss还在降那就是过拟合信号及早停止或加大数据增强别让训练跑满全部epochs浪费GPU时间。训练结束后weights目录下会生成best.pt和last.pt。best.pt是验证集最优模型后续推理和评估都用它。有些项目只保存last.pt这种要小心最后一轮可能已经过拟合用它推理效果大概率不如best.pt。5. 推理、评估与避坑遥感目标检测的五个高频翻车点模型训练完只是开始推理和评估才是真正检验成果的地方。这一章把大图推理的坐标还原、评估指标的选择、以及遥感场景下最高频的四个翻车点一次讲清楚。5.1 大图推理TIFF切块、检测、坐标还原遥感影像推理时不能把整张TIFF直接塞进模型。即使显存够目标在整图里占比太小也检测不出来。标准做法是切块推理再把子图的检测框坐标还原到大图坐标系。from ultralytics import YOLO import cv2 def infer_large_image(model, large_img_path, crop_size640, stride320, conf_thres0.3): img cv2.imread(large_img_path) h, w img.shape[:2] results [] for y in range(0, h - crop_size 1, stride): for x in range(0, w - crop_size 1, stride): crop img[y:ycrop_size, x:xcrop_size] preds model(crop, confconf_thres, verboseFalse) for box in preds[0].boxes: x1, y1, x2, y2 box.xyxy[0].cpu().numpy() cls int(box.cls[0]) conf float(box.conf[0]) # 关键子图坐标加滑窗起点还原到原图坐标 results.append((x1 x, y1 y, x2 x, y2 y, cls, conf)) return results坐标还原是这段代码的灵魂子图检测框的坐标必须加上滑窗起点才是原图坐标漏掉这一步检测结果全部错位。conf_thres在大图推理时不要设太低遥感图像背景复杂0.25置信度会产生大量误检我一般从0.3起步再往下调。重叠区域可能出现同一目标被两张子图各检出一次后处理里做NMS合并是必须的否则最终结果看起来会有大量重复框。5.2 评估指标mAP50和mAP50:95在遥感场景怎么看评价模型不能只看效果图要看val.py输出的指标。mAP50是IoU阈值0.5下的平均精度mAP50:95是从0.5到0.95每隔0.05取IoU算平均。遥感目标检测里优先看mAP50原因在于旋转框和水平框的IoU天花板很低方向略有偏差IoU就跌破0.75mAP50:95数字会非常难看但不代表模型不能用。如果项目要做精细测量再回头细抠mAP50:95。对比模型好坏时注意数据集和切图参数必须一致。很多人拿不同切图策略的效果直接比mAP那是没有意义的。stride不同、切图尺寸不同目标被切碎的概率不同mAP天然不同。同一份数据、同一套切图参数下才谈得上模型层面的对比。5.3 高频翻车点方向框丢失、小目标漏检、显存不足、类别不平衡翻车点一方向框全丢了。现象模型推理输出只有水平框训练时用roLabelImg标的角度信息完全没生效。 原因项目里的模型是水平框检测模型只读取xywh四个值角度值被标注解析脚本忽略。 解决确认模型是否支持旋转框检测头。不支持就把训练标签里的角度信息去掉用水平框硬训一定要方向框就换模型版本不要指望水平框模型能输出角度它做不到。翻车点二小目标漏检严重。现象大目标检测正常小目标几十像素的车辆、船只几乎全漏。 原因小目标在特征图下采样后信息丢失或者训练时切图没有覆盖到目标密集区。 解决回到滑窗切图缩小stride增加重叠率并适当提升训练分辨率到960或1024。这两个手段通常能让小目标mAP大幅回升。翻车点三CUDA out of memory。现象训练跑到某一步突然报显存溢出。 原因batch-size和img-size的显存占用超出GPU容量或者Windows下开了过多workers导致数据预加载占用额外显存。 解决低显存运行模型时优先把batch-size减半其次把img-size从640降到512。切图尺寸本身决定输入上限不要为了跑高分辨率硬上大batch。翻车点四类别不平衡导致检测结果偏科。现象多数类如车辆检测精确率高少数类如桥梁mAP接近0。 原因训练集里类别样本数差距过大模型学成了多数类检测器。 解决给少数类做样本复制增强或者按类别加权loss。最简单有效的是先把少数类的图片多复制几份朴素但立竿见影。5.4 低显存推理与部署导出ONNX的实用配置显存不够时不一定需要换机器。推理阶段用ONNX配合ONNX RuntimeCPU也能跑但导出ONNX时要注意动态输入尺寸和固定尺寸的区别。model.export(formatonnx, imgsz640, dynamicTrue)dynamicTrue允许输入尺寸动态变化代价是推理速度下降固定640速度最快但切图尺寸必须配合。低显存场景还有个实用技巧用half精度推理显存占用几乎减半精度损失在遥感大目标上不明显但小目标要谨慎先跑一遍验证集对比mAP掉太多就别用。6. 把检测结果落成产品从像素框到GIS矢量的一个技巧遥感检测的交付物不是一张画了框的图片而是带地理坐标的矢量文件。这一章讲一个把检测结果输出为GeoJSON的实用做法让结果能直接拖进QGIS验证而不是停留在技术自嗨。6.1 像素坐标转地理坐标由bbox到GeoJSON检测框的坐标是像素坐标对遥感业务来说没有意义地理坐标才是交付物。转换逻辑很简单已知影像左上角经纬度和单像素对应的地面尺寸就能把像素偏移量换算成经纬度偏移。import json def bbox_to_geojson(results, transform_info): features [] # transform_info 包含左上角经纬度 lon0, lat0 和分辨率 res度/像素 for x1, y1, x2, y2, cls, conf in results: lon1 transform_info[lon0] x1 * transform_info[res] lat1 transform_info[lat0] - y1 * transform_info[res] lon2 transform_info[lon0] x2 * transform_info[res] lat2 transform_info[lat0] - y2 * transform_info[res] feature { type: Feature, properties: {class: cls, confidence: round(conf, 4)}, geometry: { type: Polygon, coordinates: [[[lon1, lat1], [lon2, lat1], [lon2, lat2], [lon1, lat2], [lon1, lat1]]] } } features.append(feature) return {type: FeatureCollection, features: features}经纬度换算时注意y方向符号。遥感图像通常北在上像素y增加方向是向南所以纬度用左上角纬度减去偏移量加号写反会导致检测结果整体错位而且这个错位不叠加底图肉眼很难察觉。res可以从TIFF的地理元数据里读如果没有元数据只能手动输入务必确认单位是度还是米。6.2 与QGIS联动直接加载检测结果验证生成GeoJSON后用QGIS直接拖进去检测框套在卫星底图上的正确性一眼可见。这一步值得花时间因为它是把结果交给业务方的最终形态。我自己的习惯是检测脚本输出bbox后紧接一步坐标换算形成GeoJSON落盘后续无论是跑统计分析还是拼成果图都有了标准中间产物。因为方向框在GeoJSON里要表示成五点闭合多边形比水平四点框麻烦建议先把水平框方案跑顺再考虑旋转框导出。不要一开始就追求精细先把整条链路走通。我自己在这条路上吃过的最大亏就是检测出了框但交付不了——坐标换算出错、格式对不上业务方根本没法用。后来养成的习惯是每批结果都先落到GeoJSON再可视化检查。希望这些经验能帮你少踩几个坑早日把遥感目标检测跑出自己的可用结果。本文还有配套的精品资源点击获取