前言:c++版本由于有std标准库的存在,所以nms的实现很简单,效率也很高。但是纯c是不提供的。
C版本实现
这里先附上我的结构体信息,以免对没有写过nms的同学们造成迷糊
typedef struct
{
float x_min;
float y_min;
float x_max;
float y_max;
}BBox;
typedef struct
{
float confi;
BBox bbox;
}ObjectResult;
以下是正式代码,会附带一些注释
// > 是降序, < 是升序
int compare(const ObjectResult a, const ObjectResult b)
{
return a.confi < b.confi ? 1 : -1;
}
float iou(ObjectResult a, ObjectResult b)
{
float x1 = a.bbox.x_min > b.bbox.x_min ? a.bbox.x_min : b.bbox.x_min; // std::max
float y1 = a.bbox.y_min > b.bbox.y_min ? a.bbox.y_min : b.bbox.y_min; // std::max
float x2 = a.bbox.x_max > b.bbox.x_max ? b.bbox.x_max : a.bbox.x_max; // std::min
float y2 = a.bbox.y_max > b.bbox.y_max ? b.bbox.y_max : a.bbox.y_max; // std::min
// 没有重叠面积
if (x2 < x1 || y2 < y1) return 0;
float a_width = a.bbox.x_max - a.bbox.x_min;
float a_height = a.bbox.y_max - a.bbox.y_min;
float b_width = b.bbox.x_max - b.bbox.x_min;
float b_heihgt = b.bbox.y_max - b.bbox.y_min;
float inter_area = (x2 - x1) * (y2 - y1); // 交集
float iou = inter_area / ((a_width * a_height) + b_width * b_heihgt - inter_area); // 并集
return iou;
}
/*
nms实现主体部分
*/
void nms(ObjectResult object[], ObjectResult result[], uint16_t* total, float nmsThreshold)
{
// 按照分数进行降序排序
qsort(object, *total, sizeof(ObjectResult), compare);
for (uint16_t i = 0; i < *total; ++i)
{
uint16_t index = 1;
result[i] = object[i];
for (uint16_t j = i + 1; j < *total; ++j)
{
if (iou(result[i], object[j]) < nmsThreshold)
{
// 将待确定的检测目标挪到已确定的检测目标后面
object[index] = object[j];
index += 1;
}
}
*total = index;
// 剩余的框中不存在新的检测目标, 都是多余的框, 循环结束, 返回确定的检测框总数
if (index == 1)
{
*total = i + 1;
}
}
}
接口调用示例,请结合自己的代码做调整
uint16_t total = 0; // object中检测框的数量
ObjectResult object[100] = {}; // nms前
ObjectResult result[10] = {}; // nms后
float nmsThreshold = 0.25;
nms(object, result, &total, nmsThreshold);
for (uint8_t i = 0; i < total; i++)
{
printf("socre = %f, bbox = (%f, %f, %f, %f)\n", result[i].confi, result[i].bbox.x_min,
result[i].bbox.y_min, result[i].bbox.x_max, result[i].bbox.y_max);
}
C++版本实现
下班,后续补充