import numpy
from radiomics import base, cMatrices, deprecated
[docs]class RadiomicsGLDM(base.RadiomicsFeaturesBase):
r"""
A Gray Level Dependence Matrix (GLDM) quantifies gray level dependencies in an image.
A gray level dependency is defined as a the number of connected voxels within distance :math:`\delta` that are
dependent on the center voxel.
A neighbouring voxel with gray level :math:`j` is considered dependent on center voxel with gray level :math:`i`
if :math:`|i-j|\le\alpha`. In a gray level dependence matrix :math:`\textbf{P}(i,j)` the :math:`(i,j)`\ :sup:`th`
element describes the number of times a voxel with gray level :math:`i` with :math:`j` dependent voxels
in its neighbourhood appears in image.
As a two dimensional example, consider the following 5x5 image, with 5 discrete gray levels:
.. math::
\textbf{I} = \begin{bmatrix}
5 & 2 & 5 & 4 & 4\\
3 & 3 & 3 & 1 & 3\\
2 & 1 & 1 & 1 & 3\\
4 & 2 & 2 & 2 & 3\\
3 & 5 & 3 & 3 & 2 \end{bmatrix}
For :math:`\alpha=0` and :math:`\delta = 1`, the GLDM then becomes:
.. math::
\textbf{P} = \begin{bmatrix}
0 & 1 & 2 & 1 \\
1 & 2 & 3 & 0 \\
1 & 4 & 4 & 0 \\
1 & 2 & 0 & 0 \\
3 & 0 & 0 & 0 \end{bmatrix}
Let:
- :math:`N_g` be the number of discreet intensity values in the image
- :math:`N_d` be the number of discreet dependency sizes in the image
- :math:`N_z` be the number of dependency zones in the image, which is equal to
:math:`\sum^{N_g}_{i=1}\sum^{N_d}_{j=1}{\textbf{P}(i,j)}`
- :math:`\textbf{P}(i,j)` be the dependence matrix
- :math:`p(i,j)` be the normalized dependence matrix, defined as :math:`p(i,j) = \frac{\textbf{P}(i,j)}{N_z}`
.. note::
Because incomplete zones are allowed, every voxel in the ROI has a dependency zone. Therefore, :math:`N_z = N_p`,
where :math:`N_p` is the number of voxels in the image.
Due to the fact that :math:`Nz = N_p`, the Dependence Percentage and Gray Level Non-Uniformity Normalized (GLNN)
have been removed. The first because it would always compute to 1, the latter because it is mathematically equal to
first order - Uniformity (see :py:func:`~radiomics.firstorder.RadiomicsFirstOrder.getUniformityFeatureValue()`). For
mathematical proofs, see :ref:`here <radiomics-excluded-gldm-label>`.
The following class specific settings are possible:
- distances [[1]]: List of integers. This specifies the distances between the center voxel and the neighbor, for which
angles should be generated.
- gldm_a [0]: float, :math:`\alpha` cutoff value for dependence. A neighbouring voxel with gray level :math:`j` is considered
dependent on center voxel with gray level :math:`i` if :math:`|i-j|\le\alpha`
"""
def __init__(self, inputImage, inputMask, **kwargs):
super(RadiomicsGLDM, self).__init__(inputImage, inputMask, **kwargs)
self.gldm_a = kwargs.get('gldm_a', 0)
self.P_gldm = None
self._applyBinning()
def _initCalculation(self):
self.coefficients['Np'] = len(self.labelledVoxelCoordinates[0])
self.P_gldm = self._calculateMatrix()
self.logger.debug('Feature class initialized, calculated GLDM with shape %s', self.P_gldm.shape)
def _calculateMatrix(self):
P_gldm, angles = cMatrices.calculate_gldm(self.matrix,
self.maskArray,
numpy.array(self.settings.get('distances', [1])),
self.coefficients['Ng'],
self.gldm_a,
self.settings.get('force2D', False),
self.settings.get('force2Ddimension', 0))
jvector = numpy.arange(1, P_gldm.shape[1] + 1, dtype='float64')
# Delete rows and columns that specify gray levels not present in the ROI
pd = numpy.sum(P_gldm, 0)
pg = numpy.sum(P_gldm, 1)
P_gldm = numpy.delete(P_gldm, numpy.where(pg == 0), 0)
P_gldm = numpy.delete(P_gldm, numpy.where(pd == 0), 1)
jvector = numpy.delete(jvector, numpy.where(pd == 0))
pg = numpy.delete(pg, numpy.where(pg == 0))
pd = numpy.delete(pd, numpy.where(pd == 0))
self.coefficients['pd'] = pd
self.coefficients['pg'] = pg
self.coefficients['ivector'] = self.coefficients['grayLevels']
self.coefficients['jvector'] = jvector
return P_gldm
[docs] def getSmallDependenceEmphasisFeatureValue(self):
r"""
**1. Small Dependence Emphasis (SDE)**
.. math::
SDE = \frac{\sum^{N_g}_{i=1}\sum^{N_d}_{j=1}{\frac{\textbf{P}(i,j)}{i^2}}}{N_z}
A measure of the distribution of small dependencies, with a greater value indicative
of smaller dependence and less homogeneous textures.
"""
pd = self.coefficients['pd']
jvector = self.coefficients['jvector']
Nz = self.coefficients['Np'] # Nz = Np, see class docstring
try:
sde = numpy.sum(pd / (jvector ** 2)) / Nz
return sde
except ZeroDivisionError:
return numpy.core.nan
[docs] def getLargeDependenceEmphasisFeatureValue(self):
r"""
**2. Large Dependence Emphasis (LDE)**
.. math::
LDE = \frac{\sum^{N_g}_{i=1}\sum^{N_d}_{j=1}{\textbf{P}(i,j)j^2}}{N_z}
A measure of the distribution of large dependencies, with a greater value indicative
of larger dependence and more homogeneous textures.
"""
pd = self.coefficients['pd']
jvector = self.coefficients['jvector']
Nz = self.coefficients['Np'] # Nz = Np, see class docstring
try:
lre = numpy.sum(pd * (jvector ** 2)) / Nz
return lre
except ZeroDivisionError:
return numpy.core.nan
[docs] def getGrayLevelVarianceFeatureValue(self):
r"""
**6. Gray Level Variance (GLV)**
.. math::
GLV = \displaystyle\sum^{N_g}_{i=1}\displaystyle\sum^{N_d}_{j=1}{p(i,j)(i - \mu)^2} \text{, where}
\mu = \displaystyle\sum^{N_g}_{i=1}\displaystyle\sum^{N_d}_{j=1}{ip(i,j)}
Measures the variance in grey level in the image.
"""
ivector = self.coefficients['ivector']
Nz = self.coefficients['Np'] # Nz = Np, see class docstring
pg = self.coefficients['pg'] / Nz # divide by Nz to get the normalized matrix
u_i = numpy.sum(pg * ivector)
glv = numpy.sum(pg * (ivector - u_i) ** 2)
return glv
[docs] def getDependenceVarianceFeatureValue(self):
r"""
**7. Dependence Variance (DV)**
.. math::
DV = \displaystyle\sum^{N_g}_{i=1}\displaystyle\sum^{N_d}_{j=1}{p(i,j)(j - \mu)^2} \text{, where}
\mu = \displaystyle\sum^{N_g}_{i=1}\displaystyle\sum^{N_d}_{j=1}{jp(i,j)}
Measures the variance in dependence size in the image.
"""
jvector = self.coefficients['jvector']
Nz = self.coefficients['Np'] # Nz = Np, see class docstring
pd = self.coefficients['pd'] / Nz # divide by Nz to get the normalized matrix
u_j = numpy.sum(pd * jvector)
dv = numpy.sum(pd * (jvector - u_j) ** 2)
return dv
[docs] def getDependenceEntropyFeatureValue(self):
r"""
**8. Dependence Entropy (DE)**
.. math::
Dependence Entropy = -\displaystyle\sum^{N_g}_{i=1}\displaystyle\sum^{N_d}_{j=1}{p(i,j)\log_{2}(p(i,j)+\epsilon)}
"""
eps = numpy.spacing(1)
Nz = self.coefficients['Np'] # Nz = Np, see class docstring
p_gldm = self.P_gldm / Nz # divide by Nz to get the normalized matrix
return -numpy.sum(p_gldm * numpy.log2(p_gldm + eps))
[docs] @deprecated
def getDependencePercentageFeatureValue(self):
r"""
**DEPRECATED. Dependence Percentage**
.. math::
\textit{dependence percentage} = \frac{N_z}{N_p}
.. warning::
This feature has been deprecated, as it would always compute 1. See
:ref:`here <radiomics-excluded-gldm-dependence-percentage-label>` for more details. **Enabling this feature will result in the
logging of a DeprecationWarning (does not interrupt extraction of other features), no value is calculated for this features**
"""
raise DeprecationWarning('GLDM - Dependence Percentage always computes 1, '
'see http://pyradiomics.readthedocs.io/en/latest/removedfeatures.html for more details')
[docs] def getLowGrayLevelEmphasisFeatureValue(self):
r"""
**9. Low Gray Level Emphasis (LGLE)**
.. math::
LGLE = \frac{\sum^{N_g}_{i=1}\sum^{N_d}_{j=1}{\frac{\textbf{P}(i,j)}{i^2}}}{N_z}
Measures the distribution of low gray-level values, with a higher value indicating a greater
concentration of low gray-level values in the image.
"""
pg = self.coefficients['pg']
ivector = self.coefficients['ivector']
Nz = self.coefficients['Np']
try:
lgle = numpy.sum(pg / (ivector ** 2)) / Nz
return lgle
except ZeroDivisionError:
return numpy.core.nan
[docs] def getHighGrayLevelEmphasisFeatureValue(self):
r"""
**10. High Gray Level Emphasis (HGLE)**
.. math::
HGLE = \frac{\sum^{N_g}_{i=1}\sum^{N_d}_{j=1}{\textbf{P}(i,j)i^2}}{N_z}
Measures the distribution of the higher gray-level values, with a higher value indicating
a greater concentration of high gray-level values in the image.
"""
pg = self.coefficients['pg']
ivector = self.coefficients['ivector']
Nz = self.coefficients['Np']
try:
hgle = numpy.sum(pg * (ivector ** 2)) / Nz
return hgle
except ZeroDivisionError:
return numpy.core.nan
[docs] def getSmallDependenceLowGrayLevelEmphasisFeatureValue(self):
r"""
**11. Small Dependence Low Gray Level Emphasis (SDLGLE)**
.. math::
SDLGLE = \frac{\sum^{N_g}_{i=1}\sum^{N_d}_{j=1}{\frac{\textbf{P}(i,j)}{i^2j^2}}}{N_z}
Measures the joint distribution of small dependence with lower gray-level values.
"""
ivector = self.coefficients['ivector']
jvector = self.coefficients['jvector']
Nz = self.coefficients['Np']
try:
sdlgle = numpy.sum(self.P_gldm / ((ivector[:, None] ** 2) * (jvector[None, :] ** 2))) / Nz
return sdlgle
except ZeroDivisionError:
return numpy.core.nan
[docs] def getSmallDependenceHighGrayLevelEmphasisFeatureValue(self):
r"""
**12. Small Dependence High Gray Level Emphasis (SDHGLE)**
.. math:
SDHGLE = \frac{\sum^{N_g}_{i=1}\sum^{N_d}_{j=1}{\frac{\textbf{P}(i,j)i^2}{j^2}}}{N_z}
Measures the joint distribution of small dependence with higher gray-level values.
"""
ivector = self.coefficients['ivector']
jvector = self.coefficients['jvector']
Nz = self.coefficients['Np']
try:
sdhgle = numpy.sum(self.P_gldm * (ivector[:, None] ** 2) / (jvector[None, :] ** 2)) / Nz
return sdhgle
except ZeroDivisionError:
return numpy.core.nan
[docs] def getLargeDependenceLowGrayLevelEmphasisFeatureValue(self):
r"""
**13. Large Dependence Low Gray Level Emphasis (LDLGLE)**
.. math::
LDLGLE = \frac{\sum^{N_g}_{i=1}\sum^{N_d}_{j=1}{\frac{\textbf{P}(i,j)j^2}{i^2}}}{N_z}
Measures the joint distribution of large dependence with lower gray-level values.
"""
ivector = self.coefficients['ivector']
jvector = self.coefficients['jvector']
Nz = self.coefficients['Np']
try:
ldlgle = numpy.sum(self.P_gldm * (jvector[None, :] ** 2) / (ivector[:, None] ** 2)) / Nz
return ldlgle
except ZeroDivisionError:
return numpy.core.nan
[docs] def getLargeDependenceHighGrayLevelEmphasisFeatureValue(self):
r"""
**14. Large Dependence High Gray Level Emphasis (LDHGLE)**
.. math::
LDHGLE = \frac{\sum^{N_g}_{i=1}\sum^{N_d}_{j=1}{\textbf{P}(i,j)i^2j^2}}{N_z}
Measures the joint distribution of large dependence with higher gray-level values.
"""
ivector = self.coefficients['ivector']
jvector = self.coefficients['jvector']
Nz = self.coefficients['Np']
try:
ldhgle = numpy.sum(self.P_gldm * ((jvector[None, :] ** 2) * (ivector[:, None] ** 2))) / Nz
return ldhgle
except ZeroDivisionError:
return numpy.core.nan