본문 바로가기
컴퓨터비전

IoU(Intersection over Union)의 이해와 구현 실습

by 차분한 공돌이 2024. 2. 14.

IoU(Intersection over Union)의 목적과 개념

어느 인공지능 모델이든 성능을 평가하기 위한 지표가 필요하다. Object Detection 모델은 일반적인 분류 모델과는 다른 bounding box라는 아웃풋을 출력한다. 따라서 Object Detection 모델의 성능을 측정(목적)하기 위해  새로운 성능 측정 지표가 필요하고, 이것이 바로 IoU(Intersection over Union)이다. IoU는 모델이 예측한 결과(Predicted Bounding box)와 실제(Ground Truth Bounding box)가 얼마자 정확하게 겹치는가(개념)를 나타낸다.

IoU의 계산은 다음과 같이 한다.

 

Ground Truth Bounding box는 annotation(데이터에 대한 설명이 담긴 메타데이터를 데이터에 달아주는 작업)을 통해 해당 이미지 데이터에 부여된다.

 

IoU에 따른 Detection 성능

기준을 어떻게 설정하는지에 따라 달라질 수 있지만 보통은 다음과 같이 판단한다.

0.3 <= IoU < 0.7  Poor

0.7 <= IoU < 0.9  Good

0.9 <= IoU           Excellent

 

 

IoU 구현 실습

 

입력인자로 후보 박스와 실제 박스를 받아서 IOU를 계산하는 함수 생성

 

import numpy as np

def compute_iou(cand_box, gt_box): #cand_box 와 gt_box 형태 : [좌상단 x좌표, y좌표, 우하단 x좌표, y좌표]

    # Calculate intersection areas
    x1 = np.maximum(cand_box[0], gt_box[0]) 
    y1 = np.maximum(cand_box[1], gt_box[1])
    x2 = np.minimum(cand_box[2], gt_box[2])
    y2 = np.minimum(cand_box[3], gt_box[3])

    intersection = np.maximum(x2 - x1, 0) * np.maximum(y2 - y1, 0)  #<2>

    cand_box_area = (cand_box[2] - cand_box[0]) * (cand_box[3] - cand_box[1]) #  <2> + <3>
    gt_box_area = (gt_box[2] - gt_box[0]) * (gt_box[3] - gt_box[1]) #  <1> + <2>
    union = cand_box_area + gt_box_area - intersection  #  ( <2> + <3> ) + ( <1> + <2> ) - <2>  =  <1> + <2> + <3>

    iou = intersection / union # <2> / <1> + <2> + <3>
    return iou

 

 

 

selective_search를 이용하여 바운딩 박스 좌표를 구한 후 iou계산하기

 

 

import selectivesearch

#selectivesearch.selective_search()는 이미지의 Region Proposal정보를 반환
img = cv2.imread('./data/audrey01.jpg') #지정된 경로의 이미지 파일이 읽혀져서 OpenCV에서 사용할 수 있는 형식인 Numpy 배열로 반환
img_rgb2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # #cv2.imread()로 이미지 가져오면 BGR 형태이고, matplotlib으로 이미지 표시하려면 RGB형태여야 해서 바꿔주는 것

_, regions = selectivesearch.selective_search(img_rgb2, scale=100, min_size=2000) #regions : object가 있을 만한 위치

cand_rects = [cand['rect'] for cand in regions] #cand['rect'] : [(박스1의 x1,y1,W,H),(박스2의 x1,y1,W,H),,,,,]
for index, cand_box in enumerate(cand_rects): 
    cand_box = list(cand_box)
    cand_box[2] += cand_box[0] #cand_box[0] : 박스의 좌상단 x좌표, cand_box[2] : 박스의 너비, 따라서 cand_box[2] : 박스의 우하단 x좌표
    cand_box[3] += cand_box[1] #cand_box[1] : 박스의 좌상단 y좌표, cand_box[2] : 박스의 높이, 따라서 cand_box[3] : 박스의 우하단 y좌표

    iou = compute_iou(cand_box, gt_box)
    print('index:', index, "iou:", iou)

 

코드의 결과

 

sel바운딩 박스를 size와 iou로 선별 후 시각화

 

img = cv2.imread('./data/audrey01.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
print('img shape:', img.shape)

green_rgb = (125, 255, 51)
cand_rects = [cand['rect'] for cand in regions if cand['size'] > 3000] #크기가 3000 이상인 바운딩 박스만 골라냄
gt_box = [60, 15, 320, 420]
img_rgb = cv2.rectangle(img_rgb, (gt_box[0], gt_box[1]), (gt_box[2], gt_box[3]), color=red, thickness=2)

for index, cand_box in enumerate(cand_rects):

    cand_box = list(cand_box)
    cand_box[2] += cand_box[0]
    cand_box[3] += cand_box[1]
    
    iou = compute_iou(cand_box, gt_box)

    if iou > 0.5:  #gt_box와의 iou가 0.5 이상인 바운딩 박스만 출력
        print('index:', index, "iou:", iou, 'rectangle:',(cand_box[0], cand_box[1], cand_box[2], cand_box[3]) )
        cv2.rectangle(img_rgb, (cand_box[0], cand_box[1]), (cand_box[2], cand_box[3]), color=green_rgb, thickness=1)
        text = "{}: {:.2f}".format(index, iou)
        cv2.putText(img_rgb, text, (cand_box[0]+ 100, cand_box[1]+10), cv2.FONT_HERSHEY_SIMPLEX, 0.4, color=green_rgb, thickness=1)

plt.figure(figsize=(12, 12))
plt.imshow(img_rgb)
plt.show()

 

 

 

 

Reference