def loadImage(self, ImageFilePath, MaskFilePath): """ Preprocess the image and labelmap. If ImageFilePath is a string, it is loaded as SimpleITK Image and assigned to image, if it already is a SimpleITK Image, it is just assigned to image. All other cases are ignored (nothing calculated). Equal approach is used for assignment of mask using MaskFilePath. If normalizing is enabled image is first normalized before any resampling is applied. If resampling is enabled, both image and mask are resampled and cropped to the tumor mask (with additional padding as specified in padDistance) after assignment of image and mask. """ self.logger.info('Loading image and mask') if isinstance(ImageFilePath, six.string_types) and os.path.isfile(ImageFilePath): image = sitk.ReadImage(ImageFilePath) elif isinstance(ImageFilePath, sitk.SimpleITK.Image): image = ImageFilePath else: self.logger.warning( 'Error reading image Filepath or SimpleITK object') return None, None # this function is expected to always return a tuple of 2 elements if isinstance(MaskFilePath, six.string_types) and os.path.isfile(MaskFilePath): mask = sitk.ReadImage(MaskFilePath) elif isinstance(MaskFilePath, sitk.SimpleITK.Image): mask = MaskFilePath else: self.logger.warning( 'Error reading mask Filepath or SimpleITK object') return None, None # this function is expected to always return a tuple of 2 elements # This point is only reached if image and mask loaded correctly if self.settings['normalize']: image = imageoperations.normalizeImage( image, self.settings['normalizeScale'], self.settings['removeOutliers']) if self.settings['interpolator'] is not None and self.settings[ 'resampledPixelSpacing'] is not None: image, mask = imageoperations.resampleImage( image, mask, self.settings['resampledPixelSpacing'], self.settings['interpolator'], self.settings['label'], self.settings['padDistance']) elif self.settings['preCrop']: bb, correctedMask = imageoperations.checkMask( image, mask, **self.settings) if correctedMask is not None: # Update the mask if it had to be resampled mask = correctedMask if bb is None: # Mask checks failed return None, None image, mask = imageoperations.cropToTumorMask( image, mask, bb, self.settings['padDistance']) return image, mask
def setFeatureClassAndTestCase(self, className, testCase): """ Set testing suite to specified testCase and feature class. Throws an assertion error if either class or test case are not recognized. These have to be set here together, as the settings with which the test case has to be loaded are defined per feature class in the baseline (extracted from provenance information). Only (re)loads an image/mask if the test case has changed, or the change of feature class causes a change in test settings. If feature class and test case are unchanged, nothing is reloaded and function returns False. If either feature class or test case is changed, function returns True. """ if self._featureClassName == className and self._testCase == testCase: return False # First set featureClass if necessary, because if settings have changed, testCase needs te be reloaded if self._featureClassName != className: self._logger.debug('Setting feature class name to %s', className) assert className in self.getFeatureClasses() self._featureClassName = className # Check if test settings have changed if self._kwargs != self.getBaselineSettings(className, testCase): self._kwargs = self.getBaselineSettings(className, testCase) self._testCase = None # forces image to be reloaded (as settings have changed) # Next, set testCase if necessary if self._testCase != testCase: self._logger.info("Reading the image and mask for test case %s", testCase) assert testCase in self.getTestCases() self._testedSet.add(testCase) imageName = str( os.path.join(self._dataDir, testCase + '_image.nrrd')) maskName = str( os.path.join(self._dataDir, testCase + '_label.nrrd')) self._image = sitk.ReadImage(imageName) self._mask = sitk.ReadImage(maskName) interpolator = self._kwargs.get('interpolator', sitk.sitkBSpline) resampledPixelSpacing = self._kwargs.get('resampledPixelSpacing', None) if interpolator is not None and resampledPixelSpacing is not None: self._image, self._mask = imageoperations.resampleImage( self._image, self._mask, resampledPixelSpacing, interpolator, self._kwargs.get('label', 1), self._kwargs.get('padDistance', 5)) bb = imageoperations.checkMask(self._image, self._mask) self._image, self._mask = imageoperations.cropToTumorMask( self._image, self._mask, bb, self._kwargs.get('label', 1)) self._testCase = testCase return True
def loadImage(self, ImageFilePath, MaskFilePath): """ Preprocess the image and labelmap. If ImageFilePath is a string, it is loaded as SimpleITK Image and assigned to image, if it already is a SimpleITK Image, it is just assigned to image. All other cases are ignored (nothing calculated). Equal approach is used for assignment of mask using MaskFilePath. If normalizing is enabled image is first normalized before any resampling is applied. If resampling is enabled, both image and mask are resampled and cropped to the tumor mask (with additional padding as specified in padDistance) after assignment of image and mask. """ if isinstance(ImageFilePath, six.string_types) and os.path.exists(ImageFilePath): image = sitk.ReadImage(ImageFilePath) elif isinstance(ImageFilePath, sitk.SimpleITK.Image): image = ImageFilePath else: self.logger.warning( 'Error reading image Filepath or SimpleITK object') if self.kwargs['verbose']: print("Error reading image Filepath or SimpleITK object") image = None if isinstance(MaskFilePath, six.string_types) and os.path.exists(MaskFilePath): mask = sitk.ReadImage(MaskFilePath) elif isinstance(MaskFilePath, sitk.SimpleITK.Image): mask = MaskFilePath else: self.logger.warning( 'Error reading mask Filepath or SimpleITK object') if self.kwargs['verbose']: print("Error reading mask Filepath or SimpleITK object") mask = None if image is not None and mask is not None: if self.kwargs['normalize']: image = imageoperations.normalizeImage( image, self.kwargs['normalizeScale'], self.kwargs['removeOutliers']) if self.kwargs['interpolator'] is not None and self.kwargs[ 'resampledPixelSpacing'] is not None: image, mask = imageoperations.resampleImage( image, mask, self.kwargs['resampledPixelSpacing'], self.kwargs['interpolator'], self.kwargs['label'], self.kwargs['padDistance']) return image, mask
def demo(): imageName, maskName = getTestCase('brain1') image = sitk.ReadImage(imageName) mask = sitk.ReadImage(maskName) settings = { 'binWidth': 25, 'interpolator': sitk.sitkBSpline, 'resampledPixelSpacing': None } # # If enabled, resample image (resampled image is automatically cropped. # interpolator = settings.get('interpolator') resampledPixelSpacing = settings.get('resampledPixelSpacing') if interpolator is not None and resampledPixelSpacing is not None: image, mask = imageoperations.resampleImage(image, mask, **settings) bb, correctedMask = imageoperations.checkMask(image, mask) if correctedMask is not None: mask = correctedMask image, mask = imageoperations.cropToTumorMask(image, mask, bb) ### firstOrderFeatures = firstorder.RadiomicsFirstOrder( image, mask, **settings) results = firstOrderFeatures.execute() print(results) ### shapeFeatures = shape.RadiomicsShape(image, mask, **settings) shapeFeatures.enableAllFeatures() results = shapeFeatures.execute() ### glcmFeatures = glcm.RadiomicsGLCM(image, mask, **settings) glcmFeatures.enableAllFeatures() results = glcmFeatures.execute() ### glrlmFeatures = glrlm.RadiomicsGLRLM(image, mask, **settings) glrlmFeatures.enableAllFeatures() results = glrlmFeatures.execute() ### glszmFeatures = glszm.RadiomicsGLSZM(image, mask, **settings) glszmFeatures.enableAllFeatures() results = glszmFeatures.execute()
#!/usr/bin/env python import sys import SimpleITK as sitk from radiomics import imageoperations image = sitk.ReadImage(sys.argv[1]) mask = sitk.ReadImage(sys.argv[2]) # Resamples and crops onto bounding box defined by the label (ii, im) = imageoperations.resampleImage(image, mask, resampledImageSpaceing=[2, 2, 2], label=1, padDistance=5) sitk.WriteImage(ii, sys.argv[3]) sitk.WriteImage(im, sys.argv[4])
def loadImage(self, ImageFilePath, MaskFilePath): """ Preprocess the image and labelmap. If ImageFilePath is a string, it is loaded as SimpleITK Image and assigned to image, if it already is a SimpleITK Image, it is just assigned to image. All other cases are ignored (nothing calculated). Equal approach is used for assignment of mask using MaskFilePath. If normalizing is enabled image is first normalized before any resampling is applied. If resampling is enabled, both image and mask are resampled and cropped to the tumor mask (with additional padding as specified in padDistance) after assignment of image and mask. """ normalize = self.settings.get('normalize', False) interpolator = self.settings.get('interpolator') resampledPixelSpacing = self.settings.get('resampledPixelSpacing') preCrop = self.settings.get('preCrop', False) label = self.settings.get('label', 1) self.logger.info('Loading image and mask') if isinstance(ImageFilePath, six.string_types) and os.path.isfile(ImageFilePath): image = sitk.ReadImage(ImageFilePath) elif isinstance(ImageFilePath, sitk.SimpleITK.Image): image = ImageFilePath else: raise ValueError( 'Error reading image Filepath or SimpleITK object') if isinstance(MaskFilePath, six.string_types) and os.path.isfile(MaskFilePath): mask = sitk.ReadImage(MaskFilePath, sitk.sitkUInt32) elif isinstance(MaskFilePath, sitk.SimpleITK.Image): mask = sitk.Cast(MaskFilePath, sitk.sitkUInt32) else: raise ValueError('Error reading mask Filepath or SimpleITK object') if self.generalInfo is not None: self.generalInfo.addImageElements(image) # Do not include the image here, as the overlap between image and mask have not been checked # It is therefore possible that image and mask do not align, or even have different sizes. self.generalInfo.addMaskElements(None, mask, label) # This point is only reached if image and mask loaded correctly if normalize: image = imageoperations.normalizeImage(image, **self.settings) if interpolator is not None and resampledPixelSpacing is not None: image, mask = imageoperations.resampleImage( image, mask, **self.settings) if self.generalInfo is not None: self.generalInfo.addImageElements(image, 'interpolated') self.generalInfo.addMaskElements(image, mask, self.settings.get('label', 1), 'interpolated') elif preCrop: bb, correctedMask = imageoperations.checkMask( image, mask, **self.settings) if correctedMask is not None: # Update the mask if it had to be resampled mask = correctedMask if bb is None: # Mask checks failed raise ValueError('Mask checks failed during pre-crop') image, mask = imageoperations.cropToTumorMask( image, mask, bb, **self.settings) return image, mask
def loadImage(ImageFilePath, MaskFilePath, otherImageFilePath, generalInfo=None, **kwargs): """ Load and pre-process the image and labelmap. If ImageFilePath is a string, it is loaded as SimpleITK Image and assigned to ``image``, if it already is a SimpleITK Image, it is just assigned to ``image``. All other cases are ignored (nothing calculated). Equal approach is used for assignment of ``mask`` using MaskFilePath. If necessary, a segmentation object (i.e. mask volume with vector-image type) is then converted to a labelmap (=scalar image type). Data type is forced to UInt32. See also :py:func:`~imageoperations.getMask()`. If normalizing is enabled image is first normalized before any resampling is applied. If resampling is enabled, both image and mask are resampled and cropped to the tumor mask (with additional padding as specified in padDistance) after assignment of image and mask. :param ImageFilePath: SimpleITK.Image object or string pointing to SimpleITK readable file representing the image to use. :param MaskFilePath: SimpleITK.Image object or string pointing to SimpleITK readable file representing the mask to use. :param generalInfo: GeneralInfo Object. If provided, it is used to store diagnostic information of the pre-processing. :param kwargs: Dictionary containing the settings to use for this particular image type. :return: 2 SimpleITK.Image objects representing the loaded image and mask, respectively. """ global logger normalize = kwargs.get('normalize', False) interpolator = kwargs.get('interpolator') resampledPixelSpacing = kwargs.get('resampledPixelSpacing') preCrop = kwargs.get('preCrop', False) label = kwargs.get('label', 1) logger.info('Loading image and mask') if isinstance(ImageFilePath, six.string_types) and os.path.isfile(ImageFilePath): image = sitk.ReadImage(ImageFilePath) elif isinstance(ImageFilePath, sitk.SimpleITK.Image): image = ImageFilePath else: raise ValueError( 'Error reading image Filepath or SimpleITK object') if isinstance(MaskFilePath, six.string_types) and os.path.isfile(MaskFilePath): mask = sitk.ReadImage(MaskFilePath) elif isinstance(MaskFilePath, sitk.SimpleITK.Image): mask = MaskFilePath else: raise ValueError('Error reading mask Filepath or SimpleITK object') # Read other image for washin if otherImageFilePath is not None: if isinstance( otherImageFilePath, six.string_types) and os.path.isfile(otherImageFilePath): imageOther = sitk.ReadImage(otherImageFilePath) elif isinstance(otherImageFilePath, sitk.SimpleITK.Image): imageOther = otherImageFilePath else: raise ValueError( 'Error reading image Filepath or SimpleITK object') # Align input types caster = sitk.CastImageFilter() caster.SetOutputPixelType(sitk.sitkFloat64) image = caster.Execute(image) imageOther = caster.Execute(imageOther) # t1 - t0 f_subtract = sitk.SubtractImageFilter() wash = f_subtract.Execute(imageOther, image) # (t1 - t0) / t0 f_divide = sitk.DivideImageFilter() wash = f_divide.Execute(wash, image) # Replace max Values with 0 f_less = sitk.LessEqualImageFilter() value_mask = f_less.Execute(wash, sys.float_info.max / 2) f_mask = sitk.MaskImageFilter() image = f_mask.Execute(wash, value_mask) # process the mask mask = imageoperations.getMask(mask, **kwargs) if generalInfo is not None: generalInfo.addImageElements(image) # Do not include the image here, as the overlap between image and mask have not been checked # It is therefore possible that image and mask do not align, or even have different sizes. generalInfo.addMaskElements(None, mask, label) # This point is only reached if image and mask loaded correctly if normalize: image = imageoperations.normalizeImage(image, **kwargs) if interpolator is not None and resampledPixelSpacing is not None: image, mask = imageoperations.resampleImage(image, mask, **kwargs) if generalInfo is not None: generalInfo.addImageElements(image, 'interpolated') generalInfo.addMaskElements(image, mask, label, 'interpolated') elif preCrop: bb, correctedMask = imageoperations.checkMask( image, mask, **kwargs) if correctedMask is not None: # Update the mask if it had to be resampled mask = correctedMask if bb is None: # Mask checks failed raise ValueError('Mask checks failed during pre-crop') image, mask = imageoperations.cropToTumorMask( image, mask, bb, **kwargs) return image, mask
def setFeatureClassAndTestCase(self, className, test): """ Set testing suite to specified testCase and feature class. Throws an assertion error if either class or test case are not recognized. These have to be set here together, as the settings with which the test case has to be loaded are defined per feature class in the baseline (extracted from provenance information). Only (re)loads an image/mask if the test case has changed, or the change of feature class causes a change in test settings. If feature class and test case are unchanged, nothing is reloaded and function returns False. If either feature class or test case is changed, function returns True. """ global TEST_CASES if self._featureClassName == className and self._test == test: return False self._test = test self._testedSet.add(self._test) # First set featureClass if necessary, because if settings have changed, testCase needs te be reloaded if self._featureClassName != className: self._logger.debug('Setting feature class name to %s', className) assert className in self._baseline.keys( ) # Check if a baseline has been read for this class self._featureClassName = className # Check if test settings have changed if self._current_config != self._baseline[className].getTestConfig( test): self._current_config = self._baseline[className].getTestConfig( test) self._testCase = None # forces image to be reloaded (as settings have changed) # Next, set testCase if necessary if self._testCase != self._current_config['TestCase']: self._testCase = self._current_config['TestCase'] self._logger.info("Reading the image and mask for test case %s", self._testCase) assert self._current_config['TestCase'] in TEST_CASES imageName, maskName = getTestCase(self._testCase) assert imageName is not None assert maskName is not None self._image = sitk.ReadImage(imageName) self._mask = sitk.ReadImage(maskName) if 'ImageHash' in self._current_config: assert sitk.Hash( self._image) == self._current_config['ImageHash'] if 'MaskHash' in self._current_config: assert sitk.Hash( self._mask) == self._current_config['MaskHash'] settings = self._current_config.get('Settings', {}) interpolator = settings.get('interpolator', sitk.sitkBSpline) resampledPixelSpacing = settings.get('resampledPixelSpacing', None) if interpolator is not None and resampledPixelSpacing is not None: self._image, self._mask = imageoperations.resampleImage( self._image, self._mask, resampledPixelSpacing, interpolator, settings.get('label', 1), settings.get('padDistance', 5)) self._bb, correctedMask = imageoperations.checkMask( self._image, self._mask, **settings) if correctedMask is not None: self._mask = correctedMask self._imageType = None return True
applyLog = False applyWavelet = False # Setting for the feature calculation. # Currently, resampling is disabled. # Can be enabled by setting 'resampledPixelSpacing' to a list of 3 floats (new voxel size in mm for x, y and z) kwargs = {'binWidth': 25, 'interpolator': sitk.sitkBSpline, 'resampledPixelSpacing': None} # # If enabled, resample image (resampled image is automatically cropped. # If resampling is not enabled, crop image instead # if kwargs['interpolator'] is not None and kwargs['resampledPixelSpacing'] is not None: image, mask = imageoperations.resampleImage(image, mask, kwargs['resampledPixelSpacing'], kwargs['interpolator']) else: bb, correctedMask = imageoperations.checkMask(image, mask) if correctedMask is not None: mask = correctedMask image, mask = imageoperations.cropToTumorMask(image, mask, bb) # # Show the first order feature calculations # firstOrderFeatures = firstorder.RadiomicsFirstOrder(image, mask, **kwargs) firstOrderFeatures.enableFeatureByName('Mean', True) # firstOrderFeatures.enableAllFeatures() print('Will calculate the following first order features: ')
def calc_radio_fea(img: np.ndarray, mask: np.ndarray) -> List[np.ndarray]: assert type( img ) == np.ndarray, f"TypeError, expected np.ndarray but Got {type(img)}" assert img.shape == mask.shape, f"SizeError, expected to be same, but Got {img.shape} and {mask.shape}" image = sitk.GetImageFromArray(img) mask = sitk.GetImageFromArray(mask) # Setting for the feature calculation. # Currently, resampling is disabled. # Can be enabled by setting 'resampledPixelSpacing' to a list of 3 floats (new voxel size in mm for x, y and z) settings = { 'binWidth': 25, 'interpolator': sitk.sitkBSpline, 'resampledPixelSpacing': None } # # If enabled, resample image (resampled image is automatically cropped. # interpolator = settings.get('interpolator') resampledPixelSpacing = settings.get('resampledPixelSpacing') if interpolator is not None and resampledPixelSpacing is not None: image, mask = imageoperations.resampleImage(image, mask, **settings) bb, correctedMask = imageoperations.checkMask(image, mask) if correctedMask is not None: mask = correctedMask image, mask = imageoperations.cropToTumorMask(image, mask, bb) results_collect = dict() results_np = list() # Fisrt order firstOrderFeatures = firstorder.RadiomicsFirstOrder( image, mask, **settings) # firstOrderFeatures.enableFeatureByName('Mean', True) firstOrderFeatures.enableAllFeatures() results: dict = firstOrderFeatures.execute() # dict() # results_collect['FirstOrder'] = results results_np.append(np.array([value for key, value in results.items()])) # shapeFeatures = shape.RadiomicsShape(image, mask, **settings) shapeFeatures.enableAllFeatures() results = shapeFeatures.execute() # results_collect['ShapeFeature'] = results results_np.append(np.array([value for key, value in results.items()])) ### glcmFeatures = glcm.RadiomicsGLCM(image, mask, **settings) glcmFeatures.enableAllFeatures() results = glcmFeatures.execute() # results_collect['GLCM'] = results results_np.append(np.array([value for key, value in results.items()])) ### glrlmFeatures = glrlm.RadiomicsGLRLM(image, mask, **settings) glrlmFeatures.enableAllFeatures() results = glrlmFeatures.execute() # results_collect['GLRLM'] = results results_np.append(np.array([value for key, value in results.items()])) ### glszmFeatures = glszm.RadiomicsGLSZM(image, mask, **settings) glszmFeatures.enableAllFeatures() results = glszmFeatures.execute() # results_collect['GLSZM'] = results results_np.append(np.array([value for key, value in results.items()])) gldmFeatures = gldm.RadiomicsGLDM(image, mask, **settings) gldmFeatures.enableAllFeatures() results = gldmFeatures.execute() results_np.append(np.array([value for key, value in results.items()])) return results_np
import sys import SimpleITK as sitk from radiomics import imageoperations image = sitk.ReadImage(sys.argv[1]) mask = sitk.ReadImage(sys.argv[2]) (ii, im) = imageoperations.resampleImage(image, mask, [2, 2, 2]) sitk.WriteImage(ii, sys.argv[3]) sitk.WriteImage(im, sys.argv[4])
def pyradiomics_extractor(data_objects, working_dir, settings): """Run to extract radiomics from data objects Args: data_objects (list): List of data objects to process working_dir (str): Path to directory used for working settings ([type]): The settings to use for processing radiomics Returns: list: List of output data objects """ logger.info("Running PyRadiomics Extract") logger.info("Using settings: " + str(settings)) pyrad_settings = settings["pyradiomics_settings"] # If no Radiomics are supplied then extract for all first order radiomics if len(settings["radiomics"].keys()) == 0: features = firstorder.RadiomicsFirstOrder.getFeatureNames() settings["radiomics"] = {"firstorder": [f for f in features if not features[f]]} results = None meta_data_cols = [("", "Contour")] for data_obj in data_objects: try: if len(data_obj.children) > 0: logger.info("Running on data object: " + data_obj.path) # Read the image series load_path = data_obj.path if data_obj.type == "DICOM": load_path = sitk.ImageSeriesReader().GetGDCMSeriesFileNames(data_obj.path) # Children of Image Data Object are masks, compute PyRadiomics for all of them! output_frame = pd.DataFrame() for child_obj in data_obj.children: contour_name = child_obj.path.split("/")[-1].split(".")[0] if len(settings["contours"]) > 0 and not contour_name in settings["contours"]: # If a contour list is provided and this contour isn't in the list then # skip it logger.debug("Skipping Contour: ", contour_name) continue # Reload the image for each new contour in case resampling is occuring, # should start fresh each time. image = sitk.ReadImage(load_path) mask = sitk.ReadImage(child_obj.path) logger.debug("Image Origin: " + str(image.GetOrigin())) logger.debug("Mask Origin: " + str(mask.GetOrigin())) logger.debug("Image Direction: " + str(image.GetDirection())) logger.debug("Mask Direction: " + str(mask.GetDirection())) logger.debug("Image Size: " + str(image.GetSize())) logger.debug("Mask Size: " + str(mask.GetSize())) logger.info(child_obj.path) interpolator = pyrad_settings.get("interpolator") resample_pixel_spacing = pyrad_settings.get("resampledPixelSpacing") if settings["resample_to_image"]: logger.info("Will resample to spacing of image") resample_pixel_spacing = list(image.GetSpacing()) pyrad_settings["resampledPixelSpacing"] = resample_pixel_spacing if interpolator is not None and resample_pixel_spacing is not None: logger.info("Resampling Image and Mask") image, mask = imageoperations.resampleImage(image, mask, **pyrad_settings) # output[contour_name] = {"Contour": contour_name} df_contour = pd.DataFrame() logger.info("Computing Radiomics for contour: {0}", contour_name) for rad in settings["radiomics"].keys(): logger.info("Computing {0} radiomics".format(rad)) if rad not in AVAILABLE_RADIOMICS.keys(): logger.warning("Radiomic Class not found: {0}", rad) continue radiomics_obj = AVAILABLE_RADIOMICS[rad] features = radiomics_obj(image, mask, **pyrad_settings) features.disableAllFeatures() # All features seem to be computed if all are disabled (possible # pyradiomics bug?). Skip if all features in a class are disabled. if len(settings["radiomics"][rad]) == 0: continue for feature in settings["radiomics"][rad]: try: features.enableFeatureByName(feature, True) except LookupError: # Feature not available in this set logger.warning("Feature not found: {0}", feature) feature_result = features.execute() feature_result = dict( ((rad, key), value) for (key, value) in feature_result.items() ) df_feature_result = pd.DataFrame(feature_result, index=[contour_name]) # Merge the results df_contour = pd.concat([df_contour, df_feature_result], axis=1) df_contour[("", "Contour")] = contour_name output_frame = pd.concat([output_frame, df_contour]) # Add the meta data for this contour if there is any if child_obj.meta_data: for key in child_obj.meta_data: col_key = ("", key) output_frame[col_key] = child_obj.meta_data[key] if col_key not in meta_data_cols: meta_data_cols.append(col_key) # Add Image Series Data Object's Meta Data to the table if data_obj.meta_data: for key in data_obj.meta_data.keys(): col_key = ("", key) output_frame[col_key] = pd.Series( [data_obj.meta_data[key] for p in range(len(output_frame.index))], index=output_frame.index, ) if col_key not in meta_data_cols: meta_data_cols.append(col_key) if results is None: results = output_frame else: results = results.append(output_frame) except Exception as exception: # pylint: disable=broad-except logger.error("An Error occurred while computing the Radiomics: {0}", exception) # Set the order of the columns output cols = results.columns.tolist() new_cols = list(meta_data_cols) new_cols += [c for c in cols if not c in meta_data_cols] results = results[new_cols] # Write output to file output_file = os.path.join(working_dir, "output.csv") results = results.reset_index() results = results.drop(columns=["index"]) results.to_csv(output_file) logger.info("Radiomics written to {0}".format(output_file)) # Create the output Data Object and add it to output_objects data_object = DataObject(type="FILE", path=output_file) output_objects = [data_object] return output_objects
# Setting for the feature calculation. # Currently, resampling is disabled. # Can be enabled by setting 'resampledPixelSpacing' to a list of 3 floats (new voxel size in mm for x, y and z) settings = { 'binWidth': 25, 'interpolator': sitk.sitkBSpline, 'resampledPixelSpacing': None } # # If enabled, resample image (resampled image is automatically cropped. # interpolator = settings.get('interpolator') resampledPixelSpacing = settings.get('resampledPixelSpacing') if interpolator is not None and resampledPixelSpacing is not None: image, mask = imageoperations.resampleImage(image, mask, **settings) bb, correctedMask = imageoperations.checkMask(image, mask) if correctedMask is not None: mask = correctedMask image, mask = imageoperations.cropToTumorMask(image, mask, bb) # # Show the first order feature calculations # firstOrderFeatures = firstorder.RadiomicsFirstOrder(image, mask, **settings) # firstOrderFeatures.enableFeatureByName('Mean', True) firstOrderFeatures.enableAllFeatures() # print('Will calculate the following first order features: ')
def features_extractor(patients_nrrd_path, valid_IDs, applyLog = False, applyWavelet = False): feature_vectors = {} cnt = 0 for case_id in valid_IDs: feature_vectors[case_id] = {} cnt += 1 # try: ct_nrrd_path = os.path.join(patients_nrrd_path,case_id, "image.nrrd") ss_nrrd_path = os.path.join(patients_nrrd_path,case_id, "mask.nrrd") print("Reading ct image") image = sitk.ReadImage(ct_nrrd_path) # image, header = nrrd.read(ct_nrrd_path) print("Reading roi mask") mask = sitk.ReadImage(ss_nrrd_path) # mask, header = nrrd.read(ss_nrrd_path) print("Getting ct image array") image_array = sitk.GetArrayFromImage(image) print("Getting roi mask array") mask_array = sitk.GetArrayFromImage(mask) print(image_array.shape, mask_array.shape) # simple_plot_nrrd(image_array, mask_array, sliceNumber=75, plotSrc='sitk') print (cnt, "_ Calculating features: ",case_id) settings = {'binWidth': 25, 'interpolator': sitk.sitkBSpline, 'resampledPixelSpacing': None} interpolator = settings.get('interpolator') resampledPixelSpacing = settings.get('resampledPixelSpacing') if interpolator is not None and resampledPixelSpacing is not None: image, mask = imageoperations.resampleImage(image, mask, **settings) bb, correctedMask = imageoperations.checkMask(image, mask) if correctedMask is not None: mask = correctedMask image, mask = imageoperations.cropToTumorMask(image, mask, bb) firstOrderFeatures = firstorder.RadiomicsFirstOrder(image, mask, **settings) # firstOrderFeatures.enableFeatureByName('Mean', True) firstOrderFeatures.enableAllFeatures() # print('Will calculate the following first order features: ') # for f in firstOrderFeatures.enabledFeatures.keys(): # print(' ', f) # print(getattr(firstOrderFeatures, 'get%sFeatureValue' % f).__doc__) # print('Calculating first order features...') results = firstOrderFeatures.execute() # print('done') print('Calculated first order features: ') for (key, val) in six.iteritems(results): firstOrderFeatureName = '%s_%s' % ('firstOrder', key) if firstOrderFeatureName not in feature_vectors[case_id]: feature_vectors[case_id][firstOrderFeatureName] = val else: print('Error: firstOrder key existing! %s'%firstOrderFeatureName) # break # print(' ', key, ':', val) # # Show Shape features # shapeFeatures = shape.RadiomicsShape(image, mask, **settings) shapeFeatures.enableAllFeatures() # print('Will calculate the following Shape features: ') # for f in shapeFeatures.enabledFeatures.keys(): # print(' ', f) # print(getattr(shapeFeatures, 'get%sFeatureValue' % f).__doc__) # print('Calculating Shape features...') results = shapeFeatures.execute() # print('done') print('Calculated Shape features: ') for (key, val) in six.iteritems(results): ShapeFeatureName = '%s_%s' % ('Shape', key) if ShapeFeatureName not in feature_vectors[case_id]: feature_vectors[case_id][ShapeFeatureName] = val else: print('Error: shape key existing! %s'%ShapeFeatureName) # break # print(' ', key, ':', val) # # Show GLCM features: Gray Level Co-occurrence Matrix (GLCM) Features # glcmFeatures = glcm.RadiomicsGLCM(image, mask, **settings) glcmFeatures.enableAllFeatures() # print('Will calculate the following GLCM features: ') # for f in glcmFeatures.enabledFeatures.keys(): # print(' ', f) # print(getattr(glcmFeatures, 'get%sFeatureValue' % f).__doc__) # print('Calculating GLCM features...') results = glcmFeatures.execute() # print('done') print('Calculated GLCM features: ') for (key, val) in six.iteritems(results): GLCMFeatureName = '%s_%s' % ('GLCM', key) if GLCMFeatureName not in feature_vectors[case_id]: feature_vectors[case_id][GLCMFeatureName] = val else: print('Error: GLCM key existing! %s'%GLCMFeatureName) # break # print(' ', key, ':', val) # # Show GLSZM features; Gray Level Size Zone Matrix (GLSZM) Features # glszmFeatures = glszm.RadiomicsGLSZM(image, mask, **settings) glszmFeatures.enableAllFeatures() # print('Will calculate the following GLSZM features: ') # for f in glszmFeatures.enabledFeatures.keys(): # print(' ', f) # print(getattr(glszmFeatures, 'get%sFeatureValue' % f).__doc__) # print('Calculating GLSZM features...') results = glszmFeatures.execute() print('done') print('Calculated GLSZM features: ') for (key, val) in six.iteritems(results): GLSZMFeatureName = '%s_%s' % ('GLSZM', key) if GLSZMFeatureName not in feature_vectors[case_id]: feature_vectors[case_id][GLSZMFeatureName] = val else: print('Error: GLSZM key existing! %s'%GLSZMFeatureName) # break # print(' ', key, ':', val) # # Show GLRLM features; Gray Level Run Length Matrix (GLRLM) Features # glrlmFeatures = glrlm.RadiomicsGLRLM(image, mask, **settings) glrlmFeatures.enableAllFeatures() # print('Will calculate the following GLRLM features: ') # for f in glrlmFeatures.enabledFeatures.keys(): # print(' ', f) # print(getattr(glrlmFeatures, 'get%sFeatureValue' % f).__doc__) # print('Calculating GLRLM features...') results = glrlmFeatures.execute() # print('done') print('Calculated GLRLM features: ') for (key, val) in six.iteritems(results): GLRLMFeatureName = '%s_%s' % ('GLRLM', key) if GLRLMFeatureName not in feature_vectors[case_id]: feature_vectors[case_id][GLRLMFeatureName] = val else: print('Error: GLRLM key existing! %s'%GLRLMFeatureName) # break # print(' ', key, ':', val) # # Show NGTDM features; Neighbouring Gray Tone Difference Matrix (NGTDM) Features # ngtdmFeatures = ngtdm.RadiomicsNGTDM(image, mask, **settings) ngtdmFeatures.enableAllFeatures() # print('Will calculate the following NGTDM features: ') # for f in ngtdmFeatures.enabledFeatures.keys(): # print(' ', f) # print(getattr(ngtdmFeatures, 'get%sFeatureValue' % f).__doc__) # print('Calculating NGTDM features...') results = ngtdmFeatures.execute() # print('done') print('Calculated NGTDM features: ') for (key, val) in six.iteritems(results): NGTDMFeatureName = '%s_%s' % ('NGTDM', key) if NGTDMFeatureName not in feature_vectors[case_id]: feature_vectors[case_id][NGTDMFeatureName] = val else: print('Error: NGTDM key existing! %s'%NGTDMFeatureName) # break # print(' ', key, ':', val) # # Show GLDM features; Gray Level Dependence Matrix (GLDM) Features # gldmFeatures = gldm.RadiomicsGLDM(image, mask, **settings) gldmFeatures.enableAllFeatures() # print('Will calculate the following GLDM features: ') # for f in gldmFeatures.enabledFeatures.keys(): # print(' ', f) # print(getattr(gldmFeatures, 'get%sFeatureValue' % f).__doc__) # print('Calculating GLDM features...') results = gldmFeatures.execute() # print('done') print('Calculated GLDM features: ') for (key, val) in six.iteritems(results): GLDMFeatureName = '%s_%s' % ('GLDM', key) if GLDMFeatureName not in feature_vectors[case_id]: feature_vectors[case_id][GLDMFeatureName] = val else: print('Error: GLDM key existing! %s'%GLDMFeatureName) # break # print(' ', key, ':', val) # # Show FirstOrder features, calculated on a LoG filtered image # if applyLog: sigmaValues = np.arange(5., 0., -.5)[::1] for logImage, imageTypeName, inputKwargs in imageoperations.getLoGImage(image, mask, sigma=sigmaValues): logFirstorderFeatures = firstorder.RadiomicsFirstOrder(logImage, mask, **inputKwargs) logFirstorderFeatures.enableAllFeatures() results = logFirstorderFeatures.execute() for (key, val) in np.iteritems(results): laplacianFeatureName = '%s_%s' % (imageTypeName, key) if laplacianFeatureName not in feature_vectors[case_id]: feature_vectors[case_id][laplacianFeatureName] = val else: print('Error: LoG key existing! %s'%laplacianFeatureName) # break # print(' ', laplacianFeatureName, ':', val) # # Show FirstOrder features, calculated on a wavelet filtered image # if applyWavelet: for decompositionImage, decompositionName, inputKwargs in imageoperations.getWaveletImage(image, mask): waveletFirstOrderFeaturs = firstorder.RadiomicsFirstOrder(decompositionImage, mask, **inputKwargs) waveletFirstOrderFeaturs.enableAllFeatures() results = waveletFirstOrderFeaturs.execute() print('Calculated firstorder features with wavelet ', decompositionName) for (key, val) in six.iteritems(results): waveletFeatureName = '%s_%s' % (str(decompositionName), key) if waveletFeatureName not in feature_vectors[case_id]: feature_vectors[case_id][waveletFeatureName] = val else: print('Error: wavelet key existing! %s'%waveletFeatureName) # break # print(' ', waveletFeatureName, ':', val) mask = None image = None return feature_vectors
#!/usr/bin/env python import sys import SimpleITK as sitk from radiomics import imageoperations image = sitk.ReadImage(sys.argv[1]) mask = sitk.ReadImage(sys.argv[2]) # Resamples and crops onto bounding box defined by the label (ii, im) = imageoperations.resampleImage(image, mask, [2, 2, 2], label=1, padDistance=5) sitk.WriteImage(ii, sys.argv[3]) sitk.WriteImage(im, sys.argv[4])