Download box_utils.py from YMmim/object-detection-scratch: direct link, hf CLI and curl.
- Browser
- Download file 5.85 kB
-
https://huggingface.co/YMmim/object-detection-scratch/resolve/main/box_utils.py
- Command line
-
hf download hf://YMmim/object-detection-scratch/box_utils.py
-
curl -L -o box_utils.py https://huggingface.co/YMmim/object-detection-scratch/resolve/main/box_utils.py
5.85 kB
| """ | |
| Faster R-CNN ๋ฐ๋ฐ๋ฅ ๊ตฌํ โ [2/5] ๋ฐ์ค ์ฐ์ฐ ์ ํธ | |
| ================================================== | |
| ๊ฐ์ฒดํ์ง์ ์ํ์ ํต์ฌ์ด ๋ชจ๋ ์ฌ๊ธฐ์ ์๋ค. | |
| 1) ์ต์ปค(anchor) ์์ฑ : ๊ฒฉ์๋ง๋ค ์ฌ๋ฌ ํฌ๊ธฐยท๋น์จ์ ๊ธฐ์ค ๋ฐ์ค๋ฅผ ๊น๋ค | |
| 2) IoU : ๋ ๋ฐ์ค๊ฐ ์ผ๋ง๋ ๊ฒน์น๋๊ฐ | |
| 3) ๋ฐ์ค ์ธ์ฝ๋ฉ/๋์ฝ๋ฉ : (๋ฐ์ค โ ํ๊ท ํ๊น) / (์์ธก๊ฐ โ ๋ฐ์ค) | |
| 4) NMS : ๊ฒน์น๋ ์ค๋ณต ์์ธก์ ์ ๊ฑฐ | |
| ์ด ํ์ผ๋ง ์ดํดํ๋ฉด Faster R-CNN์ ์ ๋ฐ์ ์ดํดํ ๊ฒ์ด๋ค. | |
| """ | |
| import torch | |
| # --------------------------------------------------------------- | |
| # 1) ์ต์ปค ์์ฑ | |
| # --------------------------------------------------------------- | |
| 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) # [K,4] | |
| # [K,1,4] + [1,A,4] โ [K,A,4] โ [K*A,4] | |
| anchors = shifts[:, None, :] + base_anchors[None, :, :] | |
| return anchors.reshape(-1, 4) | |
| # --------------------------------------------------------------- | |
| # 2) IoU (Intersection over Union) | |
| # --------------------------------------------------------------- | |
| 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) | |
| # --------------------------------------------------------------- | |
| # 3) ๋ฐ์ค ์ธ์ฝ๋ฉ / ๋์ฝ๋ฉ | |
| # --------------------------------------------------------------- | |
| 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,dh ํญ์ฃผ ๋ฐฉ์ง ํด๋จํ | |
| 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 | |
| # --------------------------------------------------------------- | |
| # 4) NMS (Non-Maximum Suppression) | |
| # --------------------------------------------------------------- | |
| 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) | |