檢測實(shí)戰(zhàn):YOLO小目標(biāo)漏檢與切片推理避坑指南)
簡介本資源為基于Python實(shí)現(xiàn)的遙感圖像物體目標(biāo)檢測完整項(xiàng)目包面向計(jì)算機(jī)、人工智能、通信工程等專業(yè)的在校學(xué)生與開發(fā)者可用于畢業(yè)設(shè)計(jì)、課程設(shè)計(jì)、大作業(yè)或項(xiàng)目立項(xiàng)演示。包內(nèi)共404個文件以350個Python源碼為核心輔以22個yaml配置、22個md說明文檔、2個ipynb實(shí)驗(yàn)筆記及csv、txt等輔助文件壓縮包約7.54MB結(jié)構(gòu)清晰便于按模塊查閱。項(xiàng)目基于FAIR1M2.0遙感數(shù)據(jù)集訓(xùn)練涵蓋環(huán)境安裝、數(shù)據(jù)集灰度化與目錄整理、模型訓(xùn)練與推理等完整流程并附有項(xiàng)目說明與配置文檔幫助讀者理解遙感目標(biāo)檢測的實(shí)現(xiàn)思路與關(guān)鍵環(huán)節(jié)。目前已有729人學(xué)習(xí)下載適合具備一定Python基礎(chǔ)、希望快速上手遙感檢測實(shí)戰(zhàn)或在此基礎(chǔ)上二次開發(fā)的學(xué)習(xí)者參考使用。1. 遙感圖像目標(biāo)檢測為什么總在“小目標(biāo)”上翻車拿到一份「基于python實(shí)現(xiàn)遙感圖像物體目標(biāo)檢測源碼模型項(xiàng)目說明.zip」多數(shù)人第一反應(yīng)是解壓、裝依賴、跑detect.py然后看著幾張航拍圖上的框發(fā)朋友圈。但真正把它用到自己的數(shù)據(jù)上十有八九會在小目標(biāo)上翻車飛機(jī)、車輛、船只這些在遙感圖里可能只占十幾個像素模型要么漏檢要么把一片屋頂認(rèn)成停車場。遙感圖像目標(biāo)檢測和自然圖像檢測最大的差別就在這里——尺度極端、方向任意、背景雜亂而且標(biāo)注成本高得離譜。這個方向能解決的問題很具體從衛(wèi)星或無人機(jī)影像里自動找出并定位感興趣的目標(biāo)用于土地利用核查、船舶監(jiān)測、災(zāi)害評估、農(nóng)林統(tǒng)計(jì)。適合兩類人一是手里已經(jīng)有遙感數(shù)據(jù)集、想快速跑通 baseline 的算法工程師二是想拿它做課程設(shè)計(jì)或畢設(shè)、需要一份能改能跑源碼的學(xué)生。它不適合指望“開箱即用、精度 99%”的人遙感檢測的精度提升基本靠數(shù)據(jù)、尺度和后處理一點(diǎn)點(diǎn)摳出來。下面按“先立住原理、再動手復(fù)現(xiàn)、最后避坑”的順序講清楚。2. 遙感目標(biāo)檢測的選型為什么是 YOLO 而不是兩階段2.1 遙感場景下 one-stage 與 two-stage 的真實(shí)差距遙感圖像有個特點(diǎn)單張圖幅面極大動輒 4000×4000 以上但目標(biāo)極小。Faster R-CNN 這類兩階段檢測器精度穩(wěn)可推理速度慢在滑窗切片后圖塊數(shù)量爆炸的情況下整體耗時很難接受。YOLO 系列屬于 one-stage一次前向就出框配合切片推理能撐住大圖。常見做法是把大圖切成 512×512 或 640×640 的帶重疊切片逐塊檢測再合并這樣既控制了顯存也緩解了小目標(biāo)在縮放后消失的問題。選型時還要看標(biāo)注格式。遙感數(shù)據(jù)集常見 DOTA 格式標(biāo)注是任意四邊形旋轉(zhuǎn)框而 YOLO 默認(rèn)是水平框。如果你的目標(biāo)是艦船、飛機(jī)這類有明確朝向的物體水平框會引入大量背景建議用 YOLOv8-OBB 或帶旋轉(zhuǎn)框的版本如果只是統(tǒng)計(jì)數(shù)量、不關(guān)心朝向水平框夠用改造成本也低。我一般會先問一句你要的是“數(shù)出來”還是“框準(zhǔn)方向”這決定了后面一半的工作量。2.2 從零跑通的最小環(huán)境與依賴先確認(rèn) Python 版本遙感檢測常用的 torch、opencv、numpy 對版本敏感建議 Python 3.83.10。下面是一套能跑通的最小依賴安裝流程命令按順序執(zhí)行即可。# 創(chuàng)建獨(dú)立環(huán)境避免和系統(tǒng)包沖突 conda create -n rs_detect python3.10 -y conda activate rs_detect # 安裝 PyTorch按自己 CUDA 版本去官網(wǎng)選對應(yīng)命令這里以 CUDA 11.8 為例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 遙感檢測常用庫opencv 讀圖、numpy 數(shù)值、pyyaml 讀配置、tqdm 進(jìn)度條 pip install opencv-python numpy pyyaml tqdm matplotlib # 如果源碼基于 ultralytics直接裝它 pip install ultralytics邏輯說明conda建環(huán)境是為了隔離遙感項(xiàng)目經(jīng)常要裝gdal、rasterio這類地理庫和系統(tǒng) Python 混裝極易沖突。torch的 CUDA 版本必須和本機(jī)驅(qū)動匹配裝錯會報(bào)CUDA error: no kernel image is available。ultralytics是 YOLOv8 官方庫如果源碼是 YOLOv5 結(jié)構(gòu)則改用pip install -r requirements.txt。參數(shù)上--index-url指定 PyTorch 官方源國內(nèi)網(wǎng)絡(luò)慢可以換鏡像但要注意鏡像的 CUDA 版本是否齊全。2.3 數(shù)據(jù)準(zhǔn)備DOTA 轉(zhuǎn) YOLO 的目錄結(jié)構(gòu)源碼包通常自帶示例數(shù)據(jù)但你要用自己的數(shù)據(jù)就得先統(tǒng)一格式。YOLO 要求每張圖對應(yīng)一個同名.txt每行類別 x_center y_center w h全部歸一化到 01。目錄結(jié)構(gòu)建議如下dataset/ images/ train/ val/ labels/ train/ val/ data.yamldata.yaml內(nèi)容示例path: ./dataset train: images/train val: images/val nc: 5 names: [plane, ship, vehicle, storage-tank, bridge]邏輯說明path是數(shù)據(jù)集根目錄train/val是相對路徑nc是類別數(shù)names順序必須和標(biāo)注里的類別 id 一致錯一位整個訓(xùn)練就廢了。遙感數(shù)據(jù)集類別名建議用英文短詞避免中文路徑在部分庫下讀取出錯。如果你的原始標(biāo)注是 DOTA 的x1 y1 x2 y2 x3 y3 x4 y4需要寫腳本轉(zhuǎn)成水平框取四點(diǎn)的 min/max 得到外接矩形再歸一化。這一步的坑后面會專門講。3. 訓(xùn)練與推理把源碼跑起來并調(diào)出可用精度3.1 用切片推理解決大圖小目標(biāo)遙感大圖直接 resize 到 640 會把小目標(biāo)壓沒正確做法是滑窗切片。下面是一個帶重疊的切片函數(shù)訓(xùn)練和推理都能用。import cv2 import numpy as np def slice_image(img, slice_size640, overlap128): 把大圖切成帶重疊的小塊返回圖塊和左上角坐標(biāo) h, w img.shape[:2] stride slice_size - overlap patches [] coords [] for y in range(0, h, stride): for x in range(0, w, stride): # 邊界處理最后一塊貼邊避免出現(xiàn)極小殘片 x2 min(x slice_size, w) y2 min(y slice_size, h) x1 max(0, x2 - slice_size) y1 max(0, y2 - slice_size) patch img[y1:y2, x1:x2] # 不足 slice_size 的補(bǔ)零保證輸入尺寸一致 if patch.shape[0] slice_size or patch.shape[1] slice_size: patch cv2.copyMakeBorder( patch, 0, slice_size - patch.shape[0], 0, slice_size - patch.shape[1], cv2.BORDER_CONSTANT, value(114, 114, 114)) patches.append(patch) coords.append((x1, y1)) return patches, coords邏輯說明stride slice_size - overlap決定滑動步長overlap越大越不容易在切片邊界漏掉目標(biāo)但圖塊數(shù)量成倍增加。overlap128是常用折中目標(biāo)尺寸小于 128 像素時基本不會被切斷。邊界用copyMakeBorder補(bǔ)灰邊114 是 YOLO 默認(rèn)填充值而不是直接丟棄否則邊緣目標(biāo)會丟。參數(shù)上slice_size要和訓(xùn)練時的imgsz一致訓(xùn)練用 640 推理也用 640否則尺度對不上。3.2 訓(xùn)練命令與關(guān)鍵超參假設(shè)源碼基于 ultralytics訓(xùn)練命令如下yolo detect train \ datadataset/data.yaml \ modelyolov8s.pt \ imgsz640 \ epochs100 \ batch16 \ lr00.01 \ patience20 \ device0 \ projectruns/rs \ nameexp1邏輯說明modelyolov8s.pt是預(yù)訓(xùn)練權(quán)重遙感數(shù)據(jù)量通常不大從預(yù)訓(xùn)練微調(diào)比從頭訓(xùn)收斂快得多。imgsz640要和切片尺寸一致。batch16看顯存顯存不夠就降到 8 或 4同時把lr0按比例調(diào)小否則梯度噪聲大。patience20是早停20 輪驗(yàn)證指標(biāo)不升就停省時間。device0指定第一塊 GPU多卡用device0,1。遙感數(shù)據(jù)有個反直覺的點(diǎn)數(shù)據(jù)增強(qiáng)不能照搬自然圖像。mosaic增強(qiáng)在遙感里容易把不同地物拼在一起產(chǎn)生不合理上下文建議把mosaic概率調(diào)低到 0.5 以下flipud上下翻轉(zhuǎn)在航拍圖里是合理的可以開。旋轉(zhuǎn)增強(qiáng)對任意朝向目標(biāo)有幫助但水平框標(biāo)注下旋轉(zhuǎn)后框會變大要謹(jǐn)慎。3.3 推理與結(jié)果合并切片推理后要把各圖塊的框映射回原圖坐標(biāo)再用 NMS 去重。from ultralytics import YOLO model YOLO(runs/rs/exp1/weights/best.pt) patches, coords slice_image(img, 640, 128) all_boxes [] for patch, (ox, oy) in zip(patches, coords): results model.predict(patch, conf0.25, iou0.5, verboseFalse) for box in results[0].boxes: x1, y1, x2, y2 box.xyxy[0].tolist() # 映射回原圖坐標(biāo) all_boxes.append([x1 ox, y1 oy, x2 ox, y2 oy, box.conf[0].item(), box.cls[0].item()]) # 跨切片 NMS去掉重疊框邏輯說明conf0.25是置信度閾值遙感小目標(biāo)建議先設(shè)低一點(diǎn)0.20.25再靠 NMS 過濾設(shè)太高會漏檢。iou0.5是 NMS 的 IoU 閾值。映射時x1ox把圖塊坐標(biāo)還原到原圖??缜衅?NMS 必須做否則重疊區(qū)域的目標(biāo)會被重復(fù)框好幾次。這一步是很多源碼包沒寫全的地方跑出來框重疊嚴(yán)重多半就是漏了全局 NMS。4. 避坑與排查遙感檢測最常見的 5 個翻車現(xiàn)場4.1 現(xiàn)象訓(xùn)練 loss 正常下降但驗(yàn)證 mAP 一直是 0原因標(biāo)注類別 id 和data.yaml的names順序?qū)Σ簧匣蛘邩?biāo)注坐標(biāo)沒歸一化。遙感數(shù)據(jù)集轉(zhuǎn)換時最容易出這個問題DOTA 原始坐標(biāo)是像素值忘了除以寬高YOLO 讀進(jìn)去全是越界框直接忽略。解決寫個校驗(yàn)?zāi)_本遍歷所有 label 文件檢查每行 5 個值、后 4 個都在 01 之間、類別 id 小于nc。發(fā)現(xiàn)越界就回查轉(zhuǎn)換腳本確認(rèn)除以的是對應(yīng)圖像的寬高而不是固定值。4.2 現(xiàn)象小目標(biāo)幾乎全漏大目標(biāo)正常原因訓(xùn)練時imgsz設(shè)太大比如 1280小目標(biāo)在縮放后只剩幾個像素或者切片時overlap太小目標(biāo)被切在邊界上。解決訓(xùn)練和推理統(tǒng)一用 640配合切片。把overlap提到 128 甚至 192代價(jià)是推理變慢。另外可以在data.yaml里開rect訓(xùn)練減少填充但對小目標(biāo)幫助有限核心還是切片尺度。4.3 現(xiàn)象推理結(jié)果框大量重疊同一個目標(biāo)框了七八次原因切片推理后沒做跨切片 NMS每個圖塊各自輸出重疊區(qū)域重復(fù)計(jì)數(shù)。解決把所有圖塊的框收集起來用torchvision.ops.nms或cv2.dnn.NMSBoxes做一次全局 NMS。注意 NMS 的 IoU 閾值別設(shè)太低遙感目標(biāo)密集時 0.5 比較穩(wěn)設(shè) 0.3 會把相鄰的真實(shí)目標(biāo)誤刪。4.4 現(xiàn)象換了自采數(shù)據(jù)后精度斷崖式下跌原因域偏移。源碼自帶的模型是在 DOTA 或 NWPU VHR-10 上訓(xùn)的你的數(shù)據(jù)可能是不同傳感器、不同分辨率、不同季節(jié)顏色和紋理分布差異大。解決別指望直接推理至少拿幾百張自己的圖做微調(diào)。微調(diào)時把lr0降到 0.001凍結(jié) backbone 前幾層只訓(xùn)檢測頭收斂快且不容易過擬合。數(shù)據(jù)量太少就做離線增強(qiáng)把圖塊旋轉(zhuǎn)、翻轉(zhuǎn)、加噪聲擴(kuò)充幾倍。4.5 現(xiàn)象訓(xùn)練報(bào)顯存不足batch 降到 1 還 OOM原因遙感圖切片后單塊雖然 640但數(shù)據(jù)加載時workers開太多每個 worker 都緩存圖像或者cacheTrue把整個數(shù)據(jù)集讀進(jìn)內(nèi)存。解決把workers降到 4 以下關(guān)掉cache用rect減少填充浪費(fèi)。如果還不行檢查是不是誤把原圖直接喂進(jìn)去了原圖 4000×4000 不切片必然爆顯存。5. 把精度再摳一檔旋轉(zhuǎn)框與后處理的兩個實(shí)用技巧水平框跑通之后如果目標(biāo)是艦船、飛機(jī)這類有明確長寬比的物體換旋轉(zhuǎn)框OBB通常能漲 510 個點(diǎn) mAP。ultralytics 支持yolo detect train換成yolo obb train數(shù)據(jù)標(biāo)注從水平框改成x1 y1 x2 y2 x3 y3 x4 y4的八點(diǎn)格式。轉(zhuǎn)換時注意點(diǎn)的順序要一致否則框會扭曲。旋轉(zhuǎn)框的 NMS 用的是旋轉(zhuǎn) IoUcv2.dnn.NMSBoxes不支持得用torchvision的nms_rotated或自己實(shí)現(xiàn)。另一個技巧是測試時增強(qiáng)TTA。遙感目標(biāo)方向任意把圖塊旋轉(zhuǎn) 90°、180°、270° 各推理一次再把框映射回原方向合并能穩(wěn)定漲 13 個點(diǎn)。代價(jià)是推理時間翻四倍適合離線批處理、不追求實(shí)時的場景。我一般會在最終交付前跑一次 TTA對比一下提升幅度如果不到 1 個點(diǎn)就不值得上。還有個容易被忽略的點(diǎn)置信度閾值不要一刀切。不同類別的最優(yōu)閾值不一樣船和飛機(jī)通常可以設(shè) 0.3車輛密集且小設(shè) 0.2 更合適??梢栽隍?yàn)證集上按類別畫 P-R 曲線挑 F1 最高的點(diǎn)作為該類的閾值寫進(jìn)后處理配置里。這套流程走下來一個能用的遙感檢測 baseline 基本就立住了。血淚經(jīng)驗(yàn)是別在模型結(jié)構(gòu)上反復(fù)折騰遙感檢測的收益八成來自數(shù)據(jù)質(zhì)量和切片策略剩下兩成才是模型和調(diào)參。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取