def test_plot_2d_diffeomorphic_map(): # Test the regtools plotting interface (lightly). mv_shape = (11, 12) moving = np.random.rand(*mv_shape) st_shape = (13, 14) static = np.random.rand(*st_shape) dim = static.ndim metric = SSDMetric(dim) level_iters = [200, 100, 50, 25] sdr = SymmetricDiffeomorphicRegistration(metric, level_iters, inv_iter=50) mapping = sdr.optimize(static, moving) # Smoke testing of plots ff = regtools.plot_2d_diffeomorphic_map(mapping, 10) # Defualt shape is static shape, moving shape npt.assert_equal(ff[0].shape, st_shape) npt.assert_equal(ff[1].shape, mv_shape) # Can specify shape ff = regtools.plot_2d_diffeomorphic_map(mapping, delta = 10, direct_grid_shape=(7, 8), inverse_grid_shape=(9, 10)) npt.assert_equal(ff[0].shape, (7, 8)) npt.assert_equal(ff[1].shape, (9, 10))
def __init__(self, iter_limits=[[64, 32, 8, 2], [128, 64, 32, 8]], interpolation='nearest', metric='SSD', mode='flip', always_apply=False, p=0.75): super(Diffeomorph, self).__init__(always_apply, p) self.iter_limits = iter_limits self.interpolation = interpolation if metric == 'SSD': self.metric = SSDMetric(dim=2) else: raise NotImplementedError self.mode = mode
def run(self, static_image_files, moving_image_files, prealign_file='', inv_static=False, level_iters=[10, 10, 5], metric="cc", mopt_sigma_diff=2.0, mopt_radius=4, mopt_smooth=0.0, mopt_inner_iter=0.0, mopt_q_levels=256, mopt_double_gradient=True, mopt_step_type='', step_length=0.25, ss_sigma_factor=0.2, opt_tol=1e-5, inv_iter=20, inv_tol=1e-3, out_dir='', out_warped='warped_moved.nii.gz', out_inv_static='inc_static.nii.gz', out_field='displacement_field.nii.gz'): """ Parameters ---------- static_image_files : string Path of the static image file. moving_image_files : string Path to the moving image file. prealign_file : string, optional The text file containing pre alignment information via an affine matrix. inv_static : boolean, optional Apply the inverse mapping to the static image (default 'False'). level_iters : variable int, optional The number of iterations at each level of the gaussian pyramid. By default, a 3-level scale space with iterations sequence equal to [10, 10, 5] will be used. The 0-th level corresponds to the finest resolution. metric : string, optional The metric to be used (Default cc, 'Cross Correlation metric'). metric available: cc (Cross Correlation), ssd (Sum Squared Difference), em (Expectation-Maximization). mopt_sigma_diff : float, optional Metric option applied on Cross correlation (CC). The standard deviation of the Gaussian smoothing kernel to be applied to the update field at each iteration (default 2.0) mopt_radius : int, optional Metric option applied on Cross correlation (CC). the radius of the squared (cubic) neighborhood at each voxel to be considered to compute the cross correlation. (default 4) mopt_smooth : float, optional Metric option applied on Sum Squared Difference (SSD) and Expectation Maximization (EM). Smoothness parameter, the larger the value the smoother the deformation field. (default 1.0 for EM, 4.0 for SSD) mopt_inner_iter : int, optional Metric option applied on Sum Squared Difference (SSD) and Expectation Maximization (EM). This is number of iterations to be performed at each level of the multi-resolution Gauss-Seidel optimization algorithm (this is not the number of steps per Gaussian Pyramid level, that parameter must be set for the optimizer, not the metric). Default 5 for EM, 10 for SSD. mopt_q_levels : int, optional Metric option applied on Expectation Maximization (EM). Number of quantization levels (Default: 256 for EM) mopt_double_gradient : bool, optional Metric option applied on Expectation Maximization (EM). if True, the gradient of the expected static image under the moving modality will be added to the gradient of the moving image, similarly, the gradient of the expected moving image under the static modality will be added to the gradient of the static image. mopt_step_type : string, optional Metric option applied on Sum Squared Difference (SSD) and Expectation Maximization (EM). The optimization schedule to be used in the multi-resolution Gauss-Seidel optimization algorithm (not used if Demons Step is selected). Possible value: ('gauss_newton', 'demons'). default: 'gauss_newton' for EM, 'demons' for SSD. step_length : float, optional the length of the maximum displacement vector of the update displacement field at each iteration. ss_sigma_factor : float, optional parameter of the scale-space smoothing kernel. For example, the std. dev. of the kernel will be factor*(2^i) in the isotropic case where i = 0, 1, ..., n_scales is the scale. opt_tol : float, optional the optimization will stop when the estimated derivative of the energy profile w.r.t. time falls below this threshold. inv_iter : int, optional the number of iterations to be performed by the displacement field inversion algorithm. inv_tol : float, optional the displacement field inversion algorithm will stop iterating when the inversion error falls below this threshold. out_dir : string, optional Directory to save the transformed files (default ''). out_warped : string, optional Name of the warped file. (default 'warped_moved.nii.gz'). out_inv_static : string, optional Name of the file to save the static image after applying the inverse mapping (default 'inv_static.nii.gz'). out_field : string, optional Name of the file to save the diffeomorphic map. (default 'displacement_field.nii.gz') """ io_it = self.get_io_iterator() metric = metric.lower() if metric not in ['ssd', 'cc', 'em']: raise ValueError("Invalid similarity metric: Please" " provide a valid metric like 'ssd', 'cc', 'em'") logging.info("Starting Diffeomorphic Registration") logging.info('Using {0} Metric'.format(metric.upper())) # Init parameter if they are not setup init_param = { 'ssd': { 'mopt_smooth': 4.0, 'mopt_inner_iter': 10, 'mopt_step_type': 'demons' }, 'em': { 'mopt_smooth': 1.0, 'mopt_inner_iter': 5, 'mopt_step_type': 'gauss_newton' } } mopt_smooth = mopt_smooth or init_param[metric]['mopt_smooth'] mopt_inner_iter = mopt_inner_iter or \ init_param[metric]['mopt_inner_iter'] mopt_step_type = mopt_step_type or \ init_param[metric]['mopt_step_type'] for (static_file, moving_file, owarped_file, oinv_static_file, omap_file) in io_it: logging.info('Loading static file {0}'.format(static_file)) logging.info('Loading moving file {0}'.format(moving_file)) # Loading the image data from the input files into object. static_image, static_grid2world = load_nifti(static_file) moving_image, moving_grid2world = load_nifti(moving_file) # Sanity check for the input image dimensions. check_dimensions(static_image, moving_image) # Loading the affine matrix. prealign = np.loadtxt(prealign_file) if prealign_file else None l_metric = { "ssd": SSDMetric(static_image.ndim, smooth=mopt_smooth, inner_iter=mopt_inner_iter, step_type=mopt_step_type), "cc": CCMetric(static_image.ndim, sigma_diff=mopt_sigma_diff, radius=mopt_radius), "em": EMMetric(static_image.ndim, smooth=mopt_smooth, inner_iter=mopt_inner_iter, step_type=mopt_step_type, q_levels=mopt_q_levels, double_gradient=mopt_double_gradient) } current_metric = l_metric.get(metric.lower()) sdr = SymmetricDiffeomorphicRegistration( metric=current_metric, level_iters=level_iters, step_length=step_length, ss_sigma_factor=ss_sigma_factor, opt_tol=opt_tol, inv_iter=inv_iter, inv_tol=inv_tol) mapping = sdr.optimize(static_image, moving_image, static_grid2world, moving_grid2world, prealign) mapping_data = np.array([mapping.forward.T, mapping.backward.T]).T warped_moving = mapping.transform(moving_image) # Saving logging.info('Saving warped {0}'.format(owarped_file)) save_nifti(owarped_file, warped_moving, static_grid2world) logging.info('Saving Diffeomorphic map {0}'.format(omap_file)) save_nifti(omap_file, mapping_data, mapping.codomain_world2grid)
show_images(img_ref, img_in, 'input') """ .. figure:: input.png :align: center Input images before alignment. """ """ Let's use the general Registration function with some naive parameters, such as set `step_length` as 1 assuming maximal step 1 pixel and a reasonably small number of iterations since the deformation with already aligned images should be minimal. """ sdr = SymmetricDiffeomorphicRegistration(metric=SSDMetric(img_ref.ndim), step_length=1.0, level_iters=[50, 100], inv_iter=50, ss_sigma_factor=0.1, opt_tol=1.e-3) """ Perform the registration with equal images. """ mapping = sdr.optimize(img_ref.astype(float), img_ref.astype(float)) img_warp = mapping.transform(img_ref, 'linear') show_images(img_ref, img_warp, 'output-0') regtools.plot_2d_diffeomorphic_map(mapping, 5, 'map-0.png') """ .. figure:: output-0.png
:align: center Input images. """ """ We want to find an invertible map that transforms the moving image (circle) into the static image (the C letter). The first decision we need to make is what similarity metric is appropriate for our problem. In this example we are using two binary images, so the Sum of Squared Differences (SSD) is a good choice. """ dim = static.ndim metric = SSDMetric(dim) """ Now we define an instance of the registration class. The SyN algorithm uses a multi-resolution approach by building a Gaussian Pyramid. We instruct the registration instance to perform at most $[n_0, n_1, ..., n_k]$ iterations at each level of the pyramid. The 0-th level corresponds to the finest resolution. """ level_iters = [200, 100, 50, 25] sdr = SymmetricDiffeomorphicRegistration(metric, level_iters, inv_iter = 50) """ Now we execute the optimization, which returns a DiffeomorphicMap object, that can be used to register images back and forth between the static and moving
def register_demons_sym_diffeom(img_sense, img_ref, smooth_sigma=1., params=DIPY_DEAMONS_PARAMS, inverse=False, verbose=False): """ Register the image and reconstruction from atlas on the end we smooth the final deformation by a gaussian filter :param ndarray img_sense: :param ndarray img_ref: :param float smooth_sigma: :param dict params: :param bool verbose: whether show debug time measurements :return tuple(ndarray,ndarray): >>> np.random.seed(0) >>> img_ref = np.zeros((10, 10), dtype=int) >>> img_ref[2:6, 1:7] = 1 >>> img_ref[5:9, 4:10] = 1 >>> img_ref array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) >>> from skimage.morphology import erosion, dilation >>> img_ref_fuz = np.zeros((10, 10), dtype=float) >>> img_ref_fuz[dilation(img_ref, np.ones((3, 3))) == 1] = 0.1 >>> img_ref_fuz[img_ref == 1] = 0.5 >>> img_ref_fuz[erosion(img_ref, np.ones((3, 3))) == 1] = 1.0 >>> img_ref_fuz array([[ 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ], [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0. , 0. ], [ 0.1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 0. , 0. ], [ 0.1, 0.5, 1. , 1. , 1. , 1. , 0.5, 0.1, 0. , 0. ], [ 0.1, 0.5, 1. , 1. , 1. , 1. , 0.5, 0.1, 0.1, 0.1], [ 0.1, 0.5, 0.5, 0.5, 0.5, 1. , 0.5, 0.5, 0.5, 0.5], [ 0.1, 0.1, 0.1, 0.1, 0.5, 1. , 1. , 1. , 1. , 1. ], [ 0. , 0. , 0. , 0.1, 0.5, 1. , 1. , 1. , 1. , 1. ], [ 0. , 0. , 0. , 0.1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5], [ 0. , 0. , 0. , 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]) >>> d_deform = register_demons_sym_diffeom(img_ref_fuz, img_ref, ... smooth_sigma=1.5, inverse=True, verbose=True) >>> img_warp = warp2d_transform_image(img_ref, d_deform, method='nearest', ... inverse=True) >>> np.round(img_warp.astype(float), 1) array([[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 1., 1., 1., 1., 1., 1., 0., 0., 0.], [ 0., 1., 1., 1., 1., 1., 1., 0., 0., 0.], [ 0., 1., 1., 1., 1., 1., 1., 0., 0., 0.], [ 0., 1., 1., 1., 1., 1., 1., 1., 1., 1.], [ 0., 0., 0., 0., 1., 1., 1., 1., 1., 1.], [ 0., 0., 0., 0., 1., 1., 1., 1., 1., 1.], [ 0., 0., 0., 0., 1., 1., 1., 1., 1., 1.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]) >>> img_sense = np.zeros(img_ref.shape, dtype=int) >>> img_sense[4:9, 3:10] = 1 >>> img_sense array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) >>> d_deform = register_demons_sym_diffeom(img_sense, img_ref, smooth_sigma=0.) >>> img_warp = warp2d_transform_image(img_sense, d_deform) >>> np.round(img_warp.astype(float), 1) # doctest: +SKIP array([[ 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ], [ 0. , 0.3, 0.5, 0.3, 0.1, 0. , 0. , 0. , 0. , 0. ], [ 0. , 1. , 1. , 1. , 1. , 0.8, 0.4, 0.1, 0. , 0. ], [ 0. , 1. , 1. , 1. , 1. , 1. , 1. , 1. , 0.5, 0. ], [ 0. , 1. , 1. , 1. , 1. , 1. , 1. , 1. , 1. , 1. ], [ 0. , 0.2, 1. , 1. , 1. , 1. , 1. , 1. , 1. , 1. ], [ 0. , 0. , 0.6, 0.9, 1. , 1. , 1. , 1. , 1. , 1. ], [ 0. , 0. , 0.2, 0.4, 0.5, 0.8, 1. , 1. , 1. , 1. ], [ 0. , 0. , 0. , 0.2, 0.2, 0.3, 0.4, 0.6, 0.7, 1. ], [ 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ]]) >>> np.round(img_warp - img_sense, 1) # doctest: +SKIP """ if img_ref.max() == 0 or img_sense.max() == 0: logging.debug( 'skip image registration (demons): max values for ' 'RECONST=%d and SENSE=%d', img_ref.max(), img_sense.max()) return {'mapping': None, 'mapping-inv': None, 'package': 'dipy'} sdr_params = {k: params[k] for k in params if k in LIST_SDR_PARAMS} sdr = SmoothSymmetricDiffeomorphicRegistration(metric=SSDMetric( img_ref.ndim), smooth_sigma=smooth_sigma, **sdr_params) sdr.verbosity = VerbosityLevels.NONE t = time.time() mapping = sdr.optimize(img_ref.astype(float), img_sense.astype(float)) if verbose: logging.debug('demons took: %d s', time.time() - t) mapping.forward = smooth_deform_field(mapping.forward, sigma=smooth_sigma) mapping.backward = smooth_deform_field(mapping.backward, sigma=smooth_sigma) # img_warped = mapping.transform(img_moving, 'linear') # mapping_inv = sdr.moving_to_ref if inverse: mapping_inv = DiffeomorphicMap(img_ref.ndim, img_ref.shape, None, img_ref.shape, None, img_ref.shape, None, None) mapping_inv.forward = smooth_deform_field(sdr.moving_to_ref.forward, sigma=smooth_sigma) mapping_inv.backward = smooth_deform_field(sdr.moving_to_ref.backward, sigma=smooth_sigma) else: mapping_inv = None if verbose: logging.debug('smoothing and warping took: %d s', time.time() - t) dict_deform = { 'mapping': mapping, 'mapping-inv': mapping_inv, 'package': 'dipy' } return dict_deform