非极大值抑制——NMS

NMS(non maximum suppression),中文名非极大值抑制,在很多计算机视觉任务中都有广泛应用,如:边缘检测、目标检测等。

这里主要以人脸检测中的应用为例,来说明NMS,并给出Matlab和C++示例程序。

人脸检测的一些概念

(1) 绝大部分人脸检测器的核心是分类器,即给定一个尺寸固定图片,分类器判断是或者不是人脸;

(2)将分类器进化为检测器的关键是:在原始图像上从多个尺度产生窗口,并resize到固定尺寸,然后送给分类器做判断。最常用的方法是滑动窗口。

以下图为例,由于滑动窗口,同一个人可能有好几个框(每一个框都带有一个分类器得分)
这里写图片描述

而我们的目标是一个人只保留一个最优的框:

于是我们就要用到非极大值抑制,来抑制那些冗余的框: 抑制的过程是一个迭代-遍历-消除的过程。

(1)将所有框的得分排序,选中最高分及其对应的框:

这里写图片描述

(2)遍历其余的框,如果和当前最高分框的重叠面积(IOU)大于一定阈值,我们就将框删除。
这里写图片描述

(3)从未处理的框中继续选一个得分最高的,重复上述过程。
这里写图片描述
代码如下:
排序算法用的是简单选择排序,此处排序算法任选自己喜欢的

static void sort (int n, const vector<float> x, vector<int>& indices)
{
    // 排序函数,排序后进行交换的是indices中的数据
    // n:排序总数,x:待排序数,indices:初始为0-n-1数目
    int i, j;

    for (i = 0; i < n; i++)
    {
        for (j = i + 1; j < n; j++)
        {
            if (x[indices[j]] > x[indices[i]])
            {
                int temp = indices[i];
                indices[i] = indices[j];
                indices[j] = temp;
            }
        }
    }
}

非极大值抑制——NMS算法代码如下,思路与上方相似,此处简单赘述:
1.计算矩形面积存到box_area数组中
2.对分数进行排序,但是交换的是分数的索引indices,将分数从大到小排序
3.然后对所有窗口进行遍历,将每一个与它后面所有的窗口的重叠面积进行比较,此处窗口的索引是score的索引indices,大于阈值则设置索引为indices[i]的窗口(因为它的分数高)为被抑制,即输出窗口(此处可能存在效率问题,优化思路:设置准确阈值,将遍历过的数据不再进行遍历,此处为个人见解)
4.将抑制的所有的矩形的左上角的点坐标与右下角的点坐标存在out迭代器中

int nonMaximumSuppression (int numBoxes, const vector<CvPoint> points, const vector<CvPoint> oppositePoints, const vector<float> score, float overlapThreshold,
                           int& numBoxesOut, vector<CvPoint>& pointsOut, vector<CvPoint>& oppositePointsOut, vector<float>& scoreOut)
{
    //numBoxes:窗口数目,points:窗口左上角坐标点,oppositePoints:窗口右下角坐标点,score:窗口得分,overlapThreshold:重叠阈值监控,
    //numBoxesOut:输出窗口数目,piontsOut:输出窗口左上角坐标点,oppositePoints:输出窗口右下角坐标点,scoreOut:输出窗口得分
    int i, j, index;
    vector<float> box_area (numBoxes); //定义窗口面积变量并分配空间
    vector<int> indices (numBoxes);     //定义窗口索引并分配空间
    vector<int> is_suppressed (numBoxes);   //定义是否抑制表标志并分配空间

    //初始化indices、is_suppressed、box_area信息
    for (i = 0; i < numBoxes; i++)
    {
        indices[i] = i;
        is_suppressed[i] = 0;
        box_area[i] = (float) ( (oppositePoints[i].x - points[i].x + 1) * (oppositePoints[i].y - points[i].y));
    }

    //对输入窗口按照分数比值队形排序,排序后的编号放在indices中
    sort (numBoxes, score, indices);

    for (i = 0; i < numBoxes; i++) //循环所有窗口
    {
        if (!is_suppressed[indices[i]]) //判断窗口是否被抑制
        {
            for (j = i + 1; j < numBoxes; j++) //循环当前窗口之后的接口
            {
                if (!is_suppressed[indices[j]]) //判断窗口是否被抑制
                {
                    int x1max = max (points[indices[i]].x, points[indices[j]].x); //求两个窗口左上角x坐标的最大值
                    int x2min = min (oppositePoints[indices[i]].x, oppositePoints[indices[j]].x); //求两个窗口右下角x坐标最小值
                    int y1max = max (points[indices[i]].y, points[indices[j]].y); //计算两个窗口左上角y坐标的最大值
                    int y2min = min (oppositePoints[indices[i]].y, oppositePoints[indices[j]].y); //计算两个窗口右下角y坐标最小值
                    int overlapWidth = x2min - x1max + 1; //计算两个矩形重叠的宽度
                    int verlapHeight = y2min - y1max + 1; //计算两个矩形重叠的高度

                    if (overlapWidth > 0 && verlapHeight > 0)
                    {
                        float overlapPart = (overlapWidth * verlapHeight) / box_area[indices[j]]; //计算重叠的比率

                        if (overlapPart > overlapThreshold) //判断重叠比率是否超过重叠阈值
                        {
                            is_suppressed[indices[j]] = 1;//将窗口j标记为抑制
                        }
                    }
                }
            }
        }
    }

    numBoxesOut = 0;//初始化输出窗口数目为0

    for (i = 0; i < numBoxes; i++)
    {
        if (!is_suppressed[i]) { numBoxesOut++; } //统计输出窗口数目
    }

    index = 0;

    for (i = 0; i < numBoxes; i++)  //遍历将要输出的窗口
    {
        if (!is_suppressed[indices[i]]) //将未发生抑制的窗口信息保存到输出信息中
        {
            pointsOut.push_back (Point (points[indices[i]].x, points[indices[i]].y));
            oppositePointsOut.push_back (Point (oppositePoints[indices[i]].x, oppositePoints[indices[i]].y));
            scoreOut.push_back (score[indices[i]]);
            index++;
        }
    }

    return true;
}

这只是把算法的代码放在这里,具体的实现代码可参考https://github.com/asd463644535/NMS

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值