Image Segmentation - Preparation
Back to : deep-learning-study
์์ผ๋ก ์ด ํ๋ก์ ํธ์์ ์ฌ์ฉํ๋ ์ฝ๋๋ ๋ชจ๋ Github Repo ์ ์ฌ๋ผ๊ฐ ์์ ์ ๋๋ค. ์ค๋์ ๋จผ์ , ๋ฐ์ดํฐ ๋ฑ์ ์ค๋นํ๋ ๊ณผ์ ์ ์งํํฉ๋๋ค.
Data preparation
TU Graz์์ ์ ๊ณตํ๋ Drone aerial image ๋ฐ์ดํฐ๋ฅผ ์ด์ฉํ๋ ค๊ณ ํฉ๋๋ค. ๋งํฌ ์์ ๋ค์ด๋ก๋๋ฐ์ ์ ์์ต๋๋ค. ์ฌ์ง 400์ฅ์ ๋ฐ์ดํฐ์ ์ด์ง๋ง ๊ต์ฅํ ์ฉ๋์ด ํฌ๊ณ (4.1GB, ๊ฐ ์ด๋ฏธ์ง๊ฐ ๋ฌด๋ ค 6000 by 4000 ์ ๋๋ค) pixel-accurateํ ๋ผ๋ฒจ์ด ๋ฌ๋ ค์๋๋ฐ๋ค ํด๋์ค๋ 23๊ฐ๋ก ๋ง์ง ์์์ ์ ๋นํ๋ค๊ณ ์๊ฐํ์ต๋๋ค. ์ฌ๊ธฐ์๋ 360๊ฐ๋ฅผ training์, 40๊ฐ๋ฅผ test์ ์ฐ๊ฒ ์ต๋๋ค.
๋จผ์ , ํ์ํ ๋ชจ๋๋ค์ importํด์ ๋๋ ค๋ฃ์ต๋๋ค. ๋ณ๋ก ์ข์ practice๋ ์๋์ง๋ง, ๋ค์ํ ๋ชจ๋ธ๋ค์ ํ ์คํธํด๋ณด๋ ์๋ฏธ๊ฐ ์์ผ๋ฏ๋ก ์ฝ๋์ ์๋ฆ๋ค์์ ์ ์ ์ ์ด๋๊ธฐ๋ก ํฉ์๋ค. Jupyter Notebook์ด๋ Colab์ ์ฌ์ฉํ๋ค๋ฉด ํจ์ฌ ํธํ๊ฒ ํ ์คํธํ ์ ์๊ฒ ์ง๋ง, ์ ์ฒด๋ฅผ ๊นํ์ ์ฌ๋ ค์ ๋ฐ๋ก ๋ณผ ์ ์๊ฒ ํ๊ธฐ ์ํด ๊ทธ๋ฅ ์ผ๋ฐ ํ์ด์ฌ ์ฝ๋ฉํ ๋์ฒ๋ผ ํ๊ฒ ์ต๋๋ค.
# basics.py
import pandas as pd, numpy as np
import torch, torchvision, PIL.Image, cv2
import os, sys
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as T
import torch.nn.functional as F
from torchsummary import summary
import time
from tqdm import tqdm
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
device ๋ฑ์ ์ฌ์ค ๋ชจ๋ ๋ฅ๋ฌ๋์์ ๊ณตํต์ ์ผ๋ก ์ฐ๋ GPU ์ฝ๋์ด๋ฏ๋ก ๋ณ๋ก ํน๋ณํ ์๋ฏธ๊ฐ ์์ง๋ ์๊ณ , ํน์ดํ ์ ์ mean๊ณผ std์ ๋๋ค. ์ด ๊ฐ์ RGB ๊ฐ ์ฑ๋์ normalizeํ๊ธฐ ์ํ ๊ฐ์ธ๋ฐ์. 0.5๊ฐ ์๋ ์ด์ ๋ ์ด ๊ฐ๋ค์ด ์ฌ์ค ImageNet์์ ํ๋ จ๋ ๊ฒฐ๊ณผ ๊ฐ์ธ๋ฐ, ์์น์ ์ผ๋ก๋ ์๋ก์ด mean๊ณผ std๋ฅผ trainํ๋ ๊ฒ์ด ์๋ฏธ๊ฐ ์๊ฒ ์ง๋ง 100๋ง์ฅ์ ImageNet ๋ฐ์ดํฐ๋ฅผ ๋ฏฟ๊ณ ๊ทธ๋ฅ ์จ๋ ํฐ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
Dataset
pytorch์์ custom dataset์ ์ฌ์ฉํ ๋๋, torch.utils.data.Dataset ํด๋์ค๋ฅผ ๋ง๋ค๋ฉด ๋ฉ๋๋ค.
# datautils.py
from basics import *
class DroneDataset(Dataset):
def __init__(self, img_path, mask_path, X, test=False):
self.img_path = img_path
self.mask_path = mask_path
self.X = X
self.test = test
def __len__(self):
return len(self.X)
def __getitem__(self, idx):
img = cv2.imread(self.img_path + self.X[idx] + '.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, dsize=(600, 400), interpolation=cv2.INTER_NEAREST)
mask = cv2.imread(self.mask_path + self.X[idx] + '.png', cv2.IMREAD_GRAYSCALE)
mask = cv2.resize(mask, dsize=(600, 400), interpolation=cv2.INTER_NEAREST)
t = T.Compose([T.ToTensor(), T.Normalize(mean, std)])
img = t(img)
mask = torch.from_numpy(mask).long()
return img, mask
์ผ๋จ์ data augmentation ๋ฑ์ ์๋ฌด๊ฒ๋ ์๊ฐํ์ง ๋ง๊ณ , ์ ๋ง ์์ํ bare minimum๋ง ์๊ฐํฉ๋๋ค.
๊ฐ๋จํ ํด์ํด๋ณด๋ฉดโฆ
-
__init__
๋ img_path, mask_path ๋ฑ์ ๋ฐ์์ ์ด ๋ฐ์ดํฐ์ ์ ์์น์, ์ด๋ค transform์ ์ ์ฉํ ์ง (transform์ด๋, ์ด๋ฏธ์ง๋ฅผ ํ ์๋ก ๋ฐ๊พธ๋ ์ฐ์ฐ) ๊ธฐ์ตํฉ๋๋ค. -
__getitem__
์data[3]
๊ณผ ๊ฐ์ด ์ฐ๊ธฐ ์ํด์ overrideํ๋ method๋ก, ์ด๋ฏธ์ง๋ฅผ ์ ์ฝ๊ณ ์ ์ ํ๊ฒ ๋ณํํด์ ๋ฑ์ด์ค๋๋ค. - 6000 * 4000์ ์ง์ง ์ข ๋๋ฌด ํฌ๊ธฐ ๋๋ฌธ์, ์ด๋ฏธ์ง ํฌ๊ธฐ๋ 600 * 400์ผ๋ก ์ค์์ต๋๋ค. ์ค์ผ๋๋ NEAREST๋ฅผ ์จ์ผ mask์ ๋ผ๋ฒจ์ด ์ด์ํด์ง์ง ์์ต๋๋ค.
-
test
๋ฐ์ดํฐ์ ๋ํด์๋ Image๋ฅผ ๊ทธ๋๋ก ์ ์ฅํ๊ณ ,training
๋ฐ์ดํฐ์ ๋ํด์๋ ์ด๋ฅผ torch tensor๋ก ๋ฐ๊ฟ์ ์ ์ฅํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ ์ด์ ๋, ๋์ค์ ์ ์ฑ์ ์ผ๋ก segmentation์ ํ๋ฆฌํฐ๋ฅผ ํ์ธํ๊ณ ์ถ์ ๋ ์ด๋ฏธ์ง๋ฅผ ๊ฐ์ด displayํ๋ ค๋ฉด test์ ๋ํด์๋ ์ด๋ฏธ์ง๋ฅผ ๊ฐ๊ณ ์๋๊ฒ ํธํ๊ธฐ ๋๋ฌธ์ด
์ด์ ์ด ํ์ผ์ ์ค์ ๋ชจ๋ธ์ ์ ์ฉํ๊ธฐ ์ํด, training / test ๋ฐ์ดํฐ์
์ผ๋ก ์๋ผ์ค์ผ ํฉ๋๋ค. ์ด๋ฅผ ํธํ๊ฒ ์๋ผ์ฃผ๋ sklearn.model_selection.train_test_split
์ด ์์ต๋๋ค.
# datautils.py
from sklearn.model_selection import train_test_split
def import_drone_dataset():
IMAGE_PATH = "../dataset/semantic_drone_dataset/original_images/"
MASK_PATH = "../dataset/semantic_drone_dataset/label_images_semantic/"
name = []
for dirname, _, filenames in os.walk(IMAGE_PATH):
for filename in filenames:
name.append(filename.split('.')[0])
df = pd.DataFrame({'id': name}, index = np.arange(0, len(name)))
X_train, X_test = train_test_split(df['id'].values, test_size=0.1, random_state=0)
train_set = DroneDataset(IMAGE_PATH, MASK_PATH, X_train, test=False)
test_set = DroneDataset(IMAGE_PATH, MASK_PATH, X_test, test=True)
return train_set, test_set
Evaluation of Model
๋ชจ๋ธ์ ๋ง๋ค๊ธฐ ์ ์ ์ผ๋จ ๋ชจ๋ธ์ด ์๋ค๋ฉด ์ด๋ป๊ฒ ๋์ํด์ผ ํ ์ง๋ฅผ ๋จผ์ ์๊ฐํด ๋ด ๋๋ค. ์ข ์ค๋๋ ๋ง์ด๊ธด ํ์ง๋ง, ๋จธ์ ๋ฌ๋์ ์ ์ํ๋ ๋ฐฉ๋ฒ ์ค ํ๊ฐ์ง๋ T, P, E ๋ผ๊ณ ํด์โฆ
- Task : ์ด๋ค ๋ช ํํ๊ฒ ์ ์๋๋ ์์ ์ ์ํํ๊ณ ์ถ๊ณ ,
- Performance Measure : ํ์ฌ ๊ฐ์ง๊ณ ์๋ ํ๋ก๊ทธ๋จ์ ์ฑ๋ฅ์ ์ธก์ ํ๋ ๋ฐฉ๋ฒ์ด ์์ผ๋ฉฐ,
- Experience : ๋ฐ์ดํฐ๋ก๋ถํฐ ํ๋ก๊ทธ๋จ์ด P๋ฅผ ๋ฐ์ ์ํค๊ธฐ ์ํด ๋ ธ๋ ฅํ๋ค๋ ๊ฒ์ ๋๋ค.
์ฐ๋ฆฌ๋ ์์ง ํ๋ก๊ทธ๋จ์ ์์ฑํ์ง ์์์ง๋ง, semantic segmentation์ด๋ผ๋ T์ ์ง์คํ ๊ฒ์ ๋๋ค. P๋ฅผ ์ด๋ป๊ฒ ํ ์ง๋ ์ด ์์ฒด๋ก๋ ๋ ๋ฆฝ๋ ํฌ์คํ ์ด ํ์ํ๋ฐ, mIoU, Hausdorff distance๋ฑ ์ฌ๋ฐ๋๊ฒ ๋ง์ต๋๋ค. ์ด์ค ๊ฐ์ฅ ์๊ฐํ๊ธฐ ์ฌ์ด ๊ฒ์ ๊ทธ๋ฅ pixel๋จ์๋ก ๋ง์ ํฝ์ ์ / ์ ์ฒด ํฝ์ ์๋ฅผ ์ธ๋ ๊ฒ์ ๋๋ค.
Pytorch์์๋ ๋ชจ๋ธ์ด ์ด๋ค input image๋ฅผ ๋ฐ์์, model(x)
๊ณผ ๊ฐ์ ์์ผ๋ก callํด์ inference๋ฅผ ์งํํฉ๋๋ค. ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ค์ mask์ ๋น๊ตํด์ ์ ํ๋๋ฅผ ์ธก์ ํด์ผ ํฉ๋๋ค.
Bare minimum์ ์ฒ ํ์ ๋ฐ๋ผ ์ผ๋จ pixel accuracy๋ง์ ๊ตฌํํฉ๋๋ค. ๋ค๋ง ๋์ค์ ์ฌ๋ฌ ๋ค๋ฅธ metric์ ๊ตฌํํ ์ ์์์ ์ผ๋์ ๋๊ณ , metrics.py๋ก ๋ฐ๋ก ํ์ผ์ ๋นผ๊ฒ ์ต๋๋ค.
# metrics.py
def pixel_accuracy(output, mask):
with torch.no_grad():
output = torch.argmax(output, dim=1)
correct = torch.eq(output, mask).int()
accuracy = float(correct.sum()) / float(correct.numel())
return accuracy
Pixel accuracy๋ฅผ ๊ณ์ฐํ ๋๋ backpropagation์ฉ gradient๊ฐ ํ์ํ์ง ์์ผ๋ฏ๋ก with torch.no_grad():
๋ก ๊ฐ์ธ์ ์ ๋๋๋ค.
์ด์ , ํธํ๊ฒ ํ ์คํธ๋ฅผ ์ฌ๋ฌ๋ฒ ์๋ํ๊ธฐ ์ํด ํ ์คํธ๋ฅผ ๋๋ฆฌ๋ ํด๋์ค๋ฅผ ๋ฐ๋ก ๋ง๋ค๊ฒ ์ต๋๋ค.
# evaluate.py
from basics import *
class ModelEvaluation():
def __init__(self, model, test_dataset, metric):
self.model = model
self.test_dataset = test_dataset
self.metric = metric
def evaluate_single(self, image, mask):
self.model.eval()
self.model.to(device)
image = image.to(device)
mask = mask.to(device)
with torch.no_grad():
image = image.unsqueeze(0)
mask = mask.unsqueeze(0)
output = self.model(image)
acc = self.metric(output, mask)
masked = torch.argmax(output, dim=1)
masked = masked.cpu().squeeze(0)
return masked, acc
def evaluate_all(self):
accuracy = []
for i in tqdm(range(len(self.test_dataset))):
img, mask = self.test_dataset[i]
pred, acc = self.evaluate_single(img, mask)
accuracy.append(acc)
print(f"Mean accruacy = {np.mean(accuracy)}")
return accuracy
def show_qualitative(self, ind):
image, mask = self.test_dataset[ind]
pred_mask, score = self.evaluate_single(image, mask)
inv_normalize = T.Normalize(
mean=[-0.485/0.229, -0.456/0.224, -0.406/0.225],
std=[1/0.229, 1/0.224, 1/0.225]
)
image = inv_normalize(image)
image = image.cpu().numpy()
image = image.swapaxes(0, 1)
image = image.swapaxes(1, 2)
import matplotlib.pyplot as plt
fig, (ax1, ax2, ax3) = plt.subplots(1,3, figsize=(20,10))
ax1.imshow(image)
ax1.set_title('Picture')
ax2.imshow(mask)
ax2.set_title('Ground truth')
ax2.set_axis_off()
ax3.imshow(pred_mask)
ax3.set_title(f'Model | score {score:.3f}')
ax3.set_axis_off()
plt.show()
-
__init__
์์๋ ์ด๋ค ๋ชจ๋ธ์ ํ ์คํธํ๋์ง, ์ด๋ค ๋ฐ์ดํฐ์ ๋ํด ํ ์คํธํ๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ค metric์ ์ฌ์ฉํ ๊ฒ์ธ์ง๋ฅผ ์ ํฉ๋๋ค. -
evaluate_single()
์ ์ด๋ฏธ์ง ํ ๊ฐ๋ฅผ ๋ฐ์์ ์ด๋ฅผ normalizeํ๋ค์ ์ค์ ๋ก inferenceํด ๋ด ๋๋ค. ๊ฒฐ๊ณผ๋ก predicted mask์ ๊ทธ ์ ํ๋๋ฅผ ๋ฐํํฉ๋๋ค.unsqueeze
๋ ๊ฐ๋จํ ๊ทธ๋ฅ ํ ์๋ฅผ ์ญ ์ก์ํด์ฃผ๋ ์ฐ์ฐ์ผ๋ก ์๊ฐํ๋ฉด ๋ฉ๋๋ค. -
evaluate_all()
์ ํ๊ท ์ ํ๋๋ฅผ ์ธก์ ํฉ๋๋ค. -
show_qualitative()
๋ ๊ฒฐ๊ณผ์ ์ ์ฑ์ ํ๊ฐ๋ฅผ ์ํ ๊ฒ์ผ๋ก, ํน์ ์ด๋ฏธ์ง์ ๋ํ image, ground truth, prediction์ ๋์์ ๋์์ค๋๋ค. ์ค์ ๋ก ์ด๋ฏธ์ง๋ฅผ ๋์์ผ ํ๊ธฐ ๋๋ฌธ์, Dataset์ ๋ง๋ค๋ ToTensor์ Normalizeํ๋ ๊ฒ์ ๋ค์ ๊ฑฐ๊พธ๋ก ๋๋ ค์ค์ผ ํฉ๋๋ค. Normalize์ ์ ์๋ฅผ ์ด์ฉํ์ฌ ์ด๋ถ๋ถ์ ์ ๋นํ ์ฒ๋ฆฌํด์ค ์ ์์ต๋๋ค.
๋ค์ ํฌ์คํ ์์๋ train์ ์ด๋ป๊ฒ ์ค์ ๋ก ์คํํ ์ง์, ์ด๋ฅผ ์ด์ฉํด์ ์์ฃผ ๊ฐ๋จํ ๋ชจ๋ธ์ ํ๋ฒ ํ์ธํด๋ณด๋ ์ ๋๋ฅผ ์งํํ ์์ ์ ๋๋ค.
Back to : deep-learning-study