Non-Maximum Suppression(NMS)란
08 Mar 2021지난주 회사분들과 Non-Maximum Suppression 스터디를 진행하였고 좀 더 자세하게 공부하고자 포스팅을 남긴다.
object detectior가 예측한 bounding box중에서 정확한 bounding box를 선택하도록 하는 기법이다.
대표적으로 YOLO에 NMS기법이 적용되어 있다.
NMS 의사코드
알고리즘
- Detected 된 bounding box 별로 Confidence threshold 이하의 bounding box는 제거한다.
- 가장 높은 confidence score를 가진 box 순으로 내림차순 정렬하고 아래 로직을 모든 box에 순차적으로 적용
- 가장 높은 confience score를 가진 box와 곂치는 다른 box를 모두 조사하여 IOU가 특정 threshold 이상인 box를 모두 제거. 즉 IOU(아래 설명)가 일정 이상인 boundingbox는 동일한 물체를 detect 했다고 판단하고 곂치는 box를 제거해 주는 과정
- 남아 있는 box만 선택한다.
코드 구현
위의 과정을 통해 최대한 하나의 Object에 하나의 Detection box만이 존재하도록 만들게 된다. 이 아이디어는 이미 Tensorflow에 tf.image.non_max_suppression
으로 구현되어있기에 우리가 직접 구현할 필요는 없지만 보다 확실한 이해를 위해서 다음과 같이 Python code로 구현해볼 수 있다.
아래 코드는 github-squeezeDet 에서 참고하였다.
def nms(boxes, probs, threshold):
"""Non-Maximum supression.
Args:
boxes: array of [cx, cy, w, h] (center format)
probs: 오브젝트가 있을 확률 배열
threshold: two boxes are considered overlapping if their IOU is largher than
this threshold
form: 'center' or 'diagonal'
Returns:
keep: array of True or False.
"""
# 내림차순으로 정렬
order = probs.argsort()[::-1]
# 개수 대로 true 리스트 생성
keep = [True]*len(order)
for i in range(len(order)-1):
# IOU 검출, 자세한 코드는 하단 batch_iou코드 참조
ovps = batch_iou(boxes[order[i+1:]], boxes[order[i]])
for j, ov in enumerate(ovps):
if ov > threshold:
# IOU가 특정 threshold 이상인 box를 False로 세팅
keep[order[j+i+1]] = False
return keep
코드를 보면 알겠지만 batch_iou(IOU)가 필요하다. IOU는 전체 박스 영역 중 곂치는 부분의 비율이다.
NMS를 하는 가장 큰 이유는 중복 제거
이기 때문에 예측한 박스 중 IOU가 일정이상인 것들에 대해서 수행한다.
다음 포스트에서 IOU에 대해 좀 더 자세하게 포스팅 해보겠다.
아래는 IOU의 코드이니 참고하면 좋을 것 같다.
def batch_iou(boxes, box):
"""Compute the Intersection-Over-Union of a batch of boxes with another
box.
Args:
box1: 2D array of [cx, cy, width, height].
box2: a single array of [cx, cy, width, height]
Returns:
ious: array of a float number in range [0, 1].
"""
lr = np.maximum(
np.minimum(boxes[:,0]+0.5*boxes[:,2], box[0]+0.5*box[2]) - \
np.maximum(boxes[:,0]-0.5*boxes[:,2], box[0]-0.5*box[2]),
0
)
tb = np.maximum(
np.minimum(boxes[:,1]+0.5*boxes[:,3], box[1]+0.5*box[3]) - \
np.maximum(boxes[:,1]-0.5*boxes[:,3], box[1]-0.5*box[3]),
0
)
inter = lr*tb
union = boxes[:,2]*boxes[:,3] + box[2]*box[3] - inter
return inter/union
[참고 자료]
https://go-hard.tistory.com/25
https://donggoolosori.github.io/2020/10/14/dlcv-nms/
https://towardsdatascience.com/non-maximum-suppression-nms-93ce178e177c