Hello Radiomics example: using the feature extractor to calculate features

This example shows how to use the radiomics package and the feature extractor. The feature extractor handles preprocessing, and then calls the needed featureclasses to calculate the features. It is also possible to directly instantiate the feature classes. However, this is not recommended for use outside debugging or development. For more information, see helloFeatureClass.


In [12]:
from __future__ import print_function
import sys
import os
import logging
import six
from radiomics import featureextractor, getFeatureClasses
import radiomics

Setting up logging

Regulate verbosity of PyRadiomics (outputs to stderr)


In [13]:
# Regulate verbosity with radiomics.setVerbosity
# radiomics.setVerbosity(logging.INFO)  # Use logging.DEBUG for maximum output, default verbosity level = WARNING

Set up logging to a log file


In [14]:
# Get the PyRadiomics logger (default log-level = INFO)
logger = radiomics.logger
logger.setLevel(logging.DEBUG)  # set level to DEBUG to include debug log messages in log file

# Write out all log entries to a file
handler = logging.FileHandler(filename='testLog.txt', mode='w')
formatter = logging.Formatter('%(levelname)s:%(name)s: %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

Getting the testcase

Test cases can be downloaded to temporary files. This is handled by the radiomics.getTestCase() function, which checks if the requested test case is available and if not, downloads it. It returns a tuple with the location of the image and mask of the requested test case, or (None, None) if it fails.

Alternatively, if the data is available somewhere locally, this directory can be passed as a second argument to radiomics.getTestCase(). If that directory does not exist or does not contain the testcase, functionality reverts to default and tries to download the test data.

If getting the test case fails, PyRadiomics will log an error explaining the cause.


In [15]:
featureClasses = getFeatureClasses()
imageName, maskName = radiomics.getTestCase('brain1')

if imageName is None or maskName is None:  # Something went wrong, in this case PyRadiomics will also log an error
    raise Exception('Error getting testcase!')  # Raise exception to prevent cells below from running in case of "run all"

Initializing the feature extractor

Extraction Settings


In [16]:
# Use a parameter file, this customizes the extraction settings and also specifies the input image types to use and which features should be extracted
params = os.path.join('..', 'examples', 'exampleSettings', 'Params.yaml')

extractor = featureextractor.RadiomicsFeatureExtractor(params)

In [17]:
# Alternative: use hardcoded settings (separate for settings, input image types and enabled features)
settings = {}
settings['binWidth'] = 25
settings['resampledPixelSpacing'] = None
# settings['resampledPixelSpacing'] = [3, 3, 3]  # This is an example for defining resampling (voxels with size 3x3x3mm)
settings['interpolator'] = 'sitkBSpline'
settings['verbose'] = True

extractor = featureextractor.RadiomicsFeatureExtractor(**settings)

Input images: applying filters


In [18]:
# By default, only 'Original' (no filter applied) is enabled. Optionally enable some image types:

# extractor.enableImageTypeByName('Wavelet')
# extractor.enableImageTypeByName('LoG', customArgs={'sigma':[3.0]})
# extractor.enableImageTypeByName('Square')
# extractor.enableImageTypeByName('SquareRoot')
# extractor.enableImageTypeByName('Exponential')
# extractor.enableImageTypeByName('Logarithm')

# Alternative; set filters in one operation 
# This updates current enabled image types, i.e. overwrites custom settings specified per filter. 
# However, image types already enabled, but not passed in this call, are not disabled or altered.

# extractor.enableImageTypes(Wavelet={}, LoG={'sigma':[3.0]})

print('Enabled input images:')
for imageType in extractor.enabledImagetypes.keys():
    print('\t' + imageType)


Enabled input images:
	Original

Feature classes: setting which feature(classes) need to be calculated


In [19]:
# Disable all classes
extractor.disableAllFeatures()

# Enable all features in firstorder
extractor.enableFeatureClassByName('firstorder')

# Alternative; only enable 'Mean' and 'Skewness' features in firstorder
# extractor.enableFeaturesByName(firstorder=['Mean', 'Skewness'])

Getting the docstrings of the active features


In [20]:
print('Active features:')
for cls, features in six.iteritems(extractor.enabledFeatures):
    if len(features) == 0:
        features = [f for f, deprecated in six.iteritems(featureClasses[cls].getFeatureNames()) if not deprecated]
    for f in features:
        print(f)
        print(getattr(featureClasses[cls], 'get%sFeatureValue' % f).__doc__)


Active features:
InterquartileRange

    **10. Interquartile Range**

    .. math::
      \textit{interquartile range} = \textbf{P}_{75} - \textbf{P}_{25}

    Here :math:`\textbf{P}_{25}` and :math:`\textbf{P}_{75}` are the 25\ :sup:`th` and 75\ :sup:`th` percentile of the
    image array, respectively.
    
Skewness

    **16. Skewness**

    .. math::
      \textit{skewness} = \displaystyle\frac{\mu_3}{\sigma^3} =
      \frac{\frac{1}{N_p}\sum^{N_p}_{i=1}{(\textbf{X}(i)-\bar{X})^3}}
      {\left(\sqrt{\frac{1}{N_p}\sum^{N_p}_{i=1}{(\textbf{X}(i)-\bar{X})^2}}\right)^3}

    Where :math:`\mu_3` is the 3\ :sup:`rd` central moment.

    Skewness measures the asymmetry of the distribution of values about the Mean value. Depending on where the tail is
    elongated and the mass of the distribution is concentrated, this value can be positive or negative.

    Related links:

    https://en.wikipedia.org/wiki/Skewness

    .. note::
      In case of a flat region, the standard deviation and 4\ :sup:`rd` central moment will be both 0. In this case, a
      value of 0 is returned.
    
TotalEnergy

    **2. Total Energy**

    .. math::
      \textit{total energy} = V_{voxel}\displaystyle\sum^{N_p}_{i=1}{(\textbf{X}(i) + c)^2}

    Here, :math:`c` is optional value, defined by ``voxelArrayShift``, which shifts the intensities to prevent negative
    values in :math:`\textbf{X}`. This ensures that voxels with the lowest gray values contribute the least to Energy,
    instead of voxels with gray level intensity closest to 0.

    Total Energy is the value of Energy feature scaled by the volume of the voxel in cubic mm.

    .. note::
      This feature is volume-confounded, a larger value of :math:`c` increases the effect of volume-confounding.

    .. note::
      Not present in IBSI feature definitions
    
Uniformity

    **19. Uniformity**

    .. math::
      \textit{uniformity} = \displaystyle\sum^{N_g}_{i=1}{p(i)^2}

    Uniformity is a measure of the sum of the squares of each intensity value. This is a measure of the homogeneity of
    the image array, where a greater uniformity implies a greater homogeneity or a smaller range of discrete intensity
    values.

    .. note::
      Defined by IBSI as Intensity Histogram Uniformity.
    
Median

    **9. Median**

    The median gray level intensity within the ROI.
    
Energy

    **1. Energy**

    .. math::
      \textit{energy} = \displaystyle\sum^{N_p}_{i=1}{(\textbf{X}(i) + c)^2}

    Here, :math:`c` is optional value, defined by ``voxelArrayShift``, which shifts the intensities to prevent negative
    values in :math:`\textbf{X}`. This ensures that voxels with the lowest gray values contribute the least to Energy,
    instead of voxels with gray level intensity closest to 0.

    Energy is a measure of the magnitude of voxel values in an image. A larger values implies a greater sum of the
    squares of these values.

    .. note::
      This feature is volume-confounded, a larger value of :math:`c` increases the effect of volume-confounding.
    
RobustMeanAbsoluteDeviation

    **13. Robust Mean Absolute Deviation (rMAD)**

    .. math::
      \textit{rMAD} = \frac{1}{N_{10-90}}\displaystyle\sum^{N_{10-90}}_{i=1}
      {|\textbf{X}_{10-90}(i)-\bar{X}_{10-90}|}

    Robust Mean Absolute Deviation is the mean distance of all intensity values
    from the Mean Value calculated on the subset of image array with gray levels in between, or equal
    to the 10\ :sup:`th` and 90\ :sup:`th` percentile.
    
MeanAbsoluteDeviation

    **12. Mean Absolute Deviation (MAD)**

    .. math::
      \textit{MAD} = \frac{1}{N_p}\displaystyle\sum^{N_p}_{i=1}{|\textbf{X}(i)-\bar{X}|}

    Mean Absolute Deviation is the mean distance of all intensity values from the Mean Value of the image array.
    
Maximum

    **7. Maximum**

    .. math::
      \textit{maximum} = \max(\textbf{X})

    The maximum gray level intensity within the ROI.
    
RootMeanSquared

    **14. Root Mean Squared (RMS)**

    .. math::
      \textit{RMS} = \sqrt{\frac{1}{N_p}\sum^{N_p}_{i=1}{(\textbf{X}(i) + c)^2}}

    Here, :math:`c` is optional value, defined by ``voxelArrayShift``, which shifts the intensities to prevent negative
    values in :math:`\textbf{X}`. This ensures that voxels with the lowest gray values contribute the least to RMS,
    instead of voxels with gray level intensity closest to 0.

    RMS is the square-root of the mean of all the squared intensity values. It is another measure of the magnitude of
    the image values. This feature is volume-confounded, a larger value of :math:`c` increases the effect of
    volume-confounding.
    
90Percentile

    **6. 90th percentile**

    The 90\ :sup:`th` percentile of :math:`\textbf{X}`
    
Minimum

    **4. Minimum**

    .. math::
      \textit{minimum} = \min(\textbf{X})
    
Entropy

    **3. Entropy**

    .. math::
      \textit{entropy} = -\displaystyle\sum^{N_g}_{i=1}{p(i)\log_2\big(p(i)+\epsilon\big)}

    Here, :math:`\epsilon` is an arbitrarily small positive number (:math:`\approx 2.2\times10^{-16}`).

    Entropy specifies the uncertainty/randomness in the image values. It measures the average amount of information
    required to encode the image values.

    .. note::
      Defined by IBSI as Intensity Histogram Entropy.
    
Range

    **11. Range**

    .. math::
      \textit{range} = \max(\textbf{X}) - \min(\textbf{X})

    The range of gray values in the ROI.
    
Variance

    **18. Variance**

    .. math::
      \textit{variance} = \frac{1}{N_p}\displaystyle\sum^{N_p}_{i=1}{(\textbf{X}(i)-\bar{X})^2}

    Variance is the the mean of the squared distances of each intensity value from the Mean value. This is a measure of
    the spread of the distribution about the mean. By definition, :math:`\textit{variance} = \sigma^2`
    
10Percentile

    **5. 10th percentile**

    The 10\ :sup:`th` percentile of :math:`\textbf{X}`
    
Kurtosis

    **17. Kurtosis**

    .. math::
      \textit{kurtosis} = \displaystyle\frac{\mu_4}{\sigma^4} =
      \frac{\frac{1}{N_p}\sum^{N_p}_{i=1}{(\textbf{X}(i)-\bar{X})^4}}
      {\left(\frac{1}{N_p}\sum^{N_p}_{i=1}{(\textbf{X}(i)-\bar{X}})^2\right)^2}

    Where :math:`\mu_4` is the 4\ :sup:`th` central moment.

    Kurtosis is a measure of the 'peakedness' of the distribution of values in the image ROI. A higher kurtosis implies
    that the mass of the distribution is concentrated towards the tail(s) rather than towards the mean. A lower kurtosis
    implies the reverse: that the mass of the distribution is concentrated towards a spike near the Mean value.

    Related links:

    https://en.wikipedia.org/wiki/Kurtosis

    .. note::
      In case of a flat region, the standard deviation and 4\ :sup:`rd` central moment will be both 0. In this case, a
      value of 0 is returned.

    .. note::
      The IBSI feature definition implements excess kurtosis, where kurtosis is corrected by -3, yielding 0 for normal
      distributions. The PyRadiomics kurtosis is not corrected, yielding a value 3 higher than the IBSI kurtosis.
    
Mean

    **8. Mean**

    .. math::
      \textit{mean} = \frac{1}{N_p}\displaystyle\sum^{N_p}_{i=1}{\textbf{X}(i)}

    The average gray level intensity within the ROI.
    

Calculating the values of the active features


In [21]:
print('Calculating features')
featureVector = extractor.execute(imageName, maskName)


Calculating features

In [22]:
# Show output
for featureName in featureVector.keys():
    print('Computed %s: %s' % (featureName, featureVector[featureName]))


Computed diagnostics_Versions_PyRadiomics: 2.1.2.post68.dev0+g590d9fe
Computed diagnostics_Versions_Numpy: 1.15.1
Computed diagnostics_Versions_SimpleITK: 0.9.1
Computed diagnostics_Versions_PyWavelet: 0.5.2
Computed diagnostics_Versions_Python: 2.7.11
Computed diagnostics_Configuration_Settings: {'distances': [1], 'verbose': True, 'additionalInfo': True, 'force2D': False, 'interpolator': 'sitkBSpline', 'resampledPixelSpacing': None, 'label': 1, 'normalizeScale': 1, 'normalize': False, 'force2Ddimension': 0, 'removeOutliers': None, 'minimumROISize': None, 'binWidth': 25, 'minimumROIDimensions': 2, 'preCrop': False, 'resegmentRange': None, 'padDistance': 5}
Computed diagnostics_Configuration_EnabledImageTypes: {'Original': {}}
Computed diagnostics_Image-original_Hash: 5c9ce3ca174f0f8324aa4d277e0fef82dc5ac566
Computed diagnostics_Image-original_Dimensionality: 3D
Computed diagnostics_Image-original_Spacing: (0.7812499999999999, 0.7812499999999999, 6.499999999999998)
Computed diagnostics_Image-original_Size: (256, 256, 25)
Computed diagnostics_Image-original_Mean: 385.6564080810547
Computed diagnostics_Image-original_Minimum: 0.0
Computed diagnostics_Image-original_Maximum: 3057.0
Computed diagnostics_Mask-original_Hash: 9dc2c3137b31fd872997d92c9a92d5178126d9d3
Computed diagnostics_Mask-original_Spacing: (0.7812499999999999, 0.7812499999999999, 6.499999999999998)
Computed diagnostics_Mask-original_Size: (256, 256, 25)
Computed diagnostics_Mask-original_BoundingBox: (162, 84, 11, 47, 70, 7)
Computed diagnostics_Mask-original_VoxelNum: 4137
Computed diagnostics_Mask-original_VolumeNum: 2
Computed diagnostics_Mask-original_CenterOfMassIndex: (186.98549673676578, 106.3562968334542, 14.38917089678511)
Computed diagnostics_Mask-original_CenterOfMass: (46.47304432559825, 16.518518098863908, 15.529610829103234)
Computed original_firstorder_InterquartileRange: 253.0
Computed original_firstorder_Skewness: 0.27565085908587594
Computed original_firstorder_Uniformity: 0.045156963555862184
Computed original_firstorder_Median: 812.0
Computed original_firstorder_Energy: 2918821481.0
Computed original_firstorder_RobustMeanAbsoluteDeviation: 103.00138343026683
Computed original_firstorder_MeanAbsoluteDeviation: 133.44726195252767
Computed original_firstorder_TotalEnergy: 11579797135.314934
Computed original_firstorder_Maximum: 1266.0
Computed original_firstorder_RootMeanSquared: 839.9646448180755
Computed original_firstorder_90Percentile: 1044.4
Computed original_firstorder_Minimum: 468.0
Computed original_firstorder_Entropy: 4.601935553903786
Computed original_firstorder_Range: 798.0
Computed original_firstorder_Variance: 24527.07920837261
Computed original_firstorder_10Percentile: 632.0
Computed original_firstorder_Kurtosis: 2.1807729393860265
Computed original_firstorder_Mean: 825.2354363065023