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,16 @@
|
|
|
1
|
+
from . import *
|
|
2
|
+
from .BatchExtractor import *
|
|
3
|
+
from .BatchExtractorTexturalFilters import *
|
|
4
|
+
from .diagnostics import *
|
|
5
|
+
from .get_oriented_bound_box import *
|
|
6
|
+
from .glcm import *
|
|
7
|
+
from .gldzm import *
|
|
8
|
+
from .glrlm import *
|
|
9
|
+
from .glszm import *
|
|
10
|
+
from .int_vol_hist import *
|
|
11
|
+
from .intensity_histogram import *
|
|
12
|
+
from .local_intensity import *
|
|
13
|
+
from .morph import *
|
|
14
|
+
from .ngldm import *
|
|
15
|
+
from .ngtdm import *
|
|
16
|
+
from .stats import *
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from ..processing.segmentation import compute_bounding_box
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def extract_all(vol_obj: np.ndarray,
|
|
9
|
+
roi_obj_int: np.ndarray,
|
|
10
|
+
roi_obj_morph: np.ndarray,
|
|
11
|
+
im_type: str) -> Dict:
|
|
12
|
+
"""Computes diagnostic features
|
|
13
|
+
|
|
14
|
+
The diagnostic features help identify issues with
|
|
15
|
+
the implementation of the image processing sequence.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
vol_obj (ndarray): Imaging data.
|
|
19
|
+
roi_obj_int (ndarray): Intensity mask data.
|
|
20
|
+
roi_obj_morph (ndarray): Morphological mask data.
|
|
21
|
+
im_type (str): Image processing step.
|
|
22
|
+
|
|
23
|
+
- 'reSeg': Computes Diagnostic features right after the re-segmentaion step.
|
|
24
|
+
- 'interp' or any other arg: Computes Diagnostic features for any processing step other than re-segmentation.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Dict: Dictionnary containing the computed features.
|
|
28
|
+
"""
|
|
29
|
+
diag = {}
|
|
30
|
+
|
|
31
|
+
# FOR THE IMAGE
|
|
32
|
+
|
|
33
|
+
if im_type != 'reSeg':
|
|
34
|
+
# Image dimension x
|
|
35
|
+
diag.update({'image_' + im_type + '_dimX':
|
|
36
|
+
vol_obj.spatialRef.ImageSize[0]})
|
|
37
|
+
|
|
38
|
+
# Image dimension y
|
|
39
|
+
diag.update({'image_' + im_type + '_dimY':
|
|
40
|
+
vol_obj.spatialRef.ImageSize[1]})
|
|
41
|
+
|
|
42
|
+
# Image dimension z
|
|
43
|
+
diag.update({'image_' + im_type + '_dimz':
|
|
44
|
+
vol_obj.spatialRef.ImageSize[2]})
|
|
45
|
+
|
|
46
|
+
# Voxel dimension x
|
|
47
|
+
diag.update({'image_' + im_type + '_voxDimX':
|
|
48
|
+
vol_obj.spatialRef.PixelExtentInWorldX})
|
|
49
|
+
|
|
50
|
+
# Voxel dimension y
|
|
51
|
+
diag.update({'image_' + im_type + '_voxDimY':
|
|
52
|
+
vol_obj.spatialRef.PixelExtentInWorldY})
|
|
53
|
+
|
|
54
|
+
# Voxel dimension z
|
|
55
|
+
diag.update({'image_' + im_type + '_voxDimZ':
|
|
56
|
+
vol_obj.spatialRef.PixelExtentInWorldZ})
|
|
57
|
+
|
|
58
|
+
# Mean intensity
|
|
59
|
+
diag.update({'image_' + im_type + '_meanInt': np.mean(vol_obj.data)})
|
|
60
|
+
|
|
61
|
+
# Minimum intensity
|
|
62
|
+
diag.update({'image_' + im_type + '_minInt': np.min(vol_obj.data)})
|
|
63
|
+
|
|
64
|
+
# Maximum intensity
|
|
65
|
+
diag.update({'image_' + im_type + '_maxInt': np.max(vol_obj.data)})
|
|
66
|
+
|
|
67
|
+
# FOR THE ROI
|
|
68
|
+
box_bound_int = compute_bounding_box(roi_obj_int.data)
|
|
69
|
+
box_bound_morph = compute_bounding_box(roi_obj_morph.data)
|
|
70
|
+
|
|
71
|
+
x_gl_int = vol_obj.data[roi_obj_int.data == 1]
|
|
72
|
+
x_gl_morph = vol_obj.data[roi_obj_morph.data == 1]
|
|
73
|
+
|
|
74
|
+
# Map dimension x
|
|
75
|
+
diag.update({'roi_' + im_type + '_Int_dimX':
|
|
76
|
+
roi_obj_int.spatialRef.ImageSize[0]})
|
|
77
|
+
|
|
78
|
+
# Map dimension y
|
|
79
|
+
diag.update({'roi_' + im_type + '_Int_dimY':
|
|
80
|
+
roi_obj_int.spatialRef.ImageSize[1]})
|
|
81
|
+
|
|
82
|
+
# Map dimension z
|
|
83
|
+
diag.update({'roi_' + im_type + '_Int_dimZ':
|
|
84
|
+
roi_obj_int.spatialRef.ImageSize[2]})
|
|
85
|
+
|
|
86
|
+
# Bounding box dimension x
|
|
87
|
+
diag.update({'roi_' + im_type + '_Int_boxBoundDimX':
|
|
88
|
+
box_bound_int[0, 1] - box_bound_int[0, 0] + 1})
|
|
89
|
+
|
|
90
|
+
# Bounding box dimension y
|
|
91
|
+
diag.update({'roi_' + im_type + '_Int_boxBoundDimY':
|
|
92
|
+
box_bound_int[1, 1] - box_bound_int[1, 0] + 1})
|
|
93
|
+
|
|
94
|
+
# Bounding box dimension z
|
|
95
|
+
diag.update({'roi_' + im_type + '_Int_boxBoundDimZ':
|
|
96
|
+
box_bound_int[2, 1] - box_bound_int[2, 0] + 1})
|
|
97
|
+
|
|
98
|
+
# Bounding box dimension x
|
|
99
|
+
diag.update({'roi_' + im_type + '_Morph_boxBoundDimX':
|
|
100
|
+
box_bound_morph[0, 1] - box_bound_morph[0, 0] + 1})
|
|
101
|
+
|
|
102
|
+
# Bounding box dimension y
|
|
103
|
+
diag.update({'roi_' + im_type + '_Morph_boxBoundDimY':
|
|
104
|
+
box_bound_morph[1, 1] - box_bound_morph[1, 0] + 1})
|
|
105
|
+
|
|
106
|
+
# Bounding box dimension z
|
|
107
|
+
diag.update({'roi_' + im_type + '_Morph_boxBoundDimZ':
|
|
108
|
+
box_bound_morph[2, 1] - box_bound_morph[2, 0] + 1})
|
|
109
|
+
|
|
110
|
+
# Voxel number
|
|
111
|
+
diag.update({'roi_' + im_type + '_Int_voxNumb': np.size(x_gl_int)})
|
|
112
|
+
|
|
113
|
+
# Voxel number
|
|
114
|
+
diag.update({'roi_' + im_type + '_Morph_voxNumb': np.size(x_gl_morph)})
|
|
115
|
+
|
|
116
|
+
# Mean intensity
|
|
117
|
+
diag.update({'roi_' + im_type + '_meanInt': np.mean(x_gl_int)})
|
|
118
|
+
|
|
119
|
+
# Minimum intensity
|
|
120
|
+
diag.update({'roi_' + im_type + '_minInt': np.min(x_gl_int)})
|
|
121
|
+
|
|
122
|
+
# Maximum intensity
|
|
123
|
+
diag.update({'roi_' + im_type + '_maxInt': np.max(x_gl_int)})
|
|
124
|
+
|
|
125
|
+
return diag
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def rot_matrix(theta: float,
|
|
12
|
+
dim: int=2,
|
|
13
|
+
rot_axis: int=-1) -> np.ndarray:
|
|
14
|
+
"""Creates a 2d or 3d rotation matrix
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
theta (float): angle in radian
|
|
18
|
+
dim (int, optional): dimension size. Defaults to 2.
|
|
19
|
+
rot_axis (int, optional): rotation axis value. Defaults to -1.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
ndarray: rotation matrix
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
if dim == 2:
|
|
26
|
+
rot_mat = np.array([[np.cos(theta), -np.sin(theta)],
|
|
27
|
+
[np.sin(theta), np.cos(theta)]])
|
|
28
|
+
|
|
29
|
+
elif dim == 3:
|
|
30
|
+
if rot_axis == 0:
|
|
31
|
+
rot_mat = np.array([[1.0, 0.0, 0.0],
|
|
32
|
+
[0.0, np.cos(theta), -np.sin(theta)],
|
|
33
|
+
[0.0, np.sin(theta), np.cos(theta)]])
|
|
34
|
+
elif rot_axis == 1:
|
|
35
|
+
rot_mat = np.array([[np.cos(theta), 0.0, np.sin(theta)],
|
|
36
|
+
[0.0, 1.0, 0.0],
|
|
37
|
+
[-np.sin(theta), 0.0, np.cos(theta)]])
|
|
38
|
+
elif rot_axis == 2:
|
|
39
|
+
rot_mat = np.array([[np.cos(theta), -np.sin(theta), 0.0],
|
|
40
|
+
[np.sin(theta), np.cos(theta), 0.0],
|
|
41
|
+
[0.0, 0.0, 1.0]])
|
|
42
|
+
else:
|
|
43
|
+
rot_mat = None
|
|
44
|
+
else:
|
|
45
|
+
rot_mat = None
|
|
46
|
+
|
|
47
|
+
return rot_mat
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def sig_proc_segmentise(x: List) -> List:
|
|
51
|
+
"""Produces a list of segments from input x with values (0,1)
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
x (List): list of values
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
List: list of segments from input x with values (0,1)
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
# Create a difference vector
|
|
61
|
+
y = np.diff(x)
|
|
62
|
+
|
|
63
|
+
# Find start and end indices of sections with value 1
|
|
64
|
+
ind_1_start = np.array(np.where(y == 1)).flatten()
|
|
65
|
+
if np.shape(ind_1_start)[0] > 0:
|
|
66
|
+
ind_1_start += 1
|
|
67
|
+
ind_1_end = np.array(np.where(y == -1)).flatten()
|
|
68
|
+
|
|
69
|
+
# Check for boundary effects
|
|
70
|
+
if x[0] == 1:
|
|
71
|
+
ind_1_start = np.insert(ind_1_start, 0, 0)
|
|
72
|
+
if x[-1] == 1:
|
|
73
|
+
ind_1_end = np.append(ind_1_end, np.shape(x)[0]-1)
|
|
74
|
+
|
|
75
|
+
# Generate segment df for segments with value 1
|
|
76
|
+
if np.shape(ind_1_start)[0] == 0:
|
|
77
|
+
df_one = pd.DataFrame({"i": [],
|
|
78
|
+
"j": [],
|
|
79
|
+
"val": []})
|
|
80
|
+
else:
|
|
81
|
+
df_one = pd.DataFrame({"i": ind_1_start,
|
|
82
|
+
"j": ind_1_end,
|
|
83
|
+
"val": np.ones(np.shape(ind_1_start)[0])})
|
|
84
|
+
|
|
85
|
+
# Find start and end indices for section with value 0
|
|
86
|
+
if np.shape(ind_1_start)[0] == 0:
|
|
87
|
+
ind_0_start = np.array([0])
|
|
88
|
+
ind_0_end = np.array([np.shape(x)[0]-1])
|
|
89
|
+
else:
|
|
90
|
+
ind_0_end = ind_1_start - 1
|
|
91
|
+
ind_0_start = ind_1_end + 1
|
|
92
|
+
|
|
93
|
+
# Check for boundary effect
|
|
94
|
+
if x[0] == 0:
|
|
95
|
+
ind_0_start = np.insert(ind_0_start, 0, 0)
|
|
96
|
+
if x[-1] == 0:
|
|
97
|
+
ind_0_end = np.append(ind_0_end, np.shape(x)[0]-1)
|
|
98
|
+
|
|
99
|
+
# Check for out-of-range boundary effects
|
|
100
|
+
if ind_0_end[0] < 0:
|
|
101
|
+
ind_0_end = np.delete(ind_0_end, 0)
|
|
102
|
+
if ind_0_start[-1] >= np.shape(x)[0]:
|
|
103
|
+
ind_0_start = np.delete(ind_0_start, -1)
|
|
104
|
+
|
|
105
|
+
# Generate segment df for segments with value 0
|
|
106
|
+
if np.shape(ind_0_start)[0] == 0:
|
|
107
|
+
df_zero = pd.DataFrame({"i": [],
|
|
108
|
+
"j": [],
|
|
109
|
+
"val": []})
|
|
110
|
+
else:
|
|
111
|
+
df_zero = pd.DataFrame({"i": ind_0_start,
|
|
112
|
+
"j": ind_0_end,
|
|
113
|
+
"val": np.zeros(np.shape(ind_0_start)[0])})
|
|
114
|
+
|
|
115
|
+
df_segm = df_one.append(df_zero).sort_values(by="i").reset_index(drop=True)
|
|
116
|
+
|
|
117
|
+
return df_segm
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def sig_proc_find_peaks(x: float,
|
|
121
|
+
ddir: str="pos") -> pd.DataFrame:
|
|
122
|
+
"""Determines peak positions in array of values
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
x (float): value
|
|
126
|
+
ddir (str, optional): positive or negative value. Defaults to "pos".
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
pd.DataFrame: peak positions in array of values
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
# Invert when looking for local minima
|
|
133
|
+
if ddir == "neg":
|
|
134
|
+
x = -x
|
|
135
|
+
|
|
136
|
+
# Generate segments where slope is negative
|
|
137
|
+
|
|
138
|
+
df_segm = sig_proc_segmentise(x=(np.diff(x) < 0.0)*1)
|
|
139
|
+
|
|
140
|
+
# Start of slope coincides with position of peak (due to index shift induced by np.diff)
|
|
141
|
+
ind_peak = df_segm.loc[df_segm.val == 1, "i"].values
|
|
142
|
+
|
|
143
|
+
# Check right boundary
|
|
144
|
+
if x[-1] > x[-2]:
|
|
145
|
+
ind_peak = np.append(ind_peak, np.shape(x)[0]-1)
|
|
146
|
+
|
|
147
|
+
# Construct dataframe with index and corresponding value
|
|
148
|
+
if np.shape(ind_peak)[0] == 0:
|
|
149
|
+
df_peak = pd.DataFrame({"ind": [],
|
|
150
|
+
"val": []})
|
|
151
|
+
else:
|
|
152
|
+
if ddir == "pos":
|
|
153
|
+
df_peak = pd.DataFrame({"ind": ind_peak,
|
|
154
|
+
"val": x[ind_peak]})
|
|
155
|
+
if ddir == "neg":
|
|
156
|
+
df_peak = pd.DataFrame({"ind": ind_peak,
|
|
157
|
+
"val": -x[ind_peak]})
|
|
158
|
+
return df_peak
|