NMS非极大值抑制:用擂台赛带你从原理到代码脑洞大开恍然大悟

来源:互联网 发布:科比巅峰数据 编辑:程序博客网 时间:2024/05/18 00:11

本博记录为卤煮理解,如有疏漏,请指正。转载请注明出处。

卤煮:非文艺小燕儿

本博地址:NMS非极大值抑制:带你从原理到代码脑洞大开


楼主是在人脸识别应用时,接触到NMS的。其余目标检测同理。

简单来说就是你喂给分类器一张图片,它会给你拉出,哦不,不太雅,吐出一堆可能是人脸的候选框,每个框框还有一个得分。高分意味着这个框框里是人脸的概率大。

用下面这个图简单表示一下:

有4个框,每个框都对应一个得分。

从图上可以看出,

(1)有些框得分很低(右下),意味着这种框是人脸的概率较小,我们可以通过阈值把这种框排除。

(2)另外有些框之间有较大的重叠,比如川普脸上有2个框,而且得分都比较高。很明显可以想到,留着这么多框太冗余了,可以精简一下。

上面提到的(2),就是nms要做的工作了,简单吧。



嗯,我上面说的你们都懂,可是怎么实施呢?


OK,我们把这些框框,都看成一个个的勇士,得分的高低代表勇士的实力强弱。

我们给这些勇士提供一个NMS擂台赛。我们每次邀请一个实力最强的勇士作为擂台主,剩余的勇士可以上擂台挑战他。

少年看你骨骼清奇,我给你透漏一个秘密,下赌注的时候一定要堵擂台主赢,因为我们有个潜规则,擂台主是绝对不会输的。


由于擂台比较高,那些实力较弱的勇士,爬擂台的时候就摔死了,ORZ。。。。


能够爬上擂台的勇士,部分身上自带克制擂台主的气息,大概是上场前萝卜吃多了,导致擂台主无法近其身(IOU小于阈值),侥幸拿下平局。

你说什么是IOU?稍等,看完这场,休息的时候给你讲。


其余勇士,只能与擂台主贴身肉搏了,由于我们的潜规则,勇士英勇牺牲了。


现在擂台上,只剩下擂台主和怪味勇士了。打不起来了,肿么办呢?


好吧,都下去,休息片刻,继续下一场比赛。刚才的擂台主光荣退休。擂台属于剩下的勇士们。


下一场,将由剩下的勇士中最有实力的做擂台主,重新其余勇士的接受挑战。


当所有比赛完结,上过擂台的勇士,要么战死了,要么就是做完擂台主退休了。也就是说,现在就剩下一批牛逼哄哄的退休擂台主了。



下面说一下IOU规则。其实就是两个框重叠率。IOU=重叠面积/基准面积

基准面积有三种计算规则,

(1)Union,也就是两个框的并集所圈定的面积。

(2)Min,面积较小的框所圈定的面积。

(3)Max,面积较大的框所圈定的面积。

比如下图中,红框和蓝框有交集,交集面积很大,所以IOU就大,如果红框是擂台主,那么蓝框就被和谐牺牲了。而绿框因为没有交集,IOU都小到没有,侥幸逃过一劫。


是不是恍然大悟了,可转念一想,这代码还是不会写啊。

像我这种保姆级的楼主,必须要周全啊,翠花,上代码~!

输入的boundingBox_记录了候选框的位置信息和得分信息,BboxScore_记录了候选框的得分信息以及ID。

overlap_threshold是IOU阈值,modelname指定基准面积的计算方式。

/**********************nms非极大值抑制****************************/void mtcnn::nms(std::vector<Bbox> &boundingBox_, std::vector<orderScore> &bboxScore_, const float overlap_threshold, string modelname){    if(boundingBox_.empty()){        return;    }    std::vector<int> heros;    //sort the score    sort(bboxScore_.begin(), bboxScore_.end(), cmpScore);//cmpScore指定升序排列    int order = 0;    float IOU = 0;    float maxX = 0;    float maxY = 0;    float minX = 0;    float minY = 0;//规则,站上擂台的擂台主,永远都是胜利者。    while(bboxScore_.size()>0){        order = bboxScore_.back().oriOrder;//取得分最高勇士的编号ID。        bboxScore_.pop_back();//勇士出列        if(order<0)continue;//死的?下一个!(order在(*it).oriOrder = -1;改变)        heros.push_back(order);//记录擂台主ID        boundingBox_.at(order).exist = false;//当前这个Bbox为擂台主,签订生死簿。        for(int num=0;num<boundingBox_.size();num++){            if(boundingBox_.at(num).exist){//活着的勇士                //the iou                maxX = (boundingBox_.at(num).x1>boundingBox_.at(order).x1)?boundingBox_.at(num).x1:boundingBox_.at(order).x1;                maxY = (boundingBox_.at(num).y1>boundingBox_.at(order).y1)?boundingBox_.at(num).y1:boundingBox_.at(order).y1;                minX = (boundingBox_.at(num).x2<boundingBox_.at(order).x2)?boundingBox_.at(num).x2:boundingBox_.at(order).x2;                minY = (boundingBox_.at(num).y2<boundingBox_.at(order).y2)?boundingBox_.at(num).y2:boundingBox_.at(order).y2;                //maxX1 and maxY1 reuse                 maxX = ((minX-maxX+1)>0)?(minX-maxX+1):0;                maxY = ((minY-maxY+1)>0)?(minY-maxY+1):0;                //IOU reuse for the area of two bbox                IOU = maxX * maxY;                if(!modelname.compare("Union"))                    IOU = IOU/(boundingBox_.at(num).area + boundingBox_.at(order).area - IOU);                else if(!modelname.compare("Min")){                    IOU = IOU/((boundingBox_.at(num).area<boundingBox_.at(order).area)?boundingBox_.at(num).area:boundingBox_.at(order).area);                }                if(IOU>overlap_threshold){                    boundingBox_.at(num).exist=false;//如果该对比框与擂台主的IOU够大,挑战者勇士战死                    for(vector<orderScore>::iterator it=bboxScore_.begin(); it!=bboxScore_.end();it++){                        if((*it).oriOrder == num) {                            (*it).oriOrder = -1;//勇士战死标志                            break;                        }                    }                }//else 那些距离擂台主比较远迎战者幸免于难,将有机会作为擂台主出现            }        }    }    for(int i=0;i<heros.size();i++)        boundingBox_.at(heros.at(i)).exist = true;//从生死簿上剔除,擂台主活下来了}


齐活~


原创粉丝点击