Spaces:
Sleeping
Sleeping
| from abc import ABC, abstractmethod | |
| from typing import Callable, Sequence, Union | |
| import numpy as np | |
| def flatten_non_category_dims( | |
| xs: Union[np.ndarray, Sequence[np.ndarray]], category_dim: int = None | |
| ): | |
| """Flattens all non-category dimensions into a single dimension. | |
| Args: | |
| xs (ndarrays): Sequence of ndarrays with the same category dimension. | |
| category_dim: The dimension/axis corresponding to different categories. | |
| i.e. `C`. If `None`, behaves like `np.flatten(x)`. | |
| Returns: | |
| ndarray: Shape (C, -1) if `category_dim` specified else shape (-1,) | |
| """ | |
| single_item = isinstance(xs, np.ndarray) | |
| if single_item: | |
| xs = [xs] | |
| if category_dim is not None: | |
| dims = (xs[0].shape[category_dim], -1) | |
| xs = (np.moveaxis(x, category_dim, 0).reshape(dims) for x in xs) | |
| else: | |
| xs = (x.flatten() for x in xs) | |
| if single_item: | |
| return list(xs)[0] | |
| else: | |
| return xs | |
| class Metric(Callable, ABC): | |
| """Interface for new metrics. | |
| A metric should be implemented as a callable with explicitly defined | |
| arguments. In other words, metrics should not have `**kwargs` or `**args` | |
| options in the `__call__` method. | |
| While not explicitly constrained to the return type, metrics typically | |
| return float value(s). The number of values returned corresponds to the | |
| number of categories. | |
| * metrics should have different name() for different functionality. | |
| * `category_dim` duck type if metric can process multiple categories at | |
| once. | |
| To compute metrics: | |
| .. code-block:: python | |
| metric = Metric() | |
| results = metric(...) | |
| """ | |
| def __init__(self, units: str = ""): | |
| self.units = units | |
| def name(self): | |
| return type(self).__name__ | |
| def display_name(self): | |
| """Name to use for pretty printing and display purposes.""" | |
| name = self.name() | |
| return "{} {}".format(name, self.units) if self.units else name | |
| def __call__(self, *args, **kwargs): | |
| pass | |
| class HounsfieldUnits(Metric): | |
| FULL_NAME = "Hounsfield Unit" | |
| def __init__(self, units="hu"): | |
| super().__init__(units) | |
| def __call__(self, mask, x, category_dim: int = None): | |
| mask = mask.astype(np.bool) | |
| if category_dim is None: | |
| return np.mean(x[mask]) | |
| assert category_dim == -1 | |
| num_classes = mask.shape[-1] | |
| return np.array([np.mean(x[mask[..., c]]) for c in range(num_classes)]) | |
| def name(self): | |
| return self.FULL_NAME | |
| class CrossSectionalArea(Metric): | |
| def __call__(self, mask, spacing=None, category_dim: int = None): | |
| pixel_area = np.prod(spacing) if spacing else 1 | |
| mask = mask.astype(np.bool) | |
| mask = flatten_non_category_dims(mask, category_dim) | |
| return pixel_area * np.count_nonzero(mask, -1) / 100.0 | |
| def name(self): | |
| if self.units: | |
| return "Cross-sectional Area ({})".format(self.units) | |
| else: | |
| return "Cross-sectional Area" | |
| def manifest_to_map(manifest, model_type): | |
| """Converts a manifest to a map of metric name to metric instance. | |
| Args: | |
| manifest (dict): A dictionary of metric name to metric instance. | |
| Returns: | |
| dict: A dictionary of metric name to metric instance. | |
| """ | |
| # TODO: hacky. Update this | |
| figure_text_key = {} | |
| for manifest_dict in manifest: | |
| try: | |
| key = manifest_dict["Level"] | |
| except BaseException: | |
| key = ".".join((manifest_dict["File"].split("/")[-1]).split(".")[:-1]) | |
| muscle_hu = f"{manifest_dict['Hounsfield Unit (muscle)']:.2f}" | |
| muscle_area = f"{manifest_dict['Cross-sectional Area (cm^2) (muscle)']:.2f}" | |
| vat_hu = f"{manifest_dict['Hounsfield Unit (vat)']:.2f}" | |
| vat_area = f"{manifest_dict['Cross-sectional Area (cm^2) (vat)']:.2f}" | |
| sat_hu = f"{manifest_dict['Hounsfield Unit (sat)']:.2f}" | |
| sat_area = f"{manifest_dict['Cross-sectional Area (cm^2) (sat)']:.2f}" | |
| imat_hu = f"{manifest_dict['Hounsfield Unit (imat)']:.2f}" | |
| imat_area = f"{manifest_dict['Cross-sectional Area (cm^2) (imat)']:.2f}" | |
| if model_type.model_name == "abCT_v0.0.1": | |
| figure_text_key[key] = [ | |
| muscle_hu, | |
| muscle_area, | |
| imat_hu, | |
| imat_area, | |
| vat_hu, | |
| vat_area, | |
| sat_hu, | |
| sat_area, | |
| ] | |
| else: | |
| figure_text_key[key] = [ | |
| muscle_hu, | |
| muscle_area, | |
| vat_hu, | |
| vat_area, | |
| sat_hu, | |
| sat_area, | |
| imat_hu, | |
| imat_area, | |
| ] | |
| return figure_text_key | |