Spaces:
Sleeping
Sleeping
File size: 4,820 Bytes
a3290d1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
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
@abstractmethod
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
|