import numpy as np
from scipy.stats import pearsonr
from .._shared.utils import check_shape_equality, as_binary_ndarray
__all__ = [
'pearson_corr_coeff',
'manders_coloc_coeff',
'manders_overlap_coeff',
'intersection_coeff',
]
[文档]
def pearson_corr_coeff(image0, image1, mask=None):
r"""Calculate Pearson's Correlation Coefficient between pixel intensities
in channels.
Parameters
----------
image0 : (M, N) ndarray
Image of channel A.
image1 : (M, N) ndarray
Image of channel 2 to be correlated with channel B.
Must have same dimensions as `image0`.
mask : (M, N) ndarray of dtype bool, optional
Only `image0` and `image1` pixels within this region of interest mask
are included in the calculation. Must have same dimensions as `image0`.
Returns
-------
pcc : float
Pearson's correlation coefficient of the pixel intensities between
the two images, within the mask if provided.
p-value : float
Two-tailed p-value.
Notes
-----
Pearson's Correlation Coefficient (PCC) measures the linear correlation
between the pixel intensities of the two images. Its value ranges from -1
for perfect linear anti-correlation to +1 for perfect linear correlation.
The calculation of the p-value assumes that the intensities of pixels in
each input image are normally distributed.
Scipy's implementation of Pearson's correlation coefficient is used. Please
refer to it for further information and caveats [1]_.
.. math::
r = \frac{\sum (A_i - m_A_i) (B_i - m_B_i)}
{\sqrt{\sum (A_i - m_A_i)^2 \sum (B_i - m_B_i)^2}}
where
:math:`A_i` is the value of the :math:`i^{th}` pixel in `image0`
:math:`B_i` is the value of the :math:`i^{th}` pixel in `image1`,
:math:`m_A_i` is the mean of the pixel values in `image0`
:math:`m_B_i` is the mean of the pixel values in `image1`
A low PCC value does not necessarily mean that there is no correlation
between the two channel intensities, just that there is no linear
correlation. You may wish to plot the pixel intensities of each of the two
channels in a 2D scatterplot and use Spearman's rank correlation if a
non-linear correlation is visually identified [2]_. Also consider if you
are interested in correlation or co-occurence, in which case a method
involving segmentation masks (e.g. MCC or intersection coefficient) may be
more suitable [3]_ [4]_.
Providing the mask of only relevant sections of the image (e.g., cells, or
particular cellular compartments) and removing noise is important as the
PCC is sensitive to these measures [3]_ [4]_.
References
----------
.. [1] https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.pearsonr.html # noqa
.. [2] https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.spearmanr.html # noqa
.. [3] Dunn, K. W., Kamocka, M. M., & McDonald, J. H. (2011). A practical
guide to evaluating colocalization in biological microscopy.
American journal of physiology. Cell physiology, 300(4), C723–C742.
https://doi.org/10.1152/ajpcell.00462.2010
.. [4] Bolte, S. and Cordelières, F.P. (2006), A guided tour into
subcellular colocalization analysis in light microscopy. Journal of
Microscopy, 224: 213-232.
https://doi.org/10.1111/j.1365-2818.2006.01706.x
"""
image0 = np.asarray(image0)
image1 = np.asarray(image1)
if mask is not None:
mask = as_binary_ndarray(mask, variable_name="mask")
check_shape_equality(image0, image1, mask)
image0 = image0[mask]
image1 = image1[mask]
else:
check_shape_equality(image0, image1)
# scipy pearsonr function only takes flattened arrays
image0 = image0.reshape(-1)
image1 = image1.reshape(-1)
return tuple(float(v) for v in pearsonr(image0, image1))
[文档]
def manders_coloc_coeff(image0, image1_mask, mask=None):
r"""Manders' colocalization coefficient between two channels.
Parameters
----------
image0 : (M, N) ndarray
Image of channel A. All pixel values should be non-negative.
image1_mask : (M, N) ndarray of dtype bool
Binary mask with segmented regions of interest in channel B.
Must have same dimensions as `image0`.
mask : (M, N) ndarray of dtype bool, optional
Only `image0` pixel values within this region of interest mask are
included in the calculation.
Must have same dimensions as `image0`.
Returns
-------
mcc : float
Manders' colocalization coefficient.
Notes
-----
Manders' Colocalization Coefficient (MCC) is the fraction of total
intensity of a certain channel (channel A) that is within the segmented
region of a second channel (channel B) [1]_. It ranges from 0 for no
colocalisation to 1 for complete colocalization. It is also referred to
as M1 and M2.
MCC is commonly used to measure the colocalization of a particular protein
in a subceullar compartment. Typically a segmentation mask for channel B
is generated by setting a threshold that the pixel values must be above
to be included in the MCC calculation. In this implementation,
the channel B mask is provided as the argument `image1_mask`, allowing
the exact segmentation method to be decided by the user beforehand.
The implemented equation is:
.. math::
r = \frac{\sum A_{i,coloc}}{\sum A_i}
where
:math:`A_i` is the value of the :math:`i^{th}` pixel in `image0`
:math:`A_{i,coloc} = A_i` if :math:`Bmask_i > 0`
:math:`Bmask_i` is the value of the :math:`i^{th}` pixel in
`mask`
MCC is sensitive to noise, with diffuse signal in the first channel
inflating its value. Images should be processed to remove out of focus and
background light before the MCC is calculated [2]_.
References
----------
.. [1] Manders, E.M.M., Verbeek, F.J. and Aten, J.A. (1993), Measurement of
co-localization of objects in dual-colour confocal images. Journal
of Microscopy, 169: 375-382.
https://doi.org/10.1111/j.1365-2818.1993.tb03313.x
https://imagej.net/media/manders.pdf
.. [2] Dunn, K. W., Kamocka, M. M., & McDonald, J. H. (2011). A practical
guide to evaluating colocalization in biological microscopy.
American journal of physiology. Cell physiology, 300(4), C723–C742.
https://doi.org/10.1152/ajpcell.00462.2010
"""
image0 = np.asarray(image0)
image1_mask = as_binary_ndarray(image1_mask, variable_name="image1_mask")
if mask is not None:
mask = as_binary_ndarray(mask, variable_name="mask")
check_shape_equality(image0, image1_mask, mask)
image0 = image0[mask]
image1_mask = image1_mask[mask]
else:
check_shape_equality(image0, image1_mask)
# check non-negative image
if image0.min() < 0:
raise ValueError("image contains negative values")
sum = np.sum(image0)
if sum == 0:
return 0
return np.sum(image0 * image1_mask) / sum
[文档]
def manders_overlap_coeff(image0, image1, mask=None):
r"""Manders' overlap coefficient
Parameters
----------
image0 : (M, N) ndarray
Image of channel A. All pixel values should be non-negative.
image1 : (M, N) ndarray
Image of channel B. All pixel values should be non-negative.
Must have same dimensions as `image0`
mask : (M, N) ndarray of dtype bool, optional
Only `image0` and `image1` pixel values within this region of interest
mask are included in the calculation.
Must have ♣same dimensions as `image0`.
Returns
-------
moc: float
Manders' Overlap Coefficient of pixel intensities between the two
images.
Notes
-----
Manders' Overlap Coefficient (MOC) is given by the equation [1]_:
.. math::
r = \frac{\sum A_i B_i}{\sqrt{\sum A_i^2 \sum B_i^2}}
where
:math:`A_i` is the value of the :math:`i^{th}` pixel in `image0`
:math:`B_i` is the value of the :math:`i^{th}` pixel in `image1`
It ranges between 0 for no colocalization and 1 for complete colocalization
of all pixels.
MOC does not take into account pixel intensities, just the fraction of
pixels that have positive values for both channels[2]_ [3]_. Its usefulness
has been criticized as it changes in response to differences in both
co-occurence and correlation and so a particular MOC value could indicate
a wide range of colocalization patterns [4]_ [5]_.
References
----------
.. [1] Manders, E.M.M., Verbeek, F.J. and Aten, J.A. (1993), Measurement of
co-localization of objects in dual-colour confocal images. Journal
of Microscopy, 169: 375-382.
https://doi.org/10.1111/j.1365-2818.1993.tb03313.x
https://imagej.net/media/manders.pdf
.. [2] Dunn, K. W., Kamocka, M. M., & McDonald, J. H. (2011). A practical
guide to evaluating colocalization in biological microscopy.
American journal of physiology. Cell physiology, 300(4), C723–C742.
https://doi.org/10.1152/ajpcell.00462.2010
.. [3] Bolte, S. and Cordelières, F.P. (2006), A guided tour into
subcellular colocalization analysis in light microscopy. Journal of
Microscopy, 224: 213-232.
https://doi.org/10.1111/j.1365-2818.2006.01
.. [4] Adler J, Parmryd I. (2010), Quantifying colocalization by
correlation: the Pearson correlation coefficient is
superior to the Mander's overlap coefficient. Cytometry A.
Aug;77(8):733-42.https://doi.org/10.1002/cyto.a.20896
.. [5] Adler, J, Parmryd, I. Quantifying colocalization: The case for
discarding the Manders overlap coefficient. Cytometry. 2021; 99:
910– 920. https://doi.org/10.1002/cyto.a.24336
"""
image0 = np.asarray(image0)
image1 = np.asarray(image1)
if mask is not None:
mask = as_binary_ndarray(mask, variable_name="mask")
check_shape_equality(image0, image1, mask)
image0 = image0[mask]
image1 = image1[mask]
else:
check_shape_equality(image0, image1)
# check non-negative image
if image0.min() < 0:
raise ValueError("image0 contains negative values")
if image1.min() < 0:
raise ValueError("image1 contains negative values")
denom = (np.sum(np.square(image0)) * (np.sum(np.square(image1)))) ** 0.5
return np.sum(np.multiply(image0, image1)) / denom
[文档]
def intersection_coeff(image0_mask, image1_mask, mask=None):
r"""Fraction of a channel's segmented binary mask that overlaps with a
second channel's segmented binary mask.
Parameters
----------
image0_mask : (M, N) ndarray of dtype bool
Image mask of channel A.
image1_mask : (M, N) ndarray of dtype bool
Image mask of channel B.
Must have same dimensions as `image0_mask`.
mask : (M, N) ndarray of dtype bool, optional
Only `image0_mask` and `image1_mask` pixels within this region of
interest
mask are included in the calculation.
Must have same dimensions as `image0_mask`.
Returns
-------
Intersection coefficient, float
Fraction of `image0_mask` that overlaps with `image1_mask`.
"""
image0_mask = as_binary_ndarray(image0_mask, variable_name="image0_mask")
image1_mask = as_binary_ndarray(image1_mask, variable_name="image1_mask")
if mask is not None:
mask = as_binary_ndarray(mask, variable_name="mask")
check_shape_equality(image0_mask, image1_mask, mask)
image0_mask = image0_mask[mask]
image1_mask = image1_mask[mask]
else:
check_shape_equality(image0_mask, image1_mask)
nonzero_image0 = np.count_nonzero(image0_mask)
if nonzero_image0 == 0:
return 0
nonzero_joint = np.count_nonzero(np.logical_and(image0_mask, image1_mask))
return nonzero_joint / nonzero_image0