Exemplo n.º 1
0
    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()
Exemplo n.º 2
0
    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] = []
Exemplo n.º 3
0
  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")
Exemplo n.º 4
0
    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
Exemplo n.º 6
0
    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
Exemplo n.º 7
0
    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()
Exemplo n.º 8
0
    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)
Exemplo n.º 10
0
    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()
Exemplo n.º 11
0
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(),