Object Detection๊ณผ Segmentation ์์ ํํ ์ฌ์ฉ๋๋ COCO dataformat ์ ์ฉ Customdataset์ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํ๋ค.
ํํ ์๊ณ ์๋ COCO ๋ฐ์ดํฐ์ ์ด ์๊ณ , ๋ง์ ๋ฐ์ดํฐ์ ๋ค์ด COCO data format ์ ๋ฐ๋ฅด๋๋ฐ, ์ด๋ฌํ ๋ฐ์ดํฐ์ ์ ์ฌ์ฉํ๊ธฐ ์ํด Customdataset์ ๊ตฌ์ฑํ๋ ๋ฐฉ๋ฒ๊ณผ COCO API ์ธ Pycocotools ์ฌ์ฉ๋ฒ์ ์ค๋ช ํ๋ค.
COCO Data Format
Detection task์์๋ Bounding box์ ์์น์ class label์ด ํ์ํ๊ณ segmentation task ์์๋ segment mask ์ ๋ณด๊ฐ ํ์ํ๋ค. ์ด๋ฌํ annotation ์ ๋ณด๋ค์ json ํํ๋ก ์ ๊ณต๋๊ณ , JSON ํ์ผ์๋ Info, License, Images, Categories, Annotations 5๊ฐ์ง ์ ๋ณด๊ฐ ๋ด๊ฒจ ์๋ค. ์ฃผ์ํ ์ ์ ์ด๋ฏธ์ง ํ๋์ ์ฌ๋ฌ ๊ฐ์ฒด๊ฐ ์กด์ฌํ ์ ์์ผ๋ฏ๋ก annotation ์ ๋ณด๋ ์ฌ๋ฌ๊ฐ ์ผ ์ ์. ๋๋ฌธ์ dataset์ ๋ถ๋ฌ์ฌ ๋ ํ๋์ ์ด๋ฏธ์ง์ ํฌํจ๋ ๋ชจ๋ annotation ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๊ณ maskํ ์์ผ์ ground truth ๋ฅผ ๋ง๋๋ ์์ ์ด ํ์ํ๋ค.
<Annotation dictionary>
- image_id : ์ด๋ฏธ์ง ์ธ๋ฑ์ค
- category_id : label ์ ๋ณด
- segmentation : pixel ์ขํ
- bbox : bounding box ์ ๋ณด
- ...๋ฑ๋ฑ
Pycocotools
json ํ์ผ์์ ์ฌ์ฉํด์ผํ ์ ๋ณด๋ ์ด๋ฏธ์ง๋ณ annotation ์ ๋ณด์ธ๋ฐ, ๋ถ์ฐ๋์ด ์๋ ์ ๋ณด๋ฅผ Pycocotools ๋ฅผ ์ฌ์ฉํ์ฌ ํจ์จ์ ์ผ๋ก COCO data format์ data ๋ฅผ ๋ค๋ฃฐ ์ ์๋ค.
*์์ฃผ ์ฌ์ฉํ๋ method
coco = COCO(dataDir)
COCO Data Format Custom Dataset / Dataloaer
dataset_path = '/data' # Dataset ๊ฒฝ๋ก ์ง์ ํ์
train_path = dataset_path + '/train.json'
val_path = dataset_path + '/val.json'
test_path = dataset_path + '/test.json'
import cv2
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from pycocotools.coco import COCO
import matplotlib.pyplot as plt
class COCO_dataformat(Dataset):
def __init__(self, data_dir, mode = 'train', transform = None):
super().__init__()
self.mode = mode
self.transform = transform # transform : albumentations ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
self.coco = COCO(data_dir)
self.cat_ids = self.coco.getCatIds() # category id ๋ฐํ
self.cats = self.coco.loadCats(self.cat_ids) # category id๋ฅผ ์
๋ ฅ์ผ๋ก category name, super category ์ ๋ณด ๋ด๊ธด dict ๋ฐํ
self.classNameList = ['Backgroud'] # class name ์ ์ฅ
for i in range(len(self.cat_ids)):
self.classNameList.append(self.cats[i]['name'])
def __getitem__(self, index: int):
image_id = self.coco.getImgIds(imgIds=index) # img id ๋๋ category id ๋ฅผ ๋ฐ์์ img id ๋ฐํ
image_infos = self.coco.loadImgs(image_id)[0] # img id๋ฅผ ๋ฐ์์ image info ๋ฐํ
# cv2 ๋ฅผ ํ์ฉํ์ฌ image ๋ถ๋ฌ์ค๊ธฐ(BGR -> RGB ๋ณํ -> numpy array ๋ณํ -> normalize(0~1))
images = cv2.imread(os.path.join(dataset_path, image_infos['file_name']))
images = cv2.cvtColor(images, cv2.COLOR_BGR2RGB).astype(np.float32)
images /= 255.0 # albumentations ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก toTensor ์ฌ์ฉ์ normalize ์ํด์ค์ ๋ฏธ๋ฆฌ ํด์ค์ผ~
if (self.mode in ('train', 'val')):
ann_ids = self.coco.getAnnIds(imgIds=image_infos['id']) #img id, category id๋ฅผ ๋ฐ์์ ํด๋นํ๋ annotation id ๋ฐํ
anns = self.coco.loadAnns(ann_ids) # annotation id๋ฅผ ๋ฐ์์ annotation ์ ๋ณด ๋ฐํ
# ์ ์ฅ๋ annotation ์ ๋ณด๋ก label mask ์์ฑ, Background = 0, ๊ฐ pixel ๊ฐ์๋ "category id" ํ ๋น
masks = np.zeros((image_infos["height"], image_infos["width"]))
anns = sorted(anns, key=lambda idx : len(idx['segmentation'][0]), reverse=False)
for i in range(len(anns)): # ์ด๋ฏธ์ง ํ๋์ ์กด์ฌํ๋ annotation ์ํ
pixel_value = anns[i]['category_id'] # ํด๋น ํด๋์ค ์ด๋ฆ์ ์ธ๋ฑ์ค
#className = classNameList[anns[i]['category_id']] # ํด๋์ค ์ด๋ฆ
masks[self.coco.annToMask(anns[i]) == 1] = pixel_value # coco.annToMask(anns) : anns ์ ๋ณด๋ก mask๋ฅผ ์์ฑ / ๊ฐ์ฒด๊ฐ ์๋ ๊ณณ๋ง๋ค ๊ฐ์ฒด์ label์ ํด๋นํ๋ mask ์์ฑ
masks = masks.astype(np.int8)
if self.transform is not None:
transformed = self.transform(image=images, mask=masks)
images = transformed["image"]
masks = transformed["mask"]
return images, masks, image_infos
if self.mode == 'test':
if self.transform is not None:
transformed = self.transform(image=images)
images = transformed["image"]
return images, image_infos
def __len__(self) -> int:
return len(self.coco.getImgIds()) # ์ ์ฒด dataset์ size ๋ฐํ
import albumentations as A
from albumentations.pytorch import ToTensorV2
train_transform = A.Compose([
ToTensorV2()
])
val_transform = A.Compose([
ToTensorV2()
])
test_transform = A.Compose([
ToTensorV2()
])
train_dataset = COCO_dataformat(data_dir=train_path, mode='train', transform=train_transform)
val_dataset = COCO_dataformat(data_dir=val_path, mode='val', transform=val_transform)
test_dataset = COCO_dataformat(data_dir=test_path, mode='test', transform=test_transform)
batch_size = 8
def collate_fn(batch):
return tuple(zip(*batch))
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=4,
collate_fn=collate_fn)
val_loader = torch.utils.data.DataLoader(dataset=val_dataset,
batch_size=batch_size,
shuffle=False,
num_workers=4,
collate_fn=collate_fn)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
num_workers=4,
collate_fn=collate_fn)
Dataloader ๋ฅผ ์ด์ฉํ data ์๊ฐํ
numOfSample = 3
fig, axes = plt.subplots(nrows=numOfSample, ncols=2, figsize=(15, 15))
tmp_imgs = []
tmp_masks = []
coco = COCO('/content/gdrive/My Drive/baseline_code/input/data/train.json')
for imgs, masks, image_infos in train_loader:
for idx in range(numOfSample):
tmp_info = []
for anns in coco.loadAnns(coco.getAnnIds(image_infos[idx]['id'])) :
if [anns['category_id'],train_dataset.classNameList[anns['category_id']]] not in tmp_info:
tmp_info.append([anns['category_id'],train_dataset.classNameList[anns['category_id']]])
print(idx,'๋ฒ์งธ ์ด๋ฏธ์ง์ ํฌํจ๋ labels :',tmp_info)
axes[idx][0].imshow(imgs[idx].permute([1,2,0]))
axes[idx][0].grid(False)
axes[idx][0].set_title("input image : {}".format(image_infos[idx]['file_name']), fontsize = 15)
axes[idx][1].imshow(masks[idx])
axes[idx][1].grid(False)
axes[idx][1].set_title("input image : {}".format(image_infos[idx]['file_name']), fontsize = 15)
break
plt.show()