def execute(self, image: sitk.Image, params: ImageRegistrationParameters = None) -> sitk.Image: """Registers an image. Args: image (sitk.Image): The image. params (ImageRegistrationParameters): The registration parameters. Returns: sitk.Image: The registered image. """ # todo: replace this filter by a registration. Registration can be costly, therefore, we provide you the # transformation, which you only need to apply to the image! # warnings.warn('No registration implemented. Returning unregistered image') atlas = params.atlas transform = params.transformation is_ground_truth = params.is_ground_truth # the ground truth will be handled slightly different if is_ground_truth: # apply transformation to ground truth and brain mask using nearest neighbor interpolation image = sitk.Resample(image, atlas, transform, sitk.sitkNearestNeighbor, 0, image.GetPixelIDValue()) else: # apply transformation to T1w and T2w images using linear interpolation image = sitk.Resample(image, atlas, transform, sitk.sitkLinear, 0.0, image.GetPixelIDValue()) # note: if you are interested in registration, and want to test it, have a look at # pymia.filtering.registration.MultiModalRegistration. Think about the type of registration, i.e. # do you want to register to an atlas or inter-subject? Or just ask us, we can guide you ;-) return image
def __call__(self, x: sitk.Image) -> sitk.Image: """Apply the transform. Parameters ---------- image Image to transform. Returns ------- sitk.Image The transformed image. """ angle = -self.max_angle + 2 * self.max_angle * torch.rand(1).item() rotation_centre = np.array(x.GetSize()) / 2 rotation_centre = x.TransformContinuousIndexToPhysicalPoint( rotation_centre) rotation = sitk.Euler3DTransform( rotation_centre, 0, # the angle of rotation around the x-axis, in radians -> coronal rotation 0, # the angle of rotation around the y-axis, in radians -> saggittal rotation angle, # the angle of rotation around the z-axis, in radians -> axial rotation (0., 0., 0.) # no translation ) return sitk.Resample(x, x, rotation, sitk.sitkLinear, self.fill_value)
def apply_transform_pandas(fixed_image: sitk.Image, moving_image: sitk.Image, reference_path, index=None): transform_path = os.path.join(reference_path.parent, 'Transforms.csv') if index is None: transform_params = blk.read_pandas_row(transform_path, reference_path.name, 'Image') else: transform_params = blk.read_pandas_row(transform_path, index, 'Image') transform = sitk.AffineTransform(2) transform.Rotate(0, 1, transform_params['Rotation'], pre=True) matrix = [ transform_params['Matrix Top Left'], transform_params['Matrix Top Right'], transform_params['Matrix Bottom Left'], transform_params['Matrix Bottom Right'] ] transform.SetMatrix(matrix) transform.SetTranslation( [transform_params['X Translation'], transform_params['Y Translation']]) origin = (int(transform_params['X Origin']), int(transform_params['Y Origin'])) moving_image.SetOrigin(origin) return sitk.Resample(moving_image, fixed_image, transform, sitk.sitkLinear, 0.0, moving_image.GetPixelID())
def _image_as_numpy_array(image: sitk.Image, mask: np.ndarray = None): """Gets an image as numpy array where each row is a voxel and each column is a feature. Args: image (sitk.Image): The image. mask (np.ndarray): A mask defining which voxels to return. True is background, False is a masked voxel. Returns: np.ndarray: An array where each row is a voxel and each column is a feature. """ number_of_components = image.GetNumberOfComponentsPerPixel( ) # the number of features for this image no_voxels = np.prod(image.GetSize()) image = sitk.GetArrayFromImage(image) if mask is not None: no_voxels = np.size(mask) - np.count_nonzero(mask) if number_of_components == 1: masked_image = np.ma.masked_array(image, mask=mask) else: # image is a vector image, make a vector mask vector_mask = np.expand_dims( mask, axis=3) # shape is now (z, x, y, 1) vector_mask = np.repeat( vector_mask, number_of_components, axis=3) # shape is now (z, x, y, number_of_components) masked_image = np.ma.masked_array(image, mask=vector_mask) image = masked_image[~masked_image.mask] return image.reshape((no_voxels, number_of_components))
def assert_img_properties(img: SimpleITK.Image, internal_image: SimpleITKImage): color_space = { 1: ColorSpace.GRAY, 3: ColorSpace.RGB, 4: ColorSpace.RGBA, } assert internal_image.color_space == color_space.get( img.GetNumberOfComponentsPerPixel()) if img.GetDimension() == 4: assert internal_image.timepoints == img.GetSize()[-1] else: assert internal_image.timepoints is None if img.GetDepth(): assert internal_image.depth == img.GetDepth() assert internal_image.voxel_depth_mm == img.GetSpacing()[2] else: assert internal_image.depth is None assert internal_image.voxel_depth_mm is None assert internal_image.width == img.GetWidth() assert internal_image.height == img.GetHeight() assert internal_image.voxel_width_mm == approx(img.GetSpacing()[0]) assert internal_image.voxel_height_mm == approx(img.GetSpacing()[1])
def _map_source_images_to_segmentation( self, segmentation: sitk.Image, source_images: List[pydicom.Dataset] ) -> List[List[pydicom.Dataset]]: """Maps an list of source image datasets to the slices of a SimpleITK image. Args: segmentation: A `SimpleITK.Image` with integer labels and a single component per spatial location. source_images: A list of `pydicom.Dataset` which are the source images for the segmentation image. Returns: A `list` with a `list` for each slice, which contains the mapped `pydicom.Dataset` instances for that slice location. Slices can have zero or more matched datasets. """ result: List[List[pydicom.Dataset]] = [ list() for _ in range(segmentation.GetDepth()) ] for source_image in source_images: position = [float(x) for x in source_image.ImagePositionPatient] index = segmentation.TransformPhysicalPointToIndex(position) if index[2] < 0 or index[2] >= segmentation.GetDepth(): continue # TODO Add reverse check if segmentation is contained in image result[index[2]].append(source_image) slices_mapped = sum(len(x) > 0 for x in result) logger.info(f"{slices_mapped} of {segmentation.GetDepth()} slices" "mapped to source DICOM images") return result
def resample_image_to_voxel_size(image: sitk.Image, new_spacing, interpolator, fill_value=0) -> sitk.Image: """ resample a 3d image to a given voxel size (dx, dy, dz) :param image: 3D sitk image :param new_spacing: voxel size (dx, dy, dz) :param interpolator: :param fill_value: pixel value when a transformed pixel is outside of the image. :return: """ # computing image shape based on the desired voxel size orig_size = np.array(image.GetSize()) orig_spacing = np.array(image.GetSpacing()) new_spacing = np.array(new_spacing) new_size = orig_size * (orig_spacing / new_spacing) # Image dimensions are in integers new_size = np.ceil(new_size).astype(np.int) # SimpleITK expects lists, not ndarrays new_size = [int(s) for s in new_size] return resample_sitk_image(image, new_size, interpolator, fill_value)
def __init__(self, data: sitk.Image, segmentation: sitk.Image, bb_size): self.segmentation = segmentation # place bb so that the segmentation is centered self.offset = ( (np.array(bb_size) - np.array(self.segmentation.GetSize())) / 2).astype(int) # adjust offset if resulting bb is out of bounds segmentation_origin_in_data = data.TransformPhysicalPointToIndex( segmentation.GetOrigin()) self.offset = [ seg_or if seg_or - off < 0 else off for seg_or, off in zip(segmentation_origin_in_data, self.offset) ] self.offset = [ off + ((seg_or - off + bb_sz) - data_sz) if seg_or - off + bb_sz > data_sz else off for seg_or, off, bb_sz, data_sz in zip(segmentation_origin_in_data, self.offset, bb_size, data.GetSize()) ] cropped_origin = np.array(segmentation_origin_in_data) - self.offset assert all(cr_or >= 0 for cr_or in cropped_origin), \ f"Data size: {data.GetSize()}, BB size: {bb_size}, BB origin: {cropped_origin}" assert all(cr_or + bb_s <= data_s for cr_or, bb_s, data_s in zip(cropped_origin, bb_size, data.GetSize())), \ f"Data size: {data.GetSize()}, BB size: {bb_size}, BB origin: {cropped_origin}" self.data = data[cropped_origin[0]:cropped_origin[0] + bb_size[0], cropped_origin[1]:cropped_origin[1] + bb_size[1], cropped_origin[2]:cropped_origin[2] + bb_size[2]]
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
def preprocess_image(self, reg_image: sitk.Image) -> None: """ Run full intensity and spatial preprocessing. Creates the `reg_image` attribute Parameters ---------- reg_image: sitk.Image Raw form of image to be preprocessed """ reg_image = self.preprocess_reg_image_intensity( reg_image, self.preprocessing) if reg_image.GetDepth() >= 1: raise ValueError( "preprocessing did not result in a single image plane\n" "multi-channel or 3D image return") if reg_image.GetNumberOfComponentsPerPixel() > 1: raise ValueError( "preprocessing did not result in a single image plane\n" "multi-component / RGB(A) image returned") reg_image, pre_reg_transforms = self.preprocess_reg_image_spatial( reg_image, self.preprocessing, self.pre_reg_transforms) if len(pre_reg_transforms) > 0: self.pre_reg_transforms = pre_reg_transforms self._reg_image = reg_image
def execute(self, image: sitk.Image, params: ImageRegistrationParameters = None) -> sitk.Image: """Registers an image. Args: image (sitk.Image): The image. params (ImageRegistrationParameters): The registration parameters. Returns: sitk.Image: The registered image. """ atlas = params.atlas transform = params.transformation is_ground_truth = params.is_ground_truth # the ground truth will be handled slightly different if is_ground_truth: # apply transformation to ground truth and brain mask using nearest neighbor interpolation image = sitk.Resample(image, atlas, transform, sitk.sitkNearestNeighbor, 0, image.GetPixelIDValue()) else: # apply transformation to T1w and T2w images using linear interpolation image = sitk.Resample(image, atlas, transform, sitk.sitkLinear, 0.0, image.GetPixelIDValue()) return image
def get_largest_segment(image: sitk.Image, extract_background=False) -> sitk.Image: # get number of labels img_statistic = sitk.StatisticsImageFilter() img_statistic.Execute(image) min_val = int(img_statistic.GetMinimum()) max_val = int(img_statistic.GetMaximum()) # create empty output image img_out = sitk.Image(image.GetSize(), image.GetPixelIDValue()) img_out.CopyInformation(image) # setup connected components filter connected_comp_filter = sitk.ConnectedComponentImageFilter() connected_comp_filter.FullyConnectedOn() # extract largest segment for label in range(min_val, max_val + 1): img_label = image == label seg = connected_comp_filter.Execute(img_label != 0) if label == 0: # create temporary a new label for largest connected comp of the background seg = (sitk.RelabelComponent(seg) == 1) * (max_val + 1) * extract_background else: seg = (sitk.RelabelComponent(seg) == 1) * label img_out = img_out + seg # arr_image = sitk.GetArrayFromImage(img_out) # plt.imshow(arr_image[60,:,:], cmap='jet') # plt.show() return img_out
def centre_of_mass(image: sitk.Image) -> np.ndarray: r""" Compute the centre of mass of the image. A real-valued image represents the distribution of mass, and its centre of mass is defined as :math:`\frac{1}{\sum_p I(p)} \sum_p p I(p)`. Parameters ---------- image : sitk.Image Input image. Returns ------- np.ndarray World coordinates (x, y, z) of the centre of mass. """ data = sitk.GetArrayViewFromImage(image) grid = np.meshgrid(*[range(i) for i in data.shape], indexing='ij') grid = np.vstack([a.flatten() for a in grid]).T cm = np.average(grid, axis=0, weights=data.flatten()) return np.multiply(np.flip(cm, axis=0), image.GetSpacing()) - image.GetOrigin()
def execute(self, image: sitk.Image, params: ImageRegistrationParameters = None) -> sitk.Image: """Registers an image. Args: image (sitk.Image): The image. params (ImageRegistrationParameters): The registration parameters. Returns: sitk.Image: The registered image. """ # todo: replace this filter by a registration. Registration can be costly, therefore, we provide you the # transformation, which you only need to apply to the image! atlas = params.atlas transform = params.transformation is_ground_truth = params.is_ground_truth # the ground truth will be handled slightly different # note: if you are interested in registration, and want to test it, have a look at # pymia.filtering.registration.MultiModalRegistration. Think about the type of registration, i.e. # do you want to register to an atlas or inter-subject? Or just ask us, we can guide you ;-) # interpolator = sitk.sitkCosineWindowedSinc or sitk.sitkAffine if is_ground_truth: image = sitk.Resample(image, atlas, transform, sitk.sitkLinear, 0.0, image.GetPixelID()) else: image = sitk.Resample(image, atlas, transform, sitk.sitkLinear, 0.0, image.GetPixelID()) return image
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
def execute(self, image: sitk.Image, params: fltr.IFilterParams = None) -> sitk.Image: """Executes a neighborhood feature extractor on an image. Args: image (sitk.Image): The image. params (fltr.IFilterParams): The parameters (unused). Returns: sitk.Image: The normalized image. Raises: ValueError: If image is not 3-D. """ if image.GetDimension() != 3: raise ValueError('image needs to be 3-D') # test the function and get the output dimension for later reshaping function_output = self.function(np.array([1, 2, 3])) if np.isscalar(function_output): img_out = sitk.Image(image.GetSize(), sitk.sitkFloat32) elif not isinstance(function_output, np.ndarray): raise ValueError( 'function must return a scalar or a 1-D np.ndarray') elif function_output.ndim > 1: raise ValueError( 'function must return a scalar or a 1-D np.ndarray') elif function_output.shape[0] <= 1: raise ValueError( 'function must return a scalar or a 1-D np.ndarray with at least two elements' ) else: img_out = sitk.Image(image.GetSize(), sitk.sitkVectorFloat32, function_output.shape[0]) img_out_arr = sitk.GetArrayFromImage(img_out) img_arr = sitk.GetArrayFromImage(image) z, y, x = img_arr.shape z_offset = self.kernel[2] y_offset = self.kernel[1] x_offset = self.kernel[0] pad = ((0, z_offset), (0, y_offset), (0, x_offset)) img_arr_padded = np.pad(img_arr, pad, 'symmetric') for xx in range(x): for yy in range(y): for zz in range(z): val = self.function(img_arr_padded[zz:zz + z_offset, yy:yy + y_offset, xx:xx + x_offset]) img_out_arr[zz, yy, xx] = val img_out = sitk.GetImageFromArray(img_out_arr) img_out.CopyInformation(image) return img_out
def _img_convert_type(img: sitk.Image, output_type) -> sitk.Image: """ Convert the img into the desired pixel type. The method safely ( without overflow/underflow ) converts from one pixel range to another. :param img: a SimpleITK Image object :param output_type: a SimpleITK PixelID such as sitkUInt8, sitkFloat32 for the pixel type of the returned image """ # the sub_volume_execute if img.GetPixelID() == sitk.sitkInt8 and output_type == sitk.sitkUInt8: img = sitk.Cast(img, sitk.sitkInt16) img += 128 return sitk.Cast(img, output_type) elif img.GetPixelID() == sitk.sitkInt16 and output_type == sitk.sitkUInt8: img = sitk.Cast(img, sitk.sitkInt32) img += 32768 img /= 256 return sitk.Cast(img, output_type) elif img.GetPixelID() == sitk.sitkInt16 and output_type == sitk.sitkUInt16: img = sitk.Cast(img, sitk.sitkInt32) img += 32768 return sitk.Cast(img, output_type) else: raise Exception( f"Converting from {img.GetPixelIDTypeAsString()} to " f"{sitk.GetPixelIDValueAsString(output_type)} is not implemented.")
def downsample_image(image: sitk.Image, input_pixel_size, output_pixel_size): image.SetSpacing([input_pixel_size for i in range(3)]) size = [ int(math.ceil(i / output_pixel_size * input_pixel_size)) for i in image.GetSize() ] return sitk.Resample(image, size, sitk.Transform(), sitk.sitkLinear, [0, 0, 0], [output_pixel_size for i in range(3)])
def write_metadata(image_path: Path, image: sitk.Image): """Write down the metadata keys to a txt file""" metadata = {} for key in image.GetMetaDataKeys(): metadata[key] = image.GetMetaData(key) metadata_path = Path(image_path.parent, image_path.stem + '_metadata.txt') util.write_json(metadata, metadata_path)
def execute(self, image: sitk.Image, params: MultiModalRegistrationParams=None) -> sitk.Image: """Executes a multi-modal rigid registration. Args: image (sitk.Image): The moving image. params (MultiModalRegistrationParams): The parameters, which contain the fixed image. Returns: sitk.Image: The registered image. """ if params is None: raise ValueError("params is not defined") dimension = image.GetDimension() if dimension not in (2, 3): raise ValueError('Image dimension {} is not among the accepted (2, 3)'.format(dimension)) # set a transform that is applied to the moving image to initialize the registration if self.registration_type == RegistrationType.BSPLINE: transform_domain_mesh_size = [10] * image.GetDimension() initial_transform = sitk.BSplineTransformInitializer(params.fixed_image, transform_domain_mesh_size) else: if self.registration_type == RegistrationType.RIGID: transform_type = sitk.VersorRigid3DTransform() if dimension == 3 else sitk.Euler2DTransform() elif self.registration_type == RegistrationType.AFFINE: transform_type = sitk.AffineTransform(dimension) elif self.registration_type == RegistrationType.SIMILARITY: transform_type = sitk.Similarity3DTransform() if dimension == 3 else sitk.Similarity2DTransform() else: raise ValueError('not supported registration_type') initial_transform = sitk.CenteredTransformInitializer(sitk.Cast(params.fixed_image, image.GetPixelIDValue()), image, transform_type, sitk.CenteredTransformInitializerFilter.GEOMETRY) self.registration.SetInitialTransform(initial_transform, inPlace=True) if params.fixed_image_mask: self.registration.SetMetricFixedMask(params.fixed_image_mask) if params.callbacks is not None: for callback in params.callbacks: callback.set_params(self.registration, params.fixed_image, image, initial_transform) self.transform = self.registration.Execute(sitk.Cast(params.fixed_image, sitk.sitkFloat32), sitk.Cast(image, sitk.sitkFloat32)) if self.verbose: print('MultiModalRegistration:\n Final metric value: {0}'.format(self.registration.GetMetricValue())) print(' Optimizer\'s stopping condition, {0}'.format( self.registration.GetOptimizerStopConditionDescription())) elif self.number_of_iterations == self.registration.GetOptimizerIteration(): print('MultiModalRegistration: Optimizer terminated at number of iterations and did not converge!') return sitk.Resample(image, params.fixed_image, self.transform, self.resampling_interpolator, 0.0, image.GetPixelIDValue())
def fitting_index(image: sitk.Image, norm: Union[int, str] = 2.0, centre: Tuple[float, float, float] = None, radius: float = None, padding: bool = True) -> float: r""" Compute the fitting index of an input object. The fitting index of order `p` is defined as the Jaccard coefficient computed between the input object and a p-ball centred in the object's centre of mass. Parameters ---------- image : sitk.Image Input binary image of the object. norm : Union[int,str] Order of the Minkowski norm ('inf' or 'max' to use the Chebyshev norm). centre : Tuple[float, float, float] Forces the p-ball to be centred in a specific point. radius : float Force the radius of the p-ball. padding : bool If `True`, add enough padding to be sure that the ball will entirely fit within the volume. Returns ------- float Value of the fitting index. """ if image.GetPixelID() != sitk.sitkUInt8: raise Exception('Unsupported %s pixel type' % image.GetPixelIDTypeAsString()) if centre is None: # Use the centroid as centre lssif = sitk.LabelShapeStatisticsImageFilter() lssif.Execute(image) centre = lssif.GetCentroid(1) if padding: # Add some padding to be sure that an isovolumetric 1-ball can fit # within the same volume of a sphere touching the boundary pad = tuple([x // 4 for x in image.GetSize()]) image = sitk.ConstantPad(image, pad, pad, 0) image.SetOrigin((0, 0, 0)) centre = tuple([x + y for x, y in zip(centre, pad)]) if radius is None: radius = isovolumteric_radius(image, norm) size = image.GetSize() sphere = drawing.create_sphere(radius, size=size, centre=centre, norm=norm) > 0 return jaccard(image, sphere)
def zoom(image: sitk.Image, scale_factor: Union[float, Sequence[float]], interpolation: str = "linear", anti_alias: bool = True, anti_alias_sigma: Optional[float] = None) -> sitk.Image: """Rescale image, preserving its spatial extent. The rescaled image will have the same spatial extent (size) but will be rescaled by `scale_factor` in each dimension. Alternatively, a separate scale factor for each dimension can be specified by passing a sequence of floats. Parameters ---------- image The image to rescale. scale_factor If float, each dimension will be scaled by that factor. If tuple, each dimension will be scaled by the corresponding element. 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 `size < image.GetSize()`. This should be used to avoid aliasing artifacts. anti_alias_sigma, optional The standard deviation of the Gaussian kernel used for anti-aliasing. Returns ------- sitk.Image The rescaled image. """ dimension = image.GetDimension() if isinstance(scale_factor, float): scale_factor = (scale_factor, ) * dimension centre_idx = np.array(image.GetSize()) / 2 centre = image.TransformContinuousIndexToPhysicalPoint(centre_idx) transform = sitk.ScaleTransform(dimension, scale_factor) transform.SetCenter(centre) return resample(image, spacing=image.GetSpacing(), interpolation=interpolation, anti_alias=anti_alias, anti_alias_sigma=anti_alias_sigma, transform=transform, output_size=image.GetSize())
def get_raw_liver_image(raw: sitk.Image, liver: sitk.Image) -> sitk.Image: size = raw.GetSize() output = sitk.Image(size, sitk.sitkUInt8) output.CopyInformation(raw) for i in range(0, size[0]): for j in range(0, size[1]): if liver.GetPixel(i, j) == 1: output.SetPixel(i, j, raw.GetPixel(i, j)) return output
def copy_relevant_metadata(new_image: sitk.Image, old_image: sitk.Image, necessary_keys: list = None): if necessary_keys is None: necessary_keys = ['Unit'] for key in necessary_keys: try: new_image.SetMetaData(key, old_image.GetMetaData(key)) except: warnings.warn('The {0} key does not exist'.format(key))
def check_if_image_is_rgb(image: sitk.Image): """Check if an image is RGB by looking for a 3-component 8 bit pixel image""" components = image.GetNumberOfComponentsPerPixel() pixel_type = image.GetPixelIDTypeAsString() if components == 3 and pixel_type == 'vector of 8-bit unsigned integer': image_is_rgb = True else: image_is_rgb = False return image_is_rgb
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())
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()
def resize(image: sitk.Image, size: Union[int, Sequence[int], np.ndarray], interpolation: str = "linear", anti_alias: bool = True, anti_alias_sigma: Optional[float] = None) -> sitk.Image: """Resize image to a given size by resampling coordinates. Parameters ---------- image The image to be resize. size The new image size. If float, assumes the same size in all directions. Alternatively, a sequence of floats can be passed to specify size along each dimension. Passing 0 at any position will keep the original size along that dimension. 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 `size < image.GetSize()`. This should be used to avoid aliasing artifacts. anti_alias_sigma, optional The standard deviation of the Gaussian kernel used for anti-aliasing. Returns ------- sitk.Image The resized image. """ original_size = np.array(image.GetSize()) original_spacing = np.array(image.GetSpacing()) if isinstance(size, (float, int)): new_size = np.repeat(size, len(original_size)).astype(np.float64) else: size = np.asarray(size) new_size = np.where(size == 0, original_size, size) new_spacing = original_spacing * original_size / new_size return resample(image, new_spacing, anti_alias=anti_alias, anti_alias_sigma=anti_alias_sigma, interpolation=interpolation)
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
def execute(self, image: sitk.Image, params: MultiModalRegistrationParams = None) -> sitk.Image: """Executes a multi-modal rigid registration. Args: image (sitk.Image): The moving image. params (MultiModalRegistrationParams): The parameters, which contain the fixed image. Returns: sitk.Image: The registered image. """ if params is None: raise ValueError("params is not defined") # set a transform that is applied to the moving image to initialize the registration if self.registration_type == RegistrationType.RIGID: transform_type = sitk.VersorRigid3DTransform() elif self.registration_type == RegistrationType.AFFINE: transform_type = sitk.AffineTransform(3) else: raise ValueError('not supported registration_type') initial_transform = sitk.CenteredTransformInitializer( sitk.Cast(params.fixed_image, image.GetPixelIDValue()), image, transform_type, sitk.CenteredTransformInitializerFilter.GEOMETRY) self.registration.SetInitialTransform(initial_transform, inPlace=True) if params.fixed_image_mask: self.registration.SetMetricFixedMask(params.fixed_image_mask) if params.plot_directory_path: RegistrationPlotter(self.registration, params.fixed_image, image, initial_transform, params.plot_directory_path) self.transform = self.registration.Execute( sitk.Cast(params.fixed_image, sitk.sitkFloat32), sitk.Cast(image, sitk.sitkFloat32)) if self.verbose: print('MultiModalRegistration:\n Final metric value: {0}'.format( self.registration.GetMetricValue())) print(' Optimizer\'s stopping condition, {0}'.format( self.registration.GetOptimizerStopConditionDescription())) elif self.number_of_iterations == self.registration.GetOptimizerIteration( ): print( 'MultiModalRegistration: Optimizer terminated at number of iterations and did not converge!' ) return sitk.Resample(image, params.fixed_image, self.transform, sitk.sitkLinear, 0.0, image.GetPixelIDValue())