Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 46 additions & 47 deletions MEDimage/MEDscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,9 @@ def __init__(self, medscan=None) -> None:
self.data = medscan.data
except:
self.data = self.data()
try:
self.params = medscan.params
except:
self.params = self.Params()
try:
self.radiomics = medscan.radiomics
except:
self.radiomics = self.Radiomics()

self.params = self.Params()
self.radiomics = self.Radiomics()
self.skip = False

def __init_process_params(self, im_params: Dict) -> None:
Expand All @@ -90,6 +84,8 @@ def __init_process_params(self, im_params: Dict) -> None:
raise ValueError(f"The given parameters dict is not valid, no params found for {self.type} modality")

# re-segmentation range processing
if(im_params['reSeg']['range'] and (im_params['reSeg']['range'][0] == "inf" or im_params['reSeg']['range'][0] == "-inf")):
im_params['reSeg']['range'][0] = -np.inf
if(im_params['reSeg']['range'] and im_params['reSeg']['range'][1] == "inf"):
im_params['reSeg']['range'][1] = np.inf

Expand Down Expand Up @@ -218,6 +214,22 @@ def __init_extraction_params(self, im_params: Dict):
self.params.radiomics.ngtdm.dist_correction = False
else:
self.params.radiomics.ngtdm.dist_correction = False

# Features to extract
features = [
"Morph", "LocalIntensity", "Stats", "IntensityHistogram", "IntensityVolumeHistogram",
"GLCM", "GLRLM", "GLSZM", "GLDZM", "NGTDM", "NGLDM"
]
if "extract" in im_params.keys():
self.params.radiomics.extract = im_params['extract']
for key in self.params.radiomics.extract:
if key not in features:
raise ValueError(f"Invalid key in 'extract' parameter: {key} (Modality {self.type}).")

# Ensure each feature is in the extract dictionary with a default value of True
for feature in features:
if feature not in self.params.radiomics.extract:
self.params.radiomics.extract[feature] = True

def __init_filter_params(self, filter_params: Dict) -> None:
"""Initializes the filtering params from a given Dict.
Expand Down Expand Up @@ -351,54 +363,41 @@ def init_ntf_calculation(self, vol_obj: image_volume_obj) -> None:
'_' + ih_val_name + min_val_name

# IVH name
if not self.params.process.ivh: # CT case
if self.params.process.im_range: # The im_range defines the computation.
min_val_name = ((str(self.params.process.im_range[0])).replace('.', 'dot')).replace('-', 'M')
max_val_name = ((str(self.params.process.im_range[1])).replace('.', 'dot')).replace('-', 'M')
if max_val_name == 'inf':
# In this case, the maximum value of the ROI is used,
# so no need to report it.
range_name = '_min' + min_val_name
elif min_val_name == '-inf' or min_val_name == 'inf':
# In this case, the minimum value of the ROI is used,
# so no need to report it.
range_name = '_max' + max_val_name
else:
range_name = '_min' + min_val_name + '_max' + max_val_name
else:
# min-max of ROI will be used, no need to report it.
range_name = ''
if not self.params.process.ivh: # CT case for example
ivh_algo_name = 'algoNone'
ivh_val_name = 'bin1'
if self.params.process.im_range: # The im_range defines the computation.
min_val_name = ((str(self.params.process.im_range[0])).replace(
'.', 'dot')).replace('-', 'M')
max_val_name = ((str(self.params.process.im_range[1])).replace(
'.', 'dot')).replace('-', 'M')
range_name = '_min' + min_val_name + '_max' + max_val_name
else:
range_name = ''
else:
ivh_algo_name = 'algo' + self.params.process.ivh['type']
if 'val' in self.params.process.ivh:
ivh_algo_name = 'algo' + self.params.process.ivh['type'] if 'type' in self.params.process.ivh else 'algoNone'
if 'val' in self.params.process.ivh and self.params.process.ivh['val']:
ivh_val_name = 'bin' + (str(self.params.process.ivh['val'])).replace('.', 'dot')
else:
ivh_val_name = 'binNone'
# The im_range defines the computation.
if 'type' in self.params.process.ivh and self.params.process.ivh['type'].find('FBS') >=0:
if self.params.process.im_range:
min_val_name = ((str(self.params.process.im_range[0])).replace(
'.', 'dot')).replace('-', 'M')
max_val_name = ((str(self.params.process.im_range[1])).replace(
'.', 'dot')).replace('-', 'M')
if max_val_name == 'inf':
# In this case, the maximum value of the ROI is used,
# so no need to report it.
range_name = '_min' + min_val_name
elif min_val_name == '-inf':
# In this case, the minimum value of the ROI is used,
# so no need to report it.
range_name = '_max' + max_val_name
else:
range_name = '_min' + min_val_name + '_max' + max_val_name
else: # min-max of ROI will be used, no need to report it.
range_name = ''
else: # min-max of ROI will be used, no need to report it.
range_name = ''
self.params.radiomics.ivh_name = self.params.radiomics.scale_name + '_' + ivh_algo_name + '_' + ivh_val_name + range_name

# Now initialize the attribute that will hold the computation results
self.radiomics.image.update({
'morph_3D': {self.params.radiomics.scale_name: {}},
'locInt_3D': {self.params.radiomics.scale_name: {}},
'stats_3D': {self.params.radiomics.scale_name: {}},
'intHist_3D': {self.params.radiomics.ih_name: {}},
'intVolHist_3D': {self.params.radiomics.ivh_name: {}}
})
'morph_3D': {self.params.radiomics.scale_name: {}},
'locInt_3D': {self.params.radiomics.scale_name: {}},
'stats_3D': {self.params.radiomics.scale_name: {}},
'intHist_3D': {self.params.radiomics.ih_name: {}},
'intVolHist_3D': {self.params.radiomics.ivh_name: {}}
})

except Exception as e:
message = f"\n PROBLEM WITH PRE-PROCESSING OF FEATURES IN init_ntf_calculation(): \n {e}"
Expand Down Expand Up @@ -1037,7 +1036,7 @@ def __init__(self, **kwargs) -> None:
self.name_text_types = kwargs['name_text_types'] if 'name_text_types' in kwargs else None
self.processing_name = kwargs['processing_name'] if 'processing_name' in kwargs else None
self.scale_name = kwargs['scale_name'] if 'scale_name' in kwargs else None

self.extract = kwargs['extract'] if 'extract' in kwargs else {}

class GLCM:
"""Organizes the GLCM features extraction parameters"""
Expand Down
2 changes: 1 addition & 1 deletion MEDimage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
logging.getLogger(__name__).addHandler(stream_handler)

__author__ = "MEDomicsLab consortium"
__version__ = "0.9.7"
__version__ = "0.9.8"
__copyright__ = "Copyright (C) MEDomicsLab consortium"
__license__ = "GNU General Public License 3.0"
__maintainer__ = "MAHDI AIT LHAJ LOUTFI"
Expand Down
131 changes: 87 additions & 44 deletions MEDimage/biomarkers/BatchExtractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def __init__(
path_csv: Union[str, Path],
path_params: Union[str, Path],
path_save: Union[str, Path],
n_batch: int = 4
n_batch: int = 4,
skip_existing: bool = False
) -> None:
"""
constructor of the BatchExtractor class
Expand All @@ -41,6 +42,7 @@ def __init__(
self.roi_types = []
self.roi_type_labels = []
self.n_bacth = n_batch
self.skip_existing = skip_existing

def __load_and_process_params(self) -> Dict:
"""Load and process the computing & batch parameters from JSON file"""
Expand Down Expand Up @@ -82,6 +84,14 @@ def __compute_radiomics_one_patient(
# Setting up logging settings
logging.basicConfig(filename=log_file, level=logging.DEBUG, force=True)

# Check if features are already computed for the current scan
if self.skip_existing:
modality = name_patient.split('.')[1]
name_save = name_patient.split('.')[0] + f'({roi_type_label})' + f'.{modality}.json'
if Path(self._path_save / f'features({roi_type})' / name_save).exists():
logging.info("Skipping existing features for scan: {name_patient}")
return log_file

# start timer
t_start = time()

Expand All @@ -94,7 +104,7 @@ def __compute_radiomics_one_patient(
with open(self._path_read / name_patient, 'rb') as f: medscan = pickle.load(f)
medscan = MEDimage.MEDscan(medscan)
except Exception as e:
logging.error(f"\n ERROR LOADING PATIENT {name_patient}:\n {e}")
print(f"\n ERROR LOADING PATIENT {name_patient}:\n {e}")
return None

# Init processing & computation parameters
Expand Down Expand Up @@ -197,35 +207,44 @@ def __compute_radiomics_one_patient(

# Morphological features extraction
try:
morph = MEDimage.biomarkers.morph.extract_all(
vol=vol_obj.data,
mask_int=roi_obj_int.data,
mask_morph=roi_obj_morph.data,
res=medscan.params.process.scale_non_text,
intensity_type=medscan.params.process.intensity_type
)
if medscan.params.radiomics.extract['Morph']:
morph = MEDimage.biomarkers.morph.extract_all(
vol=vol_obj.data,
mask_int=roi_obj_int.data,
mask_morph=roi_obj_morph.data,
res=medscan.params.process.scale_non_text,
intensity_type=medscan.params.process.intensity_type
)
else:
morph = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF MORPHOLOGICAL FEATURES {e}')
morph = None

# Local intensity features extraction
try:
local_intensity = MEDimage.biomarkers.local_intensity.extract_all(
img_obj=vol_obj.data,
roi_obj=roi_obj_int.data,
res=medscan.params.process.scale_non_text,
intensity_type=medscan.params.process.intensity_type
)
if medscan.params.radiomics.extract['LocalIntensity']:
local_intensity = MEDimage.biomarkers.local_intensity.extract_all(
img_obj=vol_obj.data,
roi_obj=roi_obj_int.data,
res=medscan.params.process.scale_non_text,
intensity_type=medscan.params.process.intensity_type
)
else:
local_intensity = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF LOCAL INTENSITY FEATURES {e}')
local_intensity = None

# statistical features extraction
try:
stats = MEDimage.biomarkers.stats.extract_all(
vol=vol_int_re,
intensity_type=medscan.params.process.intensity_type
)
if medscan.params.radiomics.extract['Stats']:
stats = MEDimage.biomarkers.stats.extract_all(
vol=vol_int_re,
intensity_type=medscan.params.process.intensity_type
)
else:
stats = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF STATISTICAL FEATURES {e}')
stats = None
Expand All @@ -240,9 +259,12 @@ def __compute_radiomics_one_patient(

# Intensity histogram features extraction
try:
int_hist = MEDimage.biomarkers.intensity_histogram.extract_all(
vol=vol_quant_re
)
if medscan.params.radiomics.extract['IntensityHistogram']:
int_hist = MEDimage.biomarkers.intensity_histogram.extract_all(
vol=vol_quant_re
)
else:
int_hist = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF INTENSITY HISTOGRAM FEATURES {e}')
int_hist = None
Expand All @@ -262,12 +284,15 @@ def __compute_radiomics_one_patient(
wd = 1

# Intensity volume histogram features extraction
int_vol_hist = MEDimage.biomarkers.int_vol_hist.extract_all(
medscan=medscan,
vol=vol_quant_re,
vol_int_re=vol_int_re,
wd=wd
)
if medscan.params.radiomics.extract['IntensityVolumeHistogram']:
int_vol_hist = MEDimage.biomarkers.int_vol_hist.extract_all(
medscan=medscan,
vol=vol_quant_re,
vol_int_re=vol_int_re,
wd=wd
)
else:
int_vol_hist = None

# End of Non-Texture features extraction
logging.info(f"End of non-texture features extraction: {time() - start}\n")
Expand Down Expand Up @@ -372,52 +397,70 @@ def __compute_radiomics_one_patient(

# GLCM features extraction
try:
glcm = MEDimage.biomarkers.glcm.extract_all(
vol=vol_quant_re,
dist_correction=medscan.params.radiomics.glcm.dist_correction)
if medscan.params.radiomics.extract['GLCM']:
glcm = MEDimage.biomarkers.glcm.extract_all(
vol=vol_quant_re,
dist_correction=medscan.params.radiomics.glcm.dist_correction)
else:
glcm = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF GLCM FEATURES {e}')
glcm = None

# GLRLM features extraction
try:
glrlm = MEDimage.biomarkers.glrlm.extract_all(
vol=vol_quant_re,
dist_correction=medscan.params.radiomics.glrlm.dist_correction)
if medscan.params.radiomics.extract['GLRLM']:
glrlm = MEDimage.biomarkers.glrlm.extract_all(
vol=vol_quant_re,
dist_correction=medscan.params.radiomics.glrlm.dist_correction)
else:
glrlm = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF GLRLM FEATURES {e}')
glrlm = None

# GLSZM features extraction
try:
glszm = MEDimage.biomarkers.glszm.extract_all(
vol=vol_quant_re)
if medscan.params.radiomics.extract['GLSZM']:
glszm = MEDimage.biomarkers.glszm.extract_all(
vol=vol_quant_re)
else:
glszm = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF GLSZM FEATURES {e}')
glszm = None

# GLDZM features extraction
try:
gldzm = MEDimage.biomarkers.gldzm.extract_all(
vol_int=vol_quant_re,
mask_morph=roi_obj_morph.data)
if medscan.params.radiomics.extract['GLDZM']:
gldzm = MEDimage.biomarkers.gldzm.extract_all(
vol_int=vol_quant_re,
mask_morph=roi_obj_morph.data)
else:
gldzm = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF GLDZM FEATURES {e}')
gldzm = None

# NGTDM features extraction
try:
ngtdm = MEDimage.biomarkers.ngtdm.extract_all(
vol=vol_quant_re,
dist_correction=medscan.params.radiomics.ngtdm.dist_correction)
if medscan.params.radiomics.extract['NGTDM']:
ngtdm = MEDimage.biomarkers.ngtdm.extract_all(
vol=vol_quant_re,
dist_correction=medscan.params.radiomics.ngtdm.dist_correction)
else:
ngtdm = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF NGTDM FEATURES {e}')
ngtdm = None

# NGLDM features extraction
try:
ngldm = MEDimage.biomarkers.ngldm.extract_all(
vol=vol_quant_re)
if medscan.params.radiomics.extract['NGLDM']:
ngldm = MEDimage.biomarkers.ngldm.extract_all(
vol=vol_quant_re)
else:
ngldm = None
except Exception as e:
logging.error(f'PROBLEM WITH COMPUTATION OF NGLDM FEATURES {e}')
ngldm = None
Expand Down
Loading