Paper : https://arxiv.org/pdf/1704.04503.pdf
Soft NMS
기존 NMS 는 가장 높은 confidence 를 가지는 bbox 를 찾고, 같은 클래스 인 bbox 들 중 겹치는 영역이 일정 비율 이상인 (iou > threshold) bbox 를 제거해서 중복된 detection 결과를 없앤다. 이러한 hard 한 NMS 방식은 실제로 존재하는 object 를 제거함으로써 mAP 가 낮아지는 문제가 있다. 일례로 아래 왼쪽 그림 처럼 같은 클래스인 말(horse) 들이 겹쳐져 있고, confidence 가 각 0.8 / 0.9 / 0.8 이 나왔을 때, 가운데 말만 남고 나머지 말의 detection 결과는 없어질 수 있다.
그래서 일정 비율 이상인 (iou > threshold) 겹치는 bbox 들의 confidence를 0 으로 만들어 없애지 말고, confidence 를 줄여서 최종 mAP 를 향상 시키자는 개념이다. 아래 오른쪽 그림과 같이 사이드에 있는 말들의 confidence 즉, score 가 각각 0.4 로 낮아짐을 확인 할 수 있다.
Soft NMS pytorch 구현
def soft_nms_pytorch(dets, box_scores, sigma=0.5, thresh=0.001, cuda=0):
"""
Build a pytorch implement of Soft NMS algorithm.
# Augments
dets: boxes coordinate tensor (format:[y1, x1, y2, x2])
box_scores: box score tensors
sigma: variance of Gaussian function
thresh: score thresh
cuda: CUDA flag
# Return
the index of the selected boxes
"""
# Indexes concatenate boxes with the last column
N = dets.shape[0]
if cuda:
indexes = torch.arange(0, N, dtype=torch.float).cuda().view(N, 1)
else:
indexes = torch.arange(0, N, dtype=torch.float).view(N, 1)
dets = torch.cat((dets, indexes), dim=1)
# The order of boxes coordinate is [y1,x1,y2,x2]
y1 = dets[:, 0]
x1 = dets[:, 1]
y2 = dets[:, 2]
x2 = dets[:, 3]
scores = box_scores
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
for i in range(N):
# intermediate parameters for later parameters exchange
tscore = scores[i].clone()
pos = i + 1
if i != N - 1:
maxscore, maxpos = torch.max(scores[pos:], dim=0)
if tscore < maxscore:
dets[i], dets[maxpos.item() + i + 1] = dets[maxpos.item() + i + 1].clone(), dets[i].clone()
scores[i], scores[maxpos.item() + i + 1] = scores[maxpos.item() + i + 1].clone(), scores[i].clone()
areas[i], areas[maxpos + i + 1] = areas[maxpos + i + 1].clone(), areas[i].clone()
# IoU calculate
yy1 = np.maximum(dets[i, 0].to("cpu").numpy(), dets[pos:, 0].to("cpu").numpy())
xx1 = np.maximum(dets[i, 1].to("cpu").numpy(), dets[pos:, 1].to("cpu").numpy())
yy2 = np.minimum(dets[i, 2].to("cpu").numpy(), dets[pos:, 2].to("cpu").numpy())
xx2 = np.minimum(dets[i, 3].to("cpu").numpy(), dets[pos:, 3].to("cpu").numpy())
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = torch.tensor(w * h).cuda() if cuda else torch.tensor(w * h)
ovr = torch.div(inter, (areas[i] + areas[pos:] - inter))
# Gaussian decay
weight = torch.exp(-(ovr * ovr) / sigma)
scores[pos:] = weight * scores[pos:]
# select the boxes and keep the corresponding indexes
keep = dets[:, 4][scores > thresh].int()
return keep
참고자료 1 : https://github.com/DocF/Soft-NMS/blob/master/softnms_pytorch.py
참고자료 3 : https://towardsdatascience.com/non-maximum-suppression-nms-93ce178e177c
'AI Research Topic > Object Detection' 카테고리의 다른 글
[Object Detection] 객체 탐지 정확도 평가 지표 mAP(mean Average Precision) (3) | 2021.04.14 |
---|---|
[Object Detction] 3D Object Detection, Google Objectron (3) | 2020.05.27 |
[Object Detection] Deformable Convolutional Networks (0) | 2020.03.08 |
[Object Detection] EfficientNet and EfficientDet (0) | 2020.02.23 |
[Object Detection] The Car Connection Picture Dataset (4) | 2020.02.03 |