教程推荐,推荐!
Basics of Bounding Boxeshttps://medium.com/analytics-vidhya/basics-of-bounding-boxes-94e583b5e16cIOU (Intersection over Union)
https://medium.com/analytics-vidhya/iou-intersection-over-union-705a39e7acefNon Max Suppression (NMS)
https://medium.com/analytics-vidhya/non-max-suppression-nms-6623e6572536
Python版实现代码
快照如下
C++版实现代码
大佬的仓库:
GitHub - developer0hye/Modern-Cpp-NMS: A Modern C++ Implementation of NMS
https://github.com/martinkersner/non-maximum-suppression-cpp/tree/master
我自己写的如下(简单易懂,推荐!)
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
// 边界框结构体定义
struct Box {
float x1, y1, x2, y2, confidence; // 边界框的四个坐标和置信度
// 计算边界框的面积
float area() {
return abs(x2 - x1) * abs(y2 - y1); // 计算并返回面积
}
};
// 计算两个边界框之间的IoU (Intersection over Union)
float IOU(Box& a, Box& b);
vector<Box> NMS(vector<Box>& boxes, float threshold); // 声明非极大值抑制函数
int main() {
vector<Box> boxes; // 存储边界框的向量
// 添加一些示例边界框数据
{
// 向 boxes 中添加多个 Box 实例,包含坐标和置信度(x1,y1,x2,y2,confidence)
boxes.push_back({ 0.0984375, 0.254167, 0.553125, 0.997222, 0.252669 });
boxes.push_back({ 0.0820312, 0.275, 0.715625, 0.997222, 0.26813 });
boxes.push_back({ 0.560938, 0.0694444, 0.878125, 1, 0.28339 });
boxes.push_back({ 0.09375, 0.273611, 0.528906, 0.997222, 0.295933 });
boxes.push_back({ 0.560938, 0.0694444, 0.877344, 1, 0.296285 });
boxes.push_back({ 0.5625, 0.0652778, 0.876562, 1, 0.299446 });
boxes.push_back({ 0.0921875, 0.270833, 0.528125, 0.997222, 0.302117 });
boxes.push_back({ 0.0929687, 0.272222, 0.528906, 0.997222, 0.304497 });
boxes.push_back({ 0.078125, 0.272222, 0.722656, 0.998611, 0.306486 });
boxes.push_back({ 0.0773437, 0.272222, 0.725, 0.997222, 0.320194 });
boxes.push_back({ 0.584375, 0.0694444, 0.8875, 0.993056, 0.42026 });
boxes.push_back({ 0.580469, 0.0541667, 0.879687, 0.998611, 0.429142 });
boxes.push_back({ 0.582031, 0.0638889, 0.894531, 1, 0.459798 });
boxes.push_back({ 0.096875, 0.276389, 0.6375, 0.995833, 0.505743 });
boxes.push_back({ 0.096875, 0.273611, 0.636719, 0.997222, 0.520935 });
boxes.push_back({ 0.0984375, 0.276389, 0.633594, 0.995833, 0.522453 });
boxes.push_back({ 0.582812, 0.0680556, 0.892969, 1, 0.548471 });
boxes.push_back({ 0.582031, 0.0666667, 0.89375, 1, 0.619484 });
boxes.push_back({ 0.0992187, 0.269444, 0.557813, 1, 0.654754 });
boxes.push_back({ 0.0976562, 0.269444, 0.557813, 1, 0.664667 });
boxes.push_back({ 0.0960938, 0.268056, 0.558594, 1, 0.666693 });
boxes.push_back({ 0.578906, 0.0625, 0.888281, 1, 0.703215 });
boxes.push_back({ 0.578125, 0.0694444, 0.889063, 1, 0.705285 });
boxes.push_back({ 0.578906, 0.0694444, 0.888281, 1, 0.713431 });
boxes.push_back({ 0.5875, 0.0416667, 0.882812, 1, 0.788854 });
boxes.push_back({ 0.58125, 0.0611111, 0.890625, 1, 0.862154 });
boxes.push_back({ 0.58125, 0.0666667, 0.891406, 1, 0.87831 });
boxes.push_back({ 0.580469, 0.0666667, 0.892187, 1, 0.879861 });
// ... 省略其他边界框添加代码
}
// 运行非极大值抑制算法,保留不重叠的边界框
float threshold = 0.7; // 设置 IoU 阈值
vector<Box> result = NMS(boxes, threshold); // 获取处理后的边界框
// 输出保留下来的边界框
cout << "保留的边界框:" << endl;
for (Box& box : result) {
// 输出每个边界框的坐标和置信度
cout << "x1: " << box.x1 << ", y1: " << box.y1 << ", x2: " << box.x2 << ", y2: " << box.y2 << ", confidence: " << box.confidence << endl;
}
return 0; // 主函数结束
}
// 计算两个边界框之间的IoU
float IOU(Box& a, Box& b) {
// 计算重叠区域的坐标范围
float x_inter1 = max(a.x1, b.x1); // 取重叠区域的左上角 x 坐标
float y_inter1 = max(a.y1, b.y1); // 取重叠区域的左上角 y 坐标
float x_inter2 = min(a.x2, b.x2); // 取重叠区域的右下角 x 坐标
float y_inter2 = min(a.y2, b.y2); // 取重叠区域的右下角 y 坐标
// 计算重叠区域的面积
float intersection_area = abs(x_inter2 - x_inter1) * abs(y_inter2 - y_inter1); // 计算重叠面积
float area_a = a.area(); // 获取框 a 的面积
float area_b = b.area(); // 获取框 b 的面积
// 计算并集区域的面积
float union_area = area_a + area_b - intersection_area; // 计算并集面积
// 计算IoU
return intersection_area / union_area; // 返回 IoU 值
}
// 非极大值抑制函数
vector<Box> NMS(vector<Box>& boxes, float threshold) {
// 根据置信度排序
sort(boxes.begin(), boxes.end(), [](Box& a, Box& b) { return a.confidence > b.confidence; }); // 按置信度降序排序
vector<Box> result; // 存储结果的向量
for (int i = 0; i < boxes.size(); ++i) {
bool keep = true; // 初始化为保留该框
for (int j = 0; j < result.size(); ++j) {
// 如果当前框与结果框的 IoU 大于阈值,标记为不保留
if (IOU(boxes[i], result[j]) > threshold) {
keep = false; // 设为不保留
break; // 退出内层循环
}
}
if (keep) {
result.push_back(boxes[i]); // 如果满足条件,添加到结果中
}
}
return result; // 返回处理后的边界框列表
}
运行结果
直观图示(保留项为蓝色)(Python 绘制)