Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stain normalization #2666

Merged
merged 57 commits into from
Jul 30, 2021
Merged

Stain normalization #2666

merged 57 commits into from
Jul 30, 2021

Conversation

drbeh
Copy link
Member

@drbeh drbeh commented Jul 28, 2021

Description

This PR implement an H&E stain normalization for histopathology images, which contains:

  • an H&E stain extractor using deconvolution methods from histopathology images, and
  • an H&E stain normalizer using the extracted stain vectors.

Unlike the previous PR (#1998), which was based on cupy, its purely based on numpy without any CUDA dependency. It expects numpy.ndarray as the input image and returns numpy.ndarray.

Status

Ready

Types of changes

  • Non-breaking change (fix or new feature that would not break existing functionality).
  • New tests added to cover the changes.
  • Integration tests passed locally by running ./runtests.sh -f -u --net --coverage.
  • Quick tests passed locally by running ./runtests.sh --quick --unittests.
  • In-line docstrings updated.
  • Documentation updated, tested make html command in the docs/ folder.

nsrivathsa and others added 30 commits April 12, 2021 18:17
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
drbeh added 15 commits July 26, 2021 16:24
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
@drbeh drbeh requested review from yiheng-wang-nv and wyli and removed request for yiheng-wang-nv July 29, 2021 14:16
Copy link
Contributor

@wyli wyli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good apart from the minor comments. @Nic-Ma could you please help confirm/approve this PR?

and pseudo-max (100 - alpha percentile). Defaults to 1.
beta: absorbance threshold for transparent pixels. Defaults to 0.15
max_cref: reference maximum stain concentrations for Hematoxylin & Eosin (H&E).
Defaults to None.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the defauts are np.array([1.9705, 1.0308])

# reshape image and calculate absorbance
image = image.reshape((-1, 3))
image = image.astype(np.float32) + 1.0
absorbance = -np.log(image.clip(max=self.tli) / self.tli)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need some validation of the input of np.log?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point! We should check if the input is intensity (0-255) and not any image or other scales. Thanks @wyli!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it checks the image values to be between 0 and 255.

@wyli wyli requested a review from Nic-Ma July 29, 2021 23:49
Copy link
Contributor

@Nic-Ma Nic-Ma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me except for some minor comments.
I didn't check whether the implementation matches some algorithm theory.

Thanks.

:members:

.. automodule:: monai.apps.pathology.transforms.stain.dictionary
.. autoclass:: ExtractHEStainsD
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest to use ExtractHEStainsd to align with other docs, same as NormalizeHEStainsD.


self.max_cref = max_cref
if self.max_cref is None:
self.max_cref = np.array([1.9705, 1.0308])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @wyli commented, set default to 1.9705, 1.0308 directly.

Thanks.

"""Perform Stain Deconvolution and return stain matrix for the image.

Args:
img: uint8 RGB image to perform stain deconvolution of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe of -> on or for?

pseudo-max (100 - alpha percentile). Defaults to 1.
beta: absorbance threshold for transparent pixels. Defaults to 0.15.
target_he: target stain matrix. Defaults to None.
max_cref: reference maximum stain concentrations for Hematoxylin & Eosin (H&E).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default values of target_he and max_cref are same as the other comment.

tli: float = 240,
alpha: float = 1,
beta: float = 0.15,
max_cref: Optional[np.ndarray] = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the other comment.

tli: float = 240,
alpha: float = 1,
beta: float = 0.15,
target_he: Optional[np.ndarray] = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment for default value.

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>
@drbeh
Copy link
Member Author

drbeh commented Jul 30, 2021

@wyli and @Nic-Ma
Thanks for you comments. I addressed all of them. Please let me know if I missed anything.

@wyli wyli merged commit 30ce63d into Project-MONAI:dev Jul 30, 2021
wyli pushed a commit to wyli/MONAI that referenced this pull request Aug 3, 2021
* added stain norm and tests

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* import changes

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* changed stain extraction tests

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* edited stain norm tests

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* convert floats to float32

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* added uint8 assumption to docstring

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* add error case

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* formatting change

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* modify tests wrt cupy import

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* minor change to pass lint test

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* import changes

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* refactored classes

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* Restructure and rename transforms

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* added dict transform

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* Move stain_extractor to init

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Exclude pathology transform tests from mini tests

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Fix type checking for cupy ndarray

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Include pathology transform tests

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Update to cupy 9.0.0

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Remove exact version for cupy

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* add to docs

Signed-off-by: Neha Srivathsa <nsrivathsa@nvidia.com>

* Organize into stain dir

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Add/update init files

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Transit all from cupy to numpy

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Update imports

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Update test cases for numpy

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Rename to NormalizeHEStains and NormalizeHEStainsD

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Add dictionary variant names

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Fix typing and formatting

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Fix docs

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Update test cases

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Fix clip max

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Fix var typing

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Fix a typing issue

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Update default values, and change D to d

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Update docs

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Add image value check

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

* Add test cases for negative and invalid values

Signed-off-by: Behrooz <3968947+behxyz@users.noreply.github.com>

Co-authored-by: Neha Srivathsa <nsrivathsa@nvidia.com>
Co-authored-by: nsrivathsa <81264348+nsrivathsa@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants