코딩 및 기타/이미지

Anchor box

정지홍 2025. 1. 22. 22:49

IoU 공식. (식1)

Anchor box

  • 객체 탐지에서 주로 사용하는 개념으로, 이미지 내에서 다양한 크기와 비율로 물체를 탐지하기 위해 미리 정의된 사각형 영역을 말한다.
    주로 YOLO , Faster R-CNN , RetinaNet과 같은 객체 탐지 모델에서 사용.
  • 기본 역할
    • 이미지의 특정 위치에 대해 다양한 크기와 비율의 사전 정의된 박스를 배치한다.
      • 해당 박스는 물체 후보 영역으로 사용되며, 모델이 학습하면서 각 박스를 실제 물체와 정교히 맞추는 방식으로 탐지를 수행함.
  • 다양한 크기와 비율을 가진다.
    • 객체는 다양한 크기와 형태를 가지니, 서로 다른 크기와 비율의 Anchor Box가 필요하다.
      • 만약, 현재 위치에 대해서 3가지의 크기와 3가지의 비율을 정의하면, 해당 위치에는 9개의 Anchor Box가 만들어 진다.
  • 예측 방법
    • 모델은 각 Anchor Box에 대해서 클래스 확률과 박스 오프셋을 예측한다.
  • 장점
    • 다양한 물체 크기와 형태 탐지 가능
    • anchor box가 사전 정의되어 있어 탐지 속도가 빠르다.
  • 단점
    • 너무 많은 anchor box를 사용하면 계산량이 증가하며, 불필요한 후보영역이 많아질수있음
    • 적절한 하이퍼 파라미터를 설정해야 최적의 성능을 얻을수있다.
  • 작동과정
    • 1. 특정한 격자 위치에 대해서 여러가지 anchor box를 만든다. 이는 물체를 포함할 가능성이 있는 후보 영역이다.
      • 1-1. 특정 위치에 다양한 크기와 비율의 박스를 정의함.
        • ex) 512 x 512 image를 16x16격자로 나누면 32x32의 cell로 이미지가 나뉜다.
      • 1-2. 각 격자의 중심에서 시작해서 다양한 크기와 비율의 anchor box를 생성한다.
        • anchor box의 scale과 aspect radio(비율)는 사전에 하이퍼파라미터로 정의.
    • 2. IoU ( intersection over union ) 계산한다. 즉 이는 anchor box와 ground truth box 사이의 IoU를 계산.
      이때 IoU가 특정 임계값을 초과하면, anchor box는 물체를 포함한다고 간주하며, 이를 모델이 학습하게 한다.
      • IoU가 높으면, Anchor Box는 물체를 포함한다고 간주. 낮은 경우는 물체와 무관한 배경으로 간주.(식1참고)
        • positive anchor box
          • Iou가 특정 임계값(ex. 0.5 )를 초과하면, 해당 anchor box는 물체를 포함하는 것으로 간주.
          • positive anchor box는 ground truth box와 최대한 일치하도록 학습된다.
        • negative anchor box
          • IoU가 낮은 anchor box는 물체를 포함하지 않는 것으로 간주하며, 이를 모델이 학습한다. 
            그리고 이는 배경으로 처리된다.
    • 3. anchor box는 초기에는 고정된 크기와 위치를 가지지만, 학습이 진행되며 모델이 예측한 오프셋 값을 통헤서 물체와 더 정밀하게 일치하게 조정한다.
      • 3-1. 물체 존재 여부 및 클래스 확률 예측
        • 우선 해당되는 anchor box가 물체를 포함하는지 확인한다. ( 0 or 1 )
          그리고 anchor박스가 특정 클래스일 확률을 구한다. ( 배경이라고 판단하면 이는 수행안함 )
          그 다음에 bounding box offset을 구한다.
          • bounding box offset이란 anchor box와 ground truth box 사이의 차이를 보정하는 값이다.
            이 값은 anchor box의 중심 좌표( x , y )와 크기( w , h )를 보정한다. ( 식2 참고)
    • 4. Loss Function을 통해서 학습을 합니다.
      • 4-1. classification loss 클래스 손실
        • anchor box가 예측한 클래스와 ground truth의 실제 클래스를 비교.
          주로 cross-entropy loss를 사용
      • 4-2. localization loss 위치 손실
        • anchor box와 ground box 사이의 좌표 및 크기 차이를 최소화
          주로 smooth L1 loss or IoU Loss를 사용 
      • 4-3. objectness Loss 물체 여부 손실
        • anchor box가 물체를 포함하는지 여부를 예측
          주로 binary cross entropy loss사용
    • 5. 모델이 각 anchor box에 대해서 예측한 결과를 기반으로 물체를 탐지함.
      • 모델이 예측한 offset값을 anchor box에 적용해서 최종 bounding box를 계산
      • 동일한 객체에 대해서 여러개의 anchor box가 예측될 수 있으니, IoU가 높은 중복된 박스를 제거해서 최적의 bounding box만 남김.
        그리고 각 물체의 클래스,bounding box, 확률이 최종 결과로 출력 

 

식2

 

 

import numpy as np

def generate_anchor_boxes( img_size , grid_size , scales , aspect_radios ):
    anchor_boxes = []
    grid_width = img_size[0] / grid_size[1] # 격자 한칸의 폭 계산 
    grid_height = img_size[1] / grid_size[0]# 격자 한칸의 높이 계산
    print(f'img_size is {img_size}   grid_size is {grid_size}  scales is {scales}    aspect_radios is {aspect_radios}\ngrid_width is {grid_width}  grid_height is {grid_height} 즉, 입력 이미지를 {grid_width} x {grid_height}의 셀로 나눈다. ')
    # 각 격자 위치를 순회하면서 anchor box를 만든다.
    for i in range( grid_size[ 0 ] ): # 격자의 세로 (행) 반복
        for j in range( grid_size[ 1 ] ): # 격자의 세로 (열) 반복
            # 현재 격자의 중심 좌표를 계산
            center_x = ( j + 0.5 ) *grid_width
            center_y = ( i + 0.5 ) * grid_height
            # 주어진 scale과 aspect ratio를 사용해서 anchor box 생성
            for scale in scales:
                for ratio in aspect_ratios:
                    box_width = scale * img_size[0] * np.sqrt( ratio ) # anchor box의 폭 계산
                    box_height = scale * img_size[1] / np.sqrt( ratio ) # anchor box의 높이 계산
                    anchor_boxes.append( (center_x , center_y , box_width , box_height ) )
                    #print(f'{center_x}  {center_y}  {box_width}   {box_height}')
    return np.array( anchor_boxes )

img_size = (512 , 512) # 이미지 크기 widh , height
grid_size = ( 4 , 4 ) # 격자 크기 rows , cols 
scales = [ 0.1 , 0.2 , 0.4 ] # 앵커 박스의 크기
aspect_ratios = [1.0 , 2.0 , 0.5 ] # 앵커 박스의 비율

anchor_boxes = generate_anchor_boxes( img_size , grid_size , scales , aspect_ratios)
print(anchor_boxes)

 

 

 

 

# IoU를 계산
# center_x , center_y , width , height
def cal_iou( box1 , box2 ):
    # 첫번째 박스의 좌상단( x1_min , y1_min )과 우하단( x1_max , y1_max)을 계산
    x1_min = box1[0] - box1[2] / 2
    x1_max = box1[0] + box1[2] / 2
    y1_min = box1[1] - box1[3] / 2
    y1_max = box1[1] + box1[3] / 2
    # 두번째 상자의 좌상단( x2min , y2_min )과 우하단( y2_max , x2_max)을 계싼.
    x2_min = box2[0] - box2[2] / 2
    x2_max = box2[0] + box2[2] / 2
    y2_min = box2[1] - box2[3] / 2
    y2_max = box2[1] + box2[3] / 2

    # 교집합 영역의 좌성단 및 우하단 좌표 계산
    inter_x_min = max(x1_min, x2_min)
    inter_y_min = max(y1_min, y2_min)
    inter_x_max = min(x1_max, x2_max)
    inter_y_max = min(y1_max, y2_max)

    # 교집합 영역의 넓이를 계산하며, 음수일 경우에는 0으로 처리합니다.
    inter_area = max(0, inter_x_max - inter_x_min) * max(0, inter_y_max - inter_y_min)

    # 두 박스의 면적 계산
    area1 = (x1_max - x1_min) * (y1_max - y1_min)
    area2 = (x2_max - x2_min) * (y2_max - y2_min)
    # 합집합 영역의 넓이 계산
    union_area = area1 + area2 - inter_area

    # IoU 계산. ( 합집합이 0이면 IoU는 0으로 처리 
    iou = inter_area / union_area if union_area > 0 else 0
    return iou

 

 

 

 

 

 

# anchor box와 ground truth 매칭 함수
def match_anchors_to_ground_truth( anchors , ground_truth_boxes , iou_threshold = 0.5 ):
    matched_indices = []
    iou_scores = []
    for gt_box in ground_truth_boxes: # 모든 ground truth box에 대해서 매칭되는 anchor box를 찾음
        max_iou = 0 # 최대 iou값을 0으로 초기화
        best_anchor_idx = -1  # 촤적의 anchor box idx 초기값
        for anchor_idx , anchor in enumerate( anchors ):# 모든 anchor box에 대해서 iou를 계산
            iou = cal_iou( anchor , gt_box )
            if iou > max_iou: # 더 큰 iou를 찾으면 업데이트
                max_iou = iou
                best_anchor_dix = anchor_idx
        # iou가 임계값 이상이면 매칭으로 간주 . 즉, 물체가 존재함을 의미.
        if max_iou >= iou_threshold:
            match_indices.append( best_anchor_idx )
            iou_scores,append( max_iou )

    return matched_indices , iou_scores