彻底解决目标检测重叠框问题:Darknet中的非极大值抑制(NMS)算法实现与优化

【免费下载链接】darknet YOLOv4 / Scaled-YOLOv4 / YOLO - Neural Networks for Object Detection (Windows and Linux version of Darknet ) 【免费下载链接】darknet 项目地址: https://gitcode.com/gh_mirrors/dar/darknet

你是否在使用YOLO进行目标检测时遇到过这样的困扰:同一个物体被算法检测出多个重叠的边界框(Bounding Box)?这种"一物多框"的现象不仅影响检测精度,还会增加后处理复杂度。本文将深入解析Darknet框架中用于解决此问题的核心技术——非极大值抑制(Non-Maximum Suppression, NMS)算法,通过代码实例和优化策略,帮助你彻底理解如何从检测结果中筛选出最佳边界框。

NMS算法原理:从重叠框中选择最优解

非极大值抑制是目标检测Pipeline中的关键后处理步骤,其核心思想是:对于同一物体可能产生的多个重叠检测框,保留置信度最高的框,同时抑制那些与高置信度框重叠度超过阈值的低置信度框。

Darknet框架中NMS算法的实现遵循以下步骤:

  1. 按置信度排序:将所有检测框按置信度从高到低排序
  2. 选择候选框:以置信度最高的框为基准
  3. 计算交并比(IOU):与剩余框计算重叠度
  4. 抑制低置信度框:移除IOU超过阈值的重叠框
  5. 迭代处理:对剩余框重复上述过程

mermaid

Darknet中的NMS实现:源代码解析

在Darknet框架中,NMS算法主要实现在src/box.c文件中,提供了多种优化版本以适应不同场景需求。

标准NMS实现

基础的NMS实现位于do_nms_sort_v2函数中,采用快速排序+双重循环的经典结构:

void do_nms_sort_v2(box *boxes, float **probs, int total, int classes, float thresh)
{
    int i, j, k;
    sortable_bbox* s = (sortable_bbox*)xcalloc(total, sizeof(sortable_bbox));

    for(i = 0; i < total; ++i){
        s[i].index = i;
        s[i].class_id = 0;
        s[i].probs = probs;
    }

    for(k = 0; k < classes; ++k){
        for(i = 0; i < total; ++i){
            s[i].class_id = k;
        }
        qsort(s, total, sizeof(sortable_bbox), nms_comparator);
        for(i = 0; i < total; ++i){
            if(probs[s[i].index][k] == 0) continue;
            box a = boxes[s[i].index];
            for(j = i+1; j < total; ++j){
                box b = boxes[s[j].index];
                if (box_iou(a, b) > thresh){
                    probs[s[j].index][k] = 0;
                }
            }
        }
    }
    free(s);
}

基于目标置信度的NMS优化

针对YOLO系列算法的特性,Darknet还提供了do_nms_obj函数,优化了对目标置信度(Objectness)的处理:

void do_nms_obj(detection *dets, int total, int classes, float thresh)
{
    int i, j, k;
    k = total - 1;
    // 第一步:移除置信度为0的检测框
    for (i = 0; i <= k; ++i) {
        if (dets[i].objectness == 0) {
            detection swap = dets[i];
            dets[i] = dets[k];
            dets[k] = swap;
            --k;
            --i;
        }
    }
    total = k + 1;

    // 按置信度排序
    qsort(dets, total, sizeof(detection), nms_comparator_v3);
    
    // NMS核心逻辑
    for (i = 0; i < total; ++i) {
        if (dets[i].objectness == 0) continue;
        box a = dets[i].bbox;
        for (j = i + 1; j < total; ++j) {
            if (dets[j].objectness == 0) continue;
            box b = dets[j].bbox;
            if (box_iou(a, b) > thresh) {
                dets[j].objectness = 0;
                for (k = 0; k < classes; ++k) {
                    dets[j].prob[k] = 0;
                }
            }
        }
    }
}

IOU计算核心函数

NMS算法的关键在于交并比(IOU)的计算,Darknet实现了多种IOU变体以适应不同需求:

// 标准IOU计算 [src/box.c](https://link.gitcode.com/i/3d45cd4178a8b30eeb977afe078e891c#L163)
float box_iou(box a, box b)
{
    float I = box_intersection(a, b);
    float U = box_union(a, b);
    if (I == 0 || U == 0) return 0;
    return I / U;
}

// GIoU计算 [src/box.c](https://link.gitcode.com/i/3d45cd4178a8b30eeb977afe078e891c#L175)
float box_giou(box a, box b)
{
    boxabs ba = box_c(a, b);
    float w = ba.right - ba.left;
    float h = ba.bot - ba.top;
    float c = w*h;
    float iou = box_iou(a, b);
    if (c == 0) return iou;
    float u = box_union(a, b);
    float giou_term = (c - u) / c;
    return iou - giou_term;
}

// DIoU计算 [src/box.c](https://link.gitcode.com/i/3d45cd4178a8b30eeb977afe078e891c#L195)
float box_diou(box a, box b)
{
    boxabs ba = box_c(a, b);
    float w = ba.right - ba.left;
    float h = ba.bot - ba.top;
    float c = w * w + h * h;
    float iou = box_iou(a, b);
    if (c == 0) return iou;
    float d = (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
    float u = pow(d / c, 0.6);
    return iou - u;
}

// CIoU计算 [src/box.c](https://link.gitcode.com/i/3d45cd4178a8b30eeb977afe078e891c#L235)
float box_ciou(box a, box b)
{
    // 实现代码略,包含了边界框宽高比的惩罚项
}

NMS算法在YOLO检测流程中的应用

在Darknet的YOLO检测流程中,NMS算法在网络输出检测结果后被调用,具体位置在src/yolo_layer.c的前向传播函数中。

YOLO层的前向传播完成后,检测结果会经过NMS处理:

// YOLO检测流程简化
detection *dets = get_network_boxes(net, ...);
do_nms_sort(dets, num_boxes, classes, nms_thresh);

在实际应用中,NMS阈值的选择对检测结果影响很大:

  • 高阈值(如0.7): 保留更多框,可能导致重叠框未被抑制
  • 低阈值(如0.3): 抑制更多框,可能导致漏检

Darknet框架允许在配置文件中为不同模型设置合适的NMS阈值,例如在yolov3.cfgyolov4.cfg中:

[net]
# 其他配置...

[yolo]
mask = 6,7,8
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
classes=80
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1
nms_kind = greedynms  # 或 diounms
beta_nms=0.6         # DIoU NMS的beta参数

NMS算法的优化策略与变体

Darknet实现了多种NMS变体以应对不同场景的挑战:

1. 类别感知NMS(Category-aware NMS)

标准NMS在处理不同类别对象时可能出现抑制错误,类别感知NMS则对每个类别单独执行NMS,避免不同类别对象间的相互干扰。

2. DIoU/CIoU NMS

传统NMS仅考虑边界框的重叠面积,而Darknet实现的DIoU/CIoU NMS还考虑了边界框的中心点距离和宽高比:

float box_diou(box a, box b) {
    // 计算中心点距离与最小外接矩形对角线的比值
    float d = (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
    float c = w*w + h*h;  // 最小外接矩形对角线长度的平方
    float diou_term = d / c;
    return iou - diou_term;
}

3. 快速NMS(Fast NMS)

Darknet通过预过滤置信度为0的检测框等优化,减少了NMS算法的计算量:

// 预过滤优化 [src/box.c](https://link.gitcode.com/i/3d45cd4178a8b30eeb977afe078e891c#L778)
k = total - 1;
for (i = 0; i <= k; ++i) {
    if (dets[i].objectness == 0) {
        // 交换到数组末尾并忽略
        detection swap = dets[i];
        dets[i] = dets[k];
        dets[k] = swap;
        --k;
        --i;
    }
}
total = k + 1;  // 减少后续处理的框数量

实战调优:NMS参数选择指南

选择合适的NMS参数对模型性能至关重要,以下是基于Darknet框架的调优建议:

1. 通用场景设置

  • NMS阈值: 0.4~0.5
  • 置信度阈值: 0.25~0.3

2. 密集目标场景

如人群、车辆密集场景,建议:

  • 降低NMS阈值至0.2~0.3
  • 使用DIoU/CIoU NMS
  • 在配置文件中设置nms_kind=diounmsbeta_nms=0.6

3. 小目标检测

  • 适当提高置信度阈值
  • 使用较小的NMS阈值
  • 可尝试在配置文件中调整ignore_thresh参数

总结与展望

非极大值抑制(NMS)算法作为目标检测的关键后处理步骤,在Darknet框架中得到了充分优化。从基础的IOU计算到高级的CIoU变体,从标准实现到类别感知优化,Darknet提供了完整的NMS解决方案。

随着目标检测技术的发展,NMS算法也在不断演进。未来可能会看到更多基于深度学习的端到端NMS替代方案,但目前来看,传统NMS及其变体凭借其简单高效的特性,仍然是包括YOLO在内的主流目标检测算法的首选后处理方法。

掌握NMS算法的原理与实现,不仅能帮助你更好地使用Darknet框架,还能为你优化自定义目标检测系统提供关键思路。建议通过修改src/box.c中的NMS实现代码,结合具体应用场景进行实验,以获得最佳检测效果。

【免费下载链接】darknet YOLOv4 / Scaled-YOLOv4 / YOLO - Neural Networks for Object Detection (Windows and Linux version of Darknet ) 【免费下载链接】darknet 项目地址: https://gitcode.com/gh_mirrors/dar/darknet

更多推荐