NMS非极大值抑制

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

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

人脸检测的一些概念

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

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

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

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

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

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

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

(3)从未处理的框中继续选一个得分最高的,重复上述过程。

下面给出MATLAB下的快速NMS代码,并带有详细的注释:

%% NMS:non maximum suppression
function pick = nms(boxes,threshold,type)
% boxes: m x 5,表示有m个框,5列分别是[x1 y1 x2 y2 score]
% threshold: IOU阈值
% type:IOU阈值的定义类型

    % 输入为空,则直接返回
    if isempty(boxes)
      pick = [];
      return;
    end

    % 依次取出左上角和右下角坐标以及分类器得分(置信度)
    x1 = boxes(:,1);
    y1 = boxes(:,2);
    x2 = boxes(:,3);
    y2 = boxes(:,4);
    s = boxes(:,5);

    % 计算每一个框的面积
    area = (x2-x1+1) .* (y2-y1+1);

    %将得分升序排列
    [vals, I] = sort(s);

    %初始化
    pick = s*0;
    counter = 1;

    % 循环直至所有框处理完成
    while ~isempty(I)
        last = length(I); %当前剩余框的数量
        i = I(last);%选中最后一个,即得分最高的框
        pick(counter) = i;
        counter = counter + 1;  

        %计算相交面积
        xx1 = max(x1(i), x1(I(1:last-1)));
        yy1 = max(y1(i), y1(I(1:last-1)));
        xx2 = min(x2(i), x2(I(1:last-1)));
        yy2 = min(y2(i), y2(I(1:last-1)));  
        w = max(0.0, xx2-xx1+1);
        h = max(0.0, yy2-yy1+1); 
        inter = w.*h;

        %不同定义下的IOU
        if strcmp(type,'Min')
            %重叠面积与最小框面积的比值
            o = inter ./ min(area(i),area(I(1:last-1)));
        else
            %交集/并集
            o = inter ./ (area(i) + area(I(1:last-1)) - inter);
        end

        %保留所有重叠面积小于阈值的框,留作下次处理
        I = I(find(o<=threshold));
    end
    pick = pick(1:(counter-1));
end

C++代码

先将box中的数据分别存入x1,y1,x2,y2,s中,分别为坐标和置信度,算出每个框的面积,存入area,基于置信度s,从小到达进行排序,做一个while循环,取出置信度最高的,即排序后的最后一个,然后将该框进行保留,存入pick中,然后和其他所有的框进行比对,大于规定阈值就将别的框去掉,并将该置信度最高的框和所有比对过程,大于阈值的框存入suppress,for循环后,将I中满足suppress条件的置为空。直到I为空退出while。

static void sort(int n, const float* x, 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]])  
            {  
                //float x_tmp = x[i];  
                int index_tmp = indices[i];  
                //x[i] = x[j];  
                indices[i] = indices[j];  
                //x[j] = x_tmp;  
                indices[j] = index_tmp;  
            }  
        }  
}
int nonMaximumSuppression(int numBoxes, const CvPoint *points,  
                          const CvPoint *oppositePoints, const float *score,  
                          float overlapThreshold,  
                          int *numBoxesOut, CvPoint **pointsOut,  
                          CvPoint **oppositePointsOut, float **scoreOut)  
{  
  
// numBoxes:窗口数目// points:窗口左上角坐标点// oppositePoints:窗口右下角坐标点  
// score:窗口得分// overlapThreshold:重叠阈值控制// numBoxesOut:输出窗口数目  
// pointsOut:输出窗口左上角坐标点// oppositePoints:输出窗口右下角坐标点  
// scoreOut:输出窗口得分  
    int i, j, index;  
    float* box_area = (float*)malloc(numBoxes * sizeof(float));    // 定义窗口面积变量并分配空间   
    int* indices = (int*)malloc(numBoxes * sizeof(int));          // 定义窗口索引并分配空间   
    int* is_suppressed = (int*)malloc(numBoxes * sizeof(int));    // 定义是否抑制表标志并分配空间   
    // 初始化indices、is_supperssed、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 + 1));  
    }  
    // 对输入窗口按照分数比值进行排序,排序后的编号放在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 overlapHeight = y2min - y1max + 1;           // 计算两矩形重叠的高度   
                    if (overlapWidth > 0 && overlapHeight > 0)  
                    {  
                        float overlapPart = (overlapWidth * overlapHeight) / 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)++;    // 统计输出窗口数目   
    }  
  
    *pointsOut = (CvPoint *)malloc((*numBoxesOut) * sizeof(CvPoint));           // 分配输出窗口左上角坐标空间   
    *oppositePointsOut = (CvPoint *)malloc((*numBoxesOut) * sizeof(CvPoint));   // 分配输出窗口右下角坐标空间   
    *scoreOut = (float *)malloc((*numBoxesOut) * sizeof(float));                // 分配输出窗口得分空间   
    index = 0;  
    for (i = 0; i < numBoxes; i++)                  // 遍历所有输入窗口   
    {  
        if (!is_suppressed[indices[i]])             // 将未发生抑制的窗口信息保存到输出信息中   
        {  
            (*pointsOut)[index].x = points[indices[i]].x;  
            (*pointsOut)[index].y = points[indices[i]].y;  
            (*oppositePointsOut)[index].x = oppositePoints[indices[i]].x;  
            (*oppositePointsOut)[index].y = oppositePoints[indices[i]].y;  
            (*scoreOut)[index] = score[indices[i]];  
            index++;  
        }  
  
    }  
  
    free(indices);          // 释放indices空间   
    free(box_area);         // 释放box_area空间   
    free(is_suppressed);    // 释放is_suppressed空间   
  
    return LATENT_SVM_OK;  
}  

 
转自:https://blog.csdn.net/shuzfan/article/details/52711706

猜你喜欢

转载自blog.csdn.net/u013066730/article/details/84030097
今日推荐