NMS(非极大抑制)是深度学习目标检测中常用的小算法,用来过滤掉同一个物体上的那些置信度较低的bbboxes,最后只剩下该目标检测框集中最大置信度的那个。
算法原理 【c++|NMS 原理和c++实现,已测试通过】说它是小算法的原因是其原理很简单。
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& boxes, std::vector& output, float threshold){// 先把boxes的按照置信度从大到小排序
std::sort(boxes.begin(), boxes.end(), [](std::vector::const_reference a, std::vector::const_reference b){
return a.confidence > b.confidence;
});
// 清理输出的缓存
output.clear();
// 创建和boxes等量的bool向量,用来标记过滤,默认全为false
std::vector 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);
}
推荐阅读
- matlab|如何用matlab画一个球
- visual|使用 Visual Studio Code 编写你的第一个 C 程序
- VC++|VC++使用画刷绘图 简单画刷 FillRect函数 位图画刷 透明画刷
- VC++|VC++ 利用MFC的CWindowDC类实现画线功能 在桌面窗口中画线 绘制彩色线条 CPen nPenStyle nWidth crColor
- C++基础|【千律】C++基础(ShellExecuteEx 函数的相关使用案例)
- VC++|VC++简单绘图 MFC消息映射机制 MessageBox函数
- VC++|VC++ 类向导
- Python|Python人脸识别考勤打卡系统
- YOLOV5|2021电赛F题视觉教程+代码免费开源