Source code for monai.apps.pathology.utils
# Copyright (c) MONAI Consortium
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
from typing import Any
import numpy as np
import torch
from monai.transforms.post.array import ProbNMS
from monai.utils import optional_import
measure, _ = optional_import("skimage.measure")
ndimage, _ = optional_import("scipy.ndimage")
[docs]
def compute_multi_instance_mask(mask: np.ndarray, threshold: float) -> Any:
"""
This method computes the segmentation mask according to the binary tumor mask.
Args:
mask: the binary mask array
threshold: the threshold to fill holes
"""
neg = 255 - mask * 255
distance = ndimage.morphology.distance_transform_edt(neg)
binary = distance < threshold
filled_image = ndimage.morphology.binary_fill_holes(binary)
multi_instance_mask = measure.label(filled_image, connectivity=2)
return multi_instance_mask
[docs]
def compute_isolated_tumor_cells(tumor_mask: np.ndarray, threshold: float) -> list[int]:
"""
This method computes identifies Isolated Tumor Cells (ITC) and return their labels.
Args:
tumor_mask: the tumor mask.
threshold: the threshold (at the mask level) to define an isolated tumor cell (ITC).
A region with the longest diameter less than this threshold is considered as an ITC.
"""
max_label = np.amax(tumor_mask)
properties = measure.regionprops(tumor_mask)
itc_list = [i + 1 for i in range(max_label) if properties[i].major_axis_length < threshold]
return itc_list
[docs]
class PathologyProbNMS(ProbNMS):
"""
This class extends monai.utils.ProbNMS and add the `resolution` option for
Pathology.
"""
def __call__(self, probs_map: np.ndarray | torch.Tensor, resolution_level: int = 0) -> list[list]:
"""
probs_map: the input probabilities map, it must have shape (H[, W, ...]).
resolution_level: the level at which the probabilities map is made.
"""
resolution = pow(2, resolution_level)
org_outputs = ProbNMS.__call__(self, probs_map)
outputs = []
for org_output in org_outputs:
prob = org_output[0]
coord = np.asarray(org_output[1:])
coord_wsi = ((coord + 0.5) * resolution).astype(int)
outputs.append([prob] + list(coord_wsi))
return outputs