def test_affine_registration(): moving = subset_b0 static = subset_b0 moving_affine = static_affine = np.eye(4) xformed, affine = affine_registration(moving, static, moving_affine=moving_affine, static_affine=static_affine, level_iters=[5, 5], sigmas=[3, 1], factors=[2, 1]) # We don't ask for much: npt.assert_almost_equal(affine[:3, :3], np.eye(3), decimal=1) with pytest.raises(ValueError): # For array input, must provide affines: xformed, affine = affine_registration(moving, static) # If providing nifti image objects, don't need to provide affines: moving_img = nib.Nifti1Image(moving, moving_affine) static_img = nib.Nifti1Image(static, static_affine) xformed, affine = affine_registration(moving_img, static_img) npt.assert_almost_equal(affine[:3, :3], np.eye(3), decimal=1) # Using strings with full paths as inputs also works: t1_name, b0_name = dpd.get_fnames('syn_data') moving = b0_name static = t1_name xformed, affine = affine_registration(moving, static, level_iters=[5, 5], sigmas=[3, 1], factors=[4, 2]) npt.assert_almost_equal(affine[:3, :3], np.eye(3), decimal=1)
def prealign(self, afq_object, row, save=True): prealign_file = afq_object._get_fname( row, '_prealign_from-DWI_to-MNI_xfm.npy') if not op.exists(prealign_file): reg_subject_img, _ = afq_object._reg_img( afq_object.reg_subject, True, row) start_time = time() _, aff = affine_registration( reg_subject_img, afq_object.reg_template_img, **self.affine_kwargs) row['timing']['Registration_pre_align'] =\ row['timing']['Registration_pre_align'] + time() - start_time if save: np.save(prealign_file, aff) meta_fname = afq_object._get_fname( row, '_prealign_from-DWI_to-MNI_xfm.json') meta = dict(type="rigid") afd.write_json(meta_fname, meta) else: return aff if save: return prealign_file else: return np.load(prealign_file)
def __call__(self): xformed_img, reg_affine = affine_registration( self.sample_data, # moving image data self.static_data, # static or template image data moving_affine=self.sample_affine, static_affine=self.static_affine, nbins=32, metric='MI', pipeline=self.pipeline, level_iters=[10000, 1000, 100], sigmas=[3.0, 1.0, 0.0], factors=[4, 2, 1]) return xformed_img
def prealign(self, subses_dict, reg_subject, reg_template, save=True): prealign_file = get_fname(subses_dict, '_prealign_from-DWI_to-MNI_xfm.npy') if not op.exists(prealign_file): start_time = time() _, aff = affine_registration(reg_subject, reg_template, **self.affine_kwargs) meta = dict(type="rigid", timing=time() - start_time) if save: np.save(prealign_file, aff) meta_fname = get_fname(subses_dict, '_prealign_from-DWI_to-MNI_xfm.json') afd.write_json(meta_fname, meta) else: return aff if save: return prealign_file else: return np.load(prealign_file)
def Sequential_Registration_b0(static, static_grid2world, moving, moving_grid2world, level_iters=[5], sigmas=[3.0], factors=[2]): pipeline = [center_of_mass, translation, rigid, affine] xformed_img, reg_affine = affine_registration( moving, static, moving_affine=moving_grid2world, static_affine=static_grid2world, nbins=16, metric='MI', pipeline=pipeline, level_iters=level_iters, sigmas=sigmas, factors=factors) affine_map = AffineMap(reg_affine, static.shape, static_grid2world, moving.shape, moving_grid2world) return xformed_img, reg_affine, affine_map
def reg_func(figname, static_mask=None, moving_mask=None): """Convenience function for registration using a pipeline. Uses variables in global scope, except for static_mask and moving_mask. """ pipeline = [translation, rigid] xformed_img, reg_affine = affine_registration(moving, static, moving_affine=moving_affine, static_affine=static_affine, nbins=32, metric='MI', pipeline=pipeline, level_iters=level_iters, sigmas=sigmas, factors=factors, static_mask=static_mask, moving_mask=moving_mask) regtools.overlay_slices(static, xformed_img, None, 2, "Static", "Transformed", figname) return
def test_affine_registration(): moving = subset_b0 static = subset_b0 moving_affine = static_affine = np.eye(4) xformed, affine_mat = affine_registration(moving, static, moving_affine=moving_affine, static_affine=static_affine, level_iters=[5, 5], sigmas=[3, 1], factors=[2, 1]) # We don't ask for much: npt.assert_almost_equal(affine_mat[:3, :3], np.eye(3), decimal=1) # [center_of_mass] + ret_metric=True should raise an error with pytest.raises(ValueError): # For array input, must provide affines: xformed, affine_mat = affine_registration(moving, static, moving_affine=moving_affine, static_affine=static_affine, pipeline=["center_of_mass"], ret_metric=True) # Define list of methods reg_methods = ["center_of_mass", "translation", "rigid", "rigid_isoscaling", "rigid_scaling", "affine", center_of_mass, translation, rigid, rigid_isoscaling, rigid_scaling, affine] # Test methods individually (without returning any metric) for func in reg_methods: xformed, affine_mat = affine_registration(moving, static, moving_affine=moving_affine, static_affine=static_affine, level_iters=[5, 5], sigmas=[3, 1], factors=[2, 1], pipeline=[func]) # We don't ask for much: npt.assert_almost_equal(affine_mat[:3, :3], np.eye(3), decimal=1) # Bad method with pytest.raises(ValueError, match=r'^pipeline\[0\] must be one.*foo.*'): affine_registration( moving, static, moving_affine, static_affine, pipeline=['foo']) # Test methods individually (returning quality metric) expected_nparams = [0, 3, 6, 7, 9, 12] * 2 assert len(expected_nparams) == len(reg_methods) for i, func in enumerate(reg_methods): if func in ('center_of_mass', center_of_mass): # can't return metric with pytest.raises(ValueError, match='cannot return any quality'): affine_registration( moving, static, moving_affine, static_affine, pipeline=[func], ret_metric=True) continue xformed, affine_mat, \ xopt, fopt = affine_registration(moving, static, moving_affine=moving_affine, static_affine=static_affine, level_iters=[5, 5], sigmas=[3, 1], factors=[2, 1], pipeline=[func], ret_metric=True) # Expected number of optimization parameters npt.assert_equal(len(xopt), expected_nparams[i]) # Optimization metric must be a single numeric value npt.assert_equal(isinstance(fopt, (int, float)), True) with pytest.raises(ValueError): # For array input, must provide affines: xformed, affine_mat = affine_registration(moving, static) # Not supported transform names should raise an error npt.assert_raises(ValueError, affine_registration, moving, static, moving_affine, static_affine, pipeline=["wrong_transform"]) # If providing nifti image objects, don't need to provide affines: moving_img = nib.Nifti1Image(moving, moving_affine) static_img = nib.Nifti1Image(static, static_affine) xformed, affine_mat = affine_registration(moving_img, static_img) npt.assert_almost_equal(affine_mat[:3, :3], np.eye(3), decimal=1) # Using strings with full paths as inputs also works: t1_name, b0_name = dpd.get_fnames('syn_data') moving = b0_name static = t1_name xformed, affine_mat = affine_registration(moving, static, level_iters=[5, 5], sigmas=[3, 1], factors=[4, 2]) npt.assert_almost_equal(affine_mat[:3, :3], np.eye(3), decimal=1)
This interface constructs a pipeline of operations as a sequence of functions that each implement one of the transforms. """ pipeline = [center_of_mass, translation, rigid, affine] """ And then applies the functions in the pipeline on the input (from left to right) with a call to an `affine_registration` function, which takes optional settings for things like the iterations, sigmas and factors. """ xformed_img, reg_affine = affine_registration(moving, static, moving_affine=moving_affine, static_affine=static_affine, nbins=32, metric='MI', pipeline=pipeline, level_iters=level_iters, sigmas=sigmas, factors=factors) regtools.overlay_slices(static, xformed_img, None, 0, "Static", "Transformed", "xformed_affine_0.png") regtools.overlay_slices(static, xformed_img, None, 1, "Static", "Transformed", "xformed_affine_1.png") regtools.overlay_slices(static, xformed_img, None, 2, "Static", "Transformed", "xformed_affine_2.png") """ .. figure:: xformed_affine_0.png :align: center
# # To find the right place for the waypoint ROIs, we calculate a non-linear # transformation between the individual's brain DWI measurement (the b0 # measurements) and the MNI T2 template. # Before calculating this non-linear warping, we perform a pre-alignment # using an affine transformation. print("Registering to template...") MNI_T2_img = afd.read_mni_template() if not op.exists(op.join(working_dir, 'mapping.nii.gz')): import dipy.core.gradients as dpg gtab = dpg.gradient_table(hardi_fbval, hardi_fbvec) b0 = np.mean(img.get_fdata()[..., gtab.b0s_mask], -1) # Prealign using affine registration _, prealign = affine_registration(b0, MNI_T2_img.get_fdata(), img.affine, MNI_T2_img.affine) # Then register using a non-linear registration using the affine for # prealignment warped_hardi, mapping = reg.syn_register_dwi(hardi_fdata, gtab, prealign=prealign) reg.write_mapping(mapping, op.join(working_dir, 'mapping.nii.gz')) else: mapping = reg.read_mapping(op.join(working_dir, 'mapping.nii.gz'), img, MNI_T2_img) ########################################################################## # Read in bundle specification # ------------------------------------------- # The waypoint ROIs, in addition to bundle probability maps are stored in this
def run(self, static_image_files, moving_image_files, transform='affine', nbins=32, sampling_prop=None, metric='mi', level_iters=[10000, 1000, 100], sigmas=[3.0, 1.0, 0.0], factors=[4, 2, 1], progressive=True, save_metric=False, out_dir='', out_moved='moved.nii.gz', out_affine='affine.txt', out_quality='quality_metric.txt'): """ Parameters ---------- static_image_files : string Path to the static image file. moving_image_files : string Path to the moving image file. transform : string, optional com: center of mass, trans: translation, rigid: rigid body, rigid_isoscaling: rigid body + isotropic scaling, rigid_scaling: rigid body + scaling, affine: full affine including translation, rotation, shearing and scaling. nbins : int, optional Number of bins to discretize the joint and marginal PDF. sampling_prop : int, optional Number ([0-100]) of voxels for calculating the PDF. 'None' implies all voxels. metric : string, optional Similarity metric for gathering mutual information). level_iters : variable int, optional The number of iterations at each scale of the scale space. `level_iters[0]` corresponds to the coarsest scale, `level_iters[-1]` the finest, where n is the length of the sequence. sigmas : variable floats, optional Custom smoothing parameter to build the scale space (one parameter for each scale). factors : variable floats, optional Custom scale factors to build the scale space (one factor for each scale). progressive : boolean, optional Enable/Disable the progressive registration. save_metric : boolean, optional If true, quality assessment metric are saved in 'quality_metric.txt'. out_dir : string, optional Directory to save the transformed image and the affine matrix (default current directory). out_moved : string, optional Name for the saved transformed image. out_affine : string, optional Name for the saved affine matrix. out_quality : string, optional Name of the file containing the saved quality metric. """ io_it = self.get_io_iterator() transform = transform.lower() metric = metric.upper() if metric != 'MI': raise ValueError("Invalid similarity metric: Please provide a" "valid metric.") if progressive: pipeline_opt = { "com": ["center_of_mass"], "trans": ["center_of_mass", "translation"], "rigid": ["center_of_mass", "translation", "rigid"], "rigid_isoscaling": ["center_of_mass", "translation", "rigid_isoscaling"], "rigid_scaling": ["center_of_mass", "translation", "rigid_scaling"], "affine": ["center_of_mass", "translation", "rigid", "affine"]} else: pipeline_opt = { "com": ["center_of_mass"], "trans": ["center_of_mass", "translation"], "rigid": ["center_of_mass", "rigid"], "rigid_isoscaling": ["center_of_mass", "rigid_isoscaling"], "rigid_scaling": ["center_of_mass", "rigid_scaling"], "affine": ["center_of_mass", "affine"]} pipeline = pipeline_opt.get(transform) if pipeline is None: raise ValueError('Invalid transformation:' ' Please see program\'s help' ' for allowed values of' ' transformation.') for static_img, mov_img, moved_file, affine_matrix_file, \ qual_val_file in io_it: # Load the data from the input files and store into objects. static, static_grid2world = load_nifti(static_img) moving, moving_grid2world = load_nifti(mov_img) check_dimensions(static, moving) starting_affine = None # If only center of mass is selected do not return metric if pipeline == ["center_of_mass"]: moved_image, affine_matrix = \ affine_registration(moving, static, moving_grid2world, static_grid2world, pipeline, starting_affine, metric, level_iters, sigmas, factors, nbins=nbins, sampling_proportion=sampling_prop) else: moved_image, affine_matrix, xopt, fopt = \ affine_registration(moving, static, moving_grid2world, static_grid2world, pipeline, starting_affine, metric, level_iters, sigmas, factors, ret_metric=True, nbins=nbins, sampling_proportion=sampling_prop) """ Saving the moved image file and the affine matrix. """ logging.info("Optimal parameters: {0}".format(str(xopt))) logging.info("Similarity metric: {0}".format(str(fopt))) if save_metric: save_qa_metric(qual_val_file, xopt, fopt) save_nifti(moved_file, moved_image, static_grid2world) np.savetxt(affine_matrix_file, affine_matrix)
def hmc(data, gtab, mask=None, b0_ref=0, affine=None): data, affine = read_img_arr_or_path(data, affine=affine) if isinstance(gtab, collections.Sequence): gtab = dpg.gradient_table(*gtab) # We fix b0 to be one volume, registered to one of the # b0 volumes (first, per default): if np.sum(gtab.b0s_mask) > 1: b0_img = nib.Nifti1Image(data[..., gtab.b0s_mask], affine) trans_b0, b0_affines = dpa.register_series(b0_img, ref=b0_ref) ref_data = np.mean(trans_b0, -1)[..., np.newaxis] else: # There's only one b0 and we register everything to it trans_b0 = ref_data = data[..., gtab.b0s_mask] moving_data = data[..., ~gtab.b0s_mask] moving_bvals = gtab.bvals[~gtab.b0s_mask] moving_bvecs = gtab.bvecs[~gtab.b0s_mask] mask = np.ones(ref_data.shape[:3], dtype=bool) mask[np.where(ref_data[..., 0] == 0)] = False moved = [] affines = [] # We fit the isotropic prediction once for all the data: sfm_all = SFM4HMC( gtab, isotropic=ExponentialIsotropicModel) sff_all, iso_params = sfm_all.fit(data, alpha=10e-10, mask=mask, tol=10e-10, iso_params=None) for loo in range(moving_data.shape[-1]): print(loo) loo_idxer = np.ones(moving_data.shape[-1]).astype(bool) loo_idxer[loo] = False in_data = np.concatenate([ref_data, moving_data[..., loo_idxer]], -1) in_gtab = dpg.gradient_table( np.concatenate([np.array([0]), moving_bvals[loo_idxer]]), np.concatenate([np.array([[0, 0, 0]]), moving_bvecs[loo_idxer]])) sfm = SFM4HMC( in_gtab, isotropic=ExponentialIsotropicModel) t1 = time.time() sff, _ = sfm.fit(in_data, mask=mask, alpha=10e-10, iso_params=iso_params) t2 = time.time() print(t2 - t1) out_data = moving_data[..., ~loo_idxer] out_gtab = dpg.gradient_table(moving_bvals[~loo_idxer], moving_bvecs[~loo_idxer]) out_pred = sff.predict(out_gtab, S0=ref_data[..., 0]) t1 = time.time() resampled, out_affine = dpa.affine_registration( out_data[..., 0], out_pred, moving_affine=affine, static_affine=affine, pipeline=[dpa.affine], level_iters=[1000, 100, 10]) t2 = time.time() print(t2 - t1) moved.append(resampled) affines.append(out_affine) in_data[..., loo] = resampled # XXX Also rotate the b-vector here new_out_gtab = dpg.reorient_bvecs(out_gtab, [out_affine]) moving_bvals[~loo_idxer] = new_out_gtab.bvals moving_bvecs[~loo_idxer] = new_out_gtab.bvecs return moved, affines # Reuse USV from a single SVD decomposition of X at the beginning of each # loop through all of the volumes. Should speed up the RR fit in every volume. # <= We can't do that, because the directions are different each time # Use a sliding window to fit only to n nearest neighbors. # Fit isotropic component once per voxel and be done with it
# transformation between the individual's brain DWI measurement (the b0 # measurements) and the MNI T1 template. # Before calculating this non-linear warping, we perform a pre-alignment # using an affine transformation. print("Registering to template...") MNI_T1w_img = afd.read_mni_template(weight="T1w") if not op.exists(op.join(working_dir, 'mapping.nii.gz')): import dipy.core.gradients as dpg gtab = dpg.gradient_table(hardi_fbval, hardi_fbvec) # Prealign using affine registration _, prealign = affine_registration( apm, MNI_T1w_img.get_fdata(), img.affine, MNI_T1w_img.affine) # Then register using a non-linear registration using the affine for # prealignment warped_hardi, mapping = reg.syn_register_dwi(hardi_fdata, gtab, prealign=prealign) reg.write_mapping(mapping, op.join(working_dir, 'mapping.nii.gz')) else: mapping = reg.read_mapping(op.join(working_dir, 'mapping.nii.gz'), img, MNI_T1w_img) ########################################################################## # Bundle specification