Module qute.transforms.debug

Debugging transforms.

Classes

class DebugCheckAndFixAffineDimensions (expected_voxel_size, *args, **kwargs)

Check that the affine transform is (4 x 4).

Constructor.

Parameters

name : str
Name of the Informer. It is printed as a prefix to the output.
Expand source code
class DebugCheckAndFixAffineDimensions(Transform):
    """Check that the affine transform is (4 x 4)."""

    def __init__(self, expected_voxel_size, *args, **kwargs):
        """Constructor.

        Parameters
        ----------

        name: str
            Name of the Informer. It is printed as a prefix to the output.
        """
        super().__init__()
        self.name = ""
        if "name" in kwargs:
            self.name = kwargs["name"]
        self.expected_voxel_size = expected_voxel_size

    def __call__(self, data):
        """Call the Transform."""
        prefix = f"{self.name} :: " if self.name != "" else ""
        if type(data) is list:
            for item in data:
                if type(item) is dict:
                    for key in item.keys():
                        sub_item = item[key]
                        if type(sub_item) is MetaTensor and hasattr(sub_item, "affine"):

                            if sub_item.affine.shape != (4, 4):
                                print(
                                    f"{prefix}Affine matrix needs correcting! Current shape is {sub_item.affine.shape}"
                                )
                                sub_item.affine = torch.tensor(
                                    [
                                        [self.expected_voxel_size[0], 0.0, 0.0, 0.0],
                                        [0.0, self.expected_voxel_size[1], 0.0, 0.0],
                                        [0.0, 0.0, self.expected_voxel_size[2], 0.0],
                                        [0.0, 0.0, 0.0, 1.0],
                                    ]
                                )
                            elif (
                                sub_item.affine[0, 0] != self.expected_voxel_size[0]
                                or sub_item.affine[1, 1] != self.expected_voxel_size[1]
                                or sub_item.affine[2, 2] != self.expected_voxel_size[2]
                                or sub_item.affine[0, 3] != 0.0
                            ):
                                print(
                                    f"{prefix}Affine matrix needs correcting! Current matrix is {item.affine}"
                                )
                                sub_item.affine = torch.tensor(
                                    [
                                        [self.expected_voxel_size[0], 0.0, 0.0, 0.0],
                                        [0.0, self.expected_voxel_size[1], 0.0, 0.0],
                                        [0.0, 0.0, self.expected_voxel_size[2], 0.0],
                                        [0.0, 0.0, 0.0, 1.0],
                                    ]
                                )
                            else:
                                # The affine matrix is fine, nothing to correct
                                return data

        if type(data) is dict:
            for key in data.keys():
                item = data[key]
                if type(item) is MetaTensor and hasattr(item, "affine"):

                    if item.affine.shape != (4, 4):
                        print(
                            f"{prefix}Affine matrix needs correcting! Current shape is {item.affine.shape}"
                        )
                        item.affine = torch.tensor(
                            [
                                [self.expected_voxel_size[0], 0.0, 0.0, 0.0],
                                [0.0, self.expected_voxel_size[1], 0.0, 0.0],
                                [0.0, 0.0, self.expected_voxel_size[2], 0.0],
                                [0.0, 0.0, 0.0, 1.0],
                            ]
                        )
                    elif (
                        item.affine[0, 0] != self.expected_voxel_size[0]
                        or item.affine[1, 1] != self.expected_voxel_size[1]
                        or item.affine[2, 2] != self.expected_voxel_size[2]
                        or item.affine[0, 3] != 0.0
                    ):
                        print(
                            f"{prefix}Affine matrix needs correcting! Current matrix is {item.affine}"
                        )
                        item.affine = torch.tensor(
                            [
                                [self.expected_voxel_size[0], 0.0, 0.0, 0.0],
                                [0.0, self.expected_voxel_size[1], 0.0, 0.0],
                                [0.0, 0.0, self.expected_voxel_size[2], 0.0],
                                [0.0, 0.0, 0.0, 1.0],
                            ]
                        )
                    else:
                        # The affine matrix is fine, nothing to correct
                        pass

        elif type(data) is MetaTensor and hasattr(data, "affine"):
            if data.affine.shape != (4, 4):
                print(
                    f"{prefix}Affine matrix needs correcting! Current shape is {data.affine.shape}"
                )
                data.affine = torch.tensor(
                    [
                        [self.expected_voxel_size[0], 0.0, 0.0, 0.0],
                        [0.0, self.expected_voxel_size[1], 0.0, 0.0],
                        [0.0, 0.0, self.expected_voxel_size[2], 0.0],
                        [0.0, 0.0, 0.0, 1.0],
                    ]
                )
            elif (
                data.affine[0, 0] != self.expected_voxel_size[0]
                or data.affine[1, 1] != self.expected_voxel_size[1]
                or data.affine[2, 2] != self.expected_voxel_size[2]
                or data.affine[0, 3] != 0.0
            ):
                print(
                    f"{prefix}Affine matrix needs correcting! Current matrix is {data.affine}"
                )
                data.affine = torch.tensor(
                    [
                        [self.expected_voxel_size[0], 0.0, 0.0, 0.0],
                        [0.0, self.expected_voxel_size[1], 0.0, 0.0],
                        [0.0, 0.0, self.expected_voxel_size[2], 0.0],
                        [0.0, 0.0, 0.0, 1.0],
                    ]
                )
            else:
                # The affine matrix is fine, nothing to correct
                pass

        return data

Ancestors

  • monai.transforms.transform.Transform
  • abc.ABC
class DebugExtractChannel (channel_num: int, mask: bool = False, mask_threshold: float = 0.01, to_binary: bool = False)

Extract just one channel from a multi-channel tensor.

Constructor.

Parameters

channel_num : int
Index of channel to be extracted.
mask : bool = False
Set to Ture to mask the channel by its binarized version. It
mask_threshold : float = 1e-3
Threshold for the masked image.
to_binary : bool = False
Set to True to convert to binary via sigmoid transform and thresholding. It implies to_binary = True and will ignore the passed argument for it..
Expand source code
class DebugExtractChannel(Transform):
    """
    Extract just one channel from a multi-channel tensor.
    """

    def __init__(
        self,
        channel_num: int,
        mask: bool = False,
        mask_threshold: float = 1e-2,
        to_binary: bool = False,
    ):
        """Constructor.

        Parameters
        ----------

        channel_num: int
            Index of channel to be extracted.

        mask: bool = False
            Set to Ture to mask the channel by its binarized version. It

        mask_threshold: float = 1e-3
            Threshold for the masked image.

        to_binary: bool = False
            Set to True to convert to binary via sigmoid transform and thresholding. It implies to_binary = True
            and will ignore the passed argument for it..
        """
        super().__init__()
        self.channel_num = channel_num
        self.mask = mask
        self.mask_threshold = mask_threshold
        self.to_binary = to_binary

    def __call__(self, data):
        """Call the Transform."""

        # Check the number of dimensions in output tensor to determine if it's 2D or 3D
        if len(data.shape) not in [4, 5]:
            raise ValueError(
                "Unexpected number of dimensions, expected 4 (2D) or 5 (3D)."
            )

        # Determine if the data is 2D or 3D and adjust indexing accordingly
        dim_idx = (slice(None),) * (len(data.shape) - 2)

        # Extract the selected channel
        out = data[:, self.channel_num, None, Unpack[dim_idx]]

        # Binarize if requested
        bw = 1.0
        if self.to_binary or self.mask:
            bw = torch.sigmoid(out) > 0.5

        # Mask if requested
        if self.mask:
            out = bw * out
            out[out <= self.mask_threshold] = 0

        return out

Ancestors

  • monai.transforms.transform.Transform
  • abc.ABC
class DebugInformer (*args, **kwargs)

Simple reporter to be added to a Composed list of Transforms to return some information. The data is returned untouched.

Constructor.

Parameters

name : str
Name of the Informer. It is printed as a prefix to the output.
Expand source code
class DebugInformer(Transform):
    """
    Simple reporter to be added to a Composed list of Transforms
    to return some information. The data is returned untouched.
    """

    def __init__(self, *args, **kwargs):
        """Constructor.

        Parameters
        ----------

        name: str
            Name of the Informer. It is printed as a prefix to the output.
        """
        super().__init__()
        self.name = ""
        if "name" in kwargs:
            self.name = kwargs["name"]

    def __call__(self, data):
        """Call the Transform."""
        prefix = f"{self.name} :: " if self.name != "" else ""
        if type(data) is tuple and len(data) > 1:
            data = data[0]
        if type(data) is torch.Tensor:
            print(
                f"{prefix}"
                f"Type = Torch Tensor: "
                f"size = {data.size()}, "
                f"type = {data.dtype}, "
                f"min = {data.min()}, "
                f"mean = {data.double().mean()}, "
                f"median = {torch.median(data).item()}, "
                f"max = {data.max()}"
            )
        elif type(data) is np.ndarray:
            print(
                f"{prefix}"
                f"Type = Numpy Array: "
                f"size = {data.shape}, "
                f"type = {data.dtype}, "
                f"min = {data.min()}, "
                f"mean = {data.mean()}, "
                f"median = {np.median(data)}, "
                f"max = {data.max()}"
            )
        elif type(data) is str:
            print(f"{prefix}String: value = '{str}'")
        elif str(type(data)).startswith("<class 'itk."):
            # This is a bit of a hack...
            data_numpy = np.array(data)
            print(
                f"{prefix}"
                f"Type = ITK Image: "
                f"size = {data_numpy.shape}, "
                f"type = {data_numpy.dtype}, "
                f"min = {data_numpy.min()}, "
                f"mean = {data_numpy.mean()}, "
                f"median = {np.median(data_numpy)}, "
                f"max = {data_numpy.max()}"
            )
        elif type(data) is dict:
            # A dictionary, most likely of "image" and "label"
            print(f"{prefix}Dictionary with keys: ", end=" ")
            for key in data.keys():
                value = data[key]
                t = type(value)
                if t is MetaTensor:
                    print(
                        f"'{key}': shape={data[key].shape}, dtype={data[key].dtype};",
                        end=" ",
                    )
                    if hasattr(data[key], "meta") and "affine" in data[key].meta:
                        a = data[key].meta["affine"]
                        print(
                            f"voxel size=({a[0, 0]}, {a[1, 1]}, {a[2, 2]});",
                            end=" ",
                        )
                elif t is dict:
                    print(f"'{key}': dict;", end=" ")
                elif t is pathlib.PosixPath:
                    print(f"'{key}': path={str(data[key])};", end=" ")
                else:
                    print(f"'{key}': {t};", end=" ")
            print()
        elif type(data) is MetaTensor:
            print(f"{prefix}MONAI MetaTensor: shape={data.shape}, dtype={data.dtype}")
        else:
            try:
                print(f"{prefix}{type(data)}: {str(data)}")
            except:
                print(f"{prefix}Unknown type!")
        return data

Ancestors

  • monai.transforms.transform.Transform
  • abc.ABC
class DebugMinNumVoxelCheckerd (keys: tuple, class_num: int, min_fraction: float = 0.0)

Simple reporter to be added to a Composed list of Transforms to return some information. The data is returned untouched.

Constructor.

Parameters

class_num : int
Number of the class to check for presence.
Expand source code
class DebugMinNumVoxelCheckerd(MapTransform):
    """
    Simple reporter to be added to a Composed list of Transforms
    to return some information. The data is returned untouched.
    """

    def __init__(self, keys: tuple, class_num: int, min_fraction: float = 0.0):
        """Constructor.

        Parameters
        ----------

        class_num: int
            Number of the class to check for presence.
        """
        super().__init__(keys=keys)
        self.keys = keys
        self.class_num = class_num
        self.min_fraction = min_fraction

    def __call__(self, data):
        """Call the Transform."""

        # Make a copy of the input dictionary
        d = dict(data)

        # Get the keys
        keys = d.keys()

        # Only work on the expected keys
        for key in keys:
            if key not in self.keys:
                continue

            # The Tensor should be in OneHot format, and the class number should
            # map to the index of the first dimension.
            if self.class_num > (d[key].shape[0] - 1):
                raise Exception("`num_class` is out of boundaries.")

            num_voxels = d[key][self.class_num].sum()
            if self.min_fraction == 0.0:
                if num_voxels > 0:
                    return d

            if self.min_fraction > 0.0:
                area = d[key].shape[1] * d[key].shape[2]
                if num_voxels / area >= self.min_fraction:
                    return d

            raise Exception(
                f"The number of voxels for {self.class_num} is lower than expected ({num_voxels})."
            )

        # Return data
        return d

Ancestors

  • monai.transforms.transform.MapTransform
  • monai.transforms.transform.Transform
  • abc.ABC