
简介基于yolov5与opencv的卫星拍摄倒塌房屋区域检测方案面向遥感图像分析与目标检测方向的开发者、研究生及相关工程人员主要解决灾后建筑损毁区域的自动识别与定位问题。资源共79个文件约42MB包含Python源码、YOLOv5模型权重、YAML配置文件、训练/测试脚本、评估结果图与使用说明等类型其中yolov5s.pt为可直接调用的模型py/pyc文件组成检测主流程jpg/png图片及results.txt记录了训练与验证效果目录结构清楚便于快速上手。已有284人学习下载。模型迭代200次loss、Recall、Precision、mAP等评估曲线齐全拟合效果良好可帮助读者理解YOLOv5训练过程与调参思路。整体提供完整可运行的检测代码、预训练模型和配套说明适合作为学习YOLOv5在遥感场景中应用的参考项目也可在此基础上进行迁移学习或二次开发。1. 卫星影像里的倒塌房屋检测卡点不在模型结构拿到这样一个 zip 包时不少人第一反应是装环境、跑 detect.py然后对着卫星大图按回车。真实情况是卫星影像通常是几千乘几千像素而倒塌房屋在画面里往往只占几十个像素。直接把整张图缩放到 640x640 丢给 yolov5下采样几轮之后目标可能只剩几个像素漏检几乎没有悬念。这个场景的核心难点不在 yolov5 本身而在于数据管线超大图的切片策略、标注格式的转换、模型文件里类别头和锚框的适配最后才能谈评估指标曲线好不好看。这篇文章就顺着这条链路讲清楚拿到源码和模型文件之后怎么在 opencvpython 的环境里把这件事做扎实。2. yolov5 在遥感场景的选型与模型文件适配2.1 为什么遥感目标检测优先选 yolov5 而不是更重的结构yolov5 在检测速度与精度之间取得了很好的平衡生态成熟训练和推理的代码路径清晰。对于倒塌房屋检测这类目标单一、背景复杂的遥感任务yolov5s 或 yolov5m 足够覆盖绝大多数需求显存要求也不高。遥感影像的特点是目标尺寸分布广完整的房屋、部分倒塌的废墟、以及被阴影遮挡的区域在画面里呈现的尺度差异可能超过十倍。yolov5 的 FPNPAN 结构在不同尺度特征层上做预测对这种多尺度情况有天然优势。另一个选型原因是迁移学习成本低。yolov5 在 COCO 上预训练过的权重可以直接用来微调哪怕是换成单类检测任务只需要改动最后的输出头。源码包的训练脚本、验证脚本、导出脚本都是现成的要把这批源码跑起来重点在于模型文件里的类别数、锚框尺寸和权重文件的正确加载。2.2 类别头改造单类检测时 nc 和 anchors 的联动关系yolov5 的模型结构由 yaml 文件定义。源码包里如果有models/yolov5s.yaml打开后你会看到nc: 80这是 COCO 数据集的类别数。对于倒塌房屋检测只需要一个类别因此把它改成nc: 1。但这里藏着一个容易忽略的点类别数变化后检测头的输出通道数会变。yolov5 的每个检测头输出形状是[batch, 3 * (nc 5), grid_h, grid_w]其中 3 是每个网格的锚框数量5 是中心点坐标、宽高和置信度。nc 从 80 改成 1每个锚框的输出维度从 85 变成 6整个卷积层的权重数量就变了。这意味着不能直接把 COCO 预训练权重完整加载进来。常见的做法是加载权重时屏蔽输出层只迁移骨干网络和颈部网络的参数。import torch # 加载 COCO 预训练权重但去掉最后一层不匹配的权重 ckpt torch.load(yolov5s.pt, map_locationcpu) state_dict ckpt[model].float().state_dict() # 过滤输出层相关权重 filtered {k: v for k, v in state_dict.items() if not k.startswith(model.24)} # 构建新模型后加载过滤后的权重 model.load_state_dict(filtered, strictFalse)这段代码的核心逻辑是strictFalse允许权重字典与模型结构不完全匹配。model.24对应的是 yolov5s 的最后一个检测头用startswith过滤掉该层的卷积权重和偏置。这样加载后骨干网络已经具备通用的特征提取能力最后几层从零开始训练由新数据决定输出。代码里map_locationcpu是为了避免 CUDA 环境下设备不匹配的问题如果你在 GPU 机器上训练也可以直接改成cuda:0。2.3 锚框调整策略用 autoanchor 重新聚类COCO 数据集的锚框是针对自然图像物体设计的比如人、车、猫狗的典型宽高比。卫星影像里倒塌房屋的形状相对规则通常呈矩形但长宽比可能比较集中且整体尺寸偏小。如果锚框不匹配训练初期损失会下降缓慢甚至出现大量低置信度预测。yolov5 提供了一个自动锚框工具。在训练脚本里有一个--noautoanchor参数默认情况下每次训练前都会自动检查锚框与当前数据集的匹配度。python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --noautoanchor如果不想关闭自动锚框可以不加这个参数。加了之后系统会打印一段提示告诉你当前锚框与训练集的适配情况比如anchor mean metric is low。看到这句话说明锚框需要重新聚类此时把--noautoanchor去掉yolov5 会用 k-means 算法在训练集标注框上重新计算锚框尺寸。聚类的关键是检查data.yaml里的训练图片路径是否指向了正确的文件夹否则聚类结果的参考价值会打折扣。2.4 模型文件的完整结构.pt 里不只是权重你的源码包里的模型文件通常是一个 .pt 文件。很多人误以为它是纯粹的权重实际上 .pt 文件是一个 dict里面可能包含 model 结构、epoch、超参数、类别名以及 optimizer 状态。加载后你可以通过ckpt.keys()查看具体内容。ckpt torch.load(best.pt, map_locationcpu) print(ckpt.keys()) print(ckpt[model].names)这段代码会输出类似dict_keys([epoch, best_fitness, model, ema, updates, optimizer, train_metrics, train_results, date])这样的结果。names属性记录了类别名称映射推理时画框的标签文字就来自这里。如果你的项目里names显示的还是 COCO 的类别名称说明这个模型文件没有被正确微调过需要检查训练时data.yaml中的names配置。另外如果你需要把一个训练好的模型转成 ONNX 或 TensorRT 格式openvino 或者 onnxruntime 在部署阶段也经常会用到这个在第五章会提到。3. OpenCV 在数据管线里的三个不为零的环节3.1 超大影像的滑窗切图策略卫星影像直接输入模型是不现实的。yolov5 的输入尺寸通常是 640x640 或更高一些但原图可能超过一万像素。将整图缩放到 640 会丢失大量细节因此需要把大图切成若干个小块再分别送入模型推理或训练。切图的质量直接影响检测结果这也是 opencv 在整套流程中最重要的一环。常见的做法是固定窗口大小加重叠滑窗。窗口大小一般取 640 或 1024重叠率取 20%-50%。重叠是为了防止目标恰好落在切割边界上被截断。倒塌房屋的轮廓通常不规则截断会显著影响检测置信度。import cv2 import os from pathlib import Path def split_image(image_path, output_dir, win_size640, overlap128): img cv2.imread(image_path) if img is None: print(f无法读取图片: {image_path}) return h, w img.shape[:2] step win_size - overlap Path(output_dir).mkdir(parentsTrue, exist_okTrue) count 0 for y in range(0, h - win_size 1, step): for x in range(0, w - win_size 1, step): crop img[y:ywin_size, x:xwin_size] out_path os.path.join(output_dir, ftile_{y}_{x}.jpg) cv2.imwrite(out_path, crop) count 1 # 处理右边缘和下边缘剩余区域 if w % step ! 0: for y in range(0, h - win_size 1, step): x w - win_size crop img[y:ywin_size, x:xwin_size] cv2.imwrite(os.path.join(output_dir, ftile_{y}_{x}.jpg), crop) count 1 if h % step ! 0: for x in range(0, w - win_size 1, step): y h - win_size crop img[y:ywin_size, x:xwin_size] cv2.imwrite(os.path.join(output_dir, ftile_{y}_{x}.jpg), crop) count 1 if h % step ! 0 and w % step ! 0: x w - win_size y h - win_size crop img[y:ywin_size, x:xwin_size] cv2.imwrite(os.path.join(output_dir, ftile_{y}_{x}.jpg), crop) print(f共生成 {count} 张切片)这段代码的关键参数有三个。win_size是切片窗口边长建议与训练时的输入尺寸一致避免训练与推理之间的尺度偏差。overlap是重叠像素数比例通常控制在 20%-50%。step是滑动步长等于win_size - overlap。代码后半段补上了图像右边缘和下边缘的残留区域否则靠边的房屋会被漏掉。如果你的卫星影像特别大建议在切图前先对整体灰度做直方图均衡化增强地面建筑物与废墟的对比度但这一步要谨慎过度增强会把阴影区域的纹理抹掉。3.2 标注格式从 VOC XML 转换到 YOLO 的 txt 格式卫星房屋检测的数据集来源多样有的给的是 LabelImg 标注的 VOC XML 格式有的是 COCO JSON 格式。yolov5 训练需要的是每张图片对应一个同名 txt 文件每一行一个目标格式是class_id x_center y_center width height四项坐标全部归一化到 0-1 之间。常见的坑是坐标系弄反。VOC 的bndbox给出的是左上角和右下角的绝对坐标而 YOLO 格式需要的是目标中心点和宽高的相对坐标。import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_path, txt_path, class_names): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) 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) x1 float(bndbox.find(xmin).text) y1 float(bndbox.find(ymin).text) x2 float(bndbox.find(xmax).text) y2 float(bndbox.find(ymax).text) x_center (x1 x2) / 2 / img_w y_center (y1 y2) / 2 / img_h width (x2 - x1) / img_w height (y2 - y1) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines))转换逻辑本身不复杂但有三个细节值得注意。一是class_names的列表顺序必须与训练时的data.yaml完全一致否则类别索引错位。二是当宽高比极端时比如一个细长的废墟带归一化后的width或height可能接近 1这种情况不要直接丢弃而是检查标注是否合理。三是转换后随机抽几张图验证用可视化脚本把 txt 画回图片上比对原始标注是否有偏移。3.3 训练阶段用 OpenCV 做数据增强的边界条件yolov5 自带不少数据增强比如马赛克mosaic、混合mixup、随机透视等。在遥感场景下有些增强需要额外补充比如亮度对比度扰动和图像旋转。卫星影像可能来自不同传感器、不同拍摄时间光照条件差异很大。源码包里如果自带增强配置一般会放在data/hyps/hyp.scratch-low.yaml或类似文件里你可以直接修改其中的参数。如果你打算用 opencv 在数据管线里自己写增强要控制尺度变化范围。遥感影像中房屋的朝向并不固定旋转增强可以模拟不同方向的房屋但过度旋转会产生大量无效背景区域。常见做法是限制在正负 30 度以内。import cv2 import numpy as np def rotate_image(img, angle15): h, w img.shape[:2] matrix cv2.getRotationMatrix2D((w // 2, h // 2), angle, 1.0) rotated cv2.warpAffine(img, matrix, (w, h), flagscv2.INTER_LINEAR) return rotated训练时数据增强要保留目标位置和尺寸信息这意味着增强操作必须同时作用于图片与标注框。如果你在源码包里看到类似日志或可视化脚本会发现标注框是绘制在原图上的验证增强结果是否正确就对照这些图片进行检查。yolov5 自带的数据增强已经涵盖大部分需求自己用 opencv 写增强的主要作用是针对特定数据分布做定向补充。4. 训练到评估指标曲线的落地路径4.1 数据集目录结构与人有点容易记错的 data.yamlyolov5 训练前要求数据集按固定目录结构组织。这个结构的核心是 train 和 val 的 images 与 labels 一一对应文件名完全相同只是目录不同。datasets/ ├── train │ ├── images │ └── labels ├── val │ ├── images │ └── labels └── data.yamldata.yaml 是训练配置的关键。写这个文件时有两个容易出错的地方。一是路径。建议全部使用绝对路径防止终端工作目录变化导致图片找不到二是names列表必须与标注 txt 中的 class_id 对应。train: /home/user/datasets/train/images val: /home/user/datasets/val/images nc: 1 names: [collapsed_building]你还可以在这个文件里写上test字段但 val 是评估指标曲线的基础没有 val 数据就无法计算 mAP。关于 train 与 val 的划分比例一般训练集占绝大多数但 val 至少要保留几百张否则评估曲线起伏会非常大误差区间太宽。4.2 训练命令里的关键超参数设置表确认数据目录无误后运行以下命令启动训练。python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --workers 8 --device 0各参数的含义和调整建议如下表所示参数建议值说明--img640 或 1024输入尺寸越大小目标检测效果越好但显存占用直线上升--batch16 或 32显存有限时优先调低 batch而不是调低 img--epochs100-150超过 150 轮容易出现过拟合除非数据集特别大--workers4-8在 Linux 下可以开到 8Windows 下建议不要超过 4--device0指定 GPU 编号CPU 训练遥感数据效率很低训练时你可以看到命令行输出的每一轮的 loss 和 mAP 数值。常见问题是 loss 一直在降但 mAP 不动通常是正负样本比例失衡或者锚框与目标尺寸不匹配。这个时候重点检查第二章节提到的自动锚框聚类结果。另一个常见问题是训练过程中显存溢出这时可以把--batch降到 8同时把--img降到 640。如果想要模型轻量化可以在训练后使用--weights best.pt和--img 640导出为 TensorRT 或 ONNX 格式。4.3 评估指标曲线的生成与判读训练结束后runs/train/exp目录下会生成一组 PNG 图片。源码包里的评估指标曲线指的就是这些文件PR_curve.png、F1_curve.png、P_curve.png、R_curve.png、confusion_matrix.png。其中最有价值的是 PR 曲线横轴是召回率 Recall纵轴是精确率 Precision。曲线越往右上角凸说明模型在保持低误检的同时能做到高召回。如果曲线在右上角急剧下降说明模型存在大量低置信度误检如果曲线整体偏左下说明模型没有学到足够的特征需要回看训练数据是否存在标注错误。F1 曲线用于寻找最优置信度阈值。检测时的conf_thres超参数决定了多少置信度以上的预测才算有效F1 曲线的峰值点就是最合适的阈值位置。评估指标曲线按训练集和验证集分别计算如果你的 val 集标注质量较差曲线波动会比训练集明显得多。遇到这种情况先检查 val 标签是否与图片对齐再用可视化脚本画出 val 集样本做人工核对。4.4 显存与输出策略的取舍遥感数据集的图片数量通常不如自然图像数据集多且每张切片都是高度相似的场景。为了让模型见更多的样本训练时建议使用--cache参数把图片缓存到内存。yolov5 默认会做马赛克增强如果你的数据量很小马赛克操作反倒会引入大量重复目标此时可以关闭--mosaic或者把--mosaic 0写在超参数里。源码包里如果有使用说明文件一般会提到如何在训练时保存最佳的权重。--save-period参数可以控制每隔多少轮保存一个 checkpoint。如果显存吃紧同时还想保留评估曲线的平滑度可以采用两阶段训练先用--img 640快速跑 50 轮再用--resume继续训练并增大--img到 1024。这种方式在遥感小目标检测中比一次性采用--img 1024更稳定。5. 推理阶段值得留下的两个技巧5.1 灰度统计过滤空白切片对超大卫星影像做滑窗推理时大量瓦片其实只有空地和道路没有房屋也就没有检测的必要。在送入模型之前先用 opencv 做一次快速统计能省下不少推理时间。import cv2 def is_blank_tile(img, threshold12): gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) std_val cv2.meanStdDev(gray)[1][0][0] return std_val threshold灰度标准差低于阈值的切片基本可以判定为均匀背景。这个阈值的合理范围与影像地表特征有关城区影像纹理丰富阈值可以适度调高乡村和山区影像灰度变化小阈值调低以免误杀。这个技巧不是要让你的模型跳过所有空白区域而是让推理过程更聚焦尤其适用于大范围应急评估场景。5.2 画框输出与置信度阈值优化推理结果的可视化不只是在图上画框更重要的是把检测结果落地成可统计的数据。常见的做法是用 opencv 画出检测框同时把坐标、置信度写入 CSV方便后续做面积估算或受灾区域统计。import cv2 import torch def draw_detections(img, results, conf_thres0.35): for det in results.xyxy[0].cpu().numpy(): if det[4] conf_thres: continue x1, y1, x2, y2 map(int, det[:4]) conf det[4] label fcollapsed {conf:.2f} cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) return img画框时的置信度阈值不是固定的。如果你更关注不漏报阈值可以降到 0.25如果更看重检测结果的准确率阈值建议提高到 0.5。具体阈值可以从第五章第一节提到的 F1 曲线峰值点得知。results.xyxy是 yolo 推理结果的一种常见表示你使用源码包里的detect.py后得到的输出结构也是一致的。最后还有一点要提醒滑窗切图在推理时产生的边界重复检测因为同一栋房屋可能同时出现在相邻两张切片中。处理方式是在所有切片的检测结果上做一次全局 NMS把重叠度过高的框合并。如果源码包没有提供现成的处理脚本可以用torchvision.ops.nms对全部预测框做一次过滤阈值设为 0.5 即可有效减少重复框同时不会明显误删相邻房屋的预测结果。本文还有配套的精品资源点击获取