| """ |
| Faster R-CNN ๋ฐ๋ฐ๋ฅ ๊ตฌํ โ [2/5] ๋ฐ์ค ์ฐ์ฐ ์ ํธ |
| ================================================== |
| ๊ฐ์ฒดํ์ง์ ์ํ์ ํต์ฌ์ด ๋ชจ๋ ์ฌ๊ธฐ์ ์๋ค. |
| |
| 1) ์ต์ปค(anchor) ์์ฑ : ๊ฒฉ์๋ง๋ค ์ฌ๋ฌ ํฌ๊ธฐยท๋น์จ์ ๊ธฐ์ค ๋ฐ์ค๋ฅผ ๊น๋ค |
| 2) IoU : ๋ ๋ฐ์ค๊ฐ ์ผ๋ง๋ ๊ฒน์น๋๊ฐ |
| 3) ๋ฐ์ค ์ธ์ฝ๋ฉ/๋์ฝ๋ฉ : (๋ฐ์ค โ ํ๊ท ํ๊น) / (์์ธก๊ฐ โ ๋ฐ์ค) |
| 4) NMS : ๊ฒน์น๋ ์ค๋ณต ์์ธก์ ์ ๊ฑฐ |
| |
| ์ด ํ์ผ๋ง ์ดํดํ๋ฉด Faster R-CNN์ ์ ๋ฐ์ ์ดํดํ ๊ฒ์ด๋ค. |
| """ |
|
|
| import torch |
|
|
|
|
| |
| |
| |
| def generate_anchors(base_size=16, ratios=(0.5, 1.0, 2.0), |
| scales=(8, 16, 32)): |
| """ |
| ํ ๊ฒฉ์์ (cell)์ ๋์ ๊ธฐ์ค ์ต์ปค๋ค์ ๋ง๋ ๋ค. |
| ratios(๊ฐ๋ก์ธ๋ก๋น) ร scales(ํฌ๊ธฐ) ์กฐํฉ โ ๋ณดํต 9๊ฐ ์ต์ปค. |
| |
| ๋ฐํ: [num_anchors, 4] ํํ์ (x1,y1,x2,y2), ์ค์ฌ์ด ์์ ๊ธฐ์ค. |
| """ |
| anchors = [] |
| for scale in scales: |
| area = (base_size * scale) ** 2 |
| for ratio in ratios: |
| |
| w = round((area / ratio) ** 0.5) |
| h = round(w * ratio) |
| anchors.append([-w / 2, -h / 2, w / 2, h / 2]) |
| return torch.tensor(anchors, dtype=torch.float32) |
|
|
|
|
| def shift_anchors(base_anchors, feat_h, feat_w, stride): |
| """ |
| ๊ธฐ์ค ์ต์ปค๋ฅผ ํน์ง๋งต ์ ์ฒด ๊ฒฉ์์ ๋ณต์ ยท์ด๋์์ผ |
| ๋ชจ๋ ์์น์ ์ต์ปค๋ฅผ ๋ง๋ ๋ค. |
| |
| feat_h, feat_w : ํน์ง๋งต ํฌ๊ธฐ |
| stride : ์๋ณธ ์ด๋ฏธ์ง ๋๋น ํน์ง๋งต ์ถ์ ๋ฐฐ์จ(์: 16) |
| ๋ฐํ: [feat_h*feat_w*num_anchors, 4] (์๋ณธ ์ด๋ฏธ์ง ์ขํ๊ณ) |
| """ |
| |
| shift_x = (torch.arange(feat_w) + 0.5) * stride |
| shift_y = (torch.arange(feat_h) + 0.5) * stride |
| sy, sx = torch.meshgrid(shift_y, shift_x, indexing="ij") |
| shifts = torch.stack([sx.reshape(-1), sy.reshape(-1), |
| sx.reshape(-1), sy.reshape(-1)], dim=1) |
|
|
| |
| anchors = shifts[:, None, :] + base_anchors[None, :, :] |
| return anchors.reshape(-1, 4) |
|
|
|
|
| |
| |
| |
| def box_iou(boxes1, boxes2): |
| """ |
| [N,4], [M,4] โ [N,M] IoU ํ๋ ฌ. |
| IoU = ๊ต์งํฉ ๋์ด / ํฉ์งํฉ ๋์ด. 0(์ ๊ฒน์นจ)~1(์์ ์ผ์น). |
| """ |
| area1 = (boxes1[:, 2] - boxes1[:, 0]) * (boxes1[:, 3] - boxes1[:, 1]) |
| area2 = (boxes2[:, 2] - boxes2[:, 0]) * (boxes2[:, 3] - boxes2[:, 1]) |
|
|
| lt = torch.max(boxes1[:, None, :2], boxes2[None, :, :2]) |
| rb = torch.min(boxes1[:, None, 2:], boxes2[None, :, 2:]) |
| wh = (rb - lt).clamp(min=0) |
| inter = wh[:, :, 0] * wh[:, :, 1] |
|
|
| union = area1[:, None] + area2[None, :] - inter |
| return inter / union.clamp(min=1e-6) |
|
|
|
|
| |
| |
| |
| def encode_boxes(gt, anchors): |
| """ |
| ์ ๋ต ๋ฐ์ค(gt)๋ฅผ ์ต์ปค ๊ธฐ์ค ํ๊ท ํ๊น (dx,dy,dw,dh)์ผ๋ก ๋ณํ. |
| ๋คํธ์ํฌ๋ ์ ๋ ์ขํ๊ฐ ์๋๋ผ "์ต์ปค๋ก๋ถํฐ์ ์๋ ๋ณํ"์ ๋ฐฐ์ด๋ค. |
| """ |
| aw = anchors[:, 2] - anchors[:, 0] |
| ah = anchors[:, 3] - anchors[:, 1] |
| ax = anchors[:, 0] + 0.5 * aw |
| ay = anchors[:, 1] + 0.5 * ah |
|
|
| gw = gt[:, 2] - gt[:, 0] |
| gh = gt[:, 3] - gt[:, 1] |
| gx = gt[:, 0] + 0.5 * gw |
| gy = gt[:, 1] + 0.5 * gh |
|
|
| dx = (gx - ax) / aw |
| dy = (gy - ay) / ah |
| dw = torch.log(gw / aw) |
| dh = torch.log(gh / ah) |
| return torch.stack([dx, dy, dw, dh], dim=1) |
|
|
|
|
| def decode_boxes(deltas, anchors): |
| """ |
| ๋คํธ์ํฌ๊ฐ ์์ธกํ (dx,dy,dw,dh)๋ฅผ ์ค์ ๋ฐ์ค ์ขํ๋ก ๋ณต์. |
| encode_boxes์ ์ญ์ฐ์ฐ. |
| """ |
| aw = anchors[:, 2] - anchors[:, 0] |
| ah = anchors[:, 3] - anchors[:, 1] |
| ax = anchors[:, 0] + 0.5 * aw |
| ay = anchors[:, 1] + 0.5 * ah |
|
|
| dx, dy, dw, dh = deltas[:, 0], deltas[:, 1], deltas[:, 2], deltas[:, 3] |
| |
| dw = torch.clamp(dw, max=4.135) |
| dh = torch.clamp(dh, max=4.135) |
|
|
| px = dx * aw + ax |
| py = dy * ah + ay |
| pw = torch.exp(dw) * aw |
| ph = torch.exp(dh) * ah |
|
|
| x1 = px - 0.5 * pw |
| y1 = py - 0.5 * ph |
| x2 = px + 0.5 * pw |
| y2 = py + 0.5 * ph |
| return torch.stack([x1, y1, x2, y2], dim=1) |
|
|
|
|
| def clip_boxes(boxes, img_h, img_w): |
| """๋ฐ์ค๋ฅผ ์ด๋ฏธ์ง ๊ฒฝ๊ณ ์์ผ๋ก ์๋ฅธ๋ค.""" |
| boxes[:, 0].clamp_(min=0, max=img_w) |
| boxes[:, 1].clamp_(min=0, max=img_h) |
| boxes[:, 2].clamp_(min=0, max=img_w) |
| boxes[:, 3].clamp_(min=0, max=img_h) |
| return boxes |
|
|
|
|
| |
| |
| |
| def nms(boxes, scores, iou_thresh=0.7): |
| """ |
| ์ ์ ๋์ ๋ฐ์ค๋ถํฐ ๋จ๊ธฐ๊ณ , ๊ทธ์ ๋ง์ด ๊ฒน์น๋ ๋ฐ์ค๋ ์ ๊ฑฐ. |
| torchvision.ops.nms ๋ฅผ ์จ๋ ๋์ง๋ง, ์๋ฆฌ ํ์ต์ฉ์ผ๋ก ์ง์ ๊ตฌํ. |
| ๋ฐํ: ๋จ๊ธธ ์ธ๋ฑ์ค. |
| """ |
| if boxes.numel() == 0: |
| return torch.empty((0,), dtype=torch.int64) |
|
|
| order = scores.argsort(descending=True) |
| keep = [] |
| while order.numel() > 0: |
| i = order[0].item() |
| keep.append(i) |
| if order.numel() == 1: |
| break |
| ious = box_iou(boxes[i].unsqueeze(0), boxes[order[1:]]).squeeze(0) |
| |
| order = order[1:][ious <= iou_thresh] |
| return torch.tensor(keep, dtype=torch.int64) |
|
|