Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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