diff --git a/MEDimage/MEDscan.py b/MEDimage/MEDscan.py
index 865fe0d..857a185 100644
--- a/MEDimage/MEDscan.py
+++ b/MEDimage/MEDscan.py
@@ -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:
@@ -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
@@ -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.
@@ -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}"
@@ -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"""
diff --git a/MEDimage/__init__.py b/MEDimage/__init__.py
index 647477d..6ed02d7 100644
--- a/MEDimage/__init__.py
+++ b/MEDimage/__init__.py
@@ -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"
diff --git a/MEDimage/biomarkers/BatchExtractor.py b/MEDimage/biomarkers/BatchExtractor.py
index daf2954..f5d3c77 100644
--- a/MEDimage/biomarkers/BatchExtractor.py
+++ b/MEDimage/biomarkers/BatchExtractor.py
@@ -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
@@ -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"""
@@ -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()
@@ -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
@@ -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
@@ -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
@@ -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")
@@ -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
diff --git a/MEDimage/wrangling/DataManager.py b/MEDimage/wrangling/DataManager.py
index 348bb7e..6105012 100644
--- a/MEDimage/wrangling/DataManager.py
+++ b/MEDimage/wrangling/DataManager.py
@@ -3,7 +3,6 @@
import os
import pickle
import re
-import warnings
from dataclasses import dataclass
from pathlib import Path
from time import time
@@ -23,7 +22,6 @@
from ..MEDscan import MEDscan
from ..processing.compute_suv_map import compute_suv_map
-from ..processing.interpolation import interp_volume
from ..processing.segmentation import get_roi_from_indexes
from ..utils.get_file_paths import get_file_paths
from ..utils.get_patient_names import get_patient_names
@@ -782,6 +780,10 @@ def __pre_radiomics_checks_dimensions(
print("Wildcard is empty, the pre-checks will be aborted")
return
+ # Updating plotting params
+ plt.rcParams["figure.figsize"] = (20,20)
+ plt.rcParams.update({'font.size': 22})
+
# TODO: seperate by studies and scan type (MRscan, CTscan...)
# TODO: Two summaries (df, list of names saves) ->
# name_save = name_save(ROI) : Glioma-Huashan-001__T1.MRscan.npy({GTV})
@@ -846,7 +848,11 @@ def __pre_radiomics_checks_dimensions(
x.grid(False)
plt.title(f"Voxels xy-spacing checks for {wildcard}")
plt.legend()
- plt.show()
+ # Save the plot
+ if save:
+ plt.savefig(self.paths._path_save_checks / ('Voxels_xy_check.png'))
+ else:
+ plt.show()
# Plotting z-spacing data histogram
df_z = pd.DataFrame(z_dim["data"], columns=['data'])
@@ -860,7 +866,11 @@ def __pre_radiomics_checks_dimensions(
x.grid(False)
plt.title(f"Voxels z-spacing checks for {wildcard}")
plt.legend()
- plt.show()
+ # Save the plot
+ if save:
+ plt.savefig(self.paths._path_save_checks / ('Voxels_z_check.png'))
+ else:
+ plt.show()
# Saving files using wildcard for name
if save:
@@ -902,6 +912,10 @@ def __pre_radiomics_checks_window(
Returns:
None.
"""
+ # Updating plotting params
+ plt.rcParams["figure.figsize"] = (20,20)
+ plt.rcParams.update({'font.size': 22})
+
if type(wildcards_window) is str:
wildcards_window = [wildcards_window]
@@ -1032,7 +1046,11 @@ def __pre_radiomics_checks_window(
x.xaxis.set_tick_params(pad=15)
plt.title(f"Intensity range checks for {wildcard}, bw={bin_width}")
plt.legend()
- plt.show()
+ # Save the plot
+ if save:
+ plt.savefig(self.paths._path_save_checks / ('Intensity_range_check_' + f'bw_{bin_width}.png'))
+ else:
+ plt.show()
# save final checks
if save:
diff --git a/README.md b/README.md
index 8a1bbee..74e5d25 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[](https://www.python.org/downloads/release/python-380/)
-[](https://pypi.org/project/medimage-pkg/)
+[](https://pypi.org/project/medimage-pkg/)
[](https://github.com/MahdiAll99/MEDimage/actions/workflows/python-app.yml)
[](https://medimage.readthedocs.io/en/latest/?badge=latest)
[](LICENSE)
diff --git a/pyproject.toml b/pyproject.toml
index 0b62d96..8fcb5f9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,12 +1,12 @@
[tool.poetry]
name = "medimage-pkg"
-version = "0.9.7"
+version = "0.9.8"
description = "MEDimage is a Python package for processing and extracting features from medical images"
authors = ["MEDomics Consortium "]
license = "GPL-3.0"
readme = "README.md"
-homepage = "https://github.com/medomics/MEDomicsLab-develop"
-repository = "https://github.com/medomics/MEDomicsLab-develop"
+homepage = "https://medimage.app/"
+repository = "https://github.com/MEDomics-UdeS/MEDimage/"
keywords = ["python", "ibsi", "medical-imaging",
"cancer-imaging-research", "radiomics",
"medical-image-analysis", "features-extraction",
diff --git a/setup.py b/setup.py
index 4aa657d..a9502f4 100644
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,7 @@
setup(
name="MEDimage",
- version="0.9.7",
+ version="0.9.8",
author="MEDomics consortium",
author_email="medomics.info@gmail.com",
description="Python Open-source package for medical images processing and radiomic features extraction",