-
Notifications
You must be signed in to change notification settings - Fork 0
/
metrics.py
177 lines (135 loc) · 5.56 KB
/
metrics.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
176
177
import numpy as np
import torch
import torch.nn.functional as F
import SimpleITK as sitk
from scipy.spatial.distance import directed_hausdorff
def mean_iou(y_true_in, y_pred_in, print_table=False):
if True: #not np.sum(y_true_in.flatten()) == 0:
labels = y_true_in
y_pred = y_pred_in
true_objects = 2
pred_objects = 2
intersection = np.histogram2d(labels.flatten(), y_pred.flatten(), bins=(true_objects, pred_objects))[0]
# Compute areas (needed for finding the union between all objects)
area_true = np.histogram(labels, bins = true_objects)[0]
area_pred = np.histogram(y_pred, bins = pred_objects)[0]
area_true = np.expand_dims(area_true, -1)
area_pred = np.expand_dims(area_pred, 0)
# Compute union
union = area_true + area_pred - intersection
# Exclude background from the analysis
intersection = intersection[1:,1:]
union = union[1:,1:]
union[union == 0] = 1e-9
# Compute the intersection over union
iou = intersection / union
# Precision helper function
def precision_at(threshold, iou):
matches = iou > threshold
true_positives = np.sum(matches, axis=1) == 1 # Correct objects
false_positives = np.sum(matches, axis=0) == 0 # Missed objects
false_negatives = np.sum(matches, axis=1) == 0 # Extra objects
tp, fp, fn = np.sum(true_positives), np.sum(false_positives), np.sum(false_negatives)
return tp, fp, fn
# Loop over IoU thresholds
prec = []
if print_table:
print("Thresh\tTP\tFP\tFN\tPrec.")
for t in np.arange(0.5, 1.0, 0.05):
tp, fp, fn = precision_at(t, iou)
if (tp + fp + fn) > 0:
p = tp / (tp + fp + fn)
else:
p = 0
if print_table:
print("{:1.3f}\t{}\t{}\t{}\t{:1.3f}".format(t, tp, fp, fn, p))
prec.append(p)
if print_table:
print("AP\t-\t-\t-\t{:1.3f}".format(np.mean(prec)))
return np.mean(prec)
else:
if np.sum(y_pred_in.flatten()) == 0:
return 1
else:
return 0
def batch_iou(output, target):
output = torch.sigmoid(output).data.cpu().numpy() > 0.5
target = (target.data.cpu().numpy() > 0.5).astype('int')
output = output[:,0,:,:]
target = target[:,0,:,:]
ious = []
for i in range(output.shape[0]):
ious.append(mean_iou(output[i], target[i]))
return np.mean(ious)
def mean_iou(output, target):
smooth = 1e-5
output = torch.sigmoid(output).data.cpu().numpy()
target = target.data.cpu().numpy()
ious = []
for t in np.arange(0.5, 1.0, 0.05):
output_ = output > t
target_ = target > t
intersection = (output_ & target_).sum()
union = (output_ | target_).sum()
iou = (intersection + smooth) / (union + smooth)
ious.append(iou)
return np.mean(ious)
def iou_score(output, target):
smooth = 1e-5
if torch.is_tensor(output):
output = torch.sigmoid(output).data.cpu().numpy()
if torch.is_tensor(target):
target = target.data.cpu().numpy()
output_ = output > 0.5
target_ = target > 0.5
intersection = (output_ & target_).sum()
union = (output_ | target_).sum()
return (intersection + smooth) / (union + smooth)
def dice_coef(output, target):
smooth = 1e-5
if torch.is_tensor(output):
output = torch.sigmoid(output).data.cpu().numpy()
if torch.is_tensor(target):
target = target.data.cpu().numpy()
#output = torch.sigmoid(output).view(-1).data.cpu().numpy()
#target = target.view(-1).data.cpu().numpy()
intersection = (output * target).sum()
return (2. * intersection + smooth) / (output.sum() + target.sum() + smooth)
def accuracy_score(output, target):
output = torch.sigmoid(output).view(-1).data.cpu().numpy()
output = (np.round(output)).astype('int')
target = target.view(-1).data.cpu().numpy()
target = (np.round(target)).astype('int')
(output == target).sum()
return (output == target).sum() / len(output)
def ppv_score(output, target):
smooth = 1e-5
if torch.is_tensor(output):
output = torch.sigmoid(output).data.cpu().numpy()
if torch.is_tensor(target):
target = target.data.cpu().numpy()
intersection = (output * target).sum()
return (intersection + smooth) / (output.sum() + smooth)
def sensitivity_score(output, target):
smooth = 1e-5
if torch.is_tensor(output):
output = torch.sigmoid(output).data.cpu().numpy()
if torch.is_tensor(target):
target = target.data.cpu().numpy()
intersection = (output * target).sum()
return (intersection + smooth) / (target.sum() + smooth)
def hausdorff_dist(pred_mask, true_mask, voxel_spacing=None):
# Convert the binary segmentation masks to 3D point sets
pred_pts = np.array(np.nonzero(pred_mask)).T
true_pts = np.array(np.nonzero(true_mask)).T
if voxel_spacing is not None:
# Convert voxel spacing to physical distance
voxel_spacing = np.array(voxel_spacing)
pred_pts *= voxel_spacing
true_pts *= voxel_spacing
# Calculate the directed Hausdorff distance from pred_mask to true_mask
dist1 = directed_hausdorff(pred_pts, true_pts)[0]
# Calculate the directed Hausdorff distance from true_mask to pred_mask
dist2 = directed_hausdorff(true_pts, pred_pts)[0]
# Return the maximum of the two directed Hausdorff distances
return max(dist1, dist2)