mediml 0.9.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- MEDiml/MEDscan.py +1696 -0
- MEDiml/__init__.py +21 -0
- MEDiml/biomarkers/BatchExtractor.py +806 -0
- MEDiml/biomarkers/BatchExtractorTexturalFilters.py +840 -0
- MEDiml/biomarkers/__init__.py +16 -0
- MEDiml/biomarkers/diagnostics.py +125 -0
- MEDiml/biomarkers/get_oriented_bound_box.py +158 -0
- MEDiml/biomarkers/glcm.py +1602 -0
- MEDiml/biomarkers/gldzm.py +523 -0
- MEDiml/biomarkers/glrlm.py +1315 -0
- MEDiml/biomarkers/glszm.py +555 -0
- MEDiml/biomarkers/int_vol_hist.py +527 -0
- MEDiml/biomarkers/intensity_histogram.py +615 -0
- MEDiml/biomarkers/local_intensity.py +89 -0
- MEDiml/biomarkers/morph.py +1756 -0
- MEDiml/biomarkers/ngldm.py +780 -0
- MEDiml/biomarkers/ngtdm.py +414 -0
- MEDiml/biomarkers/stats.py +373 -0
- MEDiml/biomarkers/utils.py +389 -0
- MEDiml/filters/TexturalFilter.py +299 -0
- MEDiml/filters/__init__.py +9 -0
- MEDiml/filters/apply_filter.py +134 -0
- MEDiml/filters/gabor.py +215 -0
- MEDiml/filters/laws.py +283 -0
- MEDiml/filters/log.py +147 -0
- MEDiml/filters/mean.py +121 -0
- MEDiml/filters/textural_filters_kernels.py +1738 -0
- MEDiml/filters/utils.py +107 -0
- MEDiml/filters/wavelet.py +237 -0
- MEDiml/learning/DataCleaner.py +198 -0
- MEDiml/learning/DesignExperiment.py +480 -0
- MEDiml/learning/FSR.py +667 -0
- MEDiml/learning/Normalization.py +112 -0
- MEDiml/learning/RadiomicsLearner.py +714 -0
- MEDiml/learning/Results.py +2237 -0
- MEDiml/learning/Stats.py +694 -0
- MEDiml/learning/__init__.py +10 -0
- MEDiml/learning/cleaning_utils.py +107 -0
- MEDiml/learning/ml_utils.py +1015 -0
- MEDiml/processing/__init__.py +6 -0
- MEDiml/processing/compute_suv_map.py +121 -0
- MEDiml/processing/discretisation.py +149 -0
- MEDiml/processing/interpolation.py +275 -0
- MEDiml/processing/resegmentation.py +66 -0
- MEDiml/processing/segmentation.py +912 -0
- MEDiml/utils/__init__.py +25 -0
- MEDiml/utils/batch_patients.py +45 -0
- MEDiml/utils/create_radiomics_table.py +131 -0
- MEDiml/utils/data_frame_export.py +42 -0
- MEDiml/utils/find_process_names.py +16 -0
- MEDiml/utils/get_file_paths.py +34 -0
- MEDiml/utils/get_full_rad_names.py +21 -0
- MEDiml/utils/get_institutions_from_ids.py +16 -0
- MEDiml/utils/get_patient_id_from_scan_name.py +22 -0
- MEDiml/utils/get_patient_names.py +26 -0
- MEDiml/utils/get_radiomic_names.py +27 -0
- MEDiml/utils/get_scan_name_from_rad_name.py +22 -0
- MEDiml/utils/image_reader_SITK.py +37 -0
- MEDiml/utils/image_volume_obj.py +22 -0
- MEDiml/utils/imref.py +340 -0
- MEDiml/utils/initialize_features_names.py +62 -0
- MEDiml/utils/inpolygon.py +159 -0
- MEDiml/utils/interp3.py +43 -0
- MEDiml/utils/json_utils.py +78 -0
- MEDiml/utils/mode.py +31 -0
- MEDiml/utils/parse_contour_string.py +58 -0
- MEDiml/utils/save_MEDscan.py +30 -0
- MEDiml/utils/strfind.py +32 -0
- MEDiml/utils/textureTools.py +188 -0
- MEDiml/utils/texture_features_names.py +115 -0
- MEDiml/utils/write_radiomics_csv.py +47 -0
- MEDiml/wrangling/DataManager.py +1724 -0
- MEDiml/wrangling/ProcessDICOM.py +512 -0
- MEDiml/wrangling/__init__.py +3 -0
- mediml-0.9.9.dist-info/LICENSE.md +674 -0
- mediml-0.9.9.dist-info/METADATA +232 -0
- mediml-0.9.9.dist-info/RECORD +78 -0
- mediml-0.9.9.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Dict, Tuple, Union
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_matrix(roi_only:np.ndarray,
|
|
12
|
+
levels:np.ndarray,
|
|
13
|
+
dist_correction: bool=False) -> Tuple[np.ndarray, np.ndarray]:
|
|
14
|
+
"""This function computes the Neighborhood Gray-Tone Difference Matrix
|
|
15
|
+
(NGTDM) of the region of interest (ROI) of an input volume. The input
|
|
16
|
+
volume is assumed to be isotropically resampled. The ngtdm is computed
|
|
17
|
+
using 26-voxel connectivity. To account for discretization length
|
|
18
|
+
differences, all averages around a center voxel are performed such that
|
|
19
|
+
the neighbours at a distance of :math:`\sqrt{3}` voxels are given a weight of
|
|
20
|
+
:math:`\sqrt{3}`, and the neighbours at a distance of :math:`\sqrt{2}` voxels are given a
|
|
21
|
+
weight of :math:`\sqrt{2}`.
|
|
22
|
+
This matrix refers to "Neighbourhood grey tone difference based features" (ID = IPET)
|
|
23
|
+
in the `IBSI1 reference manual <https://arxiv.org/pdf/1612.07003.pdf>`_.
|
|
24
|
+
|
|
25
|
+
Note:
|
|
26
|
+
This function is compatible with 2D analysis (language not adapted in the text)
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
roi_only (ndarray): Smallest box containing the ROI, with the imaging data ready
|
|
30
|
+
for texture analysis computations. Voxels outside the ROI are set to NaNs.
|
|
31
|
+
levels (ndarray): Vector containing the quantized gray-levels in the tumor region
|
|
32
|
+
(or reconstruction ``levels`` of quantization).
|
|
33
|
+
dist_correction (bool, optional): Set this variable to true in order to use
|
|
34
|
+
discretization length difference corrections as used by the `Institute of Physics and
|
|
35
|
+
Engineering in Medicine <https://doi.org/10.1088/0031-9155/60/14/5471>`_.
|
|
36
|
+
Set this variable to false to replicate IBSI results.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Tuple[np.ndarray, np.ndarray]:
|
|
40
|
+
- ngtdm: Neighborhood Gray-Tone Difference Matrix of ``roi_only'``.
|
|
41
|
+
- count_valid: Array of number of valid voxels used in the ngtdm computation.
|
|
42
|
+
|
|
43
|
+
REFERENCES:
|
|
44
|
+
[1] Amadasun, M., & King, R. (1989). Textural Features Corresponding to
|
|
45
|
+
Textural Properties. IEEE Transactions on Systems Man and Cybernetics,
|
|
46
|
+
19(5), 1264–1274.
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
# PARSING "dist_correction" ARGUMENT
|
|
51
|
+
if type(dist_correction) is not bool:
|
|
52
|
+
# The user did not input either "true" or "false",
|
|
53
|
+
# so the default behavior is used.
|
|
54
|
+
dist_correction = True # By default
|
|
55
|
+
|
|
56
|
+
# PRELIMINARY
|
|
57
|
+
if np.size(np.shape(roi_only)) == 2: # generalization to 2D inputs
|
|
58
|
+
two_d = 1
|
|
59
|
+
else:
|
|
60
|
+
two_d = 0
|
|
61
|
+
|
|
62
|
+
roi_only = np.pad(roi_only, [1, 1], 'constant', constant_values=np.NaN)
|
|
63
|
+
|
|
64
|
+
# # QUANTIZATION EFFECTS CORRECTION
|
|
65
|
+
# # In case (for example) we initially wanted to have 64 levels, but due to
|
|
66
|
+
# # quantization, only 60 resulted.
|
|
67
|
+
unique_vol = levels.astype('int')
|
|
68
|
+
NL = np.size(levels)
|
|
69
|
+
temp = roi_only.copy().astype('int')
|
|
70
|
+
for i in range(1, NL+1):
|
|
71
|
+
roi_only[temp == unique_vol[i-1]] = i
|
|
72
|
+
|
|
73
|
+
# INTIALIZATION
|
|
74
|
+
ngtdm = np.zeros(NL)
|
|
75
|
+
count_valid = np.zeros(NL)
|
|
76
|
+
|
|
77
|
+
# COMPUTATION OF ngtdm
|
|
78
|
+
if two_d:
|
|
79
|
+
indices = np.where(~np.isnan(np.reshape(
|
|
80
|
+
roi_only, np.size(roi_only), order='F')))[0]
|
|
81
|
+
pos_valid = np.unravel_index(indices, np.shape(roi_only), order='F')
|
|
82
|
+
n_valid_temp = np.size(pos_valid[0])
|
|
83
|
+
w4 = 1
|
|
84
|
+
if dist_correction:
|
|
85
|
+
# Weights given to different neighbors to correct
|
|
86
|
+
# for discretization length differences
|
|
87
|
+
w8 = 1/np.sqrt(2)
|
|
88
|
+
else:
|
|
89
|
+
w8 = 1
|
|
90
|
+
|
|
91
|
+
weights = np.array([w8, w4, w8, w4, w4, w4, w8, w4, w8])
|
|
92
|
+
|
|
93
|
+
for n in range(1, n_valid_temp+1):
|
|
94
|
+
|
|
95
|
+
neighbours = roi_only[(pos_valid[0][n-1]-1):(pos_valid[0][n-1]+2),
|
|
96
|
+
(pos_valid[1][n-1]-1):(pos_valid[1][n-1]+2)].copy()
|
|
97
|
+
neighbours = np.reshape(neighbours, 9, order='F')
|
|
98
|
+
neighbours = neighbours*weights
|
|
99
|
+
value = neighbours[4].astype('int')
|
|
100
|
+
neighbours[4] = np.NaN
|
|
101
|
+
neighbours = neighbours/np.sum(weights[~np.isnan(neighbours)])
|
|
102
|
+
neighbours = np.delete(neighbours, 4) # Remove the center voxel
|
|
103
|
+
# Thus only excluding voxels with NaNs only as neighbors.
|
|
104
|
+
if np.size(neighbours[~np.isnan(neighbours)]) > 0:
|
|
105
|
+
ngtdm[value-1] = ngtdm[value-1] + np.abs(
|
|
106
|
+
value-np.sum(neighbours[~np.isnan(neighbours)]))
|
|
107
|
+
count_valid[value-1] = count_valid[value-1] + 1
|
|
108
|
+
else:
|
|
109
|
+
|
|
110
|
+
indices = np.where(~np.isnan(np.reshape(
|
|
111
|
+
roi_only, np.size(roi_only), order='F')))[0]
|
|
112
|
+
pos_valid = np.unravel_index(indices, np.shape(roi_only), order='F')
|
|
113
|
+
n_valid_temp = np.size(pos_valid[0])
|
|
114
|
+
w6 = 1
|
|
115
|
+
if dist_correction:
|
|
116
|
+
# Weights given to different neighbors to correct
|
|
117
|
+
# for discretization length differences
|
|
118
|
+
w26 = 1 / np.sqrt(3)
|
|
119
|
+
w18 = 1 / np.sqrt(2)
|
|
120
|
+
else:
|
|
121
|
+
w26 = 1
|
|
122
|
+
w18 = 1
|
|
123
|
+
|
|
124
|
+
weights = np.array([w26, w18, w26, w18, w6, w18, w26, w18, w26, w18,
|
|
125
|
+
w6, w18, w6, w6, w6, w18, w6, w18, w26, w18,
|
|
126
|
+
w26, w18, w6, w18, w26, w18, w26])
|
|
127
|
+
|
|
128
|
+
for n in range(1, n_valid_temp+1):
|
|
129
|
+
neighbours = roi_only[(pos_valid[0][n-1]-1) : (pos_valid[0][n-1]+2),
|
|
130
|
+
(pos_valid[1][n-1]-1) : (pos_valid[1][n-1]+2),
|
|
131
|
+
(pos_valid[2][n-1]-1) : (pos_valid[2][n-1]+2)].copy()
|
|
132
|
+
neighbours = np.reshape(neighbours, 27, order='F')
|
|
133
|
+
neighbours = neighbours * weights
|
|
134
|
+
value = neighbours[13].astype('int')
|
|
135
|
+
neighbours[13] = np.NaN
|
|
136
|
+
neighbours = neighbours / np.sum(weights[~np.isnan(neighbours)])
|
|
137
|
+
neighbours = np.delete(neighbours, 13) # Remove the center voxel
|
|
138
|
+
# Thus only excluding voxels with NaNs only as neighbors.
|
|
139
|
+
if np.size(neighbours[~np.isnan(neighbours)]) > 0:
|
|
140
|
+
ngtdm[value-1] = ngtdm[value-1] + np.abs(value - np.sum(neighbours[~np.isnan(neighbours)]))
|
|
141
|
+
count_valid[value-1] = count_valid[value-1] + 1
|
|
142
|
+
|
|
143
|
+
return ngtdm, count_valid
|
|
144
|
+
|
|
145
|
+
def extract_all(vol: np.ndarray,
|
|
146
|
+
dist_correction :Union[bool, str]=None) -> Dict:
|
|
147
|
+
"""Compute Neighbourhood grey tone difference based features.
|
|
148
|
+
These features refer to "Neighbourhood grey tone difference based features" (ID = IPET) in
|
|
149
|
+
the `IBSI1 reference manual <https://arxiv.org/pdf/1612.07003.pdf>`__.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
|
|
153
|
+
vol (ndarray): 3D volume, isotropically resampled, quantized
|
|
154
|
+
(e.g. n_g = 32, levels = [1, ..., n_g]), with NaNs outside the region
|
|
155
|
+
of interest.
|
|
156
|
+
dist_correction (Union[bool, str], optional): Set this variable to true in order to use
|
|
157
|
+
discretization length difference corrections as used
|
|
158
|
+
by the `Institute of Physics and Engineering in
|
|
159
|
+
Medicine <https://doi.org/10.1088/0031-9155/60/14/5471>`__.
|
|
160
|
+
Set this variable to false to replicate IBSI results.
|
|
161
|
+
Or use string and specify the norm for distance weighting.
|
|
162
|
+
Weighting is only performed if this argument is
|
|
163
|
+
"manhattan", "euclidean" or "chebyshev".
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Dict: Dict of Neighbourhood grey tone difference based features.
|
|
167
|
+
"""
|
|
168
|
+
ngtdm_features = {'Fngt_coarseness': [],
|
|
169
|
+
'Fngt_contrast': [],
|
|
170
|
+
'Fngt_busyness': [],
|
|
171
|
+
'Fngt_complexity': [],
|
|
172
|
+
'Fngt_strength': []}
|
|
173
|
+
|
|
174
|
+
# GET THE NGTDM MATRIX
|
|
175
|
+
# Correct definition, without any assumption
|
|
176
|
+
levels = np.arange(1, np.max(vol[~np.isnan(vol[:])].astype("int"))+1)
|
|
177
|
+
ngtdm, count_valid = get_matrix(vol, levels, dist_correction)
|
|
178
|
+
|
|
179
|
+
n_tot = np.sum(count_valid)
|
|
180
|
+
# Now representing the probability of gray-level occurences
|
|
181
|
+
count_valid = count_valid/n_tot
|
|
182
|
+
nl = np.size(ngtdm)
|
|
183
|
+
n_g = np.sum(count_valid != 0)
|
|
184
|
+
p_valid = np.where(np.reshape(count_valid, np.size(
|
|
185
|
+
count_valid), order='F') > 0)[0]+1
|
|
186
|
+
n_valid = np.size(p_valid)
|
|
187
|
+
|
|
188
|
+
# COMPUTING TEXTURES
|
|
189
|
+
# Coarseness
|
|
190
|
+
coarseness = 1 / np.matmul(np.transpose(count_valid), ngtdm)
|
|
191
|
+
coarseness = min(coarseness, 10**6)
|
|
192
|
+
ngtdm_features['Fngt_coarseness'] = coarseness
|
|
193
|
+
|
|
194
|
+
# Contrast
|
|
195
|
+
if n_g == 1:
|
|
196
|
+
ngtdm_features['Fngt_contrast'] = 0
|
|
197
|
+
else:
|
|
198
|
+
val = 0
|
|
199
|
+
for i in range(1, nl+1):
|
|
200
|
+
for j in range(1, nl+1):
|
|
201
|
+
val = val + count_valid[i-1] * count_valid[j-1] * ((i-j)**2)
|
|
202
|
+
ngtdm_features['Fngt_contrast'] = val * np.sum(ngtdm) / (n_g*(n_g-1)*n_tot)
|
|
203
|
+
|
|
204
|
+
# Busyness
|
|
205
|
+
if n_g == 1:
|
|
206
|
+
ngtdm_features['Fngt_busyness'] = 0
|
|
207
|
+
else:
|
|
208
|
+
denom = 0
|
|
209
|
+
for i in range(1, n_valid+1):
|
|
210
|
+
for j in range(1, n_valid+1):
|
|
211
|
+
denom = denom + np.abs(p_valid[i-1]*count_valid[p_valid[i-1]-1] -
|
|
212
|
+
p_valid[j-1]*count_valid[p_valid[j-1]-1])
|
|
213
|
+
ngtdm_features['Fngt_busyness'] = np.matmul(np.transpose(count_valid), ngtdm) / denom
|
|
214
|
+
|
|
215
|
+
# Complexity
|
|
216
|
+
val = 0
|
|
217
|
+
for i in range(1, n_valid+1):
|
|
218
|
+
for j in range(1, n_valid+1):
|
|
219
|
+
val = val + (np.abs(
|
|
220
|
+
p_valid[i-1]-p_valid[j-1]) / (n_tot*(
|
|
221
|
+
count_valid[p_valid[i-1]-1] +
|
|
222
|
+
count_valid[p_valid[j-1]-1])))*(
|
|
223
|
+
count_valid[p_valid[i-1]-1]*ngtdm[p_valid[i-1]-1] +
|
|
224
|
+
count_valid[p_valid[j-1]-1]*ngtdm[p_valid[j-1]-1])
|
|
225
|
+
|
|
226
|
+
ngtdm_features['Fngt_complexity'] = val
|
|
227
|
+
|
|
228
|
+
# Strength
|
|
229
|
+
if np.sum(ngtdm) == 0:
|
|
230
|
+
ngtdm_features['Fngt_strength'] = 0
|
|
231
|
+
else:
|
|
232
|
+
val = 0
|
|
233
|
+
for i in range(1, n_valid+1):
|
|
234
|
+
for j in range(1, n_valid+1):
|
|
235
|
+
val = val + (count_valid[p_valid[i-1]-1] + count_valid[p_valid[j-1]-1])*(
|
|
236
|
+
p_valid[i-1]-p_valid[j-1])**2
|
|
237
|
+
|
|
238
|
+
ngtdm_features['Fngt_strength'] = val/np.sum(ngtdm)
|
|
239
|
+
|
|
240
|
+
return ngtdm_features
|
|
241
|
+
|
|
242
|
+
def get_single_matrix(vol: np.ndarray,
|
|
243
|
+
dist_correction = None)-> Tuple[np.ndarray,
|
|
244
|
+
np.ndarray]:
|
|
245
|
+
"""Compute neighbourhood grey tone difference matrix in order to compute the single features.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
|
|
249
|
+
vol (ndarray): 3D volume, isotropically resampled, quantized
|
|
250
|
+
(e.g. n_g = 32, levels = [1, ..., n_g]), with NaNs outside the region of interest.
|
|
251
|
+
dist_correction (Union[bool, str], optional): Set this variable to true in order to use
|
|
252
|
+
discretization length difference corrections as used
|
|
253
|
+
by the `Institute of Physics and Engineering in
|
|
254
|
+
Medicine <https://doi.org/10.1088/0031-9155/60/14/5471>`__.
|
|
255
|
+
Set this variable to false to replicate IBSI results.
|
|
256
|
+
Or use string and specify the norm for distance weighting.
|
|
257
|
+
Weighting is only performed if this argument is
|
|
258
|
+
"manhattan", "euclidean" or "chebyshev".
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
np.ndarray: array of neighbourhood grey tone difference matrix
|
|
262
|
+
"""
|
|
263
|
+
# GET THE NGTDM MATRIX
|
|
264
|
+
# Correct definition, without any assumption
|
|
265
|
+
levels = np.arange(1, np.max(vol[~np.isnan(vol[:])].astype("int"))+1)
|
|
266
|
+
|
|
267
|
+
ngtdm, count_valid = get_matrix(vol, levels, dist_correction)
|
|
268
|
+
|
|
269
|
+
return ngtdm, count_valid
|
|
270
|
+
|
|
271
|
+
def coarseness(ngtdm: np.ndarray, count_valid: np.ndarray)-> float:
|
|
272
|
+
"""
|
|
273
|
+
Computes coarseness feature.
|
|
274
|
+
This feature refers to "Coarseness" (ID = QCDE) in
|
|
275
|
+
the `IBSI1 reference manual <https://arxiv.org/pdf/1612.07003.pdf>`__.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
ngtdm (ndarray): array of neighbourhood grey tone difference matrix
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
float: coarseness value
|
|
282
|
+
|
|
283
|
+
"""
|
|
284
|
+
n_tot = np.sum(count_valid)
|
|
285
|
+
count_valid = count_valid/n_tot
|
|
286
|
+
coarseness = 1 / np.matmul(np.transpose(count_valid), ngtdm)
|
|
287
|
+
coarseness = min(coarseness, 10**6)
|
|
288
|
+
return coarseness
|
|
289
|
+
|
|
290
|
+
def contrast(ngtdm: np.ndarray, count_valid: np.ndarray)-> float:
|
|
291
|
+
"""
|
|
292
|
+
Computes contrast feature.
|
|
293
|
+
This feature refers to "Contrast" (ID = 65HE) in
|
|
294
|
+
the `IBSI1 reference manual <https://arxiv.org/pdf/1612.07003.pdf>`__.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
ngtdm (ndarray): array of neighbourhood grey tone difference matrix
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
float: contrast value
|
|
301
|
+
|
|
302
|
+
"""
|
|
303
|
+
n_tot = np.sum(count_valid)
|
|
304
|
+
count_valid = count_valid/n_tot
|
|
305
|
+
nl = np.size(ngtdm)
|
|
306
|
+
n_g = np.sum(count_valid != 0)
|
|
307
|
+
|
|
308
|
+
if n_g == 1:
|
|
309
|
+
return 0
|
|
310
|
+
else:
|
|
311
|
+
val = 0
|
|
312
|
+
for i in range(1, nl+1):
|
|
313
|
+
for j in range(1, nl+1):
|
|
314
|
+
val = val + count_valid[i-1] * count_valid[j-1] * ((i-j)**2)
|
|
315
|
+
contrast = val * np.sum(ngtdm) / (n_g*(n_g-1)*n_tot)
|
|
316
|
+
return contrast
|
|
317
|
+
|
|
318
|
+
def busyness(ngtdm: np.ndarray, count_valid: np.ndarray)-> float:
|
|
319
|
+
"""
|
|
320
|
+
Computes busyness feature.
|
|
321
|
+
This feature refers to "Busyness" (ID = NQ30) in
|
|
322
|
+
the `IBSI1 reference manual <https://arxiv.org/pdf/1612.07003.pdf>`__.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
ngtdm (ndarray): array of neighbourhood grey tone difference matrix
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
float: busyness value
|
|
329
|
+
|
|
330
|
+
"""
|
|
331
|
+
n_tot = np.sum(count_valid)
|
|
332
|
+
count_valid = count_valid/n_tot
|
|
333
|
+
n_g = np.sum(count_valid != 0)
|
|
334
|
+
p_valid = np.where(np.reshape(count_valid, np.size(
|
|
335
|
+
count_valid), order='F') > 0)[0]+1
|
|
336
|
+
n_valid = np.size(p_valid)
|
|
337
|
+
|
|
338
|
+
if n_g == 1:
|
|
339
|
+
busyness = 0
|
|
340
|
+
return busyness
|
|
341
|
+
else:
|
|
342
|
+
denom = 0
|
|
343
|
+
for i in range(1, n_valid+1):
|
|
344
|
+
for j in range(1, n_valid+1):
|
|
345
|
+
denom = denom + np.abs(p_valid[i-1]*count_valid[p_valid[i-1]-1] -
|
|
346
|
+
p_valid[j-1]*count_valid[p_valid[j-1]-1])
|
|
347
|
+
busyness = np.matmul(np.transpose(count_valid), ngtdm) / denom
|
|
348
|
+
return busyness
|
|
349
|
+
|
|
350
|
+
def complexity(ngtdm: np.ndarray, count_valid: np.ndarray)-> float:
|
|
351
|
+
"""
|
|
352
|
+
Computes complexity feature.
|
|
353
|
+
This feature refers to "Complexity" (ID = HDEZ) in
|
|
354
|
+
the `IBSI1 reference manual <https://arxiv.org/pdf/1612.07003.pdf>`__.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
ngtdm (ndarray): array of neighbourhood grey tone difference matrix
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
float: complexity value
|
|
361
|
+
|
|
362
|
+
"""
|
|
363
|
+
n_tot = np.sum(count_valid)
|
|
364
|
+
# Now representing the probability of gray-level occurences
|
|
365
|
+
count_valid = count_valid/n_tot
|
|
366
|
+
p_valid = np.where(np.reshape(count_valid, np.size(
|
|
367
|
+
count_valid), order='F') > 0)[0]+1
|
|
368
|
+
n_valid = np.size(p_valid)
|
|
369
|
+
|
|
370
|
+
val = 0
|
|
371
|
+
for i in range(1, n_valid+1):
|
|
372
|
+
for j in range(1, n_valid+1):
|
|
373
|
+
val = val + (np.abs(
|
|
374
|
+
p_valid[i-1]-p_valid[j-1]) / (n_tot*(
|
|
375
|
+
count_valid[p_valid[i-1]-1] +
|
|
376
|
+
count_valid[p_valid[j-1]-1])))*(
|
|
377
|
+
count_valid[p_valid[i-1]-1]*ngtdm[p_valid[i-1]-1] +
|
|
378
|
+
count_valid[p_valid[j-1]-1]*ngtdm[p_valid[j-1]-1])
|
|
379
|
+
complexity = val
|
|
380
|
+
return complexity
|
|
381
|
+
|
|
382
|
+
def strength(ngtdm: np.ndarray, count_valid: np.ndarray)-> float:
|
|
383
|
+
"""
|
|
384
|
+
Computes strength feature.
|
|
385
|
+
This feature refers to "Strength" (ID = 1X9X) in
|
|
386
|
+
the `IBSI1 reference manual <https://arxiv.org/pdf/1612.07003.pdf>`__.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
ngtdm (ndarray): array of neighbourhood grey tone difference matrix
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
float: strength value
|
|
393
|
+
|
|
394
|
+
"""
|
|
395
|
+
|
|
396
|
+
n_tot = np.sum(count_valid)
|
|
397
|
+
# Now representing the probability of gray-level occurences
|
|
398
|
+
count_valid = count_valid/n_tot
|
|
399
|
+
p_valid = np.where(np.reshape(count_valid, np.size(
|
|
400
|
+
count_valid), order='F') > 0)[0]+1
|
|
401
|
+
n_valid = np.size(p_valid)
|
|
402
|
+
|
|
403
|
+
if np.sum(ngtdm) == 0:
|
|
404
|
+
strength = 0
|
|
405
|
+
return strength
|
|
406
|
+
else:
|
|
407
|
+
val = 0
|
|
408
|
+
for i in range(1, n_valid+1):
|
|
409
|
+
for j in range(1, n_valid+1):
|
|
410
|
+
val = val + (count_valid[p_valid[i-1]-1] + count_valid[p_valid[j-1]-1])*(
|
|
411
|
+
p_valid[i-1]-p_valid[j-1])**2
|
|
412
|
+
|
|
413
|
+
strength = val/np.sum(ngtdm)
|
|
414
|
+
return strength
|