ARTICLE DETAIL

资讯详情

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

YOLOv5结合图像差异对比实现无人机巡检违建自动标记

YOLOv5结合图像差异对比实现无人机巡检违建自动标记 1. 项目概述与整体思路第一次把两个月前的同一块空地航拍图叠在电脑屏幕上时我整个人是懵的密密麻麻的板房、吊车、钢管和刚浇筑的地基全挤在几张普通相机根本拍不全的大场景里。人工用肉眼比对少说也得盯半小时还容易漏。这个练手项目的核心思路很直接——用YOLOv5配合图片差异对比把无人机巡检中反复出现的“哪里有变化”和“变化是什么”两个问题一起解决最后把疑似违建的建筑直接框出来省掉人工翻图的功夫。我把它定位成一个偏向工程落地的实践项目适合正在做无人机巡检、工地进度管理、甚至环保督察辅助的朋友参考。整套流程拆开看并不复杂图像预处理、特征点配准、像素差异求取、YOLOv5目标识别、交叉筛选和结果标记每一步都有成熟工具可用。你不需要从零训练一个全新的CV模型重点是怎么把已有能力组合成一个能直接跑起来的流水线并且保证误报率在可控范围内。1.1 无人机巡检到底在找什么无人机巡检这些年已经不是什么新鲜事但真正做一线业务的人都知道困扰大家的往往不是“飞不飞得起来”而是“拍回来的几百张图怎么处理”。以违章建筑巡查为例同一片区域可能每隔几天就飞一次目标可能是新增的彩钢瓦屋顶、临时搭建的工棚、楼顶加盖或者是原本空地上冒出来的活动板房。这些东西的共同点是它们要么体量不大要么颜色和周围环境接近光靠人眼在几十张高分辨率图里找效率低且容易疲劳。我做的这套方案本质上就是给无人机图做一个“变化提醒”先把两期影像在空间上对齐再用像素差把变化区域粗筛出来接着让YOLOv5在当前影像里识别具体目标最后把变化区域和检测结果做一次交叉判断。只有两个条件都满足的区域才会被标记为“疑似新增违建”。这样既能回答“哪里变了”也能回答“变出来的东西是什么”比单纯做一次目标检测靠谱很多。1.2 技术路线怎么组合很多刚接触图像变化检测的朋友会问是不是直接用OpenCV的absdiff做差就行或者干脆只跑一轮YOLOv5检测看看图上有没有建筑这两种做法单独用都会踩坑。先看纯像素差异。无人机两次拍摄的航线不可能完全一致光照、阴影、季节植被变化都会让absdiff产生一堆噪声。直接把前后两帧做差往往整张图都是“变化区域”根本没法用。所以我加了两个关键环节第一步是特征点配准把两张图尽量拉到同一空间坐标系下第二步是形态学过滤和面积阈值过滤把噪声造成的零星像素点去掉只保留连成片的可疑区域。再看纯目标检测。YOLOv5可以识别出“这里有建筑”但它不知道这栋建筑以前有没有。如果拿一个区域的历史影像和当前影像同时做检测都需要先训练一个能识别建筑类别的模型而且依然无法判断“是不是新增”。所以正确做法是把两者串起来差异对比负责缩小范围目标检测负责确认目标类型最后再用交集筛选来减少误报。这个“由粗到细”的处理顺序是整套项目能落地的最主要原因。1.3 为什么不单独用检测或单独用差异单独用差异最让人头疼的是无法对“变化”定性。一片荒地上多了一块蓝色铁皮棚像素差肯定很明显但这块铁皮到底是违建还是临时覆盖物是新增工地还是地质灾害纯图像差分回答不了。单独用YOLOv5检测也有类似问题——模型识别能力再强也只输出“是什么”不输出“是否为新增”。把两者结合后行为逻辑就清晰了假设某一期的影像作为基准新一期的影像作为待检测对象正常情况下基线区域的建筑已经存在那它在YOLOv5中的检测结果应当被标记为“存量建筑”不做报警。但凡是出现在差异掩码中的检测框说明这个目标在两期之间有明显变化再结合人工复核或业务规则就有充分理由怀疑是新建违建。这个交叉筛选的思路相当于给检测结果加了一层“按时间维度过滤”的闸门。2. 环境准备与YOLOv5模型部署2.1 环境依赖与安装YOLOv5对硬件要求不算苛刻我用一台装了RTX 3060的台式机就能跑得比较顺CPU模式也能跑只是速度会慢不少。环境搭建建议用Anaconda特别是需要同时尝试多个PyTorch版本时虚拟环境能省去很多麻烦。conda create -n yolo python3.8 -y conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118这里有个容易踩坑的地方PyTorch版本要和你的CUDA驱动匹配。别上来就最新版先跑一句nvidia-smi查看驱动支持的CUDA版本再选择对应车轮包。如果只是CPU推理直接pip install torch torchvision默认版本就行。之后克隆YOLOv5仓库并安装依赖git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txtWindows用户如果发现pycocotools装不上多半是缺少Visual C构建工具也可以直接安装预编译包pip install pycocotools-windows另外建议把opencv-python升级到4.8以上因为后面做图像配准和形态学处理时新版接口更稳定。我在旧版4.1上遇到过cv2.findContours返回值数量不一致的问题排查了半天才发现是版本差异。2.2 模型权重与推理验证YOLOv5官方预训练权重分s/m/l/x几档。我这个项目用的是yolov5s模型小、推理快对建筑这类大目标也够用如果实际场景里需要识别小目标比如楼顶小屋、碎小工棚建议上yolov5m甚至yolov5l但对应推理耗时也会增加。首次运行会在线下载权重可以先把权重文件放到yolov5/weights目录下离线环境也能用。# 在yolov5目录下执行 python detect.py --weights yolov5s.pt --source data/images --device 0如果能看到图片上成功画出检测框说明环境基本没问题。跑通之后我习惯先拿真实航拍图试一遍看一下COCO预训练模型对普通建筑、卡车、人的识别效果。需要说明的是COCO里并没有“违章建筑”这个类别所以我在业务场景里一般会把目标类别迁移成更具体的建筑构件比如“彩钢棚”“活动板房”“吊车”“裸土堆”这样做出来的检测结果才更有业务价值。2.3 训练自定义数据集从COCO到“违建类别”如果只检测一般建筑用预训练模型勉强能用但要真的用于违建标记最好还是训练自己的YOLOv5模型。自己训练有几个好处一是可以把类别设置得贴合业务比如就定义illegal_building、shed、crane等二是可以针对无人机俯拍视角做特化因为COCO模型大多是从水平视角训练出来的对俯拍建筑错检漏检比大家想象中严重。数据准备上传统标注流程是用LabelImg或AnyLabeling对俯拍图片框出目标保存为YOLO格式的txt文件每行是class_id x_center y_center width height。目录结构建议这样组织datasets/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/然后写一个data.yamltrain: datasets/images/train val: datasets/images/val nc: 3 names: [shed, crane, illegal_building]训练命令python train.py --img 640 --batch 8 --epochs 100 --data data.yaml --weights yolov5s.pt --device 0训练完成后runs/train/exp/weights/best.pt就是可以用在推理流程里的模型。别忘了同时记录验证集上的mAP如果mAP低于0.5可能需要调整标注质量、增加同场景数据或改变输入分辨率。3. 无人机图片差异对比模块的实现3.1 图像配准两次航拍怎么对齐做差异对比最基础、也最关键的一步是图像配准。无人机两次飞行的姿态、高度、角度很难完全一致如果直接把两张图按像素做差误差会大得惊人。最开始我把这个问题想简单了用普通相机的连续帧做差效果还行换成无人机的正射图就翻车原因是两期影像之间不仅有平移还有旋转和尺度变化。常用方案是提取特征点再进行仿射或透视变换。SIFT特征在航拍图上有较强的抗旋转和尺度变化能力但计算量稍大ORB算得快但对弱纹理区域效果差。如果图片本身带有地理位置信息比如DJI相机的XMP坐标或者正射影像的GeoTransform参数也可以直接用仿射变换对齐。我的做法是先尝试SIFT匹配效果不够再用地理坐标辅助。核心代码如下import cv2 import numpy as np def align_images(img_ref, img_new, max_features5000): gray_ref cv2.cvtColor(img_ref, cv2.COLOR_BGR2GRAY) gray_new cv2.cvtColor(img_new, cv2.COLOR_BGR2GRAY) sift cv2.SIFT_create() kp1, des1 sift.detectAndCompute(gray_ref, None) kp2, des2 sift.detectAndCompute(gray_new, None) matcher cv2.BFMatcher() raw_matches matcher.knnMatch(des1, des2, k2) good [] for m, n in raw_matches: if m.distance 0.75 * n.distance: good.append(m) src_pts np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2) dst_pts np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2) H, mask cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) h, w img_ref.shape[:2] aligned_new cv2.warpPerspective(img_new, H, (w, h)) return aligned_new, H在特征匹配时knnMatch配合Lowe比例检验是最常用的粗筛方式0.75是经验值越小于匹配越严。配准质量如何判断把两张图叠成半透明看也可以直接计算对齐后图像的重叠区域像素差异均值数值越低说明对齐越好。3.2 差异提取把像素差变成“可疑区域”配准完成后就可以用像素差粗筛变化区域。我会把原图转为灰度计算两张图的灰度差再用一个阈值把差图二值化。这个阈值是整套流程的重要超参数调得太低会把光照和相机噪声全部圈进来调得太高又会漏掉真正新增的小建筑。我一般从threshold40开始调针对2亿像素级别的大图可以先降到30试试但最终还要以实际目标尺寸为准。二值化之后一定要做形态学处理先用开运算把零星噪点去掉再用闭运算把断开的建筑边缘连起来。注意形态学结构核大小也是跟分辨率挂钩的对每像素约5厘米的无人机图我用5x5的核如果单片影像面积更大可以改成7x7甚至9x9。def create_diff_mask(img_ref, img_aligned, threshold40, min_area3000): gray_ref cv2.cvtColor(img_ref, cv2.COLOR_BGR2GRAY) gray_aligned cv2.cvtColor(img_aligned, cv2.COLOR_BGR2GRAY) diff cv2.absdiff(gray_ref, gray_aligned) _, mask cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations2) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations3) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) region_mask np.zeros_like(mask) for cnt in contours: if cv2.contourArea(cnt) min_area: cv2.drawContours(region_mask, [cnt], -1, 255, -1) return region_mask轮廓筛选时min_area这个值需要仔细权衡。如果遥感影像地面分辨率只有20厘米一栋几十平方米的活动板房面积也有上千像素如果是普通DJI无人机低空拍的图分辨率更高面积阈值就可以同步放大。我的习惯是先画几组不同阈值的掩码图对比效果再确定一个能包含目标又不引入大量碎片的数值。3.3 减少光照与阴影干扰的实用技巧做航拍差异对比绕不开光照问题。早上和中午飞同一片区域影子方向和长度完全不同导致整片绿地、建筑物侧面全是假变化。这里分享几个亲测有效的处理习惯。第一种是颜色空间过滤。很多假变化来自绿色植被的长势变化可以把RGB转到HSV空间把绿色像素对应的饱和度通道提取出来生成植被掩码再去差异图里把这些区域抹掉。第二种是梯度图替代灰度图。用cv2.Sobel或者cv2.Laplacian提取梯度再做absdiff这样对光照变化的敏感度会降低不少。第三种是分块处理。大图两次拍摄如果局部有云影或相机自动曝光差异整张图共用同一个阈值会不均衡按网格分块后分别计算差异阈值能明显改善效果。除了这些阴影方向造成的建筑物屋顶亮暗变化在纯像素层面几乎无法完全消除。所以我在流程里始终把差异掩码当作“候选区域”而不是最终结果后面必须用YOLOv5检测框再判断一次。经过这一步误报率已经能降一个数量级。4. 违建检测与标记模块的实现4.1 YOLOv5检测流程与参数设置差异掩码圈出来的区域可能包含很多噪声比如道路颜色变化、阴影移动、车辆移位。要判断这些变化里到底有没有真正的建筑目标还得靠YOLOv5。推理阶段我不推荐临时去改官网脚本直接写一个可复用的加载函数更顺手。import torch def load_model(weight_pathyolov5s.pt, device0): model torch.hub.load(ultralytics/yolov5, custom, pathweight_path, force_reloadFalse) model.conf 0.25 model.iou 0.45 model.max_det 200 if device ! cpu: model.model.to(torch.device(cuda)) return model关于推理参数我把置信度设置在0.25IoU阈值0.45。如果后续发现漏检多就把conf下调到0.15同时接受更多假阳性如果发现同类目标大量误报则上调conf。更稳妥的做法是同时输出漏检和误检案例根据业务方反馈不断调整。4.2 检测框与差异掩码的交叉筛选这一步是整个实战的精华部分。检测框只能说明“这里有一个目标”差异掩码能说明“这里的图像内容产生了变化”把两者相交才可能筛选出“变化后发现的目标”。我选择用一个比值来判断计算每个检测框内差异掩码像素面积占框总面积的比率。如果比率超过某个经验阈值就认为这个检测框对应的目标是新增或发生明显变化。def mask_overlay_ratio(box, mask): x1, y1, x2, y2 [int(v) for v in box] w max(x2 - x1, 1) h max(y2 - y1, 1) box_area w * h crop mask[y1:y2, x1:x2] overlay cv2.countNonZero(crop) return overlay / box_area阈值方面我在多数无人机正射影像场景中取0.15到0.25之间。取0.15会使系统更敏感凡是框内有超过15%区域发生变化就会报警取0.25则更严格但可能漏掉那些新建物被周边遮挡的情况。如果你运行后发现“存量建筑”被频繁误报建议提高这个比值反之如果很多真正的违建没被标记就降低它。交叉筛选后会产生三类结果第一类是有检测框且差异比率达标的标记为“疑似新增违建”第二类是有检测框但差异比率很低的标记为“存量建筑”第三类是没有检测框但差异掩码面积很大的标记为“疑似未识别变化”交由人工复核。这个分类信息对后续业务处理极有价值能直接帮助人工排查。4.3 绘制标记与输出结构化报告筛选出最终结果后需要把信息呈现给用户。我习惯把原图和检测结果做得尽量直观疑似新增违建框用红色绘制并写上类别、置信度和差异比率存量建筑框用蓝色绘制便于对照。标题注明这是“图片来源、拍摄时间、处理时间”等信息这样即使输出几十张图也不会乱。绘制中文标签时需要注意OpenCV内置的cv2.putText不支持中文直接写会出现乱码。我一般用PIL的ImageDraw配合中文字体文件来渲染先转RGB画完再转回BGR。如果你觉得麻烦也可以直接在检测框上写英文标签报告文件里再对应中文。结构化报告可以输出成CSV包含图片名、目标类别、置信度、坐标、差异比率和是否疑似违建。如果后续要接GIS系统或生成Excel这个CSV非常方便。我还会额外写一张文本汇总总检测数量、疑似新增数量、存量数量、待复核区域数量这样业务方不用打开图片就能知道整体情况。5. 完整代码整合与运行说明5.1 主流程代码这段代码我把前面的模块串起来直接保存为drone_inspect.py即可运行。假设你已经有训练好的权重或者直接用COCO预训练的yolov5s.pt都能跑通。import cv2 import numpy as np import torch import csv from pathlib import Path # ----------------- 模型加载 ----------------- def load_model(weight_pathyolov5s.pt, device0): model torch.hub.load(ultralytics/yolov5, custom, pathweight_path, force_reloadFalse) model.conf 0.25 model.iou 0.45 model.max_det 200 return model # ----------------- 图像配准 ----------------- def align_images(img_ref, img_new): gray_ref cv2.cvtColor(img_ref, cv2.COLOR_BGR2GRAY) gray_new cv2.cvtColor(img_new, cv2.COLOR_BGR2GRAY) sift cv2.SIFT_create() kp1, des1 sift.detectAndCompute(gray_ref, None) kp2, des2 sift.detectAndCompute(gray_new, None) matcher cv2.BFMatcher() raw_matches matcher.knnMatch(des1, des2, k2) good [] for m, n in raw_matches: if m.distance 0.75 * n.distance: good.append(m) src_pts np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2) dst_pts np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2) H, _ cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) h, w img_ref.shape[:2] aligned_new cv2.warpPerspective(img_new, H, (w, h)) return aligned_new, H # ----------------- 差异掩码 ----------------- def create_diff_mask(img_ref, img_aligned, threshold40, min_area3000): gray_ref cv2.cvtColor(img_ref, cv2.COLOR_BGR2GRAY) gray_aligned cv2.cvtColor(img_aligned, cv2.COLOR_BGR2GRAY) diff cv2.absdiff(gray_ref, gray_aligned) _, mask cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations2) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations3) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) region_mask np.zeros_like(mask) for cnt in contours: if cv2.contourArea(cnt) min_area: cv2.drawContours(region_mask, [cnt], -1, 255, -1) return region_mask # ----------------- 检测框与掩码交叉 ----------------- def mask_overlay_ratio(box, mask): x1, y1, x2, y2 [int(v) for v in box] w max(x2 - x1, 1) h max(y2 - y1, 1) crop mask[y1:y2, x1:x2] overlay cv2.countNonZero(crop) return overlay / (w * h) # ----------------- 主流程 ----------------- def run_inspection(img_old_path, img_new_path, weight_pathyolov5s.pt, threshold40, min_area3000, ratio_thresh0.15, device0): img_ref cv2.imread(img_old_path) img_new cv2.imread(img_new_path) if img_ref is None or img_new is None: raise ValueError(图片读取失败请检查路径) aligned_new, H align_images(img_ref, img_new) region_mask create_diff_mask(img_ref, aligned_new, threshold, min_area) model load_model(weight_path, device) dets model(aligned_new) # 对配准后的新影像检测坐标与差异掩码对齐 result_img aligned_new.copy() rows [] for det in dets.xyxy[0].cpu().numpy(): x1, y1, x2, y2, conf, cls_id det cls_name model.names[int(cls_id)] ratio mask_overlay_ratio([x1, y1, x2, y2], region_mask) if ratio ratio_thresh: color (0, 0, 255) status suspect else: color (255, 0, 0) status existing cv2.rectangle(result_img, (int(x1), int(y1)), (int(x2), int(y2)), color, 2) label f{cls_name} {conf:.2f} ratio{ratio:.2f} cv2.putText(result_img, label, (int(x1), max(int(y1) - 8, 20)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) rows.append([Path(img_new_path).name, cls_name, round(float(conf), 4), int(x1), int(y1), int(x2), int(y2), round(ratio, 4), status]) with open(inspection_report.csv, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow([image, class, conf, x1, y1, x2, y2, diff_ratio, status]) writer.writerows(rows) cv2.imwrite(diff_mask.png, region_mask) cv2.imwrite(result.png, result_img) print(f完成共 {len(rows)} 个检测目标详见 inspection_report.csv) if __name__ __main__: run_inspection(old.png, new.png)如果你已经有本地YOLOv5仓库可以把torch.hub.load(ultralytics/yolov5, custom, ...)改成引用本地路径避免在离线服务器上卡在网络请求。改法是model torch.hub.load(你的本地yolov5目录, custom, pathweight_path, sourcelocal)5.2 运行参数调整实际运行时最影响结果的三组参数是差异阈值、轮廓最小面积和检测置信度。我把它们的关系整理成一张表方便你快速定位问题现象优先调整参数调整方向差异图噪声多全是碎点threshold增大到50或60真正的新建筑没进掩码min_area适当调小检测框太多误报model.conf上调到0.35或0.4小目标建筑漏检model.img_size 或推理分辨率从640上调到960或1280存量建筑被误标为疑似ratio_thresh上调到0.25以上这些参数之间存在联动改其中一个经常会牵连其他参数表现。我建议在跑全量数据前先选3到5张有代表性的图做网格搜索把几组参数各跑一遍对比标记效果后再定最终组合。5.3 边缘设备部署扩展很多巡检项目需要在现场快速出结果不一定背着台式机。YOLOv5部署到树莓派5或Jetson系列上是最近被问得比较多的问题。如果算力有限建议先做几步第一把模型导出成TorchScript或ONNX去掉PyTorch动态图的额外开销第二把输入分辨率从640降到480配合yolov5n或yolov5s第三只对差异掩码框出的固定区域做裁剪检测而不是整图推理。我记得在Jetson Nano上用TensorRT加速yolov5s推理时间能从几百毫秒压到几十毫秒配合边缘端图片缓存现场跑完一轮巡检完全可行。树莓派5的CPU性能比前代强不少但跑yolov5s仍显得吃力更适合用yolov5n加小尺寸输入。如果要用树莓派实时处理视频流建议只做差异检测用于粗筛再定期跑模型没必要每一帧都调用推理。6. 常见问题与排查技巧6.1 环境安装类问题先说环境因为这块卡住人的频率最高。常见问题包括pycocotools编译失败、torch和torchvision版本不匹配、opencv-python版本过低导致部分接口不存在。围绕YOLOv5本身网上讨论最多的是官网下载、环境配置和超参数调整。我的经验是不要盲目追求最新版本确定一个稳定组合后就固定下来别频繁升级。下面梳理一份速查常见错误原因解决方案No module named pycocotoolsWindows缺编译环境pip install pycocotools-windowsImportError: libGL.so.1Linux缺OpenCV系统库sudo apt update sudo apt install libgl1torch.cuda.is_available()返回FalsePyTorch和驱动不匹配重装对应CUDA版本的PyTorchSIFT不可用OpenCV编译不含contrib升级opencv-python到4.8以上或改用ORB/SURF6.2 配准与差异误报类问题配准失败是最常见的问题。特征点匹配不足会导致单应性矩阵不稳定最终两张图错位严重。遇到这种情况先加大max_features把SIFT特征点从5000提高到10000其次把比例检验的阈值从0.75调到0.7让匹配更严格如果还是没有足够匹配考虑先用GIS信息做一个粗略的平移对齐再跑特征点匹配。差异图频繁出现大面积块状误报要警惕季节变化和阴影。我处理过一批松树林区域的航拍图两次拍摄相隔三个月树冠的绿色度差异很大差异掩码几乎把整个林区都圈了进去。后来我在HSV空间做了一遍绿色像元掩码把植被覆盖区域从差异掩码中占比超过60%的斑块直接剔除误报才降下来。6.3 检测与标记类问题YOLOv5检测结果不理想要分两种情况看。一种是漏检目标明明在图上模型没框出来。常见原因包括目标太小、对比度低、训练数据没有类似视角。解决办法是提高推理分辨率或者把大图切块后按patch检测同时把conf阈值降低试一下。另一种是误检模型把阴影、屋顶设备、地面标志当成建筑目标。这种情况优先看训练数据是否覆盖了这些负样本而不是盲目调阈值。给模型提供足够多的负样本比反复调conf有效得多。标记阶段最让人头疼的是中文标签乱码。如果你不想引入PIL字体模块可以偷个懒绘制时用英文标签比如把“疑似新增违建”写成SUSPECT_NEW报告CSV里再映射成中文。这个方案虽然显示上不够直观但胜在稳定。6.4 性能与部署类问题如果图片太大比如无人机正射影像单张就达到50MB甚至上百MB这时候先缩放到一个合理的处理尺寸再跑流程。整张resize到3000像素宽度并不会丢失太多建筑级别的特征但速度能快几倍。GPU显存不足时可以把输入宽度设到1280以下或者采用滑窗切块推理。切块时要保留一定的重叠区域避免建筑正好被切成两半。建议重叠率设为20%到30%并在合并检测结果时用NMS去重。边缘部署还有一个容易被忽略的问题模型在PC上精度很好转换到ONNX或TensorRT后输出结果出现波动。这通常是归一化方式和缩放细节没对齐。YOLOv5官方仓库的export.py已经处理了大部分转换逻辑尽量别自己手写预处理直接用官方导出脚本能省不少事。7. 实践总结与可扩展方向7.1 项目的适用边界这套组合流程最适合的场景有两个特点一是影像覆盖范围稳定能拿到同一区域不同时间的高重叠度图片二是要判断的“违建”具备明显物理形态比如屋顶加盖、彩钢棚、临时板房。如果目标是很隐蔽的地下施工或者仅仅通过颜色变化才能识别的早期违建只靠光学影像和YOLOv5会非常吃力需要结合多光谱、激光雷达或人工核查。在技术路线上也不要被YOLOv5框死。随着检测模型迭代换成YOLOv8、RT-DETR完全没问题核心的差异对比加目标筛选思想是不变的。哪怕只做像素差异不跑检测把变化区域按面积和位置聚类也能给人工复核提供很大帮助。关键是先把流程跑通再看瓶颈在哪里而不是上来就堆最新模型。7.2 后续还能怎么升级我建议三个方向。第一把“两期对比”升级成“多期时间序列”一旦发现某块区域连续多期都在变化就自动提高风险等级。这样比单期对比更容易发现持续施工的违建。第二引入地块边界和规划数据检测框与违建红线做空间叠加只有落入敏感区域的检测结果才自动上报能极大降低人工复核量。第三给疑似违建加上面积估算利用无人机飞行高度、焦距或正射影像的地面分辨率把像素面积换算成平米输出包含面积信息的报告。最后说一个我个人的经验技术流程做得再顺也不要让系统完全替代人工审核。违建认定涉及很强的业务规则和合规要求系统更适合做“优先排序”和“自动标记”把决策权和最终确认权留给熟悉现场情况的人。这样既发挥了YOLOv5和图像差异对比的效率优势又守住了关键环节的质量底线。
返回列表