### 实现YOLOv8中的Soft-NMS及其与不同类型IOU结合
在目标检测领域,尤其是面对密集遮挡场景时,改进后的NMS算法能够显著提升模型的表现。对于YOLOv8而言,在原有基础上引入Soft-NMS并结合多种类型的交并比(IOU),可以有效改善此类复杂环境下的检测效果。
#### Soft-NMS简介
传统NMS通过设定阈值来过滤重叠框,而Soft-NMS则采用更灵活的方式调整候选框得分,从而保留更多潜在的有效预测[^2]。具体来说,当两个边界框存在较大程度上的重合时,不是简单地丢弃其中一个,而是降低其置信度分数,使得最终输出更加合理。
#### 结合不同类型的IOU
为了进一步增强YOLOv8应对密集物体的能力,可以在Soft-NMS过程中融入GIOU、DIOU、CIOU、EIOU和SIOU等改进型IOU计算方法:
- **Generalized IOU (GIoU)**:不仅考虑了两矩形之间的相交区域,还加入了包围这两个矩形最小外接矩形的影响因子。
- **Distance IOU (DIoU)**:除了上述因素之外,额外考量中心点距离对相似性的贡献。
- **Complete IOU (CIoU)**:综合了尺度差异、形状匹配等多个方面的影响。
- **Enhanced IOU (EIou)**:在此基础上增加了角度偏差惩罚项。
- **Symmetric IOU (SIoU)**:强调两侧边界的相对位置关系。
这些变体形式能够在不同程度上弥补标准IOU存在的局限性,特别是在处理高度拥挤或部分被遮挡的对象时表现出色[^1]。
#### Python代码示例
以下是基于PyTorch框架下实现带有各种IOU版本的Soft-NMS函数的一个简化版例子:
```python
import torch
def soft_nms_with_iou_types(boxes, scores, iou_type='giou', sigma=0.5, thresh=0.4):
"""
:param boxes: Tensor of shape [num_boxes, 4], each row is [x_min, y_min, x_max, y_max].
:param scores: Tensor of shape [num_boxes]. Confidence score for each box.
:param iou_type: Type of IoU to use ('giou', 'diou', 'ciou', 'eiou', or 'siou').
:return: Indices of the selected boxes after applying Soft-NMS.
"""
indices = []
while scores.numel() > 0:
max_idx = torch.argmax(scores)
indices.append(max_idx.item())
keep_mask = compute_iou_matrix(boxes[max_idx:max_idx+1], boxes)[0] <= thresh
weights = torch.exp(-(compute_iou_matrix(
boxes[max_idx:max_idx+1],
boxes,
type=iou_type) ** 2)/sigma)
scores *= weights * keep_mask.float()
mask = scores >= thresh
boxes = boxes[mask]
scores = scores[mask]
return torch.tensor(indices).long()
def compute_iou_matrix(box_a, box_b, type="iou"):
"""Compute pairwise IoUs between two sets of bounding boxes."""
area_a = (box_a[:, 2]-box_a[:, 0])*(box_a[:, 3]-box_a[:, 1])
area_b = (box_b[:, 2]-box_b[:, 0])*(box_b[:, 3]-box_b[:, 1])
inter_xmin = torch.max(box_a[:, None, 0], box_b[:, 0])
inter_ymin = torch.max(box_a[:, None, 1], box_b[:, 1])
inter_xmax = torch.min(box_a[:, None, 2], box_b[:, 2])
inter_ymax = torch.min(box_a[:, None, 3], box_b[:, 3])
w_inter = torch.clamp(inter_xmax-inter_xmin, min=0.)
h_inter = torch.clamp(inter_ymax-inter_ymin, min=0.)
intersection = w_inter*h_inter
union = area_a[:,None]+area_b-intersection
if type=="iou":
ious = intersection / union
elif type=="giou": # Generalized Intersection over Union
enclosing_box = (
torch.min(box_a[:, None, :2], box_b[:,:2]),
torch.max(box_a[:, None, 2:], box_b[:,2:])
)
c_area = ((enclosing_box[1][:,:,0]-enclosing_box[0][:, :, 0])*
(enclosing_box[1][:,:,1]-enclosing_box[0][:, :, 1]))
giou_term = (c_area - union)/(union+c_area)
ious = ious-giou_term
elif type=="diou": # Distance-IoU Loss
center_dist_sqrd = ((box_a[:, None, 0]+box_a[:, None, 2])/2-(box_b[:, 0]+box_b[:, 2])/2)**2 \
+((box_a[:, None, 1]+box_a[:, None, 3])/2-(box_b[:, 1]+box_b[:, 3])/2)**2
diag_enclose_sqrd = ((enclosing_box[1][:,:,0]-enclosing_box[0][:, :, 0])**2+
(enclosing_box[1][:,:,1]-enclosing_box[0][:, :, 1])**2)
diou_term=center_dist_sqrd/(diag_enclose_sqrd+1e-7)
ious = ious-diou_term
elif type=="ciou": # Complete IoU loss
v=(torch.atan(((box_b[:, 2]-box_b[:, 0])/(box_b[:, 3]-box_b[:, 1])+1e-7))-
torch.atan(((box_a[:, None, 2]-box_a[:, None, 0])/
(box_a[:, None, 3]-box_a[:, None, 1])))).pow_(2)*(4/math.pi**2)
alpha=v/(ious+(1-ious)*v)
ciou_term=alpha*v
ious = ious-ciou_term-diou_term
elif type=="eiou": # Enhanced IoU
e_w=torch.abs((box_a[:, None, 2]-box_a[:, None, 0])-\
(box_b[:, 2]-box_b[:, 0]))
e_h=torch.abs((box_a[:, None, 3]-box_a[:, None, 1])-\
(box_b[:, 3]-box_b[:, 1]))
eiou_term=e_w/e_h
ious = ious-eiou_term
elif type=="siou": # Symmetric IoU
s_w=torch.abs((box_a[:, None, 2]+box_a[:, None, 0])-(box_b[:, 2]+box_b[:, 0]))/2
s_h=torch.abs((box_a[:, None, 3]+box_a[:, None, 1])-(box_b[:, 3]+box_b[:, 1]))/2
siou_term=s_w+s_h
ious = ious-siou_term
else:
raise ValueError(f'Unknown IoU type {type}')
return ious.squeeze_()
```
此段代码展示了如何定义一个支持多类IOU计算方式的`soft_nms_with_iou_types()` 函数,并提供了相应的辅助函数 `compute_iou_matrix()` 来完成具体的IOU运算逻辑。用户可以根据实际需求选择合适的参数组合来进行实验测试。