Exemplo n.º 1
0
def verify_same_geometry(img_1: sitk.Image, img_2: sitk.Image):
    ori1, spacing1, direction1, size1 = img_1.GetOrigin(), img_1.GetSpacing(
    ), img_1.GetDirection(), img_1.GetSize()
    ori2, spacing2, direction2, size2 = img_2.GetOrigin(), img_2.GetSpacing(
    ), img_2.GetDirection(), img_2.GetSize()

    same_ori = np.all(np.isclose(ori1, ori2))
    if not same_ori:
        print("the origin does not match between the images:")
        print(ori1)
        print(ori2)

    same_spac = np.all(np.isclose(spacing1, spacing2))
    if not same_spac:
        print("the spacing does not match between the images")
        print(spacing1)
        print(spacing2)

    same_dir = np.all(np.isclose(direction1, direction2))
    if not same_dir:
        print("the direction does not match between the images")
        print(direction1)
        print(direction2)

    same_size = np.all(np.isclose(size1, size2))
    if not same_size:
        print("the size does not match between the images")
        print(size1)
        print(size2)

    if same_ori and same_spac and same_dir and same_size:
        return True
    else:
        return False
Exemplo n.º 2
0
def assert_sitk_img_equivalence(img: SimpleITK.Image,
                                img_ref: SimpleITK.Image):
    assert img.GetDimension() == img_ref.GetDimension()
    assert img.GetDirection() == img.GetDirection()
    assert img.GetSize() == img_ref.GetSize()
    assert img.GetOrigin() == img_ref.GetOrigin()
    assert img.GetSpacing() == img_ref.GetSpacing()
    assert (img.GetNumberOfComponentsPerPixel() ==
            img_ref.GetNumberOfComponentsPerPixel())
    assert img.GetPixelIDValue() == img_ref.GetPixelIDValue()
    assert img.GetPixelIDTypeAsString() == img_ref.GetPixelIDTypeAsString()
Exemplo n.º 3
0
def sitk_to_nib(
    image: sitk.Image,
    keepdim: bool = False,
) -> Tuple[np.ndarray, np.ndarray]:
    data = sitk.GetArrayFromImage(image).transpose()
    num_components = image.GetNumberOfComponentsPerPixel()
    if num_components == 1:
        data = data[np.newaxis]  # add channels dimension
    input_spatial_dims = image.GetDimension()
    if input_spatial_dims == 2:
        data = data[..., np.newaxis]
    if not keepdim:
        data = ensure_4d(data, num_spatial_dims=input_spatial_dims)
    assert data.shape[0] == num_components
    assert data.shape[1:1 + input_spatial_dims] == image.GetSize()
    spacing = np.array(image.GetSpacing())
    direction = np.array(image.GetDirection())
    origin = image.GetOrigin()
    if len(direction) == 9:
        rotation = direction.reshape(3, 3)
    elif len(direction) == 4:  # ignore first dimension if 2D (1, W, H, 1)
        rotation_2d = direction.reshape(2, 2)
        rotation = np.eye(3)
        rotation[:2, :2] = rotation_2d
        spacing = *spacing, 1
        origin = *origin, 0
    else:
        raise RuntimeError(f'Direction not understood: {direction}')
    rotation = np.dot(FLIP_XY, rotation)
    rotation_zoom = rotation * spacing
    translation = np.dot(FLIP_XY, origin)
    affine = np.eye(4)
    affine[:3, :3] = rotation_zoom
    affine[:3, 3] = translation
    return data, affine
Exemplo n.º 4
0
def split_4d_itk(img_itk: sitk.Image) -> List[sitk.Image]:
    """
    Helper function to split 4d itk images into multiple 3 images

    Args:
        img_itk: 4D input image

    Returns:
        List[sitk.Image]: 3d output images
    """
    img_npy = sitk.GetArrayFromImage(img_itk)
    spacing = img_itk.GetSpacing()
    origin = img_itk.GetOrigin()
    direction = np.array(img_itk.GetDirection()).reshape(4, 4)

    spacing = tuple(list(spacing[:-1]))
    assert len(spacing) == 3
    origin = tuple(list(origin[:-1]))
    assert len(origin) == 3
    direction = tuple(direction[:-1, :-1].reshape(-1))
    assert len(direction) == 9

    images_new = []
    for i, t in enumerate(range(img_npy.shape[0])):
            img = img_npy[t]
            images_new.append(
                create_itk_image_spatial_props(img, spacing, origin, direction))
    return images_new
Exemplo n.º 5
0
def sitk_to_nib(
    image: sitk.Image,
    keepdim: bool = False,
) -> Tuple[np.ndarray, np.ndarray]:
    data = sitk.GetArrayFromImage(image).transpose()
    num_components = image.GetNumberOfComponentsPerPixel()
    if num_components == 1:
        data = data[np.newaxis]  # add channels dimension
    input_spatial_dims = image.GetDimension()
    if not keepdim:
        data = ensure_4d(data, False, num_spatial_dims=input_spatial_dims)
    assert data.shape[0] == num_components
    assert data.shape[-input_spatial_dims:] == image.GetSize()
    spacing = np.array(image.GetSpacing())
    direction = np.array(image.GetDirection())
    origin = image.GetOrigin()
    if len(direction) == 9:
        rotation = direction.reshape(3, 3)
    elif len(direction) == 4:  # ignore first dimension if 2D (1, 1, H, W)
        rotation_2d = direction.reshape(2, 2)
        rotation = np.eye(3)
        rotation[1:3, 1:3] = rotation_2d
        spacing = 1, *spacing
        origin = 0, *origin
    rotation = np.dot(FLIP_XY, rotation)
    rotation_zoom = rotation * spacing
    translation = np.dot(FLIP_XY, origin)
    affine = np.eye(4)
    affine[:3, :3] = rotation_zoom
    affine[:3, 3] = translation
    return data, affine
Exemplo n.º 6
0
def image_resample(image: sitk.Image):
    '''
    image = sitk.ReadImage(image_path)
    使用 SimpleITK 自带函数重新缩放图像
    '''
    origin_spacing = image.GetSpacing()  #  获取源分辨率
    origin_size = image.GetSize()

    new_spacing = [1, 1, 1]  #  设置新分辨率

    resample = sitk.ResampleImageFilter()
    resample.SetInterpolator(sitk.sitkLinear)
    resample.SetDefaultPixelValue(0)

    resample.SetOutputSpacing(new_spacing)
    resample.SetOutputOrigin(image.GetOrigin())
    resample.SetOutputDirection(image.GetDirection())

    #  计算新图像的大小
    new_size = [
        int(np.round(origin_size[0] * (origin_spacing[0] / new_spacing[0]))),
        int(np.round(origin_size[1] * (origin_spacing[1] / new_spacing[1]))),
        int(np.round(origin_size[2] * (origin_spacing[2] / new_spacing[2])))
    ]
    resample.SetSize(new_size)

    new_image = resample.Execute(image)
    return new_image
Exemplo n.º 7
0
def resample_sitk_image(sitk_image: sitk.Image,
                        new_size,
                        interpolator="gaussian",
                        fill_value=0) -> sitk.Image:
    """
        modified version from:
            https://github.com/jonasteuwen/SimpleITK-examples/blob/master/examples/resample_isotropically.py
    """

    # if pass a path to image
    if isinstance(sitk_image, str):
        sitk_image = sitk.ReadImage(sitk_image)

    assert (interpolator in _SITK_INTERPOLATOR_DICT.keys()
            ), "`interpolator` should be one of {}".format(
                _SITK_INTERPOLATOR_DICT.keys())

    if not interpolator:
        interpolator = "linear"
        pixelid = sitk_image.GetPixelIDValue()

        if pixelid not in [1, 2, 4]:
            raise NotImplementedError(
                "Set `interpolator` manually, "
                "can only infer for 8-bit unsigned or 16, 32-bit signed integers"
            )

    #  8-bit unsigned int
    if sitk_image.GetPixelIDValue() == 1:
        # if binary mask interpolate it as nearest
        interpolator = "nearest"

    sitk_interpolator = _SITK_INTERPOLATOR_DICT[interpolator]
    orig_pixelid = sitk_image.GetPixelIDValue()
    orig_origin = sitk_image.GetOrigin()
    orig_direction = sitk_image.GetDirection()

    # new spacing based on the desired output shape
    new_spacing = tuple(
        np.array(sitk_image.GetSpacing()) * np.array(sitk_image.GetSize()) /
        np.array(new_size))

    # setup image resampler - SimpleITK 2.0
    resample_filter = sitk.ResampleImageFilter()
    resample_filter.SetOutputSpacing(new_spacing)
    resample_filter.SetSize(new_size)
    resample_filter.SetOutputDirection(orig_direction)
    resample_filter.SetOutputOrigin(orig_origin)
    resample_filter.SetTransform(sitk.Transform())
    resample_filter.SetDefaultPixelValue(orig_pixelid)
    resample_filter.SetInterpolator(sitk_interpolator)
    resample_filter.SetDefaultPixelValue(fill_value)
    # run it
    resampled_sitk_image = resample_filter.Execute(sitk_image)

    return resampled_sitk_image
Exemplo n.º 8
0
def match_world_info(source: sitk.Image,
                     target: sitk.Image,
                     spacing: Union[bool, Tuple[int], List[int]] = True,
                     origin: Union[bool, Tuple[int], List[int]] = True,
                     direction: Union[bool, Tuple[int], List[int]] = True):
    """Copy world information (eg spacing, origin, direction) from one
    image object to another.

    This matching is sometimes necessary for slight differences in
    metadata perhaps from founding that may prevent ITK filters from executing.

    Args:
        source (:obj:`sitk.Image`): Source object whose relevant metadata
            will be copied into ``target``.
        target (:obj:`sitk.Image`): Target object whose corresponding
            metadata will be overwritten by that of ``source``.
        spacing: True to copy the spacing from ``source`` to ``target``, or
            the spacing to set in ``target``; defaults to True.
        origin: True to copy the origin from ``source`` to ``target``, or
            the origin to set in ``target``; defaults to True.
        direction: True to copy the direction from ``source`` to ``target``, or
            the direction to set in ``target``; defaults to True.

    """
    # get the world info from the source if not already set
    if spacing is True:
        spacing = source.GetSpacing()
    if origin is True:
        origin = source.GetOrigin()
    if direction is True:
        direction = source.GetDirection()

    # set the values in the target
    _logger.debug(
        "Adjusting spacing from %s to %s, origin from %s to %s, "
        "direction from %s to %s", target.GetSpacing(), spacing,
        target.GetOrigin(), origin, target.GetDirection(), direction)
    if spacing:
        target.SetSpacing(spacing)
    if origin:
        target.SetOrigin(origin)
    if direction:
        target.SetDirection(direction)
Exemplo n.º 9
0
def sitk_to_nib(image: sitk.Image) -> Tuple[np.ndarray, np.ndarray]:
    data = sitk.GetArrayFromImage(image).transpose()
    spacing = np.array(image.GetSpacing())
    rotation = np.array(image.GetDirection()).reshape(3, 3)
    rotation = np.dot(FLIP_XY, rotation)
    rotation_zoom = rotation * spacing
    translation = np.dot(FLIP_XY, image.GetOrigin())
    affine = np.eye(4)
    affine[:3, :3] = rotation_zoom
    affine[:3, 3] = translation
    return data, affine
Exemplo n.º 10
0
def display_info(img: sitk.Image) -> None:
    """display information about a sitk.Image

    Args:
        img (sitk.Image): [sitk image]
    """
    print('img information :')
    print('\t Origin    :', img.GetOrigin())
    print('\t Size      :', img.GetSize())
    print('\t Spacing   :', img.GetSpacing())
    print('\t Direction :', img.GetDirection())
Exemplo n.º 11
0
    def set_origin_image(self, origin_img:sitk.Image) -> None : 
        """method to set the origin sitk.Image on which we want to resample 

        Args:
            origin_img (sitk.Image): []
        """
        self.origin_img = origin_img 
        self.origin_size = origin_img.GetSize()
        self.origin_spacing = origin_img.GetSpacing()
        self.origin_direction = origin_img.GetDirection()
        self.origin_origin = origin_img.GetOrigin()
Exemplo n.º 12
0
    def _assert_3d(self, image: sitk.Image, is_vector=False):
        self.assertEqual(self.properties_3d.size, image.GetSize())

        if is_vector:
            self.assertEqual(self.no_vector_components,
                             image.GetNumberOfComponentsPerPixel())
        else:
            self.assertEqual(1, image.GetNumberOfComponentsPerPixel())

        self.assertEqual(self.origin_spacing_3d, image.GetOrigin())
        self.assertEqual(self.origin_spacing_3d, image.GetSpacing())
        self.assertEqual(self.direction_3d, image.GetDirection())
Exemplo n.º 13
0
    def __init__(self, image: sitk.Image):
        """Initializes a new instance of the ImageInformation class.

        Args:
            image (sitk.Image): The image whose properties to hold.
        """
        self.size = image.GetSize()
        self.origin = image.GetOrigin()
        self.spacing = image.GetSpacing()
        self.direction = image.GetDirection()
        self.dimensions = image.GetDimension()
        self.number_of_components_per_pixel = image.GetNumberOfComponentsPerPixel()
        self.pixel_id = image.GetPixelID()
Exemplo n.º 14
0
    def get_mask(ground_truth: sitk.Image,
                 ground_truth_labels: list,
                 label_percentages: list,
                 background_mask: sitk.Image = None) -> sitk.Image:
        """Gets a training mask.

        Args:
            ground_truth (sitk.Image): The ground truth image.
            ground_truth_labels (list of int): The ground truth labels,
                where 0=background, 1=label1, 2=label2, ..., e.g. [0, 1]
            label_percentages (list of float): The percentage of voxels of a corresponding label to extract as mask,
                e.g. [0.2, 0.2].
            background_mask (sitk.Image): A mask, where intensity 0 indicates voxels to exclude independent of the label.

        Returns:
            sitk.Image: The training mask.
        """

        # initialize mask
        ground_truth_array = sitk.GetArrayFromImage(ground_truth)
        mask_array = np.zeros(ground_truth_array.shape, dtype=np.uint8)

        # exclude background
        if background_mask is not None:
            background_mask_array = sitk.GetArrayFromImage(background_mask)
            background_mask_array = np.logical_not(background_mask_array)
            ground_truth_array = ground_truth_array.astype(
                float)  # convert to float because of np.nan
            ground_truth_array[background_mask_array] = np.nan

        for label_idx, label in enumerate(ground_truth_labels):
            indices = np.transpose(np.where(ground_truth_array == label))
            np.random.shuffle(indices)

            no_mask_items = int(indices.shape[0] *
                                label_percentages[label_idx])

            for no in range(no_mask_items):
                x = indices[no][0]
                y = indices[no][1]
                z = indices[no][2]
                mask_array[x, y, z] = 1  # this is a masked item

        mask = sitk.GetImageFromArray(mask_array)
        mask.SetOrigin(ground_truth.GetOrigin())
        mask.SetDirection(ground_truth.GetDirection())
        mask.SetSpacing(ground_truth.GetSpacing())

        return mask
Exemplo n.º 15
0
    def execute(self,
                image: sitk.Image,
                params: fltr.IFilterParams = None) -> sitk.Image:
        """Executes a atlas coordinates feature extractor on an image.

        Args:
            image (sitk.Image): The image.
            params (fltr.IFilterParams): The parameters (unused).

        Returns:
            sitk.Image: The atlas coordinates image
            (a vector image with 3 components, which represent the physical x, y, z coordinates in mm).

        Raises:
            ValueError: If image is not 3-D.
        """

        if image.GetDimension() != 3:
            raise ValueError('image needs to be 3-D')

        x, y, z = image.GetSize()

        # create matrix with homogenous indices in axis 3
        coords = np.zeros((x, y, z, 4))
        coords[..., 0] = np.arange(x)[:, np.newaxis, np.newaxis]
        coords[..., 1] = np.arange(y)[np.newaxis, :, np.newaxis]
        coords[..., 2] = np.arange(z)[np.newaxis, np.newaxis, :]
        coords[..., 3] = 1

        # reshape such that each voxel is one row
        lin_coords = np.reshape(
            coords, [coords.shape[0] * coords.shape[1] * coords.shape[2], 4])

        # generate transformation matrix
        tmpmat = image.GetDirection() + image.GetOrigin()
        tfm = np.reshape(tmpmat, [3, 4], order='F')
        tfm = np.vstack((tfm, [0, 0, 0, 1]))

        atlas_coords = (tfm @ np.transpose(lin_coords))[0:3, :]
        atlas_coords = np.reshape(np.transpose(atlas_coords), [z, y, x, 3],
                                  'F')

        img_out = sitk.GetImageFromArray(atlas_coords)
        img_out.CopyInformation(image)

        return img_out
Exemplo n.º 16
0
def set_shared_functional_groups_sequence(target: pydicom.Dataset,
                                          segmentation: sitk.Image):
    spacing = segmentation.GetSpacing()

    dataset = pydicom.Dataset()
    dataset.PixelMeasuresSequence = [pydicom.Dataset()]
    dataset.PixelMeasuresSequence[0].PixelSpacing = [
        f'{x:e}' for x in spacing[:2]
    ]
    dataset.PixelMeasuresSequence[0].SliceThickness = f'{spacing[2]:e}'
    dataset.PixelMeasuresSequence[0].SpacingBetweenSlices = f'{spacing[2]:e}'
    dataset.PlaneOrientationSequence = [pydicom.Dataset()]
    dataset.PlaneOrientationSequence[0].ImageOrientationPatient = [
        f'{x:e}' for x in np.ravel(segmentation.GetDirection())[:6]
    ]

    target.SharedFunctionalGroupsSequence = pydicom.Sequence([dataset])
Exemplo n.º 17
0
    def roi2mask(self, mask_img:sitk.Image, pet_img:sitk.Image) -> sitk.Image:
        """
        Generate the thresholded mask from the ROI with otsu, 41%, 2.5 and 4.0 segmentation
        
        Args:
            :param mask_img: sitk.Image, raw mask (i.e ROI)
            :param pet_img: sitk.Image, the corresponding pet scan

        :return: sitk.Image, the ground truth segmentation
        """
        # transform to numpy
        origin = mask_img.GetOrigin()
        spacing = mask_img.GetSpacing()
        direction = tuple(mask_img.GetDirection())
        mask_array = sitk.GetArrayFromImage(mask_img)
        pet_array = sitk.GetArrayFromImage(pet_img)

        # get 3D meta information
        if len(mask_array.shape) == 3:
            mask_array = np.expand_dims(mask_array, axis=0)
        else:
            mask_array = np.transpose(mask_array, (3,0,1,2))

        new_masks = []
        #otsu 
        #print('otsu')
        new_masks.append(self.__roi_seg(mask_array, pet_array, threshold='otsu'))
        #print('41%')
        new_masks.append(self.__roi_seg(mask_array, pet_array, threshold='0.41'))
        #2.5
        #print('2.5')
        new_masks.append(self.__roi_seg(mask_array, pet_array, threshold='2.5'))
        #4.0
        #print('4.0')
        new_masks.append(self.__roi_seg(mask_array, pet_array, threshold='4.0'))
        new_mask = np.stack(new_masks, axis=3)
        new_mask = np.mean(new_mask, axis=3)
 

        # reconvert to sitk and restore 3D meta-information
        new_mask = sitk.GetImageFromArray(new_mask)
        new_mask.SetOrigin(origin)
        new_mask.SetDirection(direction)
        new_mask.SetSpacing(spacing)
    
        return new_mask
Exemplo n.º 18
0
    def roi2mask(self, mask_img:sitk.Image, pet_img:sitk.Image) -> sitk.Image:
        """
        Generate the thresholded mask from the ROI 
        Args:
            :param mask_img: sitk.Image, raw mask (i.e ROI)
            :param pet_img: sitk.Image, the corresponding pet scan

        :return: sitk.Image, the ground truth segmentation
        """
        # transform to numpy
        origin = mask_img.GetOrigin()
        spacing = mask_img.GetSpacing()
        direction = tuple(mask_img.GetDirection())
        mask_array = sitk.GetArrayFromImage(mask_img) #[z,y,x,C]
        pet_array = sitk.GetArrayFromImage(pet_img) #[z,y,x]

        # get 3D meta information
        if len(mask_array.shape) == 3:
            mask_array = np.expand_dims(mask_array, axis=0) #[1,z,y,x]
        else : 
            mask_array = np.transpose(mask_array, (3,0,1,2)) #[C,z,y,x]

        new_mask = np.zeros(mask_array.shape[1:], dtype=np.int8) #[z,y,x]

        for num_slice in range(mask_array.shape[0]):
            mask_slice = mask_array[num_slice] #ROI 3D MATRIX
            roi = pet_array[mask_slice > 0]
            if len(roi) == 0:
                # R.O.I is empty
                continue
            try:
                threshold = self.calculate_threshold(roi)
                # apply threshold
                new_mask[np.where((pet_array >= threshold) & (mask_slice > 0))] = 1

            except Exception as e:
                print(e)
                print(sys.exc_info()[0])

        # reconvert to sitk and restore information
        new_mask = sitk.GetImageFromArray(new_mask)
        new_mask.SetOrigin(origin)
        new_mask.SetDirection(direction)
        new_mask.SetSpacing(spacing)

        return new_mask
Exemplo n.º 19
0
def scale_image(image: sitk.Image,
                new_size: Tuple[int, ...],
                interpolator: int = sitk.sitkLinear) -> sitk.Image:
    r""" Scale an image in a grid of given size.

    Parameters
    ----------
    image : sitk.Image
        An input image.
    new_size : Tuple[int, ...]
        A tuple of integers expressing the new size.
    interpolator : int
        A SimpleITK interpolator enum value.

    Returns
    -------
    sitk.Image
        Resized image.
    """

    if type(image) is not sitk.SimpleITK.Image:
        raise Exception("unsupported image object type")

    if type(new_size) != type((1, 1)):
        raise Exception("new_size must be a tuple of integers")

    size = image.GetSize()
    if len(new_size) != len(size):
        raise Exception("new_size must match the image dimensionality")

    spacing = []
    for s, x, nx in zip(image.GetSpacing(), size, new_size):
        spacing.append(s * x / nx)

    resampler = sitk.ResampleImageFilter()
    resampler.SetSize(new_size)
    resampler.SetOutputSpacing(tuple(spacing))
    resampler.SetOutputOrigin(image.GetOrigin())
    resampler.SetOutputDirection(image.GetDirection())
    resampler.SetInterpolator(interpolator)

    # Anti-aliasing
    image = sitk.SmoothingRecursiveGaussian(image, 2.0)

    return resampler.Execute(image)
Exemplo n.º 20
0
def compute_dice(b1: sitk.Image, b2: sitk.Image):

    b2 = sitk.Resample(
        b2,
        b1.GetSize(),
        sitk.Transform(),
        sitk.sitkNearestNeighbor,
        b1.GetOrigin(),
        b1.GetSpacing(),
        b1.GetDirection(),
        0,
        b2.GetPixelID(),
    )
    labstats = sitk.LabelOverlapMeasuresImageFilter()

    labstats.Execute(b1, b2)

    return labstats.GetDiceCoefficient()
Exemplo n.º 21
0
 def get_reference_image(
         image: sitk.Image,
         spacing: TypeTripletFloat,
         ) -> sitk.Image:
     old_spacing = np.array(image.GetSpacing())
     new_spacing = np.array(spacing)
     old_size = np.array(image.GetSize())
     new_size = old_size * old_spacing / new_spacing
     new_size = np.ceil(new_size).astype(np.uint16)
     new_size[old_size == 1] = 1  # keep singleton dimensions
     new_origin_index = 0.5 * (new_spacing / old_spacing - 1)
     new_origin_lps = image.TransformContinuousIndexToPhysicalPoint(
         new_origin_index)
     reference = sitk.Image(*new_size.tolist(), sitk.sitkFloat32)
     reference.SetDirection(image.GetDirection())
     reference.SetSpacing(new_spacing.tolist())
     reference.SetOrigin(new_origin_lps)
     return reference
Exemplo n.º 22
0
def copy_meta_data_itk(source: sitk.Image, target: sitk.Image) -> sitk.Image:
    """
    Copy meta data between files

    Args:
        source: source file
        target: target file

    Returns:
        sitk.Image: target file with copied meta data
    """
    # for i in source.GetMetaDataKeys():
    #     target.SetMetaData(i, source.GetMetaData(i))
    raise NotImplementedError("Does not work!")
    target.SetOrigin(source.GetOrigin())
    target.SetDirection(source.GetDirection())
    target.SetSpacing(source.GetSpacing())
    return target
Exemplo n.º 23
0
    def remove_small_roi(cls, binary_img: sitk.Image,
                         pet_img: sitk.Image) -> sitk.Image:
        """function to remove ROI under 30 ml on a binary sitk.Image

        Args:
            binary_img (sitk.Image): [sitk.Image of size (z,y,x)]
            pet_img (sitk.Image): [sitk.Image of the PET, size (z,y,x)]

        Raises:
            Exception: [raise Exception if not a 3D binary mask]

        Returns:
            [sitk.Image]: [Return cleaned image]
        """

        binary_array = sitk.GetArrayFromImage(binary_img)
        if len(binary_array.shape) != 3 or int(np.max(binary_array)) != 1:
            raise Exception(
                "Not a 3D binary mask, need to transform into 3D binary mask")
        else:
            pet_spacing = pet_img.GetSpacing()
            pet_origin = pet_img.GetOrigin()
            pet_direction = pet_img.GetDirection()
            labelled_img = sitk.ConnectedComponent(binary_img)
            stats = sitk.LabelIntensityStatisticsImageFilter()
            stats.Execute(labelled_img, pet_img)
            labelled_array = sitk.GetArrayFromImage(labelled_img).transpose()
            number_of_label = stats.GetNumberOfLabels()
            volume_voxel = pet_spacing[0] * pet_spacing[1] * pet_spacing[
                2] * 10**(-3)  #in ml
            for i in range(1, number_of_label + 1):
                volume_roi = stats.GetNumberOfPixels(i) * volume_voxel
                if volume_roi < float(30):
                    x, y, z = np.where(labelled_array == i)
                    for j in range(len(x)):
                        labelled_array[x[j], y[j], z[j]] = 0
            new_binary_array = np.zeros((labelled_array.shape))
            new_binary_array[np.where(labelled_array != 0)] = 1
            new_binary_img = sitk.GetImageFromArray(
                new_binary_array.transpose().astype(np.uint8))
            new_binary_img.SetOrigin(pet_origin)
            new_binary_img.SetSpacing(pet_spacing)
            new_binary_img.SetDirection(pet_direction)
            return new_binary_img
Exemplo n.º 24
0
def sitk_to_nib(image: sitk.Image) -> Tuple[np.ndarray, np.ndarray]:
    data = sitk.GetArrayFromImage(image).transpose()
    spacing = np.array(image.GetSpacing())
    direction = np.array(image.GetDirection())
    origin = image.GetOrigin()
    if len(direction) == 9:
        rotation = direction.reshape(3, 3)
    elif len(direction) == 4:  # ignore first dimension if 2D (1, 1, H, W)
        rotation_2d = direction.reshape(2, 2)
        rotation = np.eye(3)
        rotation[1:3, 1:3] = rotation_2d
        spacing = 1, *spacing
        origin = 0, *origin
    rotation = np.dot(FLIP_XY, rotation)
    rotation_zoom = rotation * spacing
    translation = np.dot(FLIP_XY, origin)
    affine = np.eye(4)
    affine[:3, :3] = rotation_zoom
    affine[:3, 3] = translation
    return data, affine
Exemplo n.º 25
0
    def __init__(self, image: sitk.Image):
        """Represents ITK image properties.

        Holds common ITK image meta-data such as the size, origin, spacing, and direction.

        See Also:
            SimpleITK provides `itk::simple::Image::CopyInformation`_ to copy image information.

        .. _itk::simple::Image::CopyInformation:
            https://itk.org/SimpleITKDoxygen/html/classitk_1_1simple_1_1Image.html#afa8a4757400c414e809d1767ee616bd0

        Args:
            image (sitk.Image): The image whose properties to hold.
        """
        self.size = image.GetSize()
        self.origin = image.GetOrigin()
        self.spacing = image.GetSpacing()
        self.direction = image.GetDirection()
        self.dimensions = image.GetDimension()
        self.number_of_components_per_pixel = image.GetNumberOfComponentsPerPixel()
        self.pixel_id = image.GetPixelID()
Exemplo n.º 26
0
def sitk_copy_metadata(img_source: sitk.Image, img_target: sitk.Image) -> sitk.Image:
    """
    Copy metadata (spacing, origin, direction) from source to target image

    Args
        img_source: source image
        img_target: target image

    Returns:
        SimpleITK.Image: target image with copied metadata
    """ 
    raise RuntimeError("Deprecated")
    spacing = img_source.GetSpacing()
    img_target.SetSpacing(spacing)

    origin = img_source.GetOrigin()
    img_target.SetOrigin(origin)

    direction = img_source.GetDirection()
    img_target.SetDirection(direction)
    return img_target
Exemplo n.º 27
0
 def get_reference_image(
     floating_sitk: sitk.Image,
     spacing: TypeTripletFloat,
 ) -> sitk.Image:
     old_spacing = np.array(floating_sitk.GetSpacing())
     new_spacing = np.array(spacing)
     old_size = np.array(floating_sitk.GetSize())
     new_size = old_size * old_spacing / new_spacing
     new_size = np.ceil(new_size).astype(np.uint16)
     new_size[old_size == 1] = 1  # keep singleton dimensions
     new_origin_index = 0.5 * (new_spacing / old_spacing - 1)
     new_origin_lps = floating_sitk.TransformContinuousIndexToPhysicalPoint(
         new_origin_index)
     reference = sitk.Image(
         new_size.tolist(),
         floating_sitk.GetPixelID(),
         floating_sitk.GetNumberOfComponentsPerPixel(),
     )
     reference.SetDirection(floating_sitk.GetDirection())
     reference.SetSpacing(new_spacing.tolist())
     reference.SetOrigin(new_origin_lps)
     return reference
Exemplo n.º 28
0
def field_zero_padding(
    field: sitk.Image,
    size_x: Tuple[int, int] = (1, 1),
    size_y: Tuple[int, int] = (1, 1),
    size_z: Tuple[int, int] = (1, 1)
) -> sitk.Image:
    r""" Add a zero padding to a vector field.

    Set the zero padding manually, since `sitk.ConstantPad()` does not
    support vector images.

    Parameters
    ----------
    field : sitk.Image
        Input vector field.
    size_x : (int, int)
        Amount of padding at the beginning and end of x direction.
    size_y : (int, int)
        Amount of padding at the beginning and end of y direction.
    size_z : (int, int)
        Amount of padding at the beginning and end of z direction.

    Returns
    -------
    sitk.Image
        A padded vector field.
    """

    a = np.lib.pad(sitk.GetArrayViewFromImage(field),
                   (size_x, size_y, size_z, (0, 0)),
                   'constant',
                   constant_values=0.0)

    field_pad = sitk.GetImageFromArray(a)
    field_pad.SetSpacing(field.GetSpacing())
    field_pad.SetOrigin(field.GetOrigin())
    field_pad.SetDirection(field.GetDirection())

    return field_pad
Exemplo n.º 29
0
def decompose_displacements(field1: sitk.Image,
                            field2: sitk.Image) -> sitk.Image:
    r""" Decompose two displacement fields.

    Given two displacement fields :math:`d_1` and :math:`d_2`
    associated to the transforms :math:`f_1` and :math:`f_2`,
    find a third displacement :math:`d_3` associated to the
    transform :math:`f_3`, such that

    .. math::
        f_1 &= f_3 \circ f_2 \\
        d_1(x) &= d_2(x) + d_3(d_2(x))

    Parameters
    ----------
    field1 : sitk.Image
        Total displacement.

    field2 : sitk.Image
        Component to be decomposed from the total displacement.

    Returns
    -------
    sitk.Image
        A vector image representing a displacement field such
        that its composition with the second argument gives
        the first argument.
    """

    field3 = sitk.Warp(field1 - field2,
                       sitk.InvertDisplacementField(field2),
                       outputSize=field1.GetSize(),
                       outputSpacing=field1.GetSpacing(),
                       outputOrigin=field1.GetOrigin(),
                       outputDirection=field1.GetDirection())
    field3.CopyInformation(field1)
    return field3
Exemplo n.º 30
0
def resample_image(
        image: sitk.Image,
        spacing: Union[float, Sequence[float], np.ndarray],
        interpolation: str = "linear",
        anti_alias: bool = True,
        anti_alias_sigma: Optional[float] = None,
        transform: Optional[sitk.Transform] = None,
        output_size: Optional[Sequence[float]] = None) -> sitk.Image:
    """Resample image to a given spacing, optionally applying a transformation.

    Parameters
    ----------
    image
        The image to be resampled.

    spacing
        The new image spacing. If float, assumes the same spacing in all
        directions. Alternatively, a sequence of floats can be passed to
        specify spacing along each dimension. Passing 0 at any position will
        keep the original spacing along that dimension (useful for in-plane
        resampling). If list, assumes format [x, y, z].

    interpolation, optional
        The interpolation method to use. Valid options are:
        - "linear" for bi/trilinear interpolation (default)
        - "nearest" for nearest neighbour interpolation
        - "bspline" for order-3 b-spline interpolation

    anti_alias, optional
        Whether to smooth the image with a Gaussian kernel before resampling.
        Only used when downsampling, i.e. when `spacing < image.GetSpacing()`.
        This should be used to avoid aliasing artifacts.

    anti_alias_sigma, optional
        The standard deviation of the Gaussian kernel used for anti-aliasing.

    transform, optional
        Transform to apply to input coordinates when resampling. If None,
        defaults to identity transformation.

    output_size, optional
        Size of the output image. If None, it is computed to preserve the
        whole extent of the input image.

    Returns
    -------
    sitk.Image
        The resampled image.
    """
    INTERPOLATORS = {
        "linear": sitk.sitkLinear,
        "nearest": sitk.sitkNearestNeighbor,
        "bspline": sitk.sitkBSpline,
    }

    try:
        interpolator = INTERPOLATORS[interpolation]
    except KeyError:
        raise ValueError(
            f"interpolator must be one of {list(INTERPOLATORS.keys())}, got {interpolator}."
        )

    original_spacing = np.array(image.GetSpacing())
    original_size = np.array(image.GetSize())

    if isinstance(spacing, (float, int)):
        new_spacing = np.repeat(spacing,
                                len(original_spacing)).astype(np.float64)
    else:
        spacing = np.asarray(spacing)
        new_spacing = np.where(spacing == 0, original_spacing, spacing)

    if not output_size:
        new_size = np.floor(original_size * original_spacing /
                            new_spacing).astype(np.int)
    else:
        new_size = np.asarray(output_size)

    rif = sitk.ResampleImageFilter()
    rif.SetOutputOrigin(image.GetOrigin())
    rif.SetOutputSpacing(new_spacing)
    rif.SetOutputDirection(image.GetDirection())
    rif.SetSize(new_size.tolist())

    if transform is not None:
        rif.SetTransform(transform)

    downsample = new_spacing > original_spacing
    if downsample.any() and anti_alias:
        if not anti_alias_sigma:
            # sigma computation adapted from scikit-image
            # https://github.com/scikit-image/scikit-image/blob/master/skimage/transform/_warps.py
            anti_alias_sigma = np.maximum(
                1e-11, (original_spacing / new_spacing - 1) / 2)
        sigma = np.where(downsample, anti_alias_sigma, 1e-11)
        image = sitk.SmoothingRecursiveGaussian(image, sigma)

    rif.SetInterpolator(interpolator)
    resampled_image = rif.Execute(image)

    return resampled_image