1. IoU(區域交並比)
計算IoU的公式如下圖,可以看到IoU是一個比值,即交並比。
在分子中,我們計算預測框和ground-truth之間的重疊區域;
分母是並集區域,或者更簡單地說,是預測框和ground-truth所包含的總區域。
重疊區域和並集區域的比值,就是IoU。
1.1 為什么使用IoU來評估目標檢測器
與分類任務不同,我們預測的bounding box的坐標需要去匹配ground-truth的坐標,而坐標完全匹配基本是不現實的。因此,我們需要定義一個評估指標,獎勵那些與ground-truth匹配較好(重疊較大)的預測框。
1.2 IoU的python實現
1 def bb_intersection_over_union(boxA, boxB): 2 # determine the (x, y)-coordinates of the intersection rectangle 3 # 畫個圖會很明顯,x左、y上取大的,x右、y下取小的,剛好對應交集 4 xA = max(boxA[0], boxB[0]) 5 yA = max(boxA[1], boxB[1]) 6 xB = min(boxA[2], boxB[2]) 7 yB = min(boxA[3], boxB[3]) 8 9 # compute the area of intersection rectangle 10 # 計算交集部分面積 11 interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1) 12 13 # compute the area of both the prediction and ground-truth rectangles 14 # 計算預測值和真實值的面積 15 boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1) 16 boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1) 17 18 # compute the intersection over union by taking the intersection 19 # area and dividing it by the sum of prediction + ground-truth 20 # areas - the interesection area 21 # 計算IoU,即 交/(A+B-交) 22 iou = interArea / float(boxAArea + boxBArea - interArea) 23 24 # return the intersection over union value 25 return iou
2. 非極大化抑制(NMS)
2.1 算法思想
所謂非極大值抑制:先假設有6個輸出的矩形框(即proposal_clip_box),根據分類器類別分類概率做排序,從小到大分別屬於車輛的概率(scores)分別為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是我們保留下來的第二個矩形框。 就這樣一直重復,找到所有被保留下來的矩形框。
如上圖F與BD重合度較大,可以去除BD。AE重合度較大,我們刪除A,保留scores較大的E。C和其他重疊都小保留C。最終留下了C、E、F三個。
2.2 python實現
1.無條件保留置信度最高的框;
2.刪除與保留框IOU大於閾值的候選框;
1 # -------------------------------------------------------- 2 # Fast R-CNN 3 # Copyright (c) 2015 Microsoft 4 # Licensed under The MIT License [see LICENSE for details] 5 # Written by Ross Girshick 6 # -------------------------------------------------------- 7 8 import numpy as np 9 10 def py_cpu_nms(dets, thresh): 11 """Pure Python NMS baseline.""" 12 x1 = dets[:, 0] 13 y1 = dets[:, 1] 14 x2 = dets[:, 2] 15 y2 = dets[:, 3] 16 scores = dets[:, 4] 17 18 areas = (x2 - x1 + 1) * (y2 - y1 + 1) 19 order = scores.argsort()[::-1] 20 21 keep = [] 22 while order.size > 0: 23 i = order[0] 24 keep.append(i) 25 xx1 = np.maximum(x1[i], x1[order[1:]]) 26 yy1 = np.maximum(y1[i], y1[order[1:]]) 27 xx2 = np.minimum(x2[i], x2[order[1:]]) 28 yy2 = np.minimum(y2[i], y2[order[1:]]) 29 30 w = np.maximum(0.0, xx2 - xx1 + 1) 31 h = np.maximum(0.0, yy2 - yy1 + 1) 32 inter = w * h 33 ovr = inter / (areas[i] + areas[order[1:]] - inter) 34 35 inds = np.where(ovr <= thresh)[0] 36 order = order[inds + 1] 37 38 return keep
3. soft-NMS
soft NMS提出尤其對密集物體檢測的檢測效果有一定的提升作用
絕大部分目標檢測方法,最后都要用到 NMS-非極大值抑制進行后處理。 通常的做法是將檢測框按得分排序,然后保留得分最高的框,同時刪除與該框重疊面積大於一定比例的其它框。
這種貪心式方法存在如下圖所示的問題: 紅色框和綠色框是當前的檢測結果,二者的得分分別是0.95和0.80。如果按照傳統的NMS進行處理,首先選中得分最高的紅色框,然后綠色框就會因為與之重疊面積過大而被刪掉。
另一方面,NMS的閾值也不太容易確定,設小了會出現下圖的情況(綠色框因為和紅色框重疊面積較大而被刪掉),設置過高又容易增大誤檢。
soft NMS算法的大致思路為:M為當前得分最高框,bi 為待處理框,bi 和M的IOU越大,bi 的得分si 就下降的越厲害。
算法結構如圖所示:
NMS中:
soft NMS中:
(1)線性加權:
(2)高斯加權:
soft NMS仍然有問題:其閾值仍然需要手工設定
soft nms 代碼實現:
# coding:utf-8 import numpy as np def soft_nms(boxes, sigma=0.5, Nt=0.1, threshold=0.001, method=1): N = boxes.shape[0] pos = 0 maxscore = 0 maxpos = 0 for i in range(N): maxscore = boxes[i, 4] maxpos = i tx1 = boxes[i,0] ty1 = boxes[i,1] tx2 = boxes[i,2] ty2 = boxes[i,3] ts = boxes[i,4] pos = i + 1 # get max box while pos < N: if maxscore < boxes[pos, 4]: maxscore = boxes[pos, 4] maxpos = pos pos = pos + 1 # add max box as a detection boxes[i,0] = boxes[maxpos,0] boxes[i,1] = boxes[maxpos,1] boxes[i,2] = boxes[maxpos,2] boxes[i,3] = boxes[maxpos,3] boxes[i,4] = boxes[maxpos,4] # swap ith box with position of max box boxes[maxpos,0] = tx1 boxes[maxpos,1] = ty1 boxes[maxpos,2] = tx2 boxes[maxpos,3] = ty2 boxes[maxpos,4] = ts tx1 = boxes[i,0] ty1 = boxes[i,1] tx2 = boxes[i,2] ty2 = boxes[i,3] ts = boxes[i,4] pos = i + 1 # NMS iterations, note that N changes if detection boxes fall below threshold while pos < N: x1 = boxes[pos, 0] y1 = boxes[pos, 1] x2 = boxes[pos, 2] y2 = boxes[pos, 3] s = boxes[pos, 4] area = (x2 - x1 + 1) * (y2 - y1 + 1) iw = (min(tx2, x2) - max(tx1, x1) + 1) if iw > 0: ih = (min(ty2, y2) - max(ty1, y1) + 1) if ih > 0: ua = float((tx2 - tx1 + 1) * (ty2 - ty1 + 1) + area - iw * ih) ov = iw * ih / ua #iou between max box and detection box if method == 1: # linear if ov > Nt: weight = 1 - ov else: weight = 1 elif method == 2: # gaussian weight = np.exp(-(ov * ov)/sigma) else: # original NMS if ov > Nt: weight = 0 else: weight = 1 boxes[pos, 4] = weight*boxes[pos, 4] print(boxes[:, 4]) # if box score falls below threshold, discard the box by swapping with last box # update N if boxes[pos, 4] < threshold: boxes[pos,0] = boxes[N-1, 0] boxes[pos,1] = boxes[N-1, 1] boxes[pos,2] = boxes[N-1, 2] boxes[pos,3] = boxes[N-1, 3] boxes[pos,4] = boxes[N-1, 4] N = N - 1 pos = pos - 1 pos = pos + 1 keep = [i for i in range(N)] return keep boxes = np.array([[100, 100, 150, 168, 0.63],[166, 70, 312, 190, 0.55],[221, 250, 389, 500, 0.79],[12, 190, 300, 399, 0.9],[28, 130, 134, 302, 0.3]]) keep = soft_nms(boxes) print(keep)
參考鏈接:
https://zhuanlan.zhihu.com/p/47189358
https://zhuanlan.zhihu.com/p/70768666
https://blog.csdn.net/leviopku/article/details/80886386