1. 程式人生 > >NMS 非極大值抑制的原理與C++程式碼實現

NMS 非極大值抑制的原理與C++程式碼實現

原理:

對於Bounding Box的列表B及其對應的置信度S,選擇具有最大score的檢測框M,將其從B集合中移除並加入到最終的檢測結果D中.通常將B中剩餘檢測框中與M的IoU大於閾值Nt的框從B中移除.重複這個過程,直到B為空

實現過程:

就像上面的圖片一樣,定位一個車輛,最後演算法就找出了一堆的方框,我們需要判別哪些矩形框是沒用的。

非極大值抑制:先假設有6個候選框,根據分類器類別分類概率做排序,從小到大分別屬於車輛的概率分別為A、B、C、D、E、F。

1、從最大概率矩形框F開始,分別判斷A~E與F的重疊度IOU是否大於某個設定的閾值;

2、假設B、D與F的重疊度超過閾值,那麼就扔掉B、D;並標記第一個矩形框F,是我們保留下來的。

3、從剩下的矩形框A、C、E中,選擇概率最大的E,然後判斷E與A、C的重疊度,重疊度大於一定的閾值,那麼就扔掉;並標記E是我們保留下來的第二個矩形框。

4、一直重複這個過程,找到所有曾經被保留下來的矩形框。

C++程式碼實現:

//升序排列
bool cmpScore(Bbox lsh, Bbox rsh) {
	if (lsh.score < rsh.score)
		return true;
	else
		return false;
}

void nms(vector<Bbox> &boundingBox_, const float overlap_threshold, string modelname = "Union"){

    if(boundingBox_.empty()){
        return;
    }
    //對各個候選框根據score的大小進行升序排列
    sort(boundingBox_.begin(), boundingBox_.end(), cmpScore);
    float IOU = 0;
    float maxX = 0;
    float maxY = 0;
    float minX = 0;
    float minY = 0;
    vector<int> vPick;
    int nPick = 0;
    multimap<float, int> vScores;   //存放升序排列後的score和對應的序號
    const int num_boxes = boundingBox_.size();
	vPick.resize(num_boxes);
	for (int i = 0; i < num_boxes; ++i){
		vScores.insert(pair<float, int>(boundingBox_[i].score, i));
	}
    while(vScores.size() > 0){
        int last = vScores.rbegin()->second;  //反向迭代器,獲得vScores序列的最後那個序列號
        vPick[nPick] = last;
        nPick += 1;
        for (multimap<float, int>::iterator it = vScores.begin(); it != vScores.end();){
            int it_idx = it->second;
            maxX = max(boundingBox_.at(it_idx).x1, boundingBox_.at(last).x1);
            maxY = max(boundingBox_.at(it_idx).y1, boundingBox_.at(last).y1);
            minX = min(boundingBox_.at(it_idx).x2, boundingBox_.at(last).x2);
            minY = min(boundingBox_.at(it_idx).y2, boundingBox_.at(last).y2);
            //轉換成了兩個邊界框相交區域的邊長
            maxX = ((minX-maxX+1)>0)? (minX-maxX+1) : 0;
            maxY = ((minY-maxY+1)>0)? (minY-maxY+1) : 0;
            //求交併比IOU
            
            IOU = (maxX * maxY)/(boundingBox_.at(it_idx).area + boundingBox_.at(last).area - IOU);
            if(IOU > overlap_threshold){
                it = vScores.erase(it);    //刪除交併比大於閾值的候選框,erase返回刪除元素的下一個元素
            }else{
                it++;
            }
        }
    }
    
    vPick.resize(nPick);
    vector<Bbox> tmp_;
    tmp_.resize(nPick);
    for(int i = 0; i < nPick; i++){
        tmp_[i] = boundingBox_[vPick[i]];
    }
    boundingBox_ = tmp_;
}
其中程式碼中有一個地方的設計很巧妙值得注意,while迴圈中,先確定最大的score後,讓所有候選框都跟這個候選框計算交併比(包括自己),這樣刪除了交併比大於閾值的所有候選框,包括自己,這樣就可以重複進行迴圈獲得最終的候選框。
對於Bounding Box的列表B及其對應的置信度S,採用下面的計算方式.選擇具有最大score的檢測框M,將其從B集合中移除並加入到最終的檢測結果D中.通常將B中剩餘檢測框中與M的IoU大於閾值Nt的框從B中移除.重複這個過程,直到B為空著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。對於Bounding Box的列表B及其對應的置信度S,採用下面的計算方式.選擇具有最大score的檢測框M,將其從B集合中移除並加入到最終的檢測結果D中.通常將B中剩餘檢測框中與M的IoU大於閾值Nt的框從B中移除.重複這個過程,直到B為空著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。