def __init__(self, *args, **kwargs): self.logger = logging.getLogger(__name__) self.featureClasses = getFeatureClasses() self.settings = {} self._enabledImagetypes = {} self._enabledFeatures = {} if len(args) == 1 and isinstance(args[0], six.string_types): self.logger.info("Loading parameter file") self.loadParams(args[0]) else: # Set default settings and update with and changed settings contained in kwargs self.settings = self._getDefaultSettings() if len(kwargs) > 0: self.logger.info('Applying custom settings') self.settings.update(kwargs) else: self.logger.info('No customized settings, applying defaults') self.logger.debug("Settings: %s", self.settings) self._enabledImagetypes = {'Original': {}} self.logger.info('Enabled image types: %s', self._enabledImagetypes) self._enabledFeatures = {} for featureClassName in self.getFeatureClassNames(): self._enabledFeatures[featureClassName] = [] self.logger.info('Enabled features: %s', self._enabledFeatures) self._setTolerance()
def __init__(self, *args, **kwargs): self.logger = logging.getLogger(__name__) self.featureClasses = getFeatureClasses() self.inputImages = getInputImageTypes() self.kwargs = {} self.inputImages = {} self.enabledFeatures = {} if len(args) == 1 and isinstance(args[0], six.string_types): self.loadParams(args[0]) else: # Set default settings and update with and changed settings contained in kwargs self.kwargs = { 'normalize': False, 'normalizeScale': 1, 'removeOutliers': None, 'resampledPixelSpacing': None, # No resampling by default 'interpolator': sitk.sitkBSpline, 'padDistance': 5, 'label': 1, 'verbose': False, 'enableCExtensions': True, 'additionalInfo': True } self.kwargs.update(kwargs) self.inputImages = {'Original': {}} self.enabledFeatures = {} for featureClassName in self.getFeatureClassNames(): self.enabledFeatures[featureClassName] = []
def __init__(self): self.logger = logging.getLogger('radiomics.addBaseline') self.testUtils = RadiomicsTestUtils() self.testCases = sorted(self.testUtils.getTests()) self.featureClasses = getFeatureClasses() dataDir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "data") self.baselineDir = os.path.join(dataDir, "baseline")
def compute(shape_type): logger.info('Computing %s', shape_type) featureNames = enabledFeatures[shape_type] shapeClass = getFeatureClasses()[shape_type](croppedImage, croppedMask, **kwargs) if featureNames is not None: for feature in featureNames: shapeClass.enableFeatureByName(feature) for (featureName, featureValue) in six.iteritems(shapeClass.execute()): newFeatureName = 'original_%s_%s' % (shape_type, featureName) featureVector[newFeatureName] = featureValue
def get_features_list(results, extractor): features_list = [] imageTypes = ['original','exponential','gradient','lbp-2D','logarithm','square','squareroot','wavelet'] waveletTypes = ['LLH','LHL','LHH','HLL','HLH','HHL','HHH','LLL'] for waveletType in waveletTypes: imageTypes.append('wavelet-' + waveletType) for imageType in imageTypes: for feature_class in rad.getFeatureClasses().keys(): for feature in extractor.getFeatureNames(feature_class): feature_name = '_'.join([imageType, feature_class, feature]) if feature_name in results.keys(): features_list.append(feature_name) return features_list
def computeFeatures(self, image, mask, imageTypeName, **kwargs): r""" Compute signature using image, mask and \*\*kwargs settings. This function computes the signature for just the passed image (original or derived), it does not pre-process or apply a filter to the passed image. Features / Classes to use for calculation of signature are defined in ``self.enabledFeatures``. See also :py:func:`enableFeaturesByName`. :param image: The cropped (and optionally filtered) SimpleITK.Image object representing the image used :param mask: The cropped SimpleITK.Image object representing the mask used :param imageTypeName: String specifying the filter applied to the image, or "original" if no filter was applied. :param kwargs: Dictionary containing the settings to use for this particular image type. :return: collections.OrderedDict containing the calculated features for all enabled classes. If no features are calculated, an empty OrderedDict will be returned. .. note:: shape descriptors are independent of gray level and therefore calculated separately (handled in `execute`). In this function, no shape features are calculated. """ global logger featureVector = collections.OrderedDict() featureClasses = getFeatureClasses() enabledFeatures = self.enabledFeatures # Calculate feature classes for featureClassName, featureNames in six.iteritems(enabledFeatures): # Handle calculation of shape features separately if featureClassName.startswith('shape'): continue if featureClassName in featureClasses: logger.info('Computing %s', featureClassName) featureClass = featureClasses[featureClassName](image, mask, **kwargs) if featureNames is not None: for feature in featureNames: featureClass.enableFeatureByName(feature) for (featureName, featureValue) in six.iteritems(featureClass.execute()): newFeatureName = '%s_%s_%s' % ( imageTypeName, featureClassName, featureName) featureVector[newFeatureName] = featureValue return featureVector
def __init__(self, *args, **kwargs): global logger self.settings = {} self.enabledImagetypes = {} self.enabledFeatures = {} self.featureClassNames = list(getFeatureClasses().keys()) if len(args) == 1 and isinstance(args[0], dict): logger.info("Loading parameter dictionary") self._applyParams(paramsDict=args[0]) elif len(args) == 1 and (isinstance(args[0], six.string_types) or isinstance(args[0], pathlib.PurePath)): if not os.path.isfile(args[0]): raise IOError("Parameter file %s does not exist." % args[0]) logger.info("Loading parameter file %s", str(args[0])) self._applyParams(paramsFile=args[0]) else: # Set default settings and update with and changed settings contained in kwargs self.settings = self._getDefaultSettings() logger.info('No valid config parameter, using defaults: %s', self.settings) self.enabledImagetypes = {'Original': {}} logger.info('Enabled image types: %s', self.enabledImagetypes) for featureClassName in self.featureClassNames: if featureClassName == 'shape2D': # Do not enable shape2D by default continue self.enabledFeatures[featureClassName] = [] logger.info('Enabled features: %s', self.enabledFeatures) if len(kwargs) > 0: logger.info('Applying custom setting overrides: %s', kwargs) self.settings.update(kwargs) logger.debug("Settings: %s", self.settings) if self.settings.get('binCount', None) is not None: logger.warning( 'Fixed bin Count enabled! However, we recommend using a fixed bin Width. See ' 'http://pyradiomics.readthedocs.io/en/latest/faq.html#radiomics-fixed-bin-width for more ' 'details') self._setTolerance()
def __init__(self, *args, **kwargs): self.logger = logging.getLogger(__name__) self.featureClasses = getFeatureClasses() self.generalInfo = None self.settings = {} self._enabledImagetypes = {} self._enabledFeatures = {} if len(args) == 1 and isinstance( args[0], six.string_types) and os.path.isfile(args[0]): self.logger.info("Loading parameter file") self._applyParams(paramsFile=args[0]) elif len(args) == 1 and isinstance(args[0], dict): self.logger.info("Loading parameter dictionary") self._applyParams(paramsDict=args[0]) else: # Set default settings and update with and changed settings contained in kwargs self.settings = self._getDefaultSettings() self.logger.info( 'No valid config parameter, applying defaults: %s', self.settings) self._enabledImagetypes = {'Original': {}} self.logger.info('Enabled image types: %s', self._enabledImagetypes) self._enabledFeatures = {} for featureClassName in self.getFeatureClassNames(): if featureClassName == 'shape2D': # Do not enable shape2D by default continue self._enabledFeatures[featureClassName] = [] self.logger.info('Enabled features: %s', self._enabledFeatures) if len(kwargs) > 0: self.logger.info('Applying custom setting overrides') self.settings.update(kwargs) self.logger.debug("Settings: %s", self.settings) self._setTolerance()
def _addCustomizationSection(self): customizationCollapsibleButton = ctk.ctkCollapsibleButton() customizationCollapsibleButton.text = 'Extraction Customization' customizationCollapsibleButton.collapsed = True self.layout.addWidget(customizationCollapsibleButton) customizationFormLayout = qt.QFormLayout(customizationCollapsibleButton) # # Radiobuttons to select customization Type # self.manualCustomizationRadioButton = qt.QRadioButton() self.manualCustomizationRadioButton.text = 'Manual Customization' self.manualCustomizationRadioButton.checked = True customizationFormLayout.layout().addWidget(self.manualCustomizationRadioButton) self.parameterFileCustomizationRadioButton = qt.QRadioButton() self.parameterFileCustomizationRadioButton.text = 'Parameter File Customization' self.parameterFileCustomizationRadioButton.checked = False customizationFormLayout.layout().addWidget(self.parameterFileCustomizationRadioButton) # # Manual Customization # self.manualCustomizationGroupBox = qt.QGroupBox('Manual Customization') self.manualCustomizationGroupBox.visible = True # self.manualCustomizationGroupBox.checkable = True customizationFormLayout.addWidget(self.manualCustomizationGroupBox) manualCustomizationFormLayout = qt.QFormLayout(self.manualCustomizationGroupBox) # Feature Class section featureClassCollapsibleButton = ctk.ctkCollapsibleButton() featureClassCollapsibleButton.text = 'Feature Classes' featureClassCollapsibleButton.collapsed = False manualCustomizationFormLayout.addWidget(featureClassCollapsibleButton) featureClassLayout = qt.QFormLayout(featureClassCollapsibleButton) self.featuresLayout = qt.QHBoxLayout() featureClassLayout.addRow('Features:', self.featuresLayout) self.featuresButtonGroup = qt.QButtonGroup(self.featuresLayout) self.featuresButtonGroup.exclusive = False # Get the feature classes dynamically self.features = list(getFeatureClasses().keys()) # Create a checkbox for each feature featureButtons = {} for feature in self.features: featureButtons[feature] = qt.QCheckBox(feature) # TODO: decide which features to enable by default featureButtons[feature].checked = False if feature == 'firstorder': featureButtons[feature].checked = True self.featuresButtonGroup.addButton(featureButtons[feature]) self.featuresLayout.layout().addWidget(featureButtons[feature]) # set the ID to be the index of this feature in the list self.featuresButtonGroup.setId(featureButtons[feature], self.features.index(feature)) # Add buttons to select all or none self.buttonsLayout = qt.QHBoxLayout() featureClassLayout.addRow('Toggle Features:', self.buttonsLayout) self.calculateAllFeaturesButton = qt.QPushButton('All Features') self.calculateAllFeaturesButton.toolTip = 'Calculate all feature classes.' self.calculateAllFeaturesButton.enabled = True self.buttonsLayout.addWidget(self.calculateAllFeaturesButton) self.calculateNoFeaturesButton = qt.QPushButton('No Features') self.calculateNoFeaturesButton.toolTip = 'Calculate no feature classes.' self.calculateNoFeaturesButton.enabled = True self.buttonsLayout.addWidget(self.calculateNoFeaturesButton) # Resampling and Filtering filteringCollapsibleButton = ctk.ctkCollapsibleButton() filteringCollapsibleButton.text = 'Resampling and Filtering' filteringCollapsibleButton.collapsed = False manualCustomizationFormLayout.addRow(filteringCollapsibleButton) # Layout within the dummy collapsible button filteringFormLayout = qt.QFormLayout(filteringCollapsibleButton) # Resampling self.resampledVoxelSize = qt.QLineEdit() self.resampledVoxelSize.toolTip = 'Three floating-point numbers separated by comma defining the resampled pixel ' \ 'size (mm).' filteringFormLayout.addRow('Resampled voxel size', self.resampledVoxelSize) # LoG kernel sizes. default to 5 (?) self.logKernelSizes = qt.QLineEdit() self.logKernelSizes.toolTip = 'Laplacian of Gaussian filter kernel sizes (mm), separated by comma. ' \ 'If empty, no LoG filtering will be applied.' filteringFormLayout.addRow('LoG kernel sizes', self.logKernelSizes) # Wavelet self.waveletCheckBox = qt.QCheckBox() self.waveletCheckBox.checked = 0 self.waveletCheckBox.toolTip = 'If checked, PyRadiomics will calculate features on the image after applying ' \ 'wavelet transformation' filteringFormLayout.addRow('Wavelet-based features', self.waveletCheckBox) # # Feature calculation settings # settingsCollapsibleButton = ctk.ctkCollapsibleButton() settingsCollapsibleButton.text = 'Settings' settingsCollapsibleButton.collapsed = False manualCustomizationFormLayout.addWidget(settingsCollapsibleButton) # Layout within the dummy collapsible button settingsFormLayout = qt.QFormLayout(settingsCollapsibleButton) # bin width, defaults to 25 self.binWidthSliderWidget = ctk.ctkSliderWidget() self.binWidthSliderWidget.singleStep = 1 self.binWidthSliderWidget.decimals = 2 self.binWidthSliderWidget.minimum = 0.01 self.binWidthSliderWidget.maximum = 100 self.binWidthSliderWidget.value = 25 self.binWidthSliderWidget.toolTip = 'Set the bin width' settingsFormLayout.addRow('Bin Width', self.binWidthSliderWidget) # symmetricalGLCM flag, defaults to false self.symmetricalGLCMCheckBox = qt.QCheckBox() self.symmetricalGLCMCheckBox.checked = 1 self.symmetricalGLCMCheckBox.toolTip = 'Use a symmetrical GLCM matrix' settingsFormLayout.addRow('Enforce Symmetrical GLCM', self.symmetricalGLCMCheckBox) # # Parameter File Customization # self.parameterCustomizationGroupBox = qt.QGroupBox('Parameter File Customization') self.parameterCustomizationGroupBox.visible = False # self.parameterCustomizationGroupBox.checkable = True customizationFormLayout.addWidget(self.parameterCustomizationGroupBox) parameterCustomizationFormLayout = qt.QFormLayout(self.parameterCustomizationGroupBox) # Path edit to select parameter file self.parameterFilePathLineEdit = ctk.ctkPathLineEdit() parameterCustomizationFormLayout.addRow("Parameter File", self.parameterFilePathLineEdit)
def setup(self): ScriptedLoadableModuleWidget.setup(self) # Setup a logger for the extension log messages (child logger of pyradiomics) self.logger = logging.getLogger(radiomics.logger.name + '.slicer') # Instantiate and connect widgets ... # # Parameters Area # parametersCollapsibleButton = ctk.ctkCollapsibleButton() parametersCollapsibleButton.text = 'Parameters' self.layout.addWidget(parametersCollapsibleButton) # Layout within the dummy collapsible button parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton) # # input volume selector # self.inputVolumeSelector = slicer.qMRMLNodeComboBox() self.inputVolumeSelector.nodeTypes = ['vtkMRMLScalarVolumeNode'] self.inputVolumeSelector.selectNodeUponCreation = True self.inputVolumeSelector.addEnabled = False self.inputVolumeSelector.removeEnabled = False self.inputVolumeSelector.noneEnabled = False self.inputVolumeSelector.showHidden = False self.inputVolumeSelector.showChildNodeTypes = False self.inputVolumeSelector.setMRMLScene(slicer.mrmlScene) self.inputVolumeSelector.setToolTip( 'Pick the input image for the feature calculation.') parametersFormLayout.addRow('Input Image Volume: ', self.inputVolumeSelector) # # input mask volume selector # self.inputMaskSelector = slicer.qMRMLNodeComboBox() self.inputMaskSelector.nodeTypes = ['vtkMRMLLabelMapVolumeNode'] self.inputMaskSelector.selectNodeUponCreation = True self.inputMaskSelector.addEnabled = False self.inputMaskSelector.removeEnabled = False self.inputMaskSelector.noneEnabled = True self.inputMaskSelector.showHidden = False self.inputMaskSelector.showChildNodeTypes = False self.inputMaskSelector.setMRMLScene(slicer.mrmlScene) self.inputMaskSelector.setToolTip( 'Pick the input mask for the feature calculation.') parametersFormLayout.addRow('Input LabelMap: ', self.inputMaskSelector) # # input segmentation selector # self.inputSegmentationSelector = slicer.qMRMLNodeComboBox() self.inputSegmentationSelector.nodeTypes = ['vtkMRMLSegmentationNode'] self.inputSegmentationSelector.selectNodeUponCreation = True self.inputSegmentationSelector.addEnabled = False self.inputSegmentationSelector.removeEnabled = False self.inputSegmentationSelector.noneEnabled = True self.inputSegmentationSelector.showHidden = False self.inputSegmentationSelector.showChildNodeTypes = False self.inputSegmentationSelector.setMRMLScene(slicer.mrmlScene) self.inputSegmentationSelector.setToolTip( 'Pick the input segmentation for the feature calculation.') parametersFormLayout.addRow('Input Segmentation: ', self.inputSegmentationSelector) # # Feature class selection # self.featuresLayout = qt.QHBoxLayout() parametersFormLayout.addRow('Features:', self.featuresLayout) self.featuresButtonGroup = qt.QButtonGroup(self.featuresLayout) self.featuresButtonGroup.exclusive = False # Get the feature classes dynamically self.features = getFeatureClasses().keys() # Create a checkbox for each feature featureButtons = {} for feature in self.features: featureButtons[feature] = qt.QCheckBox(feature) # TODO: decide which features to enable by default featureButtons[feature].checked = False if feature == 'firstorder': featureButtons[feature].checked = True self.featuresButtonGroup.addButton(featureButtons[feature]) self.featuresLayout.layout().addWidget(featureButtons[feature]) # set the ID to be the index of this feature in the list self.featuresButtonGroup.setId(featureButtons[feature], self.features.index(feature)) # Add buttons to select all or none self.buttonsLayout = qt.QHBoxLayout() parametersFormLayout.addRow('Toggle Features:', self.buttonsLayout) self.calculateAllFeaturesButton = qt.QPushButton('All Features') self.calculateAllFeaturesButton.toolTip = 'Calculate all feature classes.' self.calculateAllFeaturesButton.enabled = True self.buttonsLayout.addWidget(self.calculateAllFeaturesButton) self.calculateNoFeaturesButton = qt.QPushButton('No Features') self.calculateNoFeaturesButton.toolTip = 'Calculate no feature classes.' self.calculateNoFeaturesButton.enabled = True self.buttonsLayout.addWidget(self.calculateNoFeaturesButton) # # Feature calculation options # optionsCollapsibleButton = ctk.ctkCollapsibleButton() optionsCollapsibleButton.text = 'Options' optionsCollapsibleButton.collapsed = True self.layout.addWidget(optionsCollapsibleButton) # Layout within the dummy collapsible button optionsFormLayout = qt.QFormLayout(optionsCollapsibleButton) # bin width, defaults to 25 self.binWidthSliderWidget = ctk.ctkSliderWidget() self.binWidthSliderWidget.singleStep = 1 self.binWidthSliderWidget.decimals = 0 self.binWidthSliderWidget.minimum = 1 self.binWidthSliderWidget.maximum = 100 self.binWidthSliderWidget.value = 25 self.binWidthSliderWidget.toolTip = 'Set the bin width' optionsFormLayout.addRow('Bin Width', self.binWidthSliderWidget) # symmetricalGLCM flag, defaults to false self.symmetricalGLCMCheckBox = qt.QCheckBox() self.symmetricalGLCMCheckBox.checked = 1 self.symmetricalGLCMCheckBox.toolTip = 'Use a symmetrical GLCM matrix' optionsFormLayout.addRow('Enforce Symmetrical GLCM', self.symmetricalGLCMCheckBox) # label for the mask, defaults to 1 self.labelSliderWidget = ctk.ctkSliderWidget() self.labelSliderWidget.singleStep = 1 self.labelSliderWidget.decimals = 0 self.labelSliderWidget.minimum = 0 self.labelSliderWidget.maximum = 255 self.labelSliderWidget.value = 1 self.labelSliderWidget.toolTip = 'Set the label to use for masking the image' # optionsFormLayout.addRow('Label', self.labelSliderWidget) # debug logging flag, defaults to false self.debuggingCheckBox = qt.QCheckBox() self.debuggingCheckBox.checked = 0 self.debuggingCheckBox.toolTip = \ 'If checked, PyRadiomics log messages from level DEBUG and higher will be added to the slicer log' optionsFormLayout.addRow('Store debug log', self.debuggingCheckBox) # # Output table # outputCollapsibleButton = ctk.ctkCollapsibleButton() outputCollapsibleButton.text = 'Output' self.layout.addWidget(outputCollapsibleButton) outputFormLayout = qt.QFormLayout(outputCollapsibleButton) self.outputTableSelector = slicer.qMRMLNodeComboBox() self.outputTableSelector.nodeTypes = ['vtkMRMLTableNode'] self.outputTableSelector.addEnabled = True self.outputTableSelector.selectNodeUponCreation = True self.outputTableSelector.renameEnabled = True self.outputTableSelector.removeEnabled = True self.outputTableSelector.noneEnabled = False self.outputTableSelector.setMRMLScene(slicer.mrmlScene) self.outputTableSelector.toolTip = \ 'Select the table where features will be saved, resets feature values on each run.' outputFormLayout.addRow('Output table:', self.outputTableSelector) # # Apply Button # self.applyButton = qt.QPushButton('Apply') self.applyButton.toolTip = 'Run the algorithm.' self.applyButton.enabled = False self.layout.addWidget(self.applyButton) # connections self.calculateAllFeaturesButton.connect( 'clicked(bool)', self.onCalculateAllFeaturesButton) self.calculateNoFeaturesButton.connect( 'clicked(bool)', self.onCalculateNoFeaturesButton) self.applyButton.connect('clicked(bool)', self.onApplyButton) self.inputVolumeSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onSelect) self.inputMaskSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onSelect) self.inputSegmentationSelector.connect( 'currentNodeChanged(vtkMRMLNode*)', self.onSelect) # Add vertical spacer self.layout.addStretch(1) # Refresh Apply button state self.onSelect()
import logging import os from nose_parameterized import parameterized import numpy import six from radiomics import getFeatureClasses from testUtils import custom_name_func, RadiomicsTestUtils testUtils = RadiomicsTestUtils() tests = sorted(testUtils.getTests()) featureClasses = getFeatureClasses() class TestMatrices: def generate_scenarios(): global tests, featureClasses for testCase in tests: for className, featureClass in six.iteritems(featureClasses): assert (featureClass is not None) if "_calculateMatrix" in dir(featureClass): logging.debug('generate_scenarios: featureClass = %s', className) yield testCase, className @parameterized.expand(generate_scenarios(),