目標檢測模型中NMS、soft-NMS、softer-NMS的原理、LNMS文本檢測系列(python代碼實現)

非極大值抑制NMS的作用:

  • 是目標檢測框架中的後處理模塊,主要用於刪除高度冗餘的bbox。

?一、NMS【參考

非極大值抑制NMS的過程:
  • 根據置信度得分進行排序;
  • 選擇置信度最高的邊界框添加到最終輸出列表中,將其從原始邊界框列表中刪除;
  • 計算所有邊界框的面積;
  • 計算置信度最高的邊界框與其它候選框的IoU;
  • 刪除IoU大於閾值的邊界框;(一般IOU取0.3~0.5)
  • 重複上述過程,直至原始邊界框列表爲空。
def nms(bounding_boxes, Nt):
   if len(bounding_boxes) == 0:
       return [], []
   bboxes = np.array(bounding_boxes)

   # 計算 n 個候選框的面積大小
   x1 = bboxes[:, 0]
   y1 = bboxes[:, 1]
   x2 = bboxes[:, 2]
   y2 = bboxes[:, 3]
   scores = bboxes[:, 4]
   areas = (x2 - x1 + 1) * (y2 - y1 + 1)

   # 對置信度進行排序, 獲取排序後的下標序號, argsort 默認從小到大排序
   order = np.argsort(scores)

   picked_boxes = []  # 返回值
   while order.size > 0:
       # 將當前置信度最大的框加入返回值列表中
       index = order[-1]
       picked_boxes.append(bounding_boxes[index])

       # 獲取當前置信度最大的候選框與其他任意候選框的相交面積
       x11 = np.maximum(x1[index], x1[order[:-1]])
       y11 = np.maximum(y1[index], y1[order[:-1]])
       x22 = np.minimum(x2[index], x2[order[:-1]])
       y22 = np.minimum(y2[index], y2[order[:-1]])
       w = np.maximum(0.0, x22 - x11 + 1)
       h = np.maximum(0.0, y22 - y11 + 1)
       intersection = w * h

       # 利用相交的面積和兩個框自身的面積計算框的交併比, 將交併比大於閾值的框刪除
       ious = intersection / (areas[index] + areas[order[:-1]] - intersection)
       left = np.where(ious < Nt)
       order = order[left]
   return picked_boxes

?二、soft-NMS【論文原文

解決的問題:
  • 物體重合度較大的情況;
soft-NMS的原理:
  • 基於NMS的改進;
  • 將置信度改爲IoU的函數:f(IoU),具有較低的值而不至於從排序列表中刪去
def soft_nms(bboxes, Nt=0.3, sigma2=0.5, score_thresh=0.3, method=2):
   # 在 bboxes 之後添加對於的下標[0, 1, 2...], 最終 bboxes 的 shape 爲 [n, 5], 前四個爲座標, 後一個爲下標
   res_bboxes = deepcopy(bboxes)
   N = bboxes.shape[0]  # 總的 box 的數量
   indexes = np.array([np.arange(N)])  # 下標: 0, 1, 2, ..., n-1
   bboxes = np.concatenate((bboxes, indexes.T), axis=1)  # concatenate 之後, bboxes 的操作不會對外部變量產生影響
   # 計算每個 box 的面積
   x1 = bboxes[:, 0]
   y1 = bboxes[:, 1]
   x2 = bboxes[:, 2]
   y2 = bboxes[:, 3]
   scores = bboxes[:, 4]
   areas = (x2 - x1 + 1) * (y2 - y1 + 1)

   for i in range(N):
       # 找出 i 後面的最大 score 及其下標
       pos = i + 1
       if i != N - 1:
           maxscore = np.max(scores[pos:], axis=0)
           maxpos = np.argmax(scores[pos:], axis=0)
       else:
           maxscore = scores[-1]
           maxpos = 0
       # 如果當前 i 的得分小於後面的最大 score, 則與之交換, 確保 i 上的 score 最大
       if scores[i] < maxscore:
           bboxes[[i, maxpos + i + 1]] = bboxes[[maxpos + i + 1, i]]
           scores[[i, maxpos + i + 1]] = scores[[maxpos + i + 1, i]]
           areas[[i, maxpos + i + 1]] = areas[[maxpos + i + 1, i]]
       # IoU calculate
       xx1 = np.maximum(bboxes[i, 0], bboxes[pos:, 0])
       yy1 = np.maximum(bboxes[i, 1], bboxes[pos:, 1])
       xx2 = np.minimum(bboxes[i, 2], bboxes[pos:, 2])
       yy2 = np.minimum(bboxes[i, 3], bboxes[pos:, 3])
       w = np.maximum(0.0, xx2 - xx1 + 1)
       h = np.maximum(0.0, yy2 - yy1 + 1)
       intersection = w * h
       iou = intersection / (areas[i] + areas[pos:] - intersection)
       # Three methods: 1.linear 2.gaussian 3.original NMS
       if method == 1:  # linear
           weight = np.ones(iou.shape)
           weight[iou > Nt] = weight[iou > Nt] - iou[iou > Nt]
       elif method == 2:  # gaussian
           weight = np.exp(-(iou * iou) / sigma2)
       else:  # original NMS
           weight = np.ones(iou.shape)
           weight[iou > Nt] = 0
       scores[pos:] = weight * scores[pos:]
   # select the boxes and keep the corresponding indexes
   inds = bboxes[:, 5][scores > score_thresh]
   keep = inds.astype(int)
   return res_bboxes[keep]

?三、Softer-NMS【參考】【代碼

解決的問題:
  • 包圍框精度不夠的問題;
  • 高分類得分但是定位精度不統一。
softer-NMS的原理:
  • 基於soft-NMS的改進;
  • 將大於一定重疊度閾值Nt的候選包圍框根據置信度加權平均
  • 提出了一種新的包圍框迴歸的損失函數(KL Loss),用來同時學習包圍框變換和定位置信度。

?四、Locality-Aware NMS文本系列【參考

基本步驟:
  • 1.先對所有的output box集合結合相應的閾值(大於閾值則進行合併,小於閾值則不和並),依次遍歷進行加權合併,得到合併後的bbox集合;
  • 2.對合並後的bbox集合進行標準的NMS操作。
import numpy as np
from shapely.geometry import Polygon

def intersection(g, p):
   # 取g,p中的幾何體信息組成多邊形
   g = Polygon(g[:8].reshape((4, 2)))
   p = Polygon(p[:8].reshape((4, 2)))
   # 判斷g,p是否爲有效的多邊形幾何體
   if not g.is_valid or not p.is_valid:
       return 0
   # 取兩個幾何體的交集和並集
   inter = Polygon(g).intersection(Polygon(p)).area
   union = g.area + p.area - inter
   if union == 0:
       return 0
   else:
       return inter / union

def weighted_merge(g, p):
   # 取g,p兩個幾何體的加權(權重根據對應的檢測得分計算得到)
   g[:8] = (g[8] * g[:8] + p[8] * p[:8]) / (g[8] + p[8])
   # 合併後的幾何體的得分爲兩個幾何體得分的總和
   g[8] = (g[8] + p[8])
   return g

def standard_nms(S, thres):
   # 標準NMS
   order = np.argsort(S[:, 8])[::-1]
   keep = []
   while order.size > 0:
       i = order[0]
       keep.append(i)
       ovr = np.array([intersection(S[i], S[t]) for t in order[1:]])
       inds = np.where(ovr <= thres)[0]
       order = order[inds + 1]
   return S[keep]


def nms_locality(polys, thres=0.3):
   '''
  locality aware nms of EAST
  :param polys: a N*9 numpy array. first 8 coordinates, then prob
  :return: boxes after nms
  '''
   S = []  # 合併後的幾何體集合
   p = None  # 合併後的幾何體
   for g in polys:
       if p is not None and intersection(g, p) > thres:  # 若兩個幾何體的相交面積大於指定的閾值,則進行合併
           p = weighted_merge(g, p)
       else:  # 反之,則保留當前的幾何體
           if p is not None:
               S.append(p)
           p = g
   if p is not None:
       S.append(p)
   if len(S) == 0:
       return np.array([])
   return standard_nms(np.array(S), thres)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章