Skip to content

Commit

Permalink
Merge pull request #167 from MohamedOmar2020/dev_mo
Browse files Browse the repository at this point in the history
CODEX notebook
  • Loading branch information
jacob-rosenthal committed Oct 25, 2021
2 parents 4bec58d + 29d8440 commit 98bc21c
Show file tree
Hide file tree
Showing 12 changed files with 1,975 additions and 234 deletions.
3 changes: 3 additions & 0 deletions docs/source/api_preprocessing_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ Transforms
.. autoapiclass:: pathml.preprocessing.QuantifyMIF
.. autoapiclass:: pathml.preprocessing.CollapseRunsVectra
.. autoapiclass:: pathml.preprocessing.CollapseRunsCODEX
.. autoapiclass:: pathml.preprocessing.RescaleIntensity
.. autoapiclass:: pathml.preprocessing.HistogramEqualization
.. autoapiclass:: pathml.preprocessing.AdaptiveHistogramEqualization
3 changes: 3 additions & 0 deletions docs/source/examples/link_codex.nblink
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"path": "../../../examples/codex.ipynb"
}
3 changes: 0 additions & 3 deletions docs/source/examples/link_nucleus_detection.nblink

This file was deleted.

6 changes: 3 additions & 3 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ Cornell Medicine.
:maxdepth: 2
:caption: Examples

examples/link_gallery
examples/link_stain_normalization
examples/link_nucleus_detection
examples/link_preprocessing_pipeline
examples/link_train_hovernet
examples/link_multiplex_if
examples/link_codex
examples/link_train_hovernet
examples/link_gallery

.. toctree::
:maxdepth: 2
Expand Down
1,826 changes: 1,826 additions & 0 deletions examples/codex.ipynb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions examples/multiplex_if.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Multiparametric Imaging Pipelines\n",
"# Multiparametric Imaging: Quickstart\n",
"\n",
"[![View on GitHub](https://img.shields.io/badge/View-on%20GitHub-lightgrey?logo=github)](https://github.com/Dana-Farber-AIOS/pathml/blob/master/examples/multiplex_if.ipynb)\n",
"\n",
Expand Down Expand Up @@ -784,4 +784,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
220 changes: 0 additions & 220 deletions examples/nucleus_detection.ipynb

This file was deleted.

4 changes: 2 additions & 2 deletions examples/preprocessing_pipeline.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Running a Preprocessing Pipeline\n",
"# Brightfield Imaging: Quickstart\n",
"\n",
"[![View on GitHub](https://img.shields.io/badge/View-on%20GitHub-lightgrey?logo=github)](https://github.com/Dana-Farber-AIOS/pathml/blob/master/examples/preprocessing_pipeline.ipynb)\n",
"\n",
Expand Down Expand Up @@ -381,4 +381,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
4 changes: 2 additions & 2 deletions examples/stain_normalization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Stain Deconvolution and Color Normalization\n",
"# H&E Stain Deconvolution and Color Normalization\n",
"\n",
"[![View on GitHub](https://img.shields.io/badge/View-on%20GitHub-lightgrey?logo=github)](https://github.com/Dana-Farber-AIOS/pathml/blob/master/examples/stain_normalization.ipynb)\n",
"\n",
Expand Down Expand Up @@ -172,4 +172,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
3 changes: 3 additions & 0 deletions pathml/preprocessing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@
QuantifyMIF,
CollapseRunsVectra,
CollapseRunsCODEX,
RescaleIntensity,
HistogramEqualization,
AdaptiveHistogramEqualization
)
98 changes: 97 additions & 1 deletion pathml/preprocessing/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
)
from skimage import restoration
from skimage.measure import regionprops_table

from skimage.exposure import equalize_hist, equalize_adapthist, rescale_intensity

# Base class
class Transform:
Expand Down Expand Up @@ -132,6 +132,102 @@ def apply(self, tile):
tile.image = self.F(tile.image)


class RescaleIntensity(Transform):
"""
Return image after stretching or shrinking its intensity levels.
The desired intensity range of the input and output, in_range and out_range respectively, are used to stretch or shrink the intensity range of the input image
This function is a wrapper for 'rescale_intensity' function from scikit-image: https://scikit-image.org/docs/dev/api/skimage.exposure.html#skimage.exposure.rescale_intensity
Args:
in_range (str or 2-tuple, optional): Min and max intensity values of input image. The possible values for this parameter are enumerated below.
‘image’ : Use image min/max as the intensity range.
‘dtype’ : Use min/max of the image’s dtype as the intensity range.
'dtype-name' : Use intensity range based on desired dtype. Must be valid key in DTYPE_RANGE.
'2-tuple' : Use range_values as explicit min/max intensities.
out_range (str or 2-tuple, optional): Min and max intensity values of output image. The possible values for this parameter are enumerated below.
‘image’ : Use image min/max as the intensity range.
‘dtype’ : Use min/max of the image’s dtype as the intensity range.
'dtype-name' : Use intensity range based on desired dtype. Must be valid key in DTYPE_RANGE.
'2-tuple' : Use range_values as explicit min/max intensities.
"""

def __init__(self, in_range='image', out_range = 'dtype'):
self.in_range = in_range
self.out_range = out_range

def __repr__(self):
return f"RescaleIntensity(in_range={self.in_range}, out_range={self.out_range})"

def F(self, image):
image = rescale_intensity(image, in_range=self.in_range, out_range=self.out_range)
return image

def apply(self, tile):
assert isinstance(
tile, pathml.core.tile.Tile
), f"tile is type {type(tile)} but must be pathml.core.tile.Tile"
tile.image = self.F(tile.image)


class HistogramEqualization(Transform):
"""
Return image after histogram equalization.
This function is a wrapper for 'equalize_hist' function from scikit-image: https://scikit-image.org/docs/dev/api/skimage.exposure.html#skimage.exposure.equalize_hist
Args:
nbins (int, optional): Number of gray bins for histogram. Note: this argument is ignored for integer images, for which each integer is its own bin.
mask (ndarray of bools or 0s and 1s, optional): Array of same shape as image. Only points at which mask == True are used for the equalization, which is applied to the whole image.
"""

def __init__(self, nbins=256, mask=None):
self.nbins = nbins
self.mask = mask

def __repr__(self):
return f"HistogramEqualization(nbins={self.nbins}, mask = {self.mask})"

def F(self, image):
image = equalize_hist(image, nbins=self.nbins, mask = self.mask)
return image

def apply(self, tile):
assert isinstance(
tile, pathml.core.tile.Tile
), f"tile is type {type(tile)} but must be pathml.core.tile.Tile"
tile.image = self.F(tile.image)


class AdaptiveHistogramEqualization(Transform):
"""
Contrast Limited Adaptive Histogram Equalization (CLAHE).
An algorithm for local contrast enhancement, that uses histograms computed over different tile regions of the image. Local details can therefore be enhanced even in regions that are darker or lighter than most of the image.
This function is a wrapper for 'equalize_adapthist' function from scikit-image: https://scikit-image.org/docs/dev/api/skimage.exposure.html#skimage.exposure.equalize_adapthist
Args:
kernel_size (int or array_like, optional): Defines the shape of contextual regions used in the algorithm. If iterable is passed, it must have the same number of elements as image.ndim (without color channel). If integer, it is broadcasted to each image dimension. By default, kernel_size is 1/8 of image height by 1/8 of its width.
clip_limit (float): Clipping limit, normalized between 0 and 1 (higher values give more contrast).
nbins (int): Number of gray bins for histogram (“data range”).
"""

def __init__(self, kernel_size=None, clip_limit = 0.3, nbins=256):
self.kernel_size = kernel_size
self.clip_limit = clip_limit
self.nbins = nbins

def __repr__(self):
return f"AdaptiveHistogramEqualization(kernel_size={self.kernel_size}, clip_limit={self.clip_limit}, nbins={self.nbins})"

def F(self, image):
image = equalize_adapthist(image, kernel_size=self.kernel_size, clip_limit=self.clip_limit, nbins=self.nbins)
return image

def apply(self, tile):
assert isinstance(
tile, pathml.core.tile.Tile
), f"tile is type {type(tile)} but must be pathml.core.tile.Tile"
tile.image = self.F(tile.image)


class BinaryThreshold(Transform):
"""
Binary thresholding transform to create a binary mask.
Expand Down
35 changes: 34 additions & 1 deletion tests/preprocessing_tests/test_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import numpy as np
import pytest

from pathml.preprocessing import (
MedianBlur,
GaussianBlur,
Expand All @@ -23,6 +22,9 @@
QuantifyMIF,
CollapseRunsVectra,
CollapseRunsCODEX,
AdaptiveHistogramEqualization,
HistogramEqualization,
RescaleIntensity,
)
from pathml.utils import RGB_to_GREY

Expand All @@ -45,6 +47,34 @@ def test_gaussian_blur(tileHE, ksize, sigma):
assert np.array_equal(tileHE.image, t.F(orig_im))


@pytest.mark.parametrize("in_range", ["image", (0, 255), "dtype"])
@pytest.mark.parametrize("out_range", ["image", (0, 255), "dtype"])
def test_rescale_intensity(tileVectra, in_range, out_range):
t = RescaleIntensity(in_range=in_range, out_range=out_range)
orig_im = tileVectra.image
t.apply(tileVectra)
assert np.array_equal(tileVectra.image, t.F(orig_im))


@pytest.mark.parametrize("nbins", [120, 255, 500])
def test_histogram_equalization(tileVectra, nbins):
t = HistogramEqualization(nbins=nbins)
orig_im = tileVectra.image
t.apply(tileVectra)
assert np.array_equal(tileVectra.image, t.F(orig_im))


@pytest.mark.parametrize("clip_limit", [0.05, 0.1, 0.3])
@pytest.mark.parametrize("nbins", [120, 255, 500])
def test_adaptive_histogram_equalization(tileVectra, clip_limit, nbins):
t = AdaptiveHistogramEqualization(clip_limit=clip_limit, nbins=nbins)
vectra_collapse = CollapseRunsVectra()
vectra_collapse.apply(tileVectra)
orig_im = tileVectra.image
t.apply(tileVectra)
assert np.array_equal(tileVectra.image, t.F(orig_im))


@pytest.mark.parametrize("thresh", [0, 0.5, 200])
@pytest.mark.parametrize("otsu", [True, False])
def test_binary_thresholding(tileHE, thresh, otsu):
Expand Down Expand Up @@ -181,6 +211,9 @@ def test_collapse_runs_vectra(tileVectra):
QuantifyMIF(segmentation_mask="test"),
CollapseRunsVectra(),
CollapseRunsCODEX(z=0),
AdaptiveHistogramEqualization(),
HistogramEqualization(),
RescaleIntensity(),
],
)
def test_repr(transform):
Expand Down

0 comments on commit 98bc21c

Please sign in to comment.