File size: 5,358 Bytes
017c046 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | """
Faster R-CNN ๋ฐ๋ฐ๋ฅ ๊ตฌํ โ [1/5] ๋ฐ์ดํฐ์
(Pascal VOC)
=========================================================
Pascal VOC 2007/2012 ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด (์ด๋ฏธ์ง, ๋ฐ์ค, ๋ผ๋ฒจ)์ ๋ฐํํ๋ค.
VOC ์ด๋
ธํ
์ด์
์ XML ํ์์ด๋ฉฐ, ๊ฐ ๊ฐ์ฒด๋ง๋ค ๋ค์์ ๋ด๋๋ค:
- name : ํด๋์ค ์ด๋ฆ (์: 'person', 'car')
- bndbox : xmin, ymin, xmax, ymax (์ข์๋จยท์ฐํ๋จ ํฝ์
์ขํ)
- difficult : ํ๋ณ ์ด๋ ค์ด ๊ฐ์ฒด ํ์(ํ์ต ์ ๋ณดํต ์ ์ธ)
ํต์ฌ ๊ฐ๋
:
- ๋ฐ์ค ์ขํ๋ [x1, y1, x2, y2] ์ ๋ ํฝ์
์ขํ๋ก ํต์ผํ๋ค.
- ์ด๋ฏธ์ง๋ฅผ ๋ฆฌ์ฌ์ด์ฆํ๋ฉด ๋ฐ์ค๋ ๊ฐ์ ๋น์จ๋ก ์ค์ผ์ผํด์ผ ํ๋ค.
"""
import os
import xml.etree.ElementTree as ET
import torch
from torch.utils.data import Dataset
from PIL import Image
import torchvision.transforms.functional as F
# Pascal VOC 20๊ฐ ํด๋์ค (์ธ๋ฑ์ค 0์ ๋ฐฐ๊ฒฝ์ผ๋ก ์์ฝ โ ํด๋์ค๋ 1๋ถํฐ)
VOC_CLASSES = [
"aeroplane", "bicycle", "bird", "boat", "bottle",
"bus", "car", "cat", "chair", "cow",
"diningtable", "dog", "horse", "motorbike", "person",
"pottedplant", "sheep", "sofa", "train", "tvmonitor",
]
# ์ด๋ฆ โ ์ธ๋ฑ์ค (๋ฐฐ๊ฒฝ=0 ์ด๋ฏ๋ก +1)
CLASS_TO_IDX = {name: i + 1 for i, name in enumerate(VOC_CLASSES)}
NUM_CLASSES = len(VOC_CLASSES) + 1 # +1 = ๋ฐฐ๊ฒฝ(background)
class VOCDataset(Dataset):
"""
Pascal VOC ๊ฐ์ฒดํ์ง ๋ฐ์ดํฐ์
.
Args:
root: VOCdevkit/VOC2007 (๋๋ VOC2012) ๊ฒฝ๋ก
split: 'train' | 'val' | 'trainval' | 'test'
min_size: ๋ฆฌ์ฌ์ด์ฆ ์ ์ด๋ฏธ์ง ์งง์ ๋ณ์ ๋ชฉํ ๊ธธ์ด
max_size: ๊ธด ๋ณ์ ์ต๋ ๊ธธ์ด(๋น์จ ์ ์งํ๋ฉฐ ์ํ ์ ์ฉ)
keep_difficult: difficult=1 ๊ฐ์ฒด๋ฅผ ํฌํจํ ์ง ์ฌ๋ถ(ํ์ต ์ False ๊ถ์ฅ)
"""
def __init__(self, root, split="trainval", min_size=600, max_size=1000,
keep_difficult=False):
self.root = root
self.min_size = min_size
self.max_size = max_size
self.keep_difficult = keep_difficult
# ImageSets/Main/<split>.txt ์ ์ด๋ฏธ์ง ID ๋ชฉ๋ก์ด ์๋ค.
split_file = os.path.join(root, "ImageSets", "Main", f"{split}.txt")
with open(split_file) as f:
self.ids = [line.strip() for line in f if line.strip()]
def __len__(self):
return len(self.ids)
def _load_annotation(self, img_id):
"""XML์ ํ์ฑํด ๋ฐ์ค์ ๋ผ๋ฒจ์ ๋ฝ๋๋ค."""
ann_path = os.path.join(self.root, "Annotations", f"{img_id}.xml")
tree = ET.parse(ann_path)
boxes, labels = [], []
for obj in tree.findall("object"):
difficult = int(obj.findtext("difficult", "0"))
if difficult and not self.keep_difficult:
continue
name = obj.findtext("name").strip().lower()
if name not in CLASS_TO_IDX:
continue
bnd = obj.find("bndbox")
# VOC ์ขํ๋ 1๋ถํฐ ์์ โ 0-๊ธฐ๋ฐ์ผ๋ก ๋ณด์ (-1)
x1 = float(bnd.findtext("xmin")) - 1
y1 = float(bnd.findtext("ymin")) - 1
x2 = float(bnd.findtext("xmax")) - 1
y2 = float(bnd.findtext("ymax")) - 1
boxes.append([x1, y1, x2, y2])
labels.append(CLASS_TO_IDX[name])
boxes = torch.as_tensor(boxes, dtype=torch.float32).reshape(-1, 4)
labels = torch.as_tensor(labels, dtype=torch.int64)
return boxes, labels
def _resize(self, img, boxes):
"""
์งง์ ๋ณ์ min_size๋ก ๋ง์ถ๋, ๊ธด ๋ณ์ด max_size๋ฅผ ๋์ง ์๋๋ก ์ค์ผ์ผ.
๋ฐ์ค๋ ๊ฐ์ ๋น์จ๋ก ์กฐ์ ํ๋ค. (Faster R-CNN ์๋
ผ๋ฌธ ๋ฐฉ์)
"""
w, h = img.size
short, long = min(w, h), max(w, h)
scale = self.min_size / short
if long * scale > self.max_size:
scale = self.max_size / long
new_w, new_h = int(round(w * scale)), int(round(h * scale))
img = img.resize((new_w, new_h), Image.BILINEAR)
if boxes.numel() > 0:
boxes = boxes * scale # ๋ฐ์ค๋ ๋์ผ ๋ฐฐ์จ ์ ์ฉ
return img, boxes, scale
def __getitem__(self, idx):
img_id = self.ids[idx]
img_path = os.path.join(self.root, "JPEGImages", f"{img_id}.jpg")
img = Image.open(img_path).convert("RGB")
boxes, labels = self._load_annotation(img_id)
img, boxes, scale = self._resize(img, boxes)
# ํ
์ ๋ณํ + ImageNet ์ ๊ทํ(๋ฐฑ๋ณธ์ด ImageNet ์ฌ์ ํ์ต์ด๋ฏ๋ก)
img = F.to_tensor(img)
img = F.normalize(img,
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
target = {
"boxes": boxes, # [N,4] ์ ๋ ํฝ์
(๋ฆฌ์ฌ์ด์ฆ ํ)
"labels": labels, # [N] 1..20 (0=๋ฐฐ๊ฒฝ)
"image_id": img_id,
"scale": scale, # ํ๊ฐ ์ ์๋ณธ ์ขํ๋ก ๋๋๋ฆด ๋ ์ฌ์ฉ
}
return img, target
def collate_fn(batch):
"""
์ด๋ฏธ์ง๋ง๋ค ํฌ๊ธฐ๊ฐ ๋ฌ๋ผ ๊ธฐ๋ณธ collate๋ก ๋ชป ๋ฌถ๋๋ค.
๋ฆฌ์คํธ ํํ๋ก ๊ทธ๋๋ก ๋๊ธฐ๊ณ , ๋ชจ๋ธ ๋ด๋ถ์์ ์ฒ๋ฆฌํ๋ค.
(๊ฐ๋จํ๋ฅผ ์ํด batch_size=1 ์ฌ์ฉ์ ๊ถ์ฅ)
"""
imgs, targets = list(zip(*batch))
return list(imgs), list(targets)
|