|
a |
|
b/data/dataset.py |
|
|
1 |
import os |
|
|
2 |
import numpy as np |
|
|
3 |
import torch |
|
|
4 |
import pandas as pd |
|
|
5 |
from torch.utils.data import Dataset, DataLoader |
|
|
6 |
from torchvision import transforms |
|
|
7 |
from skimage import transform |
|
|
8 |
from utils.Config import opt |
|
|
9 |
import matplotlib.pylab as plt |
|
|
10 |
from sklearn.model_selection import train_test_split |
|
|
11 |
import utils.array_tool as at |
|
|
12 |
import matplotlib.patches as patches |
|
|
13 |
from data.data_utils import read_image |
|
|
14 |
|
|
|
15 |
DSB_BBOX_LABEL_NAMES = ('p') # Pneumonia |
|
|
16 |
|
|
|
17 |
|
|
|
18 |
def inverse_normalize(img): |
|
|
19 |
if opt.caffe_pretrain: |
|
|
20 |
img = img + (np.array([122.7717, 115.9465, 102.9801]).reshape(3, 1, 1)) |
|
|
21 |
return img[::-1, :, :].clip(min=0, max=255) |
|
|
22 |
# approximate un-normalize for visualize |
|
|
23 |
return (img * 0.225 + 0.45).clip(min=0, max=1) * 255 |
|
|
24 |
|
|
|
25 |
"""Transforms: |
|
|
26 |
Data augmentation |
|
|
27 |
""" |
|
|
28 |
class Transform(object): |
|
|
29 |
def __init__(self, img_size): |
|
|
30 |
self.img_size = img_size |
|
|
31 |
|
|
|
32 |
def __call__(self, in_data): |
|
|
33 |
img_id, img, mask = in_data['img_id'], in_data['image'], in_data['mask'] |
|
|
34 |
_, H, W = img.shape |
|
|
35 |
img, mask = preprocess(img, mask, self.img_size) |
|
|
36 |
|
|
|
37 |
return {'img_id': img_id, 'image': img.copy(), 'mask': mask.copy()} |
|
|
38 |
|
|
|
39 |
|
|
|
40 |
def preprocess(img, mask, img_size): |
|
|
41 |
C, H, W = img.shape |
|
|
42 |
img = img / 255. |
|
|
43 |
img = transform.resize(img, (C, img_size, img_size), mode='reflect') |
|
|
44 |
mask = mask.astype(np.float32) |
|
|
45 |
mask = transform.resize(mask, (1, img_size, img_size), mode='reflect') |
|
|
46 |
# both the longer and shorter should be less than |
|
|
47 |
# max_size and min_size |
|
|
48 |
if opt.caffe_pretrain: |
|
|
49 |
normalize = caffe_normalize |
|
|
50 |
else: |
|
|
51 |
normalize = pytorch_normalze |
|
|
52 |
|
|
|
53 |
img = normalize(img) |
|
|
54 |
|
|
|
55 |
return img, mask |
|
|
56 |
|
|
|
57 |
def pytorch_normalze(img): |
|
|
58 |
""" |
|
|
59 |
https://discuss.pytorch.org/t/how-to-preprocess-input-for-pre-trained-networks/683 |
|
|
60 |
https://github.com/pytorch/vision/issues/223 |
|
|
61 |
return appr -1~1 RGB |
|
|
62 |
""" |
|
|
63 |
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], |
|
|
64 |
std=[0.229, 0.224, 0.225]) |
|
|
65 |
img = normalize(torch.from_numpy(img)) |
|
|
66 |
return img.numpy() |
|
|
67 |
|
|
|
68 |
|
|
|
69 |
def caffe_normalize(img): |
|
|
70 |
""" |
|
|
71 |
return appr -125-125 BGR |
|
|
72 |
""" |
|
|
73 |
img = img[[2, 1, 0], :, :] # RGB-BGR |
|
|
74 |
img = img * 255 |
|
|
75 |
mean = np.array([122.7717, 115.9465, 102.9801]).reshape(3, 1, 1) |
|
|
76 |
img = (img - mean).astype(np.float32, copy=True) |
|
|
77 |
return img |
|
|
78 |
|
|
|
79 |
class RSNADataset(Dataset): |
|
|
80 |
def __init__(self, root_dir, img_id, mask_id, transform=True): |
|
|
81 |
""" |
|
|
82 |
Args: |
|
|
83 |
:param root_dir (string): Directory with all the images |
|
|
84 |
:param img_id (list): lists of image id |
|
|
85 |
:param train: if equals true, then read training set, so the output is image, mask and imgId |
|
|
86 |
if equals false, then read testing set, so the output is image and imgId |
|
|
87 |
:param transform (callable, optional): Optional transform to be applied on a sample |
|
|
88 |
""" |
|
|
89 |
self.root_dir = root_dir |
|
|
90 |
self.img_id = img_id |
|
|
91 |
self.mask_id = mask_id |
|
|
92 |
self.transform = transform |
|
|
93 |
self.tsf = Transform(opt.img_size) |
|
|
94 |
|
|
|
95 |
def __len__(self): |
|
|
96 |
return len(self.img_id) |
|
|
97 |
|
|
|
98 |
def __getitem__(self, idx): |
|
|
99 |
img_path = os.path.join(self.root_dir, 'images', self.img_id[idx].split('.')[0], 'image.png') |
|
|
100 |
mask_path = os.path.join(self.root_dir, 'masks', self.mask_id[idx]) |
|
|
101 |
image = read_image(img_path, np.float32, False) |
|
|
102 |
mask = read_image(mask_path, np.uint8, False) |
|
|
103 |
|
|
|
104 |
sample = {'img_id': self.img_id[idx], 'image':image.copy(), 'mask':mask.copy()} |
|
|
105 |
|
|
|
106 |
if self.transform: |
|
|
107 |
sample = self.tsf(sample) |
|
|
108 |
|
|
|
109 |
return sample |
|
|
110 |
|
|
|
111 |
|
|
|
112 |
class RSNADatasetTest(Dataset): |
|
|
113 |
def __init__(self, root_dir, transform=True): |
|
|
114 |
""" |
|
|
115 |
Args: |
|
|
116 |
:param root_dir (string): Directory with all the images |
|
|
117 |
:param img_id (list): lists of image id |
|
|
118 |
:param train: if equals true, then read training set, so the output is image, mask and imgId |
|
|
119 |
if equals false, then read testing set, so the output is image and imgId |
|
|
120 |
:param transform (callable, optional): Optional transform to be applied on a sample |
|
|
121 |
""" |
|
|
122 |
self.root_dir = root_dir |
|
|
123 |
self.img_id = os.listdir(root_dir) |
|
|
124 |
self.transform = transform |
|
|
125 |
self.tsf = Transform(opt.img_size) |
|
|
126 |
|
|
|
127 |
def __len__(self): |
|
|
128 |
return len(self.img_id) |
|
|
129 |
|
|
|
130 |
def __getitem__(self, idx): |
|
|
131 |
img_path = os.path.join(self.root_dir, self.img_id[idx], 'image.png') |
|
|
132 |
image = read_image(img_path, np.float32, False) |
|
|
133 |
|
|
|
134 |
C, H, W = image.shape |
|
|
135 |
image = image / 255. |
|
|
136 |
image = transform.resize(image, (C, opt.img_size, opt.img_size), mode='reflect') |
|
|
137 |
if opt.caffe_pretrain: |
|
|
138 |
normalize = caffe_normalize |
|
|
139 |
else: |
|
|
140 |
normalize = pytorch_normalze |
|
|
141 |
|
|
|
142 |
image = normalize(image) |
|
|
143 |
|
|
|
144 |
sample = {'img_id': self.img_id[idx], 'image': image.copy()} |
|
|
145 |
|
|
|
146 |
return sample |
|
|
147 |
|
|
|
148 |
def get_train_loader(root_dir, batch_size=16, shuffle=False, num_workers=4, pin_memory=False): |
|
|
149 |
|
|
|
150 |
"""Utility function for loading and returning training and validation Dataloader |
|
|
151 |
:param root_dir: the root directory of data set |
|
|
152 |
:param batch_size: batch size of training and validation set |
|
|
153 |
:param split: if split data set to training set and validation set |
|
|
154 |
:param shuffle: if shuffle the image in training and validation set |
|
|
155 |
:param num_workers: number of workers loading the data, when using CUDA, set to 1 |
|
|
156 |
:param val_ratio: ratio of validation set size |
|
|
157 |
:param pin_memory: store data in CPU pin buffer rather than memory. when using CUDA, set to True |
|
|
158 |
:return: |
|
|
159 |
- train_loader: Dataloader for training |
|
|
160 |
""" |
|
|
161 |
img_ids = os.listdir(root_dir) |
|
|
162 |
img_ids.sort() |
|
|
163 |
transformed_dataset = RSNADataset(root_dir=root_dir, img_id=img_ids, transform=True) |
|
|
164 |
dataloader = DataLoader(transformed_dataset, batch_size=batch_size, |
|
|
165 |
shuffle=shuffle, num_workers=num_workers, pin_memory=pin_memory) |
|
|
166 |
return dataloader |
|
|
167 |
|
|
|
168 |
def get_train_val_loader(root_dir, batch_size=16, val_ratio=0.2, shuffle=False, num_workers=4, pin_memory=False): |
|
|
169 |
|
|
|
170 |
"""Utility function for loading and returning training and validation Dataloader |
|
|
171 |
:param root_dir: the root directory of data set |
|
|
172 |
:param batch_size: batch size of training and validation set |
|
|
173 |
:param split: if split data set to training set and validation set |
|
|
174 |
:param shuffle: if shuffle the image in training and validation set |
|
|
175 |
:param num_workers: number of workers loading the data, when using CUDA, set to 1 |
|
|
176 |
:param val_ratio: ratio of validation set size |
|
|
177 |
:param pin_memory: store data in CPU pin buffer rather than memory. when using CUDA, set to True |
|
|
178 |
:return: |
|
|
179 |
- train_loader: Dataloader for training |
|
|
180 |
- valid_loader: Dataloader for validation |
|
|
181 |
""" |
|
|
182 |
df = pd.read_csv(os.path.join(opt.root_dir, 'train.csv')) |
|
|
183 |
img_id, mask_id = list(df['image']), list(df['label']) |
|
|
184 |
train_img_id, val_img_id, train_mask_id, val_mask_id = train_test_split(img_id, mask_id, random_state=42, test_size=val_ratio, shuffle=False) |
|
|
185 |
|
|
|
186 |
train_dataset = RSNADataset(root_dir=root_dir, img_id=train_img_id, mask_id=train_mask_id, transform=True) |
|
|
187 |
train_loader = DataLoader(train_dataset, batch_size=batch_size, |
|
|
188 |
shuffle=shuffle, num_workers=num_workers, pin_memory=pin_memory) |
|
|
189 |
val_dataset = RSNADataset(root_dir=root_dir, img_id=val_img_id, mask_id=val_mask_id, transform=True) |
|
|
190 |
val_loader = DataLoader(val_dataset, batch_size=batch_size, |
|
|
191 |
shuffle=shuffle, num_workers=num_workers, pin_memory=pin_memory) |
|
|
192 |
|
|
|
193 |
return train_loader, val_loader |
|
|
194 |
|
|
|
195 |
def get_test_loader(batch_size=16, shuffle=False, num_workers=4, pin_memory=False): |
|
|
196 |
|
|
|
197 |
"""Utility function for loading and returning training and validation Dataloader |
|
|
198 |
:param root_dir: the root directory of data set |
|
|
199 |
:param batch_size: batch size of training and validation set |
|
|
200 |
:param shuffle: if shuffle the image in training and validation set |
|
|
201 |
:param num_workers: number of workers loading the data, when using CUDA, set to 1 |
|
|
202 |
:param pin_memory: store data in CPU pin buffer rather than memory. when using CUDA, set to True |
|
|
203 |
:return: |
|
|
204 |
- testloader: Dataloader of all the test set |
|
|
205 |
""" |
|
|
206 |
transformed_dataset = RSNADatasetTest(root_dir=opt.test_root) |
|
|
207 |
testloader = DataLoader(transformed_dataset, batch_size=batch_size, |
|
|
208 |
shuffle=shuffle, num_workers=num_workers, pin_memory=pin_memory) |
|
|
209 |
return testloader |
|
|
210 |
|
|
|
211 |
def show_batch_train(sample_batched): |
|
|
212 |
""" |
|
|
213 |
Visualize one training image and its corresponding bbox |
|
|
214 |
""" |
|
|
215 |
img_id, image, mask = sample_batched['img_id'], sample_batched['image'], sample_batched['mask'] |
|
|
216 |
image, mask = np.squeeze(at.tonumpy(image)), np.squeeze(at.tonumpy(mask)) |
|
|
217 |
|
|
|
218 |
image = inverse_normalize(image) |
|
|
219 |
|
|
|
220 |
combined = np.multiply(image, mask) |
|
|
221 |
ax1 = plt.subplot(121) |
|
|
222 |
ax1.imshow(image / 255.) |
|
|
223 |
ax1.set_title(img_id[0]) |
|
|
224 |
ax2 = plt.subplot(122) |
|
|
225 |
ax2.imshow(combined / 255.) |
|
|
226 |
ax2.set_title(img_id[0]) |
|
|
227 |
plt.show() |
|
|
228 |
|
|
|
229 |
def show_batch_test(sample_batch): |
|
|
230 |
img_id, image = sample_batch['img_id'], sample_batch['image'] |
|
|
231 |
image = inverse_normalize(np.squeeze(at.tonumpy(image[0]))) |
|
|
232 |
plt.figure() |
|
|
233 |
plt.imshow(image/255) |
|
|
234 |
plt.show() |
|
|
235 |
|
|
|
236 |
|
|
|
237 |
if __name__ == '__main__': |
|
|
238 |
|
|
|
239 |
# Load training & validation set |
|
|
240 |
# train_loader, val_loader = get_train_val_loader(opt.root_dir, batch_size=1, val_ratio=0.2, |
|
|
241 |
# shuffle=False, num_workers=opt.num_workers, |
|
|
242 |
# pin_memory=opt.pin_memory) |
|
|
243 |
# for i_batch, sample in enumerate(val_loader): |
|
|
244 |
# print(sample['img_id'], ', ', sample['image'].shape, ', ', sample['mask'].shape) |
|
|
245 |
# show_batch_train(sample) |
|
|
246 |
|
|
|
247 |
test_loader = get_test_loader(batch_size=1, shuffle=False, |
|
|
248 |
num_workers=opt.num_workers, |
|
|
249 |
pin_memory=opt.pin_memory) |
|
|
250 |
for i_batch, sample in enumerate(test_loader): |
|
|
251 |
print(sample['img_id'], ', ', sample['image'].shape) |
|
|
252 |
show_batch_test(sample) |