在无人机航拍的海上搜救场景中,一个漂浮的人头可能只占画面的几十个像素。这种极端的小目标检测任务,正是AFO数据集(Aerial Dataset of Floating Objects)要解决的核心问题。作为2021年由波兰AGH科技大学发布的专业数据集,它填补了海上搜救领域高质量标注数据的空白。
我曾在多个海上监测项目中尝试使用通用目标检测模型,结果发现对小型漂浮物的漏检率高达60%以上。直到接触到AFO数据集,才真正理解专业数据对模型性能的决定性影响。这个数据集最珍贵的价值在于:
AFO数据集的采集使用了多旋翼和固定翼无人机,飞行高度在50-200米之间,这个高度区间模拟了真实搜救作业场景。图像分辨率从720p到4K不等,其中4K图像对小目标检测尤为重要——在3840×2160分辨率下,一个落水者的头部大约占据30×30像素区域。
标注团队遵循严格的准则:
数据集包含的9个类别经过精心设计,覆盖了海上搜救的典型目标:
| 类别ID | 英文名称 | 中文描述 | 样本数量 | 平均像素面积 |
|---|---|---|---|---|
| 0 | human | 落水人员 | 8,742 | 28×28 |
| 1 | boat | 各类船只 | 12,305 | 120×60 |
| 5 | wind/sup-board | 冲浪板/桨板 | 3,021 | 25×15 |
特别值得注意的是"small_obj"类别,包含各种难以分类的小型漂浮物,这类目标虽然单个重要性不高,但总量占比达到19.7%,是模型容易漏检的重灾区。
推荐使用以下硬件配置进行训练:
数据目录应按照YOLO标准格式组织:
code复制AFO_Dataset/
├── images/
│ ├── train/ # 2787张训练图像
│ ├── val/ # 339张验证图像
│ └── test/ # 514张测试图像
└── labels/
├── train/ # 对应标注文件
├── val/
└── test/
每个标注文件为.txt格式,每行表示一个目标:
code复制<class_id> <x_center> <y_center> <width> <height>
坐标值均为归一化后的相对值(0-1之间)。
针对海上小目标的特点,需要特别调整以下参数:
python复制model = YOLO('yolov8s.pt') # 基础模型选择
results = model.train(
data='afo.yaml',
epochs=150, # 延长训练轮次
imgsz=1280, # 高分辨率输入
batch=8, # 小批量应对高分辨率
optimizer='AdamW',
lr0=0.001,
hsv_h=0.015, # 增强色调变化
hsv_s=0.7, # 增强饱和度变化
hsv_v=0.4, # 增强亮度变化
degrees=15.0, # 更大旋转角度
translate=0.3, # 更大平移幅度
scale=0.9, # 缩放范围扩大
fliplr=0.5,
mosaic=1.0, # 始终启用mosaic
mixup=0.2, # 增加mixup概率
copy_paste=0.1, # 启用copy-paste增强
erasing=0.1, # 随机擦除增强
close_mosaic=10, # 最后10轮关闭mosaic
)
关键提示:当使用imgsz=1280时,建议将batch_size调至8或更低,否则容易导致显存溢出。可以启用梯度累积(未显示在代码中)来补偿小batch的影响。
python复制# 在afo.yaml中添加自定义锚框
anchors:
- [6,12, 10,20, 14,28] # 小目标层
- [20,40, 32,64, 48,96] # 中目标层
- [80,160, 128,256, 192,384] # 大目标层
这些锚框尺寸是基于AFO数据集统计得出的,特别加强了小目标检测层的配置。
python复制loss:
box: 0.05 # 降低框回归权重
cls: 0.3 # 提高分类权重
dfl: 0.1 # 分布焦点损失
由于小目标分类比定位更困难,适当调整损失权重可以提升效果。
海上目标的检测面临三大挑战:光照变化、波浪干扰和目标微小。我们开发了一套组合增强策略:
python复制def wave_distortion(image):
rows, cols = image.shape[:2]
# 创建正弦波变形场
map_x = np.zeros((rows, cols), np.float32)
map_y = np.zeros((rows, cols), np.float32)
for i in range(rows):
for j in range(cols):
map_x[i,j] = j + 5 * np.sin(i/30.0)
map_y[i,j] = i + 5 * np.cos(j/30.0)
return cv2.remap(image, map_x, map_y, cv2.INTER_LINEAR)
部署时可以采用以下技巧提升性能:
python复制def dynamic_inference(model, img, min_size=800, max_size=1280):
h, w = img.shape[:2]
scale = min(max_size / max(h, w), min_size / min(h, w))
new_size = (int(w * scale), int(h * scale))
resized = cv2.resize(img, new_size)
results = model(resized)
# 将检测框坐标转换回原图尺寸
results[0].boxes.xyxy /= scale
return results
python复制# 修改NMS参数
results = model.predict(
source=image,
conf=0.3, # 降低置信度阈值
iou=0.4, # 提高IOU阈值
agnostic_nms=True,
max_det=100 # 增加最大检测数
)
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 小目标漏检率高 | 特征图分辨率不足 | 使用更高imgsz,添加小目标检测层 |
| 波浪区域误检多 | 数据增强不足 | 增加波浪模拟增强 |
| 不同光照下性能波动大 | 颜色增强单一 | 组合使用HSV和亮度抖动 |
| 模型收敛慢 | 学习率不合适 | 采用余弦退火学习率调度 |
python复制# 在验证集上找出FP和FN样本
errors = []
for img, label in val_loader:
pred = model(img)
# 计算每个预测的匹配情况
matches = match_predictions(pred, label)
# 记录未匹配的GT和错误预测
errors.append({
'image': img,
'false_positives': pred[~matches.pred_matches],
'false_negatives': label[~matches.gt_matches]
})
# 将这些困难样本加入训练集
python复制from ensemble_boxes import weighted_boxes_fusion
def wbf_ensemble(models, image):
all_boxes = []
all_scores = []
all_labels = []
for model in models:
results = model(image)
boxes = results[0].boxes.xyxy.cpu().numpy()
scores = results[0].boxes.conf.cpu().numpy()
labels = results[0].boxes.cls.cpu().numpy()
all_boxes.append(boxes)
all_scores.append(scores)
all_labels.append(labels)
# 应用WBF算法
fused_boxes, fused_scores, fused_labels = weighted_boxes_fusion(
all_boxes, all_scores, all_labels,
weights=[1, 1.5, 2], # 给更大模型更高权重
iou_thr=0.5,
skip_box_thr=0.4
)
return fused_boxes, fused_scores, fused_labels
在实际项目中,这套方案将海上小目标的检测mAP@0.5从0.63提升到了0.79,特别是human类别的召回率提高了35%。最关键的经验是:不要盲目增大模型规模,而是要通过针对性的数据增强和锚框优化来解决海上搜救场景的特殊挑战。