본문 바로가기

인공지능/DeepLearning

[Computer Vision] Anchor Boxes(앵커 박스) - 1


Object detection(객체 감지) 알고리즘은 일반적으로 많은 수의 영역 샘플링하고, 이 영역에 관심 개체가 포함되어 있는지 여부를 확인하고, 대상의 실측 Bounding Boxes(경계 상자)를 정확하게 예측하기 위해 영역의 가장자리를 조정합니다.

 

이러한 Bounding Boxes를 찾기 위한 방법에는 여러가지가 있습니다. 그중 하나의 방법이 Anchor Boxes입니다.

각 픽셀을 중앙에 두고 크기와 종횡비가 서로 다른 Bounding Boxes를 생성합니다.

이러한 Bounding Boxes를 Anchor Boxes라고 하며, 이를 통해 물체를 감지하게 됩니다.


%matplotlib inline
from d2l import torch as d2l
import torch
import torchvision

torch.set_printoptions(2)

 

실습에 필요한 모듈과 torch의 자리수 지정을 위한 set_printoptions(2) 명령어입니다.


Multiple Anchor Boxes 생성하기

def multibox_prior(data, sizes, ratios):
    in_height, in_width = data.shape[-2:] # 이미지 높이와 너비
    device, num_sizes, num_ratios = data.device, len(sizes), len(ratios)
    
    boxes_per_pixel = (num_sizes + num_ratios - 1)
    size_tensor = torch.tensor(sizes, device=device)
    ratio_tensor = torch.tensor(ratios, device=device)
    
    # 앵커를 픽셀 중앙으로 이동하려면 오프셋이 필요합니다.
    # 픽셀 (height = 1, width = 1)이므로 중심을 0.5만큼 오프셋하도록 선택합니다.
    offset_h, offset_w = 0.5, 0.5
    steps_h = 1.0 / in_height  # y 축의 스케일 단계
    steps_w = 1.0 / in_width  # x 축의 스케일 단계

    # 앵커 박스의 모든 중심점 생성
    center_h = (torch.arange(in_height, device=device) + offset_h) * steps_h
    center_w = (torch.arange(in_width, device=device) + offset_w) * steps_w
    
    shift_y, shift_x = torch.meshgrid(center_h, center_w)
    shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1)

    # 각 중심점에는 box_per_pixel 개수의 앵커 상자가 있으므로
    # box_per_pixel 반복으로 모든 앵커 박스 중심의 그리드 생성
    out_grid = torch.stack([shift_x, shift_y, shift_x, shift_y],
                dim=1).repeat_interleave(boxes_per_pixel, dim=0)
                
    # 이후의 높이와 너비의 box_per_pixel 수 생성
    # 앵커 상자 모서리 좌표 (xmin, xmax, ymin, ymax)를 만드는 데 사용됩니다.
    w = torch.cat((size_tensor, sizes[0] * torch.sqrt(ratio_tensor[1:])))\
                * in_height / in_width / 2
    h = torch.cat((size_tensor, sizes[0] / torch.sqrt(ratio_tensor[1:]))) / 2
    anchor_manipulations = torch.stack((-w, -h, w, h)).T.repeat(
                                        in_height * in_width, 1)

    output = out_grid + anchor_manipulations
    return output.unsqueeze(0)

 

size(크기)의 범위는 (0, 1]이고, ratio(종횡비)의 범위는 (0, ∞],

Anchor Boxes의 너비와 높이는 $ws\sqrt{r}$, $h\sqrt{r}$로 가정합니다.

 

이미지의 각 픽셀을 중앙을 기준으로 모양을 생성하기 때문에 픽셀(1, 1)인 중심을 중앙인 (0.5, 0.5)입니다.

따라서, (1,1)을 0.5만큼 offset 하는 것이다. 그 후 steps에서 이미지 크기를 size의 범위인 (0, 1]로 scale 해주는 것입니다.

 

center에서 기존 이미지의 크기에 대한 모든 너비와 높이를 offset과 step을 통해 구하고 scale해줍니다.

shift에서는 앞에서 구한 너비의 중심과 높이의 중심의 2차원 변수의 격자점 좌표를 생성해주게 됩니다. 

격자점 좌표들은 Anchor Boxes의 중심 좌표이며, box_per_pixel만큼 생성하게 됩니다.

 

이후 Anchor Boxes의 공식대로 box_per_pixel만큼 너비와 높이를 구하여 anchor_manipulations 저장합니다.

 

out_grid에서 구한 중심 좌표(x, y, x, y)에

anchor_manipulations의 (-w, -h, w, h)를 더해 앵커박스의 모서리의 좌표(xmin, xmax, ymin, ymax)가 나오는 것입니다.

 

img = d2l.plt.imread('../img/catdog.jpg')
h, w = img.shape[0:2]

print(h, w)
X = torch.rand(size=(1, 3, h, w))  # Construct input data
Y = multibox_prior(X, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5])
print(Y.shape)

 

출력

 

함수의 리턴된 결과의 shape은 (batch_size, number of anchor boxes, 4=(모서리 좌표(xmin, xmax, ymin, ymax))입니다.

 

리턴된 결과의 shape을 (image height, image width, 각 Anchor boxes의 중심 픽셀 갯수, 4)로 변환이 가능합니다.

 

boxes = Y.reshape(h, w, 5, 4)
boxes[250, 250, 0, :] # 이미지 픽셀 좌표 (250, 250)의 첫번째 Anchor boxes 좌표

 

출력

 

def show_bboxes(axes, bboxes, labels=None, colors=None):
    """Show bounding boxes."""
    def _make_list(obj, default_values=None):
        if obj is None:
            obj = default_values
        elif not isinstance(obj, (list, tuple)):
            obj = [obj]
        return obj
    labels = _make_list(labels)
    colors = _make_list(colors, ['b', 'g', 'r', 'm', 'c'])
    for i, bbox in enumerate(bboxes):
        color = colors[i % len(colors)]
        rect = d2l.bbox_to_rect(d2l.numpy(bbox), color)
        axes.add_patch(rect)
        if labels and len(labels) > i:
            text_color = 'k' if color == 'w' else 'w'
            axes.text(rect.xy[0], rect.xy[1], labels[i],
                      va='center', ha='center', fontsize=9, color=text_color,
                      bbox=dict(facecolor=color, lw=0))

 

show_bboxes는 앵커박스를 시각적으로 보여주는 함수이다.

 

d2l.set_figsize()
bbox_scale = torch.tensor((w, h, w, h))
fig = d2l.plt.imshow(img)
show_bboxes(fig.axes, boxes[250, 250, :, :] * bbox_scale,
            ['s=0.75, r=1', 's=0.5, r=1', 's=0.25, r=1', 's=0.75, r=2',
             's=0.75, r=0.5'])

 

출력

 

boxes에서 구한 픽셀 좌표(250, 250)의 앵커박스 중 (s=0.75, r=1)의 앵커박스가 가장 개를 잘 덮는것을 볼 수 있다.


 

[Computer Vision] Anchor Boxes(앵커 박스) - 2

[Computer Vision] Anchor Boxes(앵커 박스) - 1 Object detection(객체 감지) 알고리즘은 일반적으로 많은 수의 영역 샘플링하고, 이 영역에 관심 개체가 포함되어 있는지 여부를 확인하고, 대상의 실측 Boundin.

jungnamgyu.tistory.com


ref. Dive into Deep Learning, Aston Zhang and Zachary C. Lipton and Mu Li and Alexander J. Smola, 2020