人機(jī)小目標(biāo)檢測(cè)數(shù)據(jù)集與增強(qiáng)策略詳解)
1. 無(wú)人機(jī)視角下的目標(biāo)檢測(cè)數(shù)據(jù)集概述在無(wú)人機(jī)航拍場(chǎng)景中目標(biāo)檢測(cè)技術(shù)面臨著獨(dú)特的挑戰(zhàn)——小目標(biāo)檢測(cè)。與常規(guī)視角不同無(wú)人機(jī)拍攝的物體往往只占畫面的極小比例通常小于32×32像素這對(duì)數(shù)據(jù)集的構(gòu)建提出了特殊要求。目前主流的小目標(biāo)檢測(cè)數(shù)據(jù)集主要分為兩類通用型數(shù)據(jù)集如COCO、VOC的子集和專用型無(wú)人機(jī)數(shù)據(jù)集如VisDrone、UAVDT。關(guān)鍵區(qū)別無(wú)人機(jī)數(shù)據(jù)集通常包含更多1%圖像面積的目標(biāo)且背景復(fù)雜度更高。以VisDrone為例其平均每個(gè)目標(biāo)僅占0.008%的圖像面積是COCO數(shù)據(jù)集中小目標(biāo)比例的1/50。2. 主流無(wú)人機(jī)小目標(biāo)檢測(cè)數(shù)據(jù)集詳解2.1 VisDrone數(shù)據(jù)集數(shù)據(jù)規(guī)模10,209張圖片6,471訓(xùn)練集548驗(yàn)證集3,190測(cè)試集標(biāo)注格式同時(shí)提供VOC和YOLO格式類別特點(diǎn)行人占比37%車輛51%自行車/三輪車12%下載方式# 官方下載指令需注冊(cè) wget http://aiskyeye.com/download/visdrone/VisDrone2019-DET-train.zip unzip VisDrone2019-DET-train.zip -d ./visdrone2.2 UAVDT數(shù)據(jù)集獨(dú)特價(jià)值包含30小時(shí)1080P視頻特別適合動(dòng)態(tài)小目標(biāo)檢測(cè)標(biāo)注特點(diǎn)車輛邊界框運(yùn)動(dòng)軌跡光照條件標(biāo)注晴天/陰天/夜間轉(zhuǎn)換YOLO格式示例import xml.etree.ElementTree as ET def voc2yolo(voc_path, yolo_path): tree ET.parse(voc_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) with open(yolo_path, w) as f: for obj in root.iter(object): cls obj.find(name).text bbox obj.find(bndbox) x_center (float(bbox.find(xmin).text) float(bbox.find(xmax).text)) / 2 / w y_center (float(bbox.find(ymin).text) float(bbox.find(ymax).text)) / 2 / h width (float(bbox.find(xmax).text) - float(bbox.find(xmin).text)) / w height (float(bbox.find(ymax).text) - float(bbox.find(ymin).text)) / h f.write(f{cls_id} {x_center} {y_center} {width} {height}\n)3. 小目標(biāo)檢測(cè)數(shù)據(jù)增強(qiáng)策略3.1 專用數(shù)據(jù)增強(qiáng)技術(shù)多尺度切片增強(qiáng)Mosaic Augmentation將4張圖像隨機(jī)縮放后拼接顯著增加小目標(biāo)出現(xiàn)頻率YOLOv5實(shí)現(xiàn)示例def mosaic_augmentation(images, labels): # 創(chuàng)建空白畫布 s 640 # 目標(biāo)尺寸 mosaic_img np.full((s*2, s*2, 3), 114, dtypenp.uint8) # 隨機(jī)選取拼接中心點(diǎn) xc, yc [int(random.uniform(s*0.25, s*0.75)) for _ in range(2)] # 處理4張圖像 for i in range(4): img, (lbl_h, lbl_w) images[i], labels[i].shape # 隨機(jī)縮放 r min(s/img.shape[0], s/img.shape[1]) img cv2.resize(img, (int(img.shape[1]*r), int(img.shape[0]*r))) # 計(jì)算放置位置 h, w img.shape[:2] if i 0: # 左上 x1a, y1a, x2a, y2a max(xc-w, 0), max(yc-h, 0), xc, yc x1b, y1b, x2b, y2b w-(x2a-x1a), h-(y2a-y1a), w, h elif i 1: # 右上 x1a, y1a, x2a, y2a xc, max(yc-h, 0), min(xcw, s*2), yc x1b, y1b, x2b, y2b 0, h-(y2a-y1a), min(w, x2a-x1a), h elif i 2: # 左下 x1a, y1a, x2a, y2a max(xc-w, 0), yc, xc, min(s*2, ych) x1b, y1b, x2b, y2b w-(x2a-x1a), 0, w, min(y2a-y1a, h) elif i 3: # 右下 x1a, y1a, x2a, y2a xc, yc, min(xcw, s*2), min(s*2, ych) x1b, y1b, x2b, y2b 0, 0, min(w, x2a-x1a), min(y2a-y1a, h) mosaic_img[y1a:y2a, x1a:x2a] img[y1b:y2b, x1b:x2b] return mosaic_img3.2 小目標(biāo)專用損失函數(shù)Focal Loss改進(jìn)版class SmallObjectFocalLoss(nn.Module): def __init__(self, alpha0.25, gamma2.0, small_obj_thresh32): super().__init__() self.alpha alpha self.gamma gamma self.thresh small_obj_thresh # 像素面積閾值 def forward(self, pred, target): BCE_loss F.binary_cross_entropy_with_logits(pred, target, reductionnone) # 計(jì)算目標(biāo)大小權(quán)重 target_area (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) area_weight torch.where(target_area self.thresh, torch.tensor(2.0).to(device), torch.tensor(1.0).to(device)) pt torch.exp(-BCE_loss) focal_loss self.alpha * (1-pt)**self.gamma * BCE_loss * area_weight return focal_loss.mean()4. 數(shù)據(jù)集標(biāo)注與質(zhì)量檢查4.1 LabelImg標(biāo)注技巧小目標(biāo)標(biāo)注規(guī)范最小接受尺寸4×4像素模糊目標(biāo)處理至少3人交叉驗(yàn)證部分遮擋處理標(biāo)注可見(jiàn)部分保存路徑設(shè)置# 啟動(dòng)時(shí)指定路徑 labelimg --labels drone_labels.txt --autosave --savedir ./annotations4.2 標(biāo)注質(zhì)量驗(yàn)證腳本def verify_annotations(img_dir, label_dir): issues [] for label_file in os.listdir(label_dir): img_file os.path.join(img_dir, label_file.replace(.txt, .jpg)) if not os.path.exists(img_file): issues.append(fMissing image: {img_file}) continue img cv2.imread(img_file) h, w img.shape[:2] with open(os.path.join(label_dir, label_file)) as f: for line in f: cls, xc, yc, bw, bh map(float, line.strip().split()) x1 int((xc - bw/2) * w) y1 int((yc - bh/2) * h) x2 int((xc bw/2) * w) y2 int((yc bh/2) * h) if x1 x2 or y1 y2: issues.append(fInvalid bbox in {label_file}: {line}) elif (x2-x1)*(y2-y1) 16: issues.append(fToo small bbox in {label_file}: {x2-x1}x{y2-y1}px) return issues5. 模型訓(xùn)練與部署實(shí)戰(zhàn)5.1 YOLOv8小目標(biāo)專用配置# yolov8-drone.yaml nc: 10 # 類別數(shù) scales: small: depth: 0.33 width: 0.25 anchors: 3 anchors_new: [[3,4, 5,8, 7,12], [9,16, 12,32, 19,48]] # 更小的anchor尺寸 train: mosaic: 1.0 mixup: 0.1 small_object_scale: 2.0 # 小目標(biāo)損失權(quán)重5.2 RK3588部署優(yōu)化模型量化python export.py --weights yolov8n.pt --include onnx --simplify --dynamic rknn-toolkit2/convert.py yolov8n.onnx --output yolov8n.rknn --mean-values 0,0,0 --std-values 255,255,255推理加速技巧使用NPU專用算子替換常規(guī)卷積將檢測(cè)頭改為共享權(quán)重結(jié)構(gòu)啟用異步DMA數(shù)據(jù)傳輸6. 常見(jiàn)問(wèn)題解決方案問(wèn)題現(xiàn)象可能原因解決方案小目標(biāo)漏檢率高Anchor尺寸不匹配使用k-means重新聚類anchor虛警多背景復(fù)雜度高增加CutMix數(shù)據(jù)增強(qiáng)推理速度慢后處理耗時(shí)改用Batch NMS標(biāo)注不一致標(biāo)注標(biāo)準(zhǔn)模糊制定最小目標(biāo)尺寸規(guī)范在實(shí)際無(wú)人機(jī)項(xiàng)目中我們發(fā)現(xiàn)最影響精度的三個(gè)關(guān)鍵因素標(biāo)注一致性建議多人交叉驗(yàn)證數(shù)據(jù)增強(qiáng)策略必須包含小目標(biāo)專用增強(qiáng)檢測(cè)頭設(shè)計(jì)建議使用高分辨率特征圖