Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion monai/apps/pathology/transforms/stain/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ def _deconvolution_extract_stain(self, image: np.ndarray) -> np.ndarray:
v_max = eigvecs[:, 1:3].dot(np.array([(np.cos(max_phi), np.sin(max_phi))], dtype=np.float32).T)

# a heuristic to make the vector corresponding to hematoxylin first and the one corresponding to eosin second
if v_min[0] > v_max[0]:
# Hematoxylin: high blue, lower red (low R/B ratio)
# Eosin: high red, lower blue (high R/B ratio)
ε = np.finfo(np.float32).eps
v_min_rb_ratio = v_min[0, 0] / (v_min[2, 0] + ε)
v_max_rb_ratio = v_max[0, 0] / (v_max[2, 0] + ε)
if v_min_rb_ratio < v_max_rb_ratio:
he = np.array((v_min[:, 0], v_max[:, 0]), dtype=np.float32).T
else:
he = np.array((v_max[:, 0], v_min[:, 0]), dtype=np.float32).T
Expand Down
16 changes: 8 additions & 8 deletions tests/apps/pathology/transforms/test_pathology_he_stain.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
# input pixels not uniformly filled, leading to two different stains extracted
EXTRACT_STAINS_TEST_CASE_5 = [
np.array([[[100, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]]]),
np.array([[0.70710677, 0.18696113], [0.0, 0.0], [0.70710677, 0.98236734]]),
np.array([[0.18696113, 0.70710677], [0.0, 0.0], [0.98236734, 0.70710677]]),
]

# input pixels all transparent and below the beta absorbance threshold
Expand All @@ -68,7 +68,7 @@
NORMALIZE_STAINS_TEST_CASE_4 = [
{"target_he": np.full((3, 2), 1)},
np.array([[[100, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]]]),
np.array([[[87, 87, 87], [33, 33, 33]], [[33, 33, 33], [33, 33, 33]], [[33, 33, 33], [33, 33, 33]]]),
np.array([[[31, 31, 31], [85, 85, 85]], [[85, 85, 85], [85, 85, 85]], [[85, 85, 85], [85, 85, 85]]]),
]


Expand Down Expand Up @@ -135,7 +135,7 @@ def test_result_value(self, image, expected_data):
[[0.18696113],[0],[0.98236734]] and
[[0.70710677],[0],[0.70710677]] respectively
- the resulting extracted stain should be
[[0.70710677,0.18696113],[0,0],[0.70710677,0.98236734]]
[[0.18696113,0.70710677],[0,0],[0.98236734,0.70710677]]
"""
if image is None:
with self.assertRaises(TypeError):
Expand Down Expand Up @@ -206,17 +206,17 @@ def test_result_value(self, arguments, image, expected_data):

For test case 4:
- For this non-uniformly filled image, the stain extracted should be
[[0.70710677,0.18696113],[0,0],[0.70710677,0.98236734]], as validated for the
[[0.18696113,0.70710677],[0,0],[0.98236734,0.70710677]], as validated for the
ExtractHEStains class. Solving the linear least squares problem (since
absorbance matrix = stain matrix * concentration matrix), we obtain the concentration
matrix that should be [[-0.3101, 7.7508, 7.7508, 7.7508, 7.7508, 7.7508],
[5.8022, 0, 0, 0, 0, 0]]
matrix that should be [[5.8022, 0, 0, 0, 0, 0],
[-0.3101, 7.7508, 7.7508, 7.7508, 7.7508, 7.7508]]
- Normalizing the concentration matrix, taking the matrix product of the
target stain matrix and the concentration matrix, using the inverse
Beer-Lambert transform to obtain the RGB image from the absorbance
image, and finally converting to uint8, we get that the stain normalized
image should be [[[87, 87, 87], [33, 33, 33]], [[33, 33, 33], [33, 33, 33]],
[[33, 33, 33], [33, 33, 33]]]
image should be [[[31, 31, 31], [85, 85, 85]], [[85, 85, 85], [85, 85, 85]],
[[85, 85, 85], [85, 85, 85]]]
"""
if image is None:
with self.assertRaises(TypeError):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
# input pixels not uniformly filled, leading to two different stains extracted
EXTRACT_STAINS_TEST_CASE_5 = [
np.array([[[100, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]]]),
np.array([[0.70710677, 0.18696113], [0.0, 0.0], [0.70710677, 0.98236734]]),
np.array([[0.18696113, 0.70710677], [0.0, 0.0], [0.98236734, 0.70710677]]),
]

# input pixels all transparent and below the beta absorbance threshold
Expand All @@ -62,7 +62,7 @@
NORMALIZE_STAINS_TEST_CASE_4 = [
{"target_he": np.full((3, 2), 1)},
np.array([[[100, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]]]),
np.array([[[87, 87, 87], [33, 33, 33]], [[33, 33, 33], [33, 33, 33]], [[33, 33, 33], [33, 33, 33]]]),
np.array([[[31, 31, 31], [85, 85, 85]], [[85, 85, 85], [85, 85, 85]], [[85, 85, 85], [85, 85, 85]]]),
]


Expand Down Expand Up @@ -129,7 +129,7 @@ def test_result_value(self, image, expected_data):
[[0.18696113],[0],[0.98236734]] and
[[0.70710677],[0],[0.70710677]] respectively
- the resulting extracted stain should be
[[0.70710677,0.18696113],[0,0],[0.70710677,0.98236734]]
[[0.18696113,0.70710677],[0,0],[0.98236734,0.70710677]]
"""
key = "image"
if image is None:
Expand Down Expand Up @@ -200,17 +200,17 @@ def test_result_value(self, arguments, image, expected_data):

For test case 4:
- For this non-uniformly filled image, the stain extracted should be
[[0.70710677,0.18696113],[0,0],[0.70710677,0.98236734]], as validated for the
[[0.18696113,0.70710677],[0,0],[0.98236734,0.70710677]], as validated for the
ExtractHEStains class. Solving the linear least squares problem (since
absorbance matrix = stain matrix * concentration matrix), we obtain the concentration
matrix that should be [[-0.3101, 7.7508, 7.7508, 7.7508, 7.7508, 7.7508],
[5.8022, 0, 0, 0, 0, 0]]
matrix that should be [[5.8022, 0, 0, 0, 0, 0],
[-0.3101, 7.7508, 7.7508, 7.7508, 7.7508, 7.7508]]
- Normalizing the concentration matrix, taking the matrix product of the
target stain matrix and the concentration matrix, using the inverse
Beer-Lambert transform to obtain the RGB image from the absorbance
image, and finally converting to uint8, we get that the stain normalized
image should be [[[87, 87, 87], [33, 33, 33]], [[33, 33, 33], [33, 33, 33]],
[[33, 33, 33], [33, 33, 33]]]
image should be [[[31, 31, 31], [85, 85, 85]], [[85, 85, 85], [85, 85, 85]],
[[85, 85, 85], [85, 85, 85]]]
"""
key = "image"
if image is None:
Expand Down
Loading