-
Notifications
You must be signed in to change notification settings - Fork 32
/
utils.py
175 lines (162 loc) · 7.38 KB
/
utils.py
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import glob
import matplotlib.patches as patches
import json
import numpy as np
from matplotlib.path import Path
import dicom
from sklearn.utils import shuffle
import cv2
def get_roi(image, contour, shape_out = 32):
"""
Create a binary mask with ROI from contour.
Extract the maximum square around the contour.
:param image: input image (needed for shape only)
:param contour: numpy array contour (d, 2)
:return: numpy array mask ROI (shape_out, shape_out)
"""
X_min, Y_min = contour[:,0].min(), contour[:,1].min()
X_max, Y_max = contour[:,0].max(), contour[:,1].max()
w = X_max - X_min
h = Y_max - Y_min
mask_roi = np.zeros(image.shape)
if w > h :
mask_roi[int(Y_min - (w -h)/2):int(Y_max + (w -h)/2), int(X_min):int(X_max)] = 1.0
else :
mask_roi[int(Y_min):int(Y_max), int(X_min - (h-w)/2):int(X_max + (h -w)/2)] = 1.0
return cv2.resize(mask_roi, (shape_out, shape_out), interpolation = cv2.INTER_NEAREST)
def create_dataset(image_shape=64, n_set='train', original_image_shape=256,
roi_shape=32, data_path='./Data/'):
"""
Creating the dataset from the images and the contour for the CNN.
:param image_shape: image dataset desired size
:param original_image_shape: original image size
:param roi_shape: binary ROI mask shape
:param data_path: path for the dataset
:return: correct size image dataset, full size image dataset, label (contours) dataset
"""
if n_set == 'train':
number_set = 3
name_set = 'Training'
elif n_set == 'test':
number_set = 1
name_set = 'Online'
# Create dataset
series = json.load(open('series_case.json'))[n_set]
images, images_fullsize, contours, contour_mask = [], [], [], []
# Loop over the series
for case, serie in series.items():
image_path_base = data_path + 'challenge_%s/%s/IM-%s' % (name_set.lower(),case, serie)
contour_path_base = data_path + 'Sunnybrook Cardiac MR Database ContoursPart%s/\
%sDataContours/%s/contours-manual/IRCCI-expert/' % (number_set, name_set, case)
contours_list = glob.glob(contour_path_base + '*')
contours_list_series = [k.split('/')[7].split('-')[2] for k in contours_list]
# Loop over the contours/images
for c in contours_list_series:
# Get contours and images path
idx_contour = contours_list_series.index(c)
image_path = image_path_base + '-%s.dcm' % c
contour_path = contours_list[idx_contour]
# open image as numpy array and resize to (image_shape, image_shape)
image_part = dicom.read_file(image_path).pixel_array
# open contours as numpy array
contour = []
file = open(contour_path, 'r')
for line in file:
contour.append(tuple(map(float, line.split())))
contour = np.array(contour)
# append binary ROI mask
contours.append(get_roi(image_part, contour))
# create mask contour with experts contours
x, y = np.meshgrid(np.arange(256), np.arange(256)) # make a canvas with coordinates
x, y = x.flatten(), y.flatten()
points = np.vstack((x,y)).T
p = Path(contour) # make a polygon
grid = p.contains_points(points)
mask_contour = grid.reshape(256,256)
mask_contour=mask_contour*1
contour_mask.append(mask_contour)
# Open image and resize it
images.append(cv2.resize(image_part, (image_shape, image_shape)))
images_fullsize.append(cv2.resize(image_part, (original_image_shape, original_image_shape)))
X_fullsize = np.array(images_fullsize)
X = np.reshape(np.array(images), [len(images), image_shape, image_shape, 1])
Y = np.reshape(np.array(contours), [len(contours), 1, roi_shape, roi_shape])
print('Dataset shape :', X.shape, Y.shape)
return shuffle(X, X_fullsize, Y, contour_mask, random_state=0)
def compute_roi_pred(X_fullsize, y_pred, contour_mask, idx, roi_shape=32):
"""
Computing and cropping a ROI from the original image for further processing in the next stage
:param X_fullsize: full size training set (256x256)
:param y_pred: predictions
:param contour_mask label: (contours) dataset
:param idx: desired image prediction index
:param roi_shape: shape of the binary mask
"""
# up sampling from 32x32 to original MR size
pred = cv2.resize(y_pred[idx].reshape((roi_shape, roi_shape)), (256,256), cv2.INTER_NEAREST)
# select the non null pixels
pos_pred = np.array(np.where(pred > 0.5))
# get the center of the mask
X_min, Y_min = pos_pred[0, :].min(), pos_pred[1, :].min()
X_max, Y_max = pos_pred[0, :].max(), pos_pred[1, :].max()
X_middle = X_min + (X_max - X_min) / 2
Y_middle = Y_min + (Y_max - Y_min) / 2
# Find ROI coordinates
X_top = int(X_middle - 50)
Y_top = int(Y_middle - 50)
X_down = int(X_middle + 50)
Y_down = int(Y_middle + 50)
# crop ROI of size 100x100
mask_roi = np.zeros((256, 256))
mask_roi = cv2.rectangle(mask_roi, (X_top, Y_top), (X_down, Y_down), 1, -1)*255
return X_fullsize[idx][X_top:X_down, Y_top:Y_down], mask_roi, contour_mask[idx][X_top:X_down, Y_top:Y_down]
def prediction_plot(X, model, idx=None):
"""
Compute the Inferred shape binary mask using the trained stacked AE model
:param X: dataset to predict
:param model: trained AE model
:param idx: index of the particular picture to return
:return: inferred shape binary mask, infered shape on the MR image
"""
if not idx:
idx= np.random.randint(len(X))
contours = model.predict(X)
contour = contours[idx].reshape((64,64))
# thresholding
binary = cv2.threshold(contour, 0, 1, cv2.INTERSECT_NONE)
return binary[1], binary[1]*X[idx].reshape(64,64), idx
def dice_metric(X, Y):
"""
Dice metric for measuring the contour overlap
:param X,Y: 2D numpy arrays
:return: metric scalar
"""
return np.sum(X[Y==1])*2.0 / (np.sum(X) + np.sum(Y))
def conformity_coefficient(X, Y):
"""
Conformity coefficient for measuring the ratio of the number of mis-segmented pixels
:param X,Y: 2D numpy arrays
:return: metric scalar
"""
if dice_metric(X,Y) !=0:
return (3*dice_metric(X,Y)-2)/dice_metric(X,Y)
def stats_results(Y_true, Y_pred, idx=None, print_=False):
"""
DM and CC for the whole dataset. If one index is passed, return DM and CC for this index
:param Y_true: True labels (Y_train)
:param Y_pred: Prediction (binarys)
:param print_: Boolean to print stats
:return: DM and CC arrays
"""
dm_tot = np.array([dice_metric(Y_true[k].reshape((64,64)), Y_pred[k]) for k in range(len(Y_true))])
cc_tot = np.array([conformity_coefficient(Y_true[k].reshape((64,64)), Y_pred[k]) for k in range(len(Y_true))])
cc_tot = cc_tot[cc_tot != None]
#return dm_tot, cc_tot
if idx:
print('For image %s :\nDice Metric : %.2f\nConformity Coefficient : %.2f\n' % (
idx, dice_metric(Y_true[idx].reshape((64,64)), Y_pred[idx]),
conformity_coefficient(Y_true[idx].reshape((64,64)), Y_pred[idx])))
if print_:
print('For the full dataset :\nDice Metric : %.2f\nConformity Coefficient : %.2f' % (
dm_tot.mean(),cc_tot.mean()))
return dm_tot, cc_tot