0
点赞
收藏
分享

微信扫一扫

NMS 原理和c++实现,已测试通过

目标践行者 2022-04-27 阅读 72

NMS(非极大抑制)是深度学习目标检测中常用的小算法,用来过滤掉同一个物体上的那些置信度较低的bbboxes,最后只剩下该目标检测框集中最大置信度的那个。

算法原理

说它是小算法的原因是其原理很简单。

1)先对输入检测框按置信度由高到低排序

2)挑选第一个检测框(即最高置信度,记为A)和其它检测框(记为B)进行iou计算

3)如果iou大于nmsThreshold, 那就将B清除掉

4)跳转到2)从剩余得框集里面找置信度最大得框和其它框分别计算iou

5)直到所有框都过滤完。

代码实现
 

box的数据结构

struct Box{
        float left, top, right, bottom, confidence;
        float landmark[10];

        cv::Rect cvbox() const{return cv::Rect(left, top, right-left, bottom-top);}
        float width()    const{return std::max(0.0f, right-left);}
        float height()   const{return std::max(0.0f, bottom-top);}
        float area()     const{return width() * height();}
        float get_left()                {return left;}
        void set_left(float value)      {left = value;}
        float get_top()                 {return top;}
        void set_top(float value)       {top = value;}
        float get_right()               {return right;}
        void set_right(float value)     {right = value;}
        float get_bottom()              {return bottom;}
        void set_bottom(float value)    {bottom = value;}
        float get_confidence()          {return confidence;}
        void set_confidence(float value){confidence = value;}
    };

NMS算法

static void cpu_nms(std::vector<Box>& boxes, std::vector<Box>& output, float threshold){

    // 先把boxes的按照置信度从大到小排序
    std::sort(boxes.begin(), boxes.end(), [](std::vector<Box>::const_reference a, std::vector<Box>::const_reference b){
        return a.confidence > b.confidence;
    });
    // 清理输出的缓存
    output.clear();
    // 创建和boxes等量的bool向量,用来标记过滤,默认全为false
    std::vector<bool> remove_flags(boxes.size());
    // 开始遍历
    for(int i = 0; i < boxes.size(); ++i){
        // 刚开始remove_flags均为false,
        if(remove_flags[i]) continue;
        // 经过上面的排序可以知道,第0个位置一定是置信度最大的那个,是我们的候选项,因此需要删除其他的候选框
        auto& a = boxes[i];
        // 把最大的先放到输出的向量里
        output.push_back(a);
        // 继续遍历0后面的元素,所以j=i+1
        for(int j = i + 1; j < boxes.size(); ++j){
            // 第1个位置也为false
            if(remove_flags[j]) continue;
            // 此时拿到第一个位置的数据
            auto& b = boxes[j];
            // 和第一个进行计算iou,如果iou阈值重合大于愈合iz,直接删除第1个位置的框,此时说明该框已经废弃,因此flag设置为true即可
            if(iou(a, b) >= threshold)
                remove_flags[j] = true;
        }
    }
    //综上,可以发现已经被废弃的box不会参与后面是循环,这样可以高性能实现nms
}

iou算法

static float iou(const Box& a, const Box& b){
        float cleft 	= std::max(a.left, b.left);
        float ctop 		= std::max(a.top, b.top);
        float cright 	= std::min(a.right, b.right);
        float cbottom 	= std::min(a.bottom, b.bottom);
        
        float c_area = std::max(cright - cleft, 0.0f) * std::max(cbottom - ctop, 0.0f);
        if(c_area == 0.0f)
            return 0.0f;
        
        float a_area = std::max(0.0f, a.right - a.left) * std::max(0.0f, a.bottom - a.top);
        float b_area = std::max(0.0f, b.right - b.left) * std::max(0.0f, b.bottom - b.top);
        return c_area / (a_area + b_area - c_area);
    }
举报

相关推荐

0 条评论