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