diff --git a/tests/test_stainnorm.py b/tests/test_stainnorm.py index 5d95faf2b..59d3045be 100644 --- a/tests/test_stainnorm.py +++ b/tests/test_stainnorm.py @@ -148,7 +148,9 @@ def test_macenko_normalize(source_image: Path, norm_macenko: Path) -> None: assert np.mean(np.absolute(macenko_img / 255.0 - transform / 255.0)) < 1e-2 -def test_vahadane_normalize(source_image: Path, norm_vahadane: Path) -> None: +def test_vahadane_normalize( + source_image: Path, norm_vahadane: Path, caplog: pytest.LogCaptureFixture +) -> None: """Test for stain normalization with stain matrix from Vahadane et al.""" source_img = imread(Path(source_image)) target_img = stain_norm_target() @@ -158,7 +160,7 @@ def test_vahadane_normalize(source_image: Path, norm_vahadane: Path) -> None: norm = get_normalizer("vahadane") norm.fit(target_img) # get stain information of target image transform = norm.transform(source_img) # transform source image - + assert "Vahadane stain extraction/normalization algorithms" in caplog.text assert np.shape(transform) == np.shape(source_img) assert np.mean(np.absolute(vahadane_img / 255.0 - transform / 255.0)) < 1e-1 diff --git a/tiatoolbox/tools/stainextract.py b/tiatoolbox/tools/stainextract.py index cb2972ae2..f9041b923 100644 --- a/tiatoolbox/tools/stainextract.py +++ b/tiatoolbox/tools/stainextract.py @@ -5,6 +5,7 @@ import numpy as np from sklearn.decomposition import DictionaryLearning +from tiatoolbox import logger from tiatoolbox.utils.misc import get_luminosity_tissue_mask from tiatoolbox.utils.transforms import rgb2od @@ -238,6 +239,13 @@ class VahadaneExtractor: This class contains code inspired by StainTools [https://github.com/Peter554/StainTools] written by Peter Byfield. + .. warning:: + Vahadane stain extraction/normalization algorithms are unstable + after the update to `dictionary learning` algorithm in + scikit-learn > v0.23.0 (see issue #382). Please be advised and + consider using other stain extraction (normalization) algorithms + or toolboxes, such as https://github.com/CielAl/torch-staintools + Args: luminosity_threshold (float): Threshold used for tissue area selection. @@ -259,6 +267,14 @@ def __init__( regularizer: float = 0.1, ) -> None: """Initialize :class:`VahadaneExtractor`.""" + # Issue a warning about the algorithm's stability + logger.warning( + "Vahadane stain extraction/normalization algorithms are unstable " + "after the update to `dictionary learning` algorithm in " + "scikit-learn > v0.23.0 (see issue #382). Please be advised and " + "consider using other stain extraction (normalization) algorithms.", + stacklevel=2, + ) self.__luminosity_threshold = luminosity_threshold self.__regularizer = regularizer @@ -267,7 +283,7 @@ def get_stain_matrix(self: VahadaneExtractor, img: np.ndarray) -> np.ndarray: Args: img (:class:`numpy.ndarray`): - Input image used for stain matrix estimation + Input image used for stain matrix estimation. Returns: :class:`numpy.ndarray`: