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

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

卤煮:非文艺小燕儿

本博地址: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;//从生死簿上剔除,擂台主活下来了
}


齐活~


  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值