1
+ '''
2
+ Simon Chemnitz Thomson's code to calculate the metric Co-occurence entropy
3
+
4
+ Code is based on the article:
5
+ Quantitative framework for prospective motion correction evaluation
6
+ Nicolas Pannetier, Theano Stavrinos, Peter Ng, Michael Herbst,
7
+ Maxim Zaitsev, Karl Young, Gerald Matson, and Norbert Schuff'''
8
+
9
+ import numpy as np
10
+ from skimage .feature .texture import greycomatrix
11
+ from utils import bin_img , crop_img
12
+
13
+
14
+ def coent3d (img , brainmask = None , n_levels = 128 , bin = True , crop = True , supress_zero = True ):
15
+ '''
16
+ Parameters
17
+ ----------
18
+ img : numpy array
19
+ Image for which the metrics should be calculated.
20
+ n_levels : int
21
+ Levels of intensities to bin image by
22
+ bin : bool
23
+ Whether or not to bin the image
24
+ crop : bool
25
+ Whether or not to crop image/ delete empty slices
26
+ Returns
27
+ -------
28
+ CoEnt : float
29
+ Co-Occurrence Entropy measure of the input image.
30
+ '''
31
+ #Apply brainmask if given one
32
+ if brainmask is not None : #alternative type(brainmask) != type(None)
33
+ img = img * brainmask
34
+ #Crop image if crop is True
35
+ if crop :
36
+ img = crop_img (img )
37
+ #Bin image if bin is True
38
+ if bin :
39
+ img = bin_img (img , n_levels = n_levels )
40
+ #Scale imgage to have intensity values in [0,255]
41
+ img = 255 * (img / np .max (img ))
42
+ #Convert image to uint8
43
+ # as greycomatrix prefers uint8 as input
44
+ img = img .astype (np .uint8 )
45
+
46
+ #Shape of the image/volume
47
+ vol_shape = np .shape (img )
48
+
49
+ #Empty matrix that will be the Co-Occurence matrix
50
+ #Note it is 256x256 as that is the shape of the
51
+ #output of skimage.feature.greycomatrix
52
+ #even though the image is binned
53
+ co_oc_mat = np .zeros ((256 ,256 ))
54
+
55
+
56
+ """
57
+ Note: For 3D encoded images the slice axis does not matter.
58
+ """
59
+ #Generate 2d co-ent matrix for each slice
60
+ for i in range (vol_shape [0 ]):
61
+ #Temporary co-ent matrix
62
+ tmp_co_oc_mat = greycomatrix (img [i ,:,:],
63
+ distances = [1 ],
64
+ angles = [0 * (np .pi / 2 ),
65
+ 1 * (np .pi / 2 ),
66
+ 2 * (np .pi / 2 ),
67
+ 3 * (np .pi / 2 )])
68
+ #greycomatrix will generate 4d array
69
+ #The value P[i,j,d,theta] is the number of times
70
+ #that grey-level j occurs at a distance d and
71
+ #at an angle theta from grey-level i
72
+ #as we only have one distance we just use
73
+ #tmp_co_oc_mat[:,:,0,:]
74
+ #As we want the total occurence not split on angles
75
+ #we sum over axis 2.
76
+ tmp_co_oc_mat = np .sum (tmp_co_oc_mat [:,:,0 ,:], axis = 2 )
77
+ #add the occurrences to the co-entropy matrix
78
+ co_oc_mat = co_oc_mat + tmp_co_oc_mat
79
+
80
+ #Generate 2d co-ent matrix for each slice
81
+ # to capture co-occurrence in the direction we sliced before
82
+ for j in range (vol_shape [1 ]):
83
+ #temporary co-ent matrix
84
+ #note only pi,-pi as angles
85
+ tmp_co_oc_mat = greycomatrix (img [:,j ,:],
86
+ distances = [1 ],
87
+ angles = [1 * (np .pi / 2 ),
88
+ 3 * (np .pi / 2 )])
89
+ #greycomatrix will generate 4d array
90
+ #The value P[i,j,d,theta] is the number of times
91
+ #that grey-level j occurs at a distance d and
92
+ #at an angle theta from grey-level i
93
+ #as we only have one distance we just use
94
+ #tmp_co_oc_mat[:,:,0,:]
95
+ #As we want the total occurence not split on angles
96
+ #we sum over axis 2.
97
+ tmp_co_oc_mat = np .sum (tmp_co_oc_mat [:,:,0 ,:], axis = 2 )
98
+ #add the occurrences to the co-entropy matrix
99
+ co_oc_mat = co_oc_mat + tmp_co_oc_mat
100
+ #Divide by 6 to get average occurance
101
+ co_oc_mat = (1 / 6 )* co_oc_mat
102
+ if supress_zero :
103
+ co_oc_mat [0 ,0 ] = 0
104
+ #Normalise
105
+ co_oc_mat = co_oc_mat / np .sum (co_oc_mat )
106
+ #Take log2 to get entropy
107
+ log_matrix = np .log2 (co_oc_mat )
108
+ #Return the entropy
109
+ return - np .nansum (co_oc_mat * log_matrix )
110
+
111
+
112
+ def coent2d (img , brainmask = None , n_levels = 128 , bin = True , crop = True , supress_zero = True ):
113
+ #Apply brainmask if given one
114
+ if brainmask is not None : #alternative type(brainmask) != type(None)
115
+ img = img * brainmask
116
+ #Crop image if crop is True
117
+ if crop :
118
+ img = crop_img (img )
119
+ #Bin image if bin is True
120
+ if bin :
121
+ img = bin_img (img , n_levels = n_levels )
122
+ #Scale imgage to have intensity values in [0,255]
123
+ img = 255 * (img / np .max (img ))
124
+ #Convert image to uint8
125
+ # as greycomatrix prefers uint8 as input
126
+ img = img .astype (np .uint8 )
127
+
128
+ #Shape of the image/volume
129
+ vol_shape = np .shape (img )
130
+ ents = []
131
+ for slice in range (vol_shape [2 ]):
132
+ tmp_co_oc_mat = greycomatrix (img [:,:,slice ],
133
+ distances = [1 ],
134
+ angles = [0 * (np .pi / 2 ),
135
+ 1 * (np .pi / 2 ),
136
+ 2 * (np .pi / 2 ),
137
+ 3 * (np .pi / 2 )])
138
+ #greycomatrix will generate 4d array
139
+ #The value P[i,j,d,theta] is the number of times
140
+ #that grey-level j occurs at a distance d and
141
+ #at an angle theta from grey-level i
142
+ #as we only have one distance we just use
143
+ #tmp_co_oc_mat[:,:,0,:]
144
+ #As we want the total occurence not split on angles
145
+ #we sum over axis 2.
146
+ tmp_co_oc_mat = np .sum (tmp_co_oc_mat [:,:,0 ,:], axis = 2 )
147
+ if supress_zero :
148
+ tmp_co_oc_mat [0 ,0 ] = 0
149
+ tmp_co_oc_mat = tmp_co_oc_mat / np .sum (tmp_co_oc_mat )
150
+ log_matrix = np .log2 (tmp_co_oc_mat )
151
+
152
+ ents .append ( - np .nansum (tmp_co_oc_mat * log_matrix ) )
153
+
154
+
155
+
156
+ return np .nanmean (ents )
157
+
158
+
159
+ def coent (img , brainmask = None , n_levels = 128 , bin = True , crop = True , supress_zero = True ):
160
+ '''
161
+ Parameters
162
+ ----------
163
+ img : numpy array
164
+ Image for which the metrics should be calculated.
165
+ n_levels : int
166
+ Levels of intensities to bin image by
167
+ bin : bool
168
+ Whether or not to bin the image
169
+ crop : bool
170
+ Whether or not to crop image/ delete empty slices
171
+ Returns
172
+
173
+ -------
174
+ CoEnt : float
175
+ Co-Occurrence Entropy measure of the input image.
176
+ '''
177
+
178
+ #Check which function to use:
179
+
180
+ #Shape of the volume image
181
+ img_vol = np .shape (img )
182
+
183
+ #Working under the assumption that the
184
+ #third axis contains the slices,
185
+ #eg a 2d encoded image
186
+ #would have shape (256,256,k)
187
+ #where k is the slice number
188
+ #additional assumption: 2d encoded seq does not have more than 100 slices
189
+ #and 3d encoded does not have less than 100 slices.
190
+ if img_vol [2 ]< 100 :
191
+ return coent2d (img , brainmask , n_levels , bin , crop , supress_zero )
192
+ else : return coent3d (img , brainmask , n_levels , bin , crop , supress_zero )
0 commit comments