TissueImageAnalytics / tiatoolbox

Computational Pathology Toolbox developed by TIA Centre, University of Warwick.
https://warwick.ac.uk/tia
Other
382 stars 79 forks source link

Macenko stain normalization deteriorate images with bad staining #289

Closed mostafajahanifar closed 2 years ago

mostafajahanifar commented 2 years ago

Description

When traying Macenko stain normalization to normalize images with that don't have perfect staining in first place, the normalization process would deteriorate the output quality. In particular, it seems that division by maxC_source is causing it:

maxC_source = np.percentile(source_concentrations, 99, axis=0).reshape((1, 2))
source_concentrations *= self.maxC_target / maxC_source

It seems that returned maxC_source is not favorable when we don't have enough stain variation in the source image.

What I Did

Consider the the target image: 181S_ 48635, 3896, 50003, 5109

and the source image: TCGA-A8-A07O-01Z-00-DX1 3D657129-F2A7-4BC1-A910-805FBDCE2212_ 5891, 8116, 9033, 9727

this will be the normalized source which is clearly deteriorated: source_normalized

the code to reproduce this:

from tiatoolbox.utils.misc import imread, imwrite
from tiatoolbox.tools.stainnorm import MacenkoNormalizer
source= imread('D:/source.png') 
target = imread('D:/target.png') 
normalizer = MacenkoNormalizer()
normalizer.fit(target)
source_normalized = normalizer.transform(source)

The similar problem exists with Macenko normalizer in staintools: source_normalized_staintools

the code to reproduce this:

import staintools

# Read data
target = staintools.read_image("/root/workspace/target.png")
to_transform = staintools.read_image("/root/workspace/source.png")

# Standardize brightness (optional, can improve the tissue mask calculation)
target = staintools.LuminosityStandardizer.standardize(target)
to_transform = staintools.LuminosityStandardizer.standardize(to_transform)

# Stain normalize
normalizer = staintools.StainNormalizer(method='macenko')
normalizer.fit(target)
transformed = normalizer.transform(to_transform)

However, if you use Vahedane normalizer, the output would look desirable like this: source_normalized_staintools

mostafajahanifar commented 2 years ago

This seems to be an intrinsic behavior from Macenko algorithm which happens when the source/target image does not show much stain variation in it. Therefore, I'm closing this issue.