|
a |
|
b/evaluation_metrics.py |
|
|
1 |
import numpy as np |
|
|
2 |
from scipy import ndimage |
|
|
3 |
|
|
|
4 |
|
|
|
5 |
|
|
|
6 |
def binary_dice3d(s,g): |
|
|
7 |
#dice score of two 3D volumes |
|
|
8 |
num=np.sum(np.multiply(s, g)) |
|
|
9 |
denom=s.sum() + g.sum() |
|
|
10 |
if denom==0: |
|
|
11 |
return 1 |
|
|
12 |
else: |
|
|
13 |
return 2.0*num/denom |
|
|
14 |
|
|
|
15 |
|
|
|
16 |
def sensitivity (seg,ground): |
|
|
17 |
#computs false negative rate |
|
|
18 |
num=np.sum(np.multiply(ground, seg )) |
|
|
19 |
denom=np.sum(ground) |
|
|
20 |
if denom==0: |
|
|
21 |
return 1 |
|
|
22 |
else: |
|
|
23 |
return num/denom |
|
|
24 |
|
|
|
25 |
def specificity (seg,ground): |
|
|
26 |
#computes false positive rate |
|
|
27 |
num=np.sum(np.multiply(ground==0, seg ==0)) |
|
|
28 |
denom=np.sum(ground==0) |
|
|
29 |
if denom==0: |
|
|
30 |
return 1 |
|
|
31 |
else: |
|
|
32 |
return num/denom |
|
|
33 |
|
|
|
34 |
|
|
|
35 |
|
|
|
36 |
def border_map(binary_img,neigh): |
|
|
37 |
""" |
|
|
38 |
Creates the border for a 3D image |
|
|
39 |
""" |
|
|
40 |
binary_map = np.asarray(binary_img, dtype=np.uint8) |
|
|
41 |
neigh = neigh |
|
|
42 |
west = ndimage.shift(binary_map, [-1, 0,0], order=0) |
|
|
43 |
east = ndimage.shift(binary_map, [1, 0,0], order=0) |
|
|
44 |
north = ndimage.shift(binary_map, [0, 1,0], order=0) |
|
|
45 |
south = ndimage.shift(binary_map, [0, -1,0], order=0) |
|
|
46 |
top = ndimage.shift(binary_map, [0, 0, 1], order=0) |
|
|
47 |
bottom = ndimage.shift(binary_map, [0, 0, -1], order=0) |
|
|
48 |
cumulative = west + east + north + south + top + bottom |
|
|
49 |
border = ((cumulative < 6) * binary_map) == 1 |
|
|
50 |
return border |
|
|
51 |
|
|
|
52 |
|
|
|
53 |
def border_distance(ref,seg): |
|
|
54 |
""" |
|
|
55 |
This functions determines the map of distance from the borders of the |
|
|
56 |
segmentation and the reference and the border maps themselves |
|
|
57 |
""" |
|
|
58 |
neigh=8 |
|
|
59 |
border_ref = border_map(ref,neigh) |
|
|
60 |
border_seg = border_map(seg,neigh) |
|
|
61 |
oppose_ref = 1 - ref |
|
|
62 |
oppose_seg = 1 - seg |
|
|
63 |
# euclidean distance transform |
|
|
64 |
distance_ref = ndimage.distance_transform_edt(oppose_ref) |
|
|
65 |
distance_seg = ndimage.distance_transform_edt(oppose_seg) |
|
|
66 |
distance_border_seg = border_ref * distance_seg |
|
|
67 |
distance_border_ref = border_seg * distance_ref |
|
|
68 |
return distance_border_ref, distance_border_seg#, border_ref, border_seg |
|
|
69 |
|
|
|
70 |
def Hausdorff_distance(ref,seg): |
|
|
71 |
""" |
|
|
72 |
This functions calculates the average symmetric distance and the |
|
|
73 |
hausdorff distance between a segmentation and a reference image |
|
|
74 |
:return: hausdorff distance and average symmetric distance |
|
|
75 |
""" |
|
|
76 |
ref_border_dist, seg_border_dist = border_distance(ref,seg) |
|
|
77 |
hausdorff_distance = np.max( |
|
|
78 |
[np.max(ref_border_dist), np.max(seg_border_dist)]) |
|
|
79 |
return hausdorff_distance |
|
|
80 |
|
|
|
81 |
|
|
|
82 |
|
|
|
83 |
def DSC_whole(pred, orig_label): |
|
|
84 |
#computes dice for the whole tumor |
|
|
85 |
return binary_dice3d(pred>0,orig_label>0) |
|
|
86 |
|
|
|
87 |
|
|
|
88 |
def DSC_en(pred, orig_label): |
|
|
89 |
#computes dice for enhancing region |
|
|
90 |
return binary_dice3d(pred==4,orig_label==4) |
|
|
91 |
|
|
|
92 |
|
|
|
93 |
def DSC_core(pred, orig_label): |
|
|
94 |
#computes dice for core region |
|
|
95 |
seg_=np.copy(pred) |
|
|
96 |
ground_=np.copy(orig_label) |
|
|
97 |
seg_[seg_==2]=0 |
|
|
98 |
ground_[ground_==2]=0 |
|
|
99 |
return binary_dice3d(seg_>0,ground_>0) |
|
|
100 |
|
|
|
101 |
|
|
|
102 |
|
|
|
103 |
def sensitivity_whole (seg,ground): |
|
|
104 |
return sensitivity(seg>0,ground>0) |
|
|
105 |
|
|
|
106 |
def sensitivity_en (seg,ground): |
|
|
107 |
return sensitivity(seg==4,ground==4) |
|
|
108 |
|
|
|
109 |
def sensitivity_core (seg,ground): |
|
|
110 |
seg_=np.copy(seg) |
|
|
111 |
ground_=np.copy(ground) |
|
|
112 |
seg_[seg_==2]=0 |
|
|
113 |
ground_[ground_==2]=0 |
|
|
114 |
return sensitivity(seg_>0,ground_>0) |
|
|
115 |
|
|
|
116 |
|
|
|
117 |
|
|
|
118 |
def specificity_whole (seg,ground): |
|
|
119 |
return specificity(seg>0,ground>0) |
|
|
120 |
|
|
|
121 |
def specificity_en (seg,ground): |
|
|
122 |
return specificity(seg==4,ground==4) |
|
|
123 |
|
|
|
124 |
def specificity_core (seg,ground): |
|
|
125 |
seg_=np.copy(seg) |
|
|
126 |
ground_=np.copy(ground) |
|
|
127 |
seg_[seg_==2]=0 |
|
|
128 |
ground_[ground_==2]=0 |
|
|
129 |
return specificity(seg_>0,ground_>0) |
|
|
130 |
|
|
|
131 |
|
|
|
132 |
def hausdorff_whole (seg,ground): |
|
|
133 |
return Hausdorff_distance(seg==0,ground==0) |
|
|
134 |
|
|
|
135 |
def hausdorff_en (seg,ground): |
|
|
136 |
return Hausdorff_distance(seg!=4,ground!=4) |
|
|
137 |
|
|
|
138 |
def hausdorff_core (seg,ground): |
|
|
139 |
seg_=np.copy(seg) |
|
|
140 |
ground_=np.copy(ground) |
|
|
141 |
seg_[seg_==2]=0 |
|
|
142 |
ground_[ground_==2]=0 |
|
|
143 |
return Hausdorff_distance(seg_==0,ground_==0) |
|
|
144 |
|
|
|
145 |
|