Example #1
0
def plot_2d_or_3d_image(
    data: Union[torch.Tensor, np.ndarray],
    step: int,
    writer,
    index: int = 0,
    max_channels: int = 1,
    max_frames: int = 64,
    tag: str = "output",
):
    """Plot 2D or 3D image on the TensorBoard, 3D image will be converted to GIF image.

    Note:
        Plot 3D or 2D image(with more than 3 channels) as separate images.

    Args:
        data (Tensor or np.array): target data to be plotted as image on the TensorBoard.
            The data is expected to have 'NCHW[D]' dimensions, and only plot the first in the batch.
        step: current step to plot in a chart.
        writer (SummaryWriter): specify TensorBoard SummaryWriter to plot the image.
        index: plot which element in the input data batch, default is the first element.
        max_channels: number of channels to plot.
        max_frames: number of frames for 2D-t plot.
        tag: tag of the plotted image on TensorBoard.
    """
    assert isinstance(writer, SummaryWriter) is True, "must provide a TensorBoard SummaryWriter."
    d = data[index]
    if torch.is_tensor(d):
        d = d.detach().cpu().numpy()

    if d.ndim == 2:
        d = rescale_array(d, 0, 1)
        dataformats = "HW"
        writer.add_image(f"{tag}_{dataformats}", d, step, dataformats=dataformats)
        return

    if d.ndim == 3:
        if d.shape[0] == 3 and max_channels == 3:  # RGB
            dataformats = "CHW"
            writer.add_image(f"{tag}_{dataformats}", d, step, dataformats=dataformats)
            return
        for j, d2 in enumerate(d[:max_channels]):
            d2 = rescale_array(d2, 0, 1)
            dataformats = "HW"
            writer.add_image(f"{tag}_{dataformats}_{j}", d2, step, dataformats=dataformats)
        return

    if d.ndim >= 4:
        spatial = d.shape[-3:]
        for j, d3 in enumerate(d.reshape([-1] + list(spatial))[:max_channels]):
            d3 = rescale_array(d3, 0, 255)
            add_animated_gif(writer, f"{tag}_HWD_{j}", d3[None], max_frames, 1.0, step)
        return
Example #2
0
def create_test_image_3d(
    height: int,
    width: int,
    depth: int,
    num_objs: int = 12,
    rad_max: int = 30,
    noise_max: float = 0.0,
    num_seg_classes: int = 5,
    channel_dim: Optional[int] = None,
    random_state: Optional[np.random.RandomState] = None,
) -> Tuple[np.ndarray, np.ndarray]:
    """
    Return a noisy 3D image and segmentation.

    Args:
        height: height of the image.
        width: width of the image.
        depth: depth of the image.
        num_objs: number of circles to generate. Defaults to `12`.
        rad_max: maximum circle radius. Defaults to `30`.
        noise_max: if greater than 0 then noise will be added to the image taken from
            the uniform distribution on range `[0,noise_max)`. Defaults to `0`.
        num_seg_classes: number of classes for segmentations. Defaults to `5`.
        channel_dim: if None, create an image without channel dimension, otherwise create
            an image with channel dimension as first dim or last dim. Defaults to `None`.
        random_state: the random generator to use. Defaults to `np.random`.

    See also:
        :py:meth:`~create_test_image_2d`
    """
    image = np.zeros((width, height, depth))
    rs = np.random if random_state is None else random_state

    for _ in range(num_objs):
        x = rs.randint(rad_max, width - rad_max)
        y = rs.randint(rad_max, height - rad_max)
        z = rs.randint(rad_max, depth - rad_max)
        rad = rs.randint(5, rad_max)
        spy, spx, spz = np.ogrid[-x:width - x, -y:height - y, -z:depth - z]
        circle = (spx * spx + spy * spy + spz * spz) <= rad * rad

        if num_seg_classes > 1:
            image[circle] = np.ceil(rs.random() * num_seg_classes)
        else:
            image[circle] = rs.random() * 0.5 + 0.5

    labels = np.ceil(image).astype(np.int32)

    norm = rs.uniform(0, num_seg_classes * noise_max, size=image.shape)
    noisyimage = rescale_array(np.maximum(image, norm))

    if channel_dim is not None:
        assert isinstance(
            channel_dim,
            int) and channel_dim in (-1, 0, 3), "invalid channel dim."
        noisyimage, labels = ((noisyimage[None],
                               labels[None]) if channel_dim == 0 else
                              (noisyimage[..., None], labels[..., None]))

    return noisyimage, labels
Example #3
0
def create_test_image_3d(height,
                         width,
                         depth,
                         num_objs=12,
                         rad_max=30,
                         noise_max=0.0,
                         num_seg_classes=5):
    """
    Return a noisy 3D image and segmentation.

    See also: create_test_image_2d
    """
    image = np.zeros((width, height, depth))

    for i in range(num_objs):
        x = np.random.randint(rad_max, width - rad_max)
        y = np.random.randint(rad_max, height - rad_max)
        z = np.random.randint(rad_max, depth - rad_max)
        rad = np.random.randint(5, rad_max)
        spy, spx, spz = np.ogrid[-x:width - x, -y:height - y, -z:depth - z]
        circle = (spx * spx + spy * spy + spz * spz) <= rad * rad

        if num_seg_classes > 1:
            image[circle] = np.ceil(np.random.random() * num_seg_classes)
        else:
            image[circle] = np.random.random() * 0.5 + 0.5

    labels = np.ceil(image).astype(np.int32)

    norm = np.random.uniform(0, num_seg_classes * noise_max, size=image.shape)
    noisyimage = rescale_array(np.maximum(image, norm))

    return noisyimage, labels
Example #4
0
def create_test_image_2d(width,
                         height,
                         num_objs=12,
                         rad_max=30,
                         noise_max=0.0,
                         num_seg_classes=5):
    """
    Return a noisy 2D image with `numObj' circles and a 2D mask image. The maximum radius of the circles is given as
    `radMax'. The mask will have `numSegClasses' number of classes for segmentations labeled sequentially from 1, plus a
    background class represented as 0. If `noiseMax' is greater than 0 then noise will be added to the image taken from
    the uniform distribution on range [0,noiseMax).
    """
    image = np.zeros((width, height))

    for i in range(num_objs):
        x = np.random.randint(rad_max, width - rad_max)
        y = np.random.randint(rad_max, height - rad_max)
        rad = np.random.randint(5, rad_max)
        spy, spx = np.ogrid[-x:width - x, -y:height - y]
        circle = (spx * spx + spy * spy) <= rad * rad

        if num_seg_classes > 1:
            image[circle] = np.ceil(np.random.random() * num_seg_classes)
        else:
            image[circle] = np.random.random() * 0.5 + 0.5

    labels = np.ceil(image).astype(np.int32)

    norm = np.random.uniform(0, num_seg_classes * noise_max, size=image.shape)
    noisyimage = rescale_array(np.maximum(image, norm))

    return noisyimage, labels
Example #5
0
def create_test_image_2d(
    width: int,
    height: int,
    num_objs: int = 12,
    rad_max: int = 30,
    noise_max: float = 0.0,
    num_seg_classes: int = 5,
    channel_dim: Optional[int] = None,
    random_state: Optional[np.random.RandomState] = None,
) -> Tuple[np.ndarray, np.ndarray]:
    """
    Return a noisy 2D image with `num_objs` circles and a 2D mask image. The maximum radius of the circles is given as
    `rad_max`. The mask will have `num_seg_classes` number of classes for segmentations labeled sequentially from 1, plus a
    background class represented as 0. If `noise_max` is greater than 0 then noise will be added to the image taken from
    the uniform distribution on range `[0,noise_max)`. If `channel_dim` is None, will create an image without channel
    dimension, otherwise create an image with channel dimension as first dim or last dim.

    Args:
        width: width of the image.
        height: height of the image.
        num_objs: number of circles to generate. Defaults to `12`.
        rad_max: maximum circle radius. Defaults to `30`.
        noise_max: if greater than 0 then noise will be added to the image taken from
            the uniform distribution on range `[0,noise_max)`. Defaults to `0`.
        num_seg_classes: number of classes for segmentations. Defaults to `5`.
        channel_dim: if None, create an image without channel dimension, otherwise create
            an image with channel dimension as first dim or last dim. Defaults to `None`.
        random_state: the random generator to use. Defaults to `np.random`.
    """
    image = np.zeros((width, height))
    rs = np.random if random_state is None else random_state

    for _ in range(num_objs):
        x = rs.randint(rad_max, width - rad_max)
        y = rs.randint(rad_max, height - rad_max)
        rad = rs.randint(5, rad_max)
        spy, spx = np.ogrid[-x : width - x, -y : height - y]
        circle = (spx * spx + spy * spy) <= rad * rad

        if num_seg_classes > 1:
            image[circle] = np.ceil(rs.random() * num_seg_classes)
        else:
            image[circle] = rs.random() * 0.5 + 0.5

    labels = np.ceil(image).astype(np.int32)

    norm = rs.uniform(0, num_seg_classes * noise_max, size=image.shape)
    noisyimage = rescale_array(np.maximum(image, norm))

    if channel_dim is not None:
        if not (isinstance(channel_dim, int) and channel_dim in (-1, 0, 2)):
            raise AssertionError("invalid channel dim.")
        if channel_dim == 0:
            noisyimage = noisyimage[None]
            labels = labels[None]
        else:
            noisyimage = noisyimage[..., None]
            labels = labels[..., None]

    return noisyimage, labels
Example #6
0
 def __call__(self, img):
     """
     Apply the transform to `img`.
     """
     if self.minv is not None and self.maxv is not None:
         return rescale_array(img, self.minv, self.maxv, img.dtype)
     else:
         return (img * (1 + self.factor)).astype(img.dtype)
Example #7
0
def blend_images(image: NdarrayOrTensor,
                 label: NdarrayOrTensor,
                 alpha: float = 0.5,
                 cmap: str = "hsv",
                 rescale_arrays: bool = True):
    """
    Blend an image and a label. Both should have the shape CHW[D].
    The image may have C==1 or 3 channels (greyscale or RGB).
    The label is expected to have C==1.

    Args:
        image: the input image to blend with label data.
        label: the input label to blend with image data.
        alpha: when blending image and label, `alpha` is the weight for the image region mapping to `label != 0`,
            and `1 - alpha` is the weight for the label region that `label != 0`, default to `0.5`.
        cmap: specify colormap in the matplotlib, default to `hsv`, for more details, please refer to:
            https://matplotlib.org/2.0.2/users/colormaps.html.
        rescale_arrays: whether to rescale the array to [0, 1] first, default to `True`.

    """

    if label.shape[0] != 1:
        raise ValueError("Label should have 1 channel")
    if image.shape[0] not in (1, 3):
        raise ValueError("Image should have 1 or 3 channels")
    # rescale arrays to [0, 1] if desired
    if rescale_arrays:
        image = rescale_array(image)
        label = rescale_array(label)
    # convert image to rgb (if necessary) and then rgba
    if image.shape[0] == 1:
        image = repeat(image, 3, axis=0)

    def get_label_rgb(cmap: str, label: NdarrayOrTensor):
        _cmap = cm.get_cmap(cmap)
        label_np, *_ = convert_data_type(label, np.ndarray)
        label_rgb_np = _cmap(label_np[0])
        label_rgb_np = np.moveaxis(label_rgb_np, -1, 0)[:3]
        label_rgb, *_ = convert_to_dst_type(label_rgb_np, label)
        return label_rgb

    label_rgb = get_label_rgb(cmap, label)
    w_image = where(label == 0, 1.0, alpha)
    w_label = where(label == 0, 0.0, 1 - alpha)
    return w_image * image + w_label * label_rgb
Example #8
0
 def test_max_none(self):
     for p in TEST_NDARRAYS:
         scaler = ScaleIntensity(minv=0.0, maxv=None, factor=0.1)
         result = scaler(p(self.imt))
         expected = rescale_array(p(self.imt), minv=0.0, maxv=None)
         assert_allclose(result,
                         expected,
                         type_test="tensor",
                         rtol=1e-3,
                         atol=1e-3)
Example #9
0
    def _add_2_or_3_d(self, data, step, tag='output'):
        # for i, d in enumerate(data):  # go through a batch of images
        d = data[0]  # show the first element in a batch

        if d.ndim == 2:
            d = rescale_array(d, 0, 1)
            dataformats = 'HW'
            self._writer.add_image('{}_{}'.format(tag, dataformats),
                                   d,
                                   step,
                                   dataformats=dataformats)
            return

        if d.ndim == 3:
            if d.shape[0] == 3 and self.max_channels == 3:  # RGB
                dataformats = 'CHW'
                self._writer.add_image('{}_{}'.format(tag, dataformats),
                                       d,
                                       step,
                                       dataformats=dataformats)
                return
            for j, d2 in enumerate(d[:self.max_channels]):
                d2 = rescale_array(d2, 0, 1)
                dataformats = 'HW'
                self._writer.add_image('{}_{}_{}'.format(tag, dataformats, j),
                                       d2,
                                       step,
                                       dataformats=dataformats)
                return

        if d.ndim >= 4:
            spatial = d.shape[-3:]
            for j, d3 in enumerate(
                    d.reshape([-1] + list(spatial))[:self.max_channels]):
                d3 = rescale_array(d3, 0, 255)
                img2tensorboard.add_animated_gif(self._writer,
                                                 '{}_HWD_{}'.format(tag, j),
                                                 d3[None], self.max_frames,
                                                 1.0, step)
            return
Example #10
0
    def __call__(self, img: np.ndarray) -> np.ndarray:
        """
        Apply the transform to `img`.

        Raises:
            ValueError: When ``self.minv=None`` or ``self.maxv=None`` and ``self.factor=None``. Incompatible values.

        """
        if self.minv is not None and self.maxv is not None:
            return rescale_array(img, self.minv, self.maxv, img.dtype)
        if self.factor is not None:
            return (img * (1 + self.factor)).astype(img.dtype)
        raise ValueError("Incompatible values: minv=None or maxv=None and factor=None.")
Example #11
0
def create_test_image_3d(
    height: int,
    width: int,
    depth: int,
    num_objs: int = 12,
    rad_max: int = 30,
    noise_max: float = 0.0,
    num_seg_classes: int = 5,
    channel_dim: Optional[int] = None,
    random_state: Optional[np.random.RandomState] = None,
):
    """
    Return a noisy 3D image and segmentation.

    See also:
        :py:meth:`~create_test_image_2d`
    """
    image = np.zeros((width, height, depth))
    rs = np.random if random_state is None else random_state

    for _ in range(num_objs):
        x = rs.randint(rad_max, width - rad_max)
        y = rs.randint(rad_max, height - rad_max)
        z = rs.randint(rad_max, depth - rad_max)
        rad = rs.randint(5, rad_max)
        spy, spx, spz = np.ogrid[-x:width - x, -y:height - y, -z:depth - z]
        circle = (spx * spx + spy * spy + spz * spz) <= rad * rad

        if num_seg_classes > 1:
            image[circle] = np.ceil(rs.random() * num_seg_classes)
        else:
            image[circle] = rs.random() * 0.5 + 0.5

    labels = np.ceil(image).astype(np.int32)

    norm = rs.uniform(0, num_seg_classes * noise_max, size=image.shape)
    noisyimage = rescale_array(np.maximum(image, norm))

    if channel_dim is not None:
        assert isinstance(
            channel_dim,
            int) and channel_dim in (-1, 0, 3), "invalid channel dim."
        noisyimage, labels = ((noisyimage[None],
                               labels[None]) if channel_dim == 0 else
                              (noisyimage[..., None], labels[..., None]))

    return noisyimage, labels
Example #12
0
def create_test_image_2d(width,
                         height,
                         num_objs=12,
                         rad_max=30,
                         noise_max=0.0,
                         num_seg_classes=5,
                         channel_dim=None):
    """
    Return a noisy 2D image with `num_obj` circles and a 2D mask image. The maximum radius of the circles is given as
    `rad_max`. The mask will have `num_seg_classes` number of classes for segmentations labeled sequentially from 1, plus a
    background class represented as 0. If `noise_max` is greater than 0 then noise will be added to the image taken from
    the uniform distribution on range `[0,noise_max)`. If `channel_dim` is None, will create an image without channel
    dimension, otherwise create an image with channel dimension as first dim or last dim.
    """
    image = np.zeros((width, height))

    for i in range(num_objs):
        x = np.random.randint(rad_max, width - rad_max)
        y = np.random.randint(rad_max, height - rad_max)
        rad = np.random.randint(5, rad_max)
        spy, spx = np.ogrid[-x:width - x, -y:height - y]
        circle = (spx * spx + spy * spy) <= rad * rad

        if num_seg_classes > 1:
            image[circle] = np.ceil(np.random.random() * num_seg_classes)
        else:
            image[circle] = np.random.random() * 0.5 + 0.5

    labels = np.ceil(image).astype(np.int32)

    norm = np.random.uniform(0, num_seg_classes * noise_max, size=image.shape)
    noisyimage = rescale_array(np.maximum(image, norm))

    if channel_dim is not None:
        assert isinstance(
            channel_dim,
            int) and channel_dim in (-1, 0, 2), "invalid channel dim."
        noisyimage, labels = (
            noisyimage[None],
            labels[None] if channel_dim == 0 else
            (noisyimage[..., None], labels[..., None]),
        )

    return noisyimage, labels
Example #13
0
def create_test_image_3d(height,
                         width,
                         depth,
                         num_objs=12,
                         rad_max=30,
                         noise_max=0.0,
                         num_seg_classes=5,
                         channel_dim=None):
    """
    Return a noisy 3D image and segmentation.

    See also:
        :py:meth:`~create_test_image_2d`
    """
    image = np.zeros((width, height, depth))

    for i in range(num_objs):
        x = np.random.randint(rad_max, width - rad_max)
        y = np.random.randint(rad_max, height - rad_max)
        z = np.random.randint(rad_max, depth - rad_max)
        rad = np.random.randint(5, rad_max)
        spy, spx, spz = np.ogrid[-x:width - x, -y:height - y, -z:depth - z]
        circle = (spx * spx + spy * spy + spz * spz) <= rad * rad

        if num_seg_classes > 1:
            image[circle] = np.ceil(np.random.random() * num_seg_classes)
        else:
            image[circle] = np.random.random() * 0.5 + 0.5

    labels = np.ceil(image).astype(np.int32)

    norm = np.random.uniform(0, num_seg_classes * noise_max, size=image.shape)
    noisyimage = rescale_array(np.maximum(image, norm))

    if channel_dim is not None:
        assert isinstance(
            channel_dim,
            int) and channel_dim in (-1, 0, 3), 'invalid channel dim.'
        noisyimage, labels = (noisyimage[None], labels[None]) \
            if channel_dim == 0 else (noisyimage[..., None], labels[..., None])

    return noisyimage, labels
Example #14
0
 def __call__(self, img):
     if self.minv is not None and self.maxv is not None:
         return rescale_array(img, self.minv, self.maxv, img.dtype)
     else:
         return (img * (1 + self.factor)).astype(img.dtype)
Example #15
0
 def __call__(self, img):
     return rescale_array(img, self.minv, self.maxv, self.dtype)