def test_reorient_bvecs(): sq2 = np.sqrt(2) / 2 bvals = np.concatenate([[0], np.ones(6) * 1000]) bvecs = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1], [sq2, sq2, 0], [sq2, 0, sq2], [0, sq2, sq2]]) gt = gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=0) # The simple case: all affines are identity affs = np.zeros((6, 4, 4)) for i in range(4): affs[:, i, i] = 1 # We should get back the same b-vectors new_gt = reorient_bvecs(gt, affs) npt.assert_equal(gt.bvecs, new_gt.bvecs) # Now apply some rotations rotation_affines = [] rotated_bvecs = bvecs[:] for i in np.where(~gt.b0s_mask)[0]: rot_ang = np.random.rand() cos_rot = np.cos(rot_ang) sin_rot = np.sin(rot_ang) rotation_affines.append(np.array([[1, 0, 0, 0], [0, cos_rot, -sin_rot, 0], [0, sin_rot, cos_rot, 0], [0, 0, 0, 1]])) rotated_bvecs[i] = np.dot(rotation_affines[-1][:3, :3], bvecs[i]) # Copy over the rotation affines full_affines = rotation_affines[:] # And add some scaling and translation to each one to make this harder for i in range(len(full_affines)): full_affines[i] = np.dot(full_affines[i], np.array([[2.5, 0, 0, -10], [0, 2.2, 0, 20], [0, 0, 1, 0], [0, 0, 0, 1]])) gt_rot = gradient_table_from_bvals_bvecs(bvals, rotated_bvecs, b0_threshold=0) new_gt = reorient_bvecs(gt_rot, full_affines) # At the end of all this, we should be able to recover the original # vectors npt.assert_almost_equal(gt.bvecs, new_gt.bvecs) # We should be able to pass just the 3-by-3 rotation components to the same # effect new_gt = reorient_bvecs(gt_rot, np.array(rotation_affines)[:, :3, :3]) npt.assert_almost_equal(gt.bvecs, new_gt.bvecs) # Verify that giving the wrong number of affines raises an error: full_affines.append(np.zeros((4, 4))) npt.assert_raises(ValueError, reorient_bvecs, gt_rot, full_affines)
def test_reorient_bvecs(): sq2 = np.sqrt(2) / 2 bvals = np.concatenate([[0], np.ones(6) * 1000]) bvecs = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1], [sq2, sq2, 0], [sq2, 0, sq2], [0, sq2, sq2]]) gt = gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=0) # The simple case: all affines are identity affs = np.zeros((6, 4, 4)) for i in range(4): affs[:, i, i] = 1 # We should get back the same b-vectors new_gt = reorient_bvecs(gt, affs) npt.assert_equal(gt.bvecs, new_gt.bvecs) # Now apply some rotations rotation_affines = [] rotated_bvecs = bvecs[:] for i in np.where(~gt.b0s_mask)[0]: rot_ang = np.random.rand() cos_rot = np.cos(rot_ang) sin_rot = np.sin(rot_ang) rotation_affines.append(np.array([[1, 0, 0, 0], [0, cos_rot, -sin_rot, 0], [0, sin_rot, cos_rot, 0], [0, 0, 0, 1]])) rotated_bvecs[i] = np.dot(rotation_affines[-1][:3, :3], bvecs[i]) # Copy over the rotation affines full_affines = rotation_affines[:] # And add some scaling and translation to each one to make this harder for i in range(len(full_affines)): full_affines[i] = np.dot(full_affines[i], np.array([[2.5, 0, 0, -10], [0, 2.2, 0, 20], [0, 0, 1, 0], [0, 0, 0, 1]])) gt_rot = gradient_table_from_bvals_bvecs(bvals, rotated_bvecs, b0_threshold=0) new_gt = reorient_bvecs(gt_rot, full_affines) # At the end of all this, we should be able to recover the original # vectors npt.assert_almost_equal(gt.bvecs, new_gt.bvecs) # We should be able to pass just the 3-by-3 rotation components to the same # effect new_gt = reorient_bvecs(gt_rot, np.array(rotation_affines)[:, :3, :3]) npt.assert_almost_equal(gt.bvecs, new_gt.bvecs) # Verify that giving the wrong number of affines raises an error: full_affines.append(np.zeros((4, 4))) assert_raises(ValueError, reorient_bvecs, gt_rot, full_affines)
def test_gradient_table_from_bvals_bvecs(): sq2 = np.sqrt(2) / 2 bvals = [0, 1, 2, 3, 4, 5, 6, 0] bvecs = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1], [sq2, sq2, 0], [sq2, 0, sq2], [0, sq2, sq2], [0, 0, 0]]) gt = gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=0) npt.assert_array_equal(gt.bvecs, bvecs) npt.assert_array_equal(gt.bvals, bvals) npt.assert_array_equal(gt.gradients, np.reshape(bvals, (-1, 1)) * bvecs) npt.assert_array_equal(gt.b0s_mask, [1, 0, 0, 0, 0, 0, 0, 1]) # Test nans are replaced by 0 new_bvecs = bvecs.copy() new_bvecs[[0, -1]] = np.nan gt = gradient_table_from_bvals_bvecs(bvals, new_bvecs, b0_threshold=0) npt.assert_array_equal(gt.bvecs, bvecs) # Bvalue > 0 for non-unit vector bad_bvals = [2, 1, 2, 3, 4, 5, 6, 0] npt.assert_raises(ValueError, gradient_table_from_bvals_bvecs, bad_bvals, bvecs, b0_threshold=0.) # num_gard inconsistent bvals, bvecs bad_bvals = np.ones(7) npt.assert_raises(ValueError, gradient_table_from_bvals_bvecs, bad_bvals, bvecs, b0_threshold=0.) # negative bvals bad_bvals = [-1, -1, -1, -5, -6, -10] npt.assert_raises(ValueError, gradient_table_from_bvals_bvecs, bad_bvals, bvecs, b0_threshold=0.) # bvals not 1d bad_bvals = np.ones((1, 8)) npt.assert_raises(ValueError, gradient_table_from_bvals_bvecs, bad_bvals, bvecs, b0_threshold=0.) # bvec not 2d bad_bvecs = np.ones((1, 8, 3)) npt.assert_raises(ValueError, gradient_table_from_bvals_bvecs, bvals, bad_bvecs, b0_threshold=0.) # bvec not (N, 3) bad_bvecs = np.ones((8, 2)) npt.assert_raises(ValueError, gradient_table_from_bvals_bvecs, bvals, bad_bvecs, b0_threshold=0.) # bvecs not unit vectors bad_bvecs = bvecs * 2 npt.assert_raises(ValueError, gradient_table_from_bvals_bvecs, bvals, bad_bvecs, b0_threshold=0.) # Test **kargs get passed along gt = gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=0, big_delta=5, small_delta=2) npt.assert_equal(gt.big_delta, 5) npt.assert_equal(gt.small_delta, 2)
def test_all_zeros(): bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) fit_methods = ['LS', 'OLS', 'NNLS', 'RESTORE'] for fit_method in fit_methods: dm = dti.TensorModel(gtab) assert_raises(ValueError, dm.fit, np.zeros(bvals.shape[0]))
def select_outer_shell(gtab): bvals = gtab.bvals bvecs = gtab.bvecs max_bval = np.max(shells(gtab)) bvals[bvals != max_bval] = 0 new_gtab = gradient_table_from_bvals_bvecs(bvals, bvecs) return new_gtab
def compute_RTOP(dwi_, mask_, bvals_, bvecs_): if path.exists(dwi_) and path.exists(mask_) and path.exists( bvals_) and path.exists(bvecs_): dwi = nib.load(dwi_) mask = nib.load(mask_) bvals = np.loadtxt(bvals_) * units.s / units.mm**2 bvecs = np.loadtxt(bvecs_) else: print("One of the files does not exist, exit.") exit() dwi_data = dwi.get_data() mask_data = mask.get_data() nodif = dwi.slicer[:, :, :, 0] gtab = gradients.gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=5) map_model = mapmri.MapmriModel(gtab, laplacian_regularization=True, laplacian_weighting=0.2) map_fit = map_model.fit(dwi_data, mask=mask_data) rtop = map_fit.rtop() rtop_cortex_norm = rtop / rtop[mask_data != 0].mean() rtop_nii = nib.Nifti1Image(rtop, affine=dwi.affine) rtop_cortex_norm_nii = nib.Nifti1Image(rtop_cortex_norm, affine=dwi.affine) rtop_nii.to_filename('RTOP_cortex.nii.gz') rtop_cortex_norm_nii.to_filename('RTOP_cortex_norm.nii.gz') return
def test_masked_array_with_tensor(): data = np.ones((2, 4, 56)) mask = np.array([[True, False, False, True], [True, False, True, False]]) bvec, bval = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bval, bvec.T) tensor_model = TensorModel(gtab) tensor = tensor_model.fit(data, mask=mask) assert_equal(tensor.shape, (2, 4)) assert_equal(tensor.fa.shape, (2, 4)) assert_equal(tensor.evals.shape, (2, 4, 3)) assert_equal(tensor.evecs.shape, (2, 4, 3, 3)) tensor = tensor[0] assert_equal(tensor.shape, (4,)) assert_equal(tensor.fa.shape, (4,)) assert_equal(tensor.evals.shape, (4, 3)) assert_equal(tensor.evecs.shape, (4, 3, 3)) tensor = tensor[0] assert_equal(tensor.shape, tuple()) assert_equal(tensor.fa.shape, tuple()) assert_equal(tensor.evals.shape, (3,)) assert_equal(tensor.evecs.shape, (3, 3)) assert_equal(type(tensor.model_params), np.ndarray)
def test_all_zeros(): bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) fit_methods = ['LS', 'OLS', 'NNLS', 'RESTORE'] for _ in fit_methods: dm = dti.TensorModel(gtab) assert_array_almost_equal(dm.fit(np.zeros(bvals.shape[0])).evals, 0)
def test_all_zeros(): bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) fit_methods = ['LS', 'OLS', 'NNLS', 'RESTORE'] for fit_method in fit_methods: dm = dti.TensorModel(gtab) assert_array_almost_equal(dm.fit(np.zeros(bvals.shape[0])).evals, 0)
def reorient_rasb(self): """Reorient the vectors based o a list of affine transforms.""" from dipy.core.gradients import (gradient_table_from_bvals_bvecs, reorient_bvecs) affines = self._transforms.copy() bvals = self._bvals bvecs = self._bvecs # Verify that number of non-B0 volumes corresponds to the number of # affines. If not, try to fix it, or raise an error: if len(self._bvals[self._bvals >= self._b0_thres]) != len(affines): b0_indices = np.where(self._bvals <= self._b0_thres)[0].tolist() if len(self._bvals[self._bvals >= self._b0_thres]) < len(affines): for i in sorted(b0_indices, reverse=True): del affines[i] if len(self._bvals[self._bvals >= self._b0_thres]) > len(affines): ras_b_mat = self._gradients.copy() ras_b_mat = np.delete(ras_b_mat, tuple(b0_indices), axis=0) bvals = ras_b_mat[:, 3] bvecs = ras_b_mat[:, 0:3] if len(self._bvals[self._bvals > self._b0_thres]) != len(affines): raise ValueError( "Affine transformations do not correspond to gradients") # Build gradient table object gt = gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=self._b0_thres) # Reorient table new_gt = reorient_bvecs(gt, [np.load(aff) for aff in affines]) return np.hstack((new_gt.bvecs, new_gt.bvals[..., np.newaxis]))
def test_all_constant(): bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) fit_methods = ['LS', 'OLS', 'NNLS'] for fit_method in fit_methods: dm = dti.TensorModel(gtab) assert_almost_equal(dm.fit(np.zeros(bvals.shape[0])).fa, 0) assert_almost_equal(dm.fit(100 * np.ones(bvals.shape[0])).fa, 0)
def test_all_zeros(): bvals, bvecs = read_bvals_bvecs(*get_fnames('55dir_grad')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs) fit_methods = ['LS', 'OLS', 'NNLS', 'RESTORE'] for _ in fit_methods: dm = dti.TensorModel(gtab) npt.assert_array_almost_equal( dm.fit(np.zeros(bvals.shape[0])).evals, 0)
def test_TensorModel(): data, gtab = dsi_voxels() dm = dti.TensorModel(gtab, 'LS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.5, True) dm = dti.TensorModel(gtab, 'WLS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.5, True) sphere = create_unit_sphere(4) assert_equal(len(dtifit.odf(sphere)), len(sphere.vertices)) assert_almost_equal(dtifit.fa, gfa(dtifit.odf(sphere)), 1) # Check that the multivoxel case works: dtifit = dm.fit(data) assert_equal(dtifit.fa.shape, data.shape[:3]) # Make some synthetic data b0 = 1000. bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) # The first b value is 0., so we take the second one: B = bvals[1] #Scale the eigenvalues and tensor by the B value so the units match D = np.array([1., 1., 1., 0., 0., 1., -np.log(b0) * B]) / B evals = np.array([2., 1., 0.]) / B md = evals.mean() tensor = from_lower_triangular(D) evecs = np.linalg.eigh(tensor)[1] #Design Matrix X = dti.design_matrix(bvecs, bvals) #Signals Y = np.exp(np.dot(X,D)) assert_almost_equal(Y[0], b0) Y.shape = (-1,) + Y.shape # Test fitting with different methods: #XXX Add NNLS methods! for fit_method in ['OLS', 'WLS']: tensor_model = dti.TensorModel(gtab, fit_method=fit_method) tensor_fit = tensor_model.fit(Y) assert_true(tensor_fit.model is tensor_model) assert_equal(tensor_fit.shape, Y.shape[:-1]) assert_array_almost_equal(tensor_fit.evals[0], evals) assert_array_almost_equal(tensor_fit.quadratic_form[0], tensor, err_msg =\ "Calculation of tensor from Y does not compare to analytical solution") assert_almost_equal(tensor_fit.md[0], md) assert_equal(tensor_fit.directions.shape[-2], 1) assert_equal(tensor_fit.directions.shape[-1], 3) # Test error-handling: assert_raises(ValueError, dti.TensorModel, gtab, fit_method='crazy_method')
def test_all_constant(): bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) fit_methods = ['LS', 'OLS', 'NNLS', 'RESTORE'] for _ in fit_methods: dm = dti.TensorModel(gtab) assert_almost_equal(dm.fit(100 * np.ones(bvals.shape[0])).fa, 0) # Doesn't matter if the signal is smaller than 1: assert_almost_equal(dm.fit(0.4 * np.ones(bvals.shape[0])).fa, 0)
def test_all_constant(): bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) fit_methods = ['LS', 'OLS', 'NNLS', 'RESTORE'] for fit_method in fit_methods: dm = dti.TensorModel(gtab) assert_almost_equal(dm.fit(100 * np.ones(bvals.shape[0])).fa, 0) # Doesn't matter if the signal is smaller than 1: assert_almost_equal(dm.fit(0.4 * np.ones(bvals.shape[0])).fa, 0)
def main(): parser = build_argparser() args = parser.parse_args() dwi_name = args.dwi.split('.')[0] bvals_filename = dwi_name + ".bvals" bvecs_filename = dwi_name + ".bvecs" if args.bvals is not None: bvals_filename = args.bvals if args.bvecs is not None: bvecs_filename = args.bvecs bvals, bvecs = read_bvals_bvecs(bvals_filename, bvecs_filename) gtab = gradient_table_from_bvals_bvecs(bvals, bvecs) idx = gtab.bvals == 1000 idx[np.logical_and(1000 - 15 <= gtab.bvals, gtab.bvals <= 1000 + 15)] = True idx[0] = True b0_idx = gtab.bvals <= 15 print("{} b=0 images will be averaged.".format(np.sum(b0_idx))) # Make sure we have the right numbers of each bval. assert np.sum(idx) == 91 assert np.sum( np.logical_and(2000 - 15 <= gtab.bvals, gtab.bvals <= 2000 + 15)) == 90 assert np.sum( np.logical_and(3000 - 20 <= gtab.bvals, gtab.bvals <= 3000 + 20)) == 90 new_bvals = gtab.bvals[idx] assert new_bvals[0] <= 5 new_bvals[0] = 0 # Make sure it is 0 new_bvecs = gtab.bvecs[idx] dwi = nib.load(args.dwi) b0 = np.mean(dwi.get_data()[:, :, :, b0_idx], axis=-1) nii = nib.Nifti1Image(b0, affine=dwi.affine, header=dwi.header) out_dir = args.out try: os.makedirs(out_dir) except: pass # Assume it already exists. # Write b0 nib.save(nii, pjoin(out_dir, args.basename + "_b0.nii.gz")) new_dwi = dwi.get_data()[:, :, :, idx] new_dwi[:, :, :, 0] = b0.copy() nii = nib.Nifti1Image(new_dwi, affine=dwi.affine, header=dwi.header) dwi_out_name = args.basename + "_b{}".format(args.b_value) nib.save(nii, pjoin(out_dir, dwi_out_name + ".nii.gz")) open(pjoin(out_dir, dwi_out_name + ".bvecs"), 'w').write("\n".join( map(lambda bv: " ".join(map(str, bv)), new_bvecs.T))) open(pjoin(out_dir, dwi_out_name + ".bvals"), 'w').write(" ".join(map(str, new_bvals)))
def test_fit_method_error(): bvec, bval = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bval, bvec.T) # This should work (smoke-testing!): TensorModel(gtab, fit_method='WLS') # This should raise an error because there is no such fit_method assert_raises(ValueError, TensorModel, gtab, min_signal=1e-9, fit_method='s')
def estimate_tensor(dwi_data, mask, bvals, bvecs): ''' Estimate the tensor image using dipy. ''' gtab = gradient_table_from_bvals_bvecs(bvals, bvecs) tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(dwi_data, mask=(mask > 0)) tensor_data = tenfit.lower_triangular().astype('float32') tensor_data = tensor_data[..., np.newaxis, :] * mask[..., np.newaxis, np.newaxis] return tensor_data
def test_all_constant(): """ """ bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) fit_methods = ['LS', 'OLS', 'NNLS'] for fit_method in fit_methods: dm = dti.TensorModel(gtab) assert_almost_equal(dm.fit(np.zeros(bvals.shape[0])).fa, 0) assert_almost_equal(dm.fit(100 * np.ones(bvals.shape[0])).fa, 0)
def test_tensor_model(): fdata, fbval, fbvec = get_data('small_25') data1 = nib.load(fdata).get_data() gtab1 = grad.gradient_table(fbval, fbvec) data2, gtab2 = dsi_voxels() for data, gtab in zip([data1, data2], [gtab1, gtab2]): dm = dti.TensorModel(gtab, 'LS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.9, True) dm = dti.TensorModel(gtab, 'WLS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.9, True) assert_equal(dtifit.fa > 0, True) sphere = create_unit_sphere(4) assert_equal(len(dtifit.odf(sphere)), len(sphere.vertices)) # Check that the multivoxel case works: dtifit = dm.fit(data) # Check that it works on signal that has already been normalized to S0: dm_to_relative = dti.TensorModel(gtab) if np.any(gtab.b0s_mask): relative_data = (data[0, 0, 0]/np.mean(data[0, 0, 0, gtab.b0s_mask])) dtifit_to_relative = dm_to_relative.fit(relative_data) npt.assert_almost_equal(dtifit.fa[0, 0, 0], dtifit_to_relative.fa, decimal=3) # And smoke-test that all these operations return sensibly-shaped arrays: assert_equal(dtifit.fa.shape, data.shape[:3]) assert_equal(dtifit.ad.shape, data.shape[:3]) assert_equal(dtifit.md.shape, data.shape[:3]) assert_equal(dtifit.rd.shape, data.shape[:3]) assert_equal(dtifit.trace.shape, data.shape[:3]) assert_equal(dtifit.mode.shape, data.shape[:3]) assert_equal(dtifit.linearity.shape, data.shape[:3]) assert_equal(dtifit.planarity.shape, data.shape[:3]) assert_equal(dtifit.sphericity.shape, data.shape[:3]) # Test for the shape of the mask assert_raises(ValueError, dm.fit, np.ones((10, 10, 3)), np.ones((3, 3))) # Make some synthetic data b0 = 1000. bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) # The first b value is 0., so we take the second one: B = bvals[1] # Scale the eigenvalues and tensor by the B value so the units match D = np.array([1., 1., 1., 0., 0., 1., -np.log(b0) * B]) / B evals = np.array([2., 1., 0.]) / B md = evals.mean() tensor = from_lower_triangular(D) A_squiggle = tensor - (1 / 3.0) * np.trace(tensor) * np.eye(3) mode = (3 * np.sqrt(6) * np.linalg.det(A_squiggle / np.linalg.norm(A_squiggle))) evals_eigh, evecs_eigh = np.linalg.eigh(tensor) # Sort according to eigen-value from large to small: evecs = evecs_eigh[:, np.argsort(evals_eigh)[::-1]] # Check that eigenvalues and eigenvectors are properly sorted through # that previous operation: for i in range(3): assert_array_almost_equal(np.dot(tensor, evecs[:, i]), evals[i] * evecs[:, i]) # Design Matrix X = dti.design_matrix(gtab) # Signals Y = np.exp(np.dot(X, D)) assert_almost_equal(Y[0], b0) Y.shape = (-1,) + Y.shape # Test fitting with different methods: for fit_method in ['OLS', 'WLS', 'NLLS']: tensor_model = dti.TensorModel(gtab, fit_method=fit_method, return_S0_hat=True) tensor_fit = tensor_model.fit(Y) assert_true(tensor_fit.model is tensor_model) assert_equal(tensor_fit.shape, Y.shape[:-1]) assert_array_almost_equal(tensor_fit.evals[0], evals) assert_array_almost_equal(tensor_fit.S0_hat, b0, decimal=3) # Test that the eigenvectors are correct, one-by-one: for i in range(3): # Eigenvectors have intrinsic sign ambiguity # (see # http://prod.sandia.gov/techlib/access-control.cgi/2007/076422.pdf) # so we need to allow for sign flips. One of the following should # always be true: assert_( np.all(np.abs(tensor_fit.evecs[0][:, i] - evecs[:, i]) < 10e-6) or np.all(np.abs(-tensor_fit.evecs[0][:, i] - evecs[:, i]) < 10e-6)) # We set a fixed tolerance of 10e-6, similar to array_almost_equal err_msg = "Calculation of tensor from Y does not compare to " err_msg += "analytical solution" assert_array_almost_equal(tensor_fit.quadratic_form[0], tensor, err_msg=err_msg) assert_almost_equal(tensor_fit.md[0], md) assert_array_almost_equal(tensor_fit.mode, mode, decimal=5) assert_equal(tensor_fit.directions.shape[-2], 1) assert_equal(tensor_fit.directions.shape[-1], 3) # Test error-handling: assert_raises(ValueError, dti.TensorModel, gtab, fit_method='crazy_method') # Test custom fit tensor method try: model = dti.TensorModel(gtab, fit_method=lambda *args, **kwargs: 42) fit = model.fit_method() except Exception as exc: assert False, "TensorModel should accept custom fit methods: %s" % exc assert fit == 42, "Custom fit method for TensorModel returned %s." % fit # Test multi-voxel data data = np.zeros((3, Y.shape[1])) # Normal voxel data[0] = Y # High diffusion voxel, all diffusing weighted signal equal to zero data[1, gtab.b0s_mask] = b0 data[1, ~gtab.b0s_mask] = 0 # Masked voxel, all data set to zero data[2] = 0. tensor_model = dti.TensorModel(gtab) fit = tensor_model.fit(data) assert_array_almost_equal(fit[0].evals, evals) # Return S0_test tensor_model = dti.TensorModel(gtab, return_S0_hat=True) fit = tensor_model.fit(data) assert_array_almost_equal(fit[0].evals, evals) assert_array_almost_equal(fit[0].S0_hat, b0) # Evals should be high for high diffusion voxel assert_(all(fit[1].evals > evals[0] * .9)) # Evals should be zero where data is masked assert_array_almost_equal(fit[2].evals, 0.)
def initial_fit(dwis, bvals, bvecs, mask, wm_roi, csf_roi, MD, csf_percentile=95, wm_percentile=5, lmin=0.1e-3, lmax=2.5e-3, evals_lmin=0.1e-3, evals_lmax=2.5e-3, md_value=0.6e-3, interpolate=True, fixed_MD=False): ''' Produce the initial estimate of the volume fraction and the initial tensor image ''' print(" - Compute baseline image and DW attenuation.") dim_x, dim_y, dim_z = mask.shape indices_dwis = (bvals > 0) nb_dwis = np.count_nonzero(indices_dwis) indices_b0 = (bvals == 0) nb_b0 = np.count_nonzero(indices_b0) # TO DO : address this line for multi-shell dwi b = bvals.max() b0 = dwis[..., indices_b0].mean(-1) # signal attenuation signal = dwis[..., indices_dwis] / b0[..., None] np.clip(signal, 1.0e-6, 1 - 1.0e-6, signal) signal[np.logical_not(mask)] = 0. # tissue b0 references csf_b0 = np.percentile(b0[csf_roi], csf_percentile) print("\t{0:2d}th percentile of b0 signal in CSF: {1}.".format( csf_percentile, csf_b0)) wm_b0 = np.percentile(b0[wm_roi], wm_percentile) print("\t{0:2d}th percentile of b0 signal in WM : {1}.".format( wm_percentile, wm_b0)) print(" - Compute initial volume fraction ...") # Eq. 7 from Pasternak 2009 MRM epsi = 1e-12 # only used to prevent log(0) init_f = 1 - np.log(b0 / wm_b0 + epsi) / np.log(csf_b0 / wm_b0) np.clip(init_f, 0.0, 1.0, init_f) alpha = init_f.copy() # exponent for interpolation print(" - Compute fixed MD VF map") init_f_MD = (np.exp(-b * MD) - np.exp(-b * d)) / (np.exp(-b * md_value) - np.exp(-b * d)) np.clip(init_f_MD, 0.01, 0.99, init_f_MD) print(" - Compute min_f and max_f from lmin, lmax") ### This was following Pasternak 2009 although with an error ### Amin = exp(-b*lmax) and Amax = exp(-b*lmin) in that paper # min_f = (signal.min(-1)-np.exp(-b*d)) / (np.exp(-b*lmin)-np.exp(-b*d)) # max_f = (signal.max(-1)-np.exp(-b*d)) / (np.exp(-b*lmax)-np.exp(-b*d)) ### From Pasternak 2009 method, Amin < At implies that the ### term with signal.min(-1) in numerator is the upper bound of f ### although in that paper the equation 6 has fmin and fmax reversed. ### With lmin, lmax=0.1e-3, 2.5e-3, Amin = 0.08, Awater = 0.04 ### and one can see that max_f here will usually be >> 1 min_f = (signal.max(-1) - np.exp(-b * d)) / (np.exp(-b * lmin) - np.exp(-b * d)) max_f = (signal.min(-1) - np.exp(-b * d)) / (np.exp(-b * lmax) - np.exp(-b * d)) # If MD of a voxel is > 3.0e-3, min_f and max_f can be negative. # These voxels should be initialized as 0 np.clip(min_f, 0.0, 1.0, min_f) np.clip(max_f, 0.0, 1.0, max_f) np.clip(init_f, min_f, max_f, init_f) if interpolate: print(" - Interpolate two estimates of volume fraction") # f = tissue fraction. with init_f high, alpha will be ~1 and init_f_MD will be weighted init_f = (np.power(init_f, (1 - alpha))) * (np.power(init_f_MD, alpha)) elif fixed_MD: print( " - Using fixed MD value of {0} for inital volume fraction".format( md_value)) init_f = init_f_MD else: print(" - Using lmin and lmax for initial volume fraction") np.clip(init_f, 0.05, 0.99, init_f) # want minimum 5% of tissue init_f[np.isnan(init_f)] = 0.5 init_f[np.logical_not(mask)] = 0.5 print(" - Compute initial tissue tensor ...") signal[np.isnan(signal)] = 0 bvecs = bvecs[indices_dwis] bvals = bvals[indices_dwis] signal_free_water = np.exp(-bvals * d) corrected_signal = (signal - (1 - init_f[..., np.newaxis]) \ * signal_free_water[np.newaxis, np.newaxis, np.newaxis, :]) \ / (init_f[..., np.newaxis]) np.clip(corrected_signal, 1.0e-3, 1. - 1.0e-3, corrected_signal) log_signal = np.log(corrected_signal) gtab = gradient_table_from_bvals_bvecs(bvals, bvecs) H = dti.design_matrix(gtab)[:, :6] pseudo_inv = np.dot(np.linalg.inv(np.dot(H.T, H)), H.T) init_tensor = np.dot(log_signal, pseudo_inv.T) dti_params = dti.eig_from_lo_tri(init_tensor).reshape( (dim_x, dim_y, dim_z, 4, 3)) evals = dti_params[..., 0, :] evecs = dti_params[..., 1:, :] if evals_lmin > 0.1e-3: print(" - Fatten tensor to {}".format(evals_lmin)) lower_triangular = clip_tensor_evals(evals, evecs, evals_lmin, evals_lmax) lower_triangular[np.logical_not(mask)] = [ evals_lmin, 0, evals_lmin, 0, 0, evals_lmin ] nan_mask = np.any(np.isnan(lower_triangular), axis=-1) lower_triangular[nan_mask] = [evals_lmin, 0, evals_lmin, 0, 0, evals_lmin] init_tensor = lower_triangular[:, :, :, np.newaxis, :] return init_f, init_tensor
def segment_from_dwi(image, bvals_file, bvecs_file, ROI, threshold, mask=None, filename=None, overwrite=True): """ Takes a dwi, bvals and bvecs files and computes FA, RGB and a binary mask estimation of the supplied ROI according to a threshold on the RGB. """ # Load raw dwi image data = image.get_data() affine = image.get_affine() # Load bval and bvec files, fit the tensor model print("Now fitting tensor model") b_vals, b_vecs = read_bvals_bvecs(bvals_file, bvecs_file) gtab = gradient_table_from_bvals_bvecs(b_vals, b_vecs) tenmodel = TensorModel(gtab) tenfit = tenmodel.fit(data) FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 FA = np.clip( FA, 0, 1) # We clamp the FA between 0 and 1 to remove degenerate tensors if mask is not None: FA = apply_mask(FA, mask) FA_vol = nib.Nifti1Image(FA.astype('float32'), affine) if filename is None: FA_path = 'FA.nii.gz' else: FA_path = filename + '_FA.nii.gz' # Check if FA already exists if os.path.exists(FA_path): print("FA", FA_path, "already exists!") if overwrite is True: nib.save(FA_vol, FA_path) print("FA", FA_path, "was overwritten") else: print("New FA was not saved") else: nib.save(FA_vol, FA_path) print("FA was saved as ", FA_path) RGB = color_fa(FA, tenfit.evecs) if filename is None: RGB_path = 'RGB.nii.gz' else: RGB_path = filename + '_RGB.nii.gz' RGB_vol = nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine) # Check if RGB already exists if os.path.exists(RGB_path): print("RGB", RGB_path, "already exists!") if overwrite is True: nib.save(RGB_vol, RGB_path) print("RGB", RGB_path, "was overwritten") else: print("New RGB was not saved") else: nib.save(RGB_vol, RGB_path) print("RGB was saved as ", RGB_path) return segment_from_RGB(RGB, ROI, threshold)
def test_TensorModel(): data, gtab = dsi_voxels() dm = dti.TensorModel(gtab, 'LS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.5, True) dm = dti.TensorModel(gtab, 'WLS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.5, True) sphere = create_unit_sphere(4) assert_equal(len(dtifit.odf(sphere)), len(sphere.vertices)) assert_almost_equal(dtifit.fa, gfa(dtifit.odf(sphere)), 1) # Check that the multivoxel case works: dtifit = dm.fit(data) # And smoke-test that all these operations return sensibly-shaped arrays: assert_equal(dtifit.fa.shape, data.shape[:3]) assert_equal(dtifit.ad.shape, data.shape[:3]) assert_equal(dtifit.md.shape, data.shape[:3]) assert_equal(dtifit.rd.shape, data.shape[:3]) assert_equal(dtifit.trace.shape, data.shape[:3]) assert_equal(dtifit.mode.shape, data.shape[:3]) assert_equal(dtifit.linearity.shape, data.shape[:3]) assert_equal(dtifit.planarity.shape, data.shape[:3]) assert_equal(dtifit.sphericity.shape, data.shape[:3]) # Test for the shape of the mask assert_raises(ValueError, dm.fit, np.ones((10, 10, 3)), np.ones((3,3))) # Make some synthetic data b0 = 1000. bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) # The first b value is 0., so we take the second one: B = bvals[1] # Scale the eigenvalues and tensor by the B value so the units match D = np.array([1., 1., 1., 0., 0., 1., -np.log(b0) * B]) / B evals = np.array([2., 1., 0.]) / B md = evals.mean() tensor = from_lower_triangular(D) A_squiggle = tensor - (1 / 3.0) * np.trace(tensor) * np.eye(3) mode = 3 * np.sqrt(6) * np.linalg.det(A_squiggle / np.linalg.norm(A_squiggle)) evecs = np.linalg.eigh(tensor)[1] # Design Matrix X = dti.design_matrix(gtab) # Signals Y = np.exp(np.dot(X, D)) assert_almost_equal(Y[0], b0) Y.shape = (-1,) + Y.shape # Test fitting with different methods: for fit_method in ['OLS', 'WLS', 'NLLS']: tensor_model = dti.TensorModel(gtab, fit_method=fit_method) tensor_fit = tensor_model.fit(Y) assert_true(tensor_fit.model is tensor_model) assert_equal(tensor_fit.shape, Y.shape[:-1]) assert_array_almost_equal(tensor_fit.evals[0], evals) assert_array_almost_equal(tensor_fit.quadratic_form[0], tensor, err_msg=\ "Calculation of tensor from Y does not compare to analytical solution") assert_almost_equal(tensor_fit.md[0], md) assert_array_almost_equal(tensor_fit.mode, mode, decimal=5) assert_equal(tensor_fit.directions.shape[-2], 1) assert_equal(tensor_fit.directions.shape[-1], 3) # Test error-handling: assert_raises(ValueError, dti.TensorModel, gtab, fit_method='crazy_method')
def test_tensor_model(): fdata, fbval, fbvec = get_data('small_25') data1 = nib.load(fdata).get_data() gtab1 = grad.gradient_table(fbval, fbvec) data2, gtab2 = dsi_voxels() for data, gtab in zip([data1, data2], [gtab1, gtab2]): dm = dti.TensorModel(gtab, 'LS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.9, True) dm = dti.TensorModel(gtab, 'WLS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.9, True) assert_equal(dtifit.fa > 0, True) sphere = create_unit_sphere(4) assert_equal(len(dtifit.odf(sphere)), len(sphere.vertices)) # Check that the multivoxel case works: dtifit = dm.fit(data) # Check that it works on signal that has already been normalized to S0: dm_to_relative = dti.TensorModel(gtab) if np.any(gtab.b0s_mask): relative_data = (data[0, 0, 0] / np.mean(data[0, 0, 0, gtab.b0s_mask])) dtifit_to_relative = dm_to_relative.fit(relative_data) npt.assert_almost_equal(dtifit.fa[0, 0, 0], dtifit_to_relative.fa, decimal=3) # And smoke-test that all these operations return sensibly-shaped arrays: assert_equal(dtifit.fa.shape, data.shape[:3]) assert_equal(dtifit.ad.shape, data.shape[:3]) assert_equal(dtifit.md.shape, data.shape[:3]) assert_equal(dtifit.rd.shape, data.shape[:3]) assert_equal(dtifit.trace.shape, data.shape[:3]) assert_equal(dtifit.mode.shape, data.shape[:3]) assert_equal(dtifit.linearity.shape, data.shape[:3]) assert_equal(dtifit.planarity.shape, data.shape[:3]) assert_equal(dtifit.sphericity.shape, data.shape[:3]) # Test for the shape of the mask assert_raises(ValueError, dm.fit, np.ones((10, 10, 3)), np.ones((3, 3))) # Make some synthetic data b0 = 1000. bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) # The first b value is 0., so we take the second one: B = bvals[1] # Scale the eigenvalues and tensor by the B value so the units match D = np.array([1., 1., 1., 0., 0., 1., -np.log(b0) * B]) / B evals = np.array([2., 1., 0.]) / B md = evals.mean() tensor = from_lower_triangular(D) A_squiggle = tensor - (1 / 3.0) * np.trace(tensor) * np.eye(3) mode = 3 * np.sqrt(6) * np.linalg.det( A_squiggle / np.linalg.norm(A_squiggle)) evecs = np.linalg.eigh(tensor)[1] # Design Matrix X = dti.design_matrix(gtab) # Signals Y = np.exp(np.dot(X, D)) assert_almost_equal(Y[0], b0) Y.shape = (-1, ) + Y.shape # Test fitting with different methods: for fit_method in ['OLS', 'WLS', 'NLLS']: tensor_model = dti.TensorModel(gtab, fit_method=fit_method) tensor_fit = tensor_model.fit(Y) assert_true(tensor_fit.model is tensor_model) assert_equal(tensor_fit.shape, Y.shape[:-1]) assert_array_almost_equal(tensor_fit.evals[0], evals) assert_array_almost_equal(tensor_fit.quadratic_form[0], tensor, err_msg=\ "Calculation of tensor from Y does not compare to analytical solution") assert_almost_equal(tensor_fit.md[0], md) assert_array_almost_equal(tensor_fit.mode, mode, decimal=5) assert_equal(tensor_fit.directions.shape[-2], 1) assert_equal(tensor_fit.directions.shape[-1], 3) # Test error-handling: assert_raises(ValueError, dti.TensorModel, gtab, fit_method='crazy_method') # Test multi-voxel data data = np.zeros((3, Y.shape[1])) # Normal voxel data[0] = Y # High diffusion voxel, all diffusing weighted signal equal to zero data[1, gtab.b0s_mask] = b0 data[1, ~gtab.b0s_mask] = 0 # Masked voxel, all data set to zero data[2] = 0. tensor_model = dti.TensorModel(gtab) fit = tensor_model.fit(data) assert_array_almost_equal(fit[0].evals, evals) # Evals should be high for high diffusion voxel assert_(all(fit[1].evals > evals[0] * .9)) # Evals should be zero where data is masked assert_array_almost_equal(fit[2].evals, 0.)
def test_tensor_model(): fdata, fbval, fbvec = get_data('small_25') data = nib.load(fdata).get_data() gtab = grad.gradient_table(fbval, fbvec) dm = dti.TensorModel(gtab, 'LS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.9, True) dm = dti.TensorModel(gtab, 'WLS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.9, True) assert_equal(dtifit.fa > 0, True) sphere = create_unit_sphere(4) assert_equal(len(dtifit.odf(sphere)), len(sphere.vertices)) # Check that the multivoxel case works: dtifit = dm.fit(data) # Check that it works on signal that has already been normalized to S0: dm_to_relative = dti.TensorModel(gtab) relative_data = (data[0, 0, 0]/np.mean(data[0, 0, 0, gtab.b0s_mask])) dtifit_to_relative = dm_to_relative.fit(relative_data) npt.assert_almost_equal(dtifit.fa[0,0,0], dtifit_to_relative.fa, decimal=3) # And smoke-test that all these operations return sensibly-shaped arrays: assert_equal(dtifit.fa.shape, data.shape[:3]) assert_equal(dtifit.ad.shape, data.shape[:3]) assert_equal(dtifit.md.shape, data.shape[:3]) assert_equal(dtifit.rd.shape, data.shape[:3]) assert_equal(dtifit.trace.shape, data.shape[:3]) assert_equal(dtifit.mode.shape, data.shape[:3]) assert_equal(dtifit.linearity.shape, data.shape[:3]) assert_equal(dtifit.planarity.shape, data.shape[:3]) assert_equal(dtifit.sphericity.shape, data.shape[:3]) # Test for the shape of the mask assert_raises(ValueError, dm.fit, np.ones((10, 10, 3)), np.ones((3,3))) # Make some synthetic data b0 = 1000. bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) # The first b value is 0., so we take the second one: B = bvals[1] # Scale the eigenvalues and tensor by the B value so the units match D = np.array([1., 1., 1., 0., 0., 1., -np.log(b0) * B]) / B evals = np.array([2., 1., 0.]) / B md = evals.mean() tensor = from_lower_triangular(D) A_squiggle = tensor - (1 / 3.0) * np.trace(tensor) * np.eye(3) mode = 3 * np.sqrt(6) * np.linalg.det(A_squiggle / np.linalg.norm(A_squiggle)) evecs = np.linalg.eigh(tensor)[1] # Design Matrix X = dti.design_matrix(gtab) # Signals Y = np.exp(np.dot(X, D)) assert_almost_equal(Y[0], b0) Y.shape = (-1,) + Y.shape # Test fitting with different methods: for fit_method in ['OLS', 'WLS', 'NLLS']: tensor_model = dti.TensorModel(gtab, fit_method=fit_method) tensor_fit = tensor_model.fit(Y) assert_true(tensor_fit.model is tensor_model) assert_equal(tensor_fit.shape, Y.shape[:-1]) assert_array_almost_equal(tensor_fit.evals[0], evals) assert_array_almost_equal(tensor_fit.quadratic_form[0], tensor, err_msg=\ "Calculation of tensor from Y does not compare to analytical solution") assert_almost_equal(tensor_fit.md[0], md) assert_array_almost_equal(tensor_fit.mode, mode, decimal=5) assert_equal(tensor_fit.directions.shape[-2], 1) assert_equal(tensor_fit.directions.shape[-1], 3) # Test error-handling: assert_raises(ValueError, dti.TensorModel, gtab, fit_method='crazy_method') # Test multi-voxel data data = np.zeros((3, Y.shape[1])) # Normal voxel data[0] = Y # High diffusion voxel, all diffusing weighted signal equal to zero data[1, gtab.b0s_mask] = b0 data[1, ~gtab.b0s_mask] = 0 # Masked voxel, all data set to zero data[2] = 0. tensor_model = dti.TensorModel(gtab) fit = tensor_model.fit(data) assert_array_almost_equal(fit[0].evals, evals) # Evals should be high for high diffusion voxel assert_(all(fit[1].evals > evals[0] * .9)) # Evals should be zero where data is masked assert_array_almost_equal(fit[2].evals, 0.)
def main(): parser = buildArgsParser() args = parser.parse_args() # Load data img = nib.load(args.input) data = img.get_data() affine = img.get_affine() # Setting suffix savename if args.savename is None: filename = "" else: filename = args.savename + "_" if os.path.exists(filename + 'fa.nii.gz'): if not args.overwrite: raise ValueError("File " + filename + "fa.nii.gz" + " already exists. Use -f option to overwrite.") print (filename + "fa.nii.gz", " already exists and will be overwritten.") if args.mask is not None: mask = nib.load(args.mask).get_data() else: print("No mask specified. Computing mask with median_otsu.") data, mask = median_otsu(data) mask_img = nib.Nifti1Image(mask.astype(np.float32), affine) nib.save(mask_img, filename + 'mask.nii.gz') # Get tensors print('Tensor estimation...') b_vals, b_vecs = read_bvals_bvecs(args.bvals, args.bvecs) gtab = gradient_table_from_bvals_bvecs(b_vals, b_vecs) tenmodel = TensorModel(gtab) tenfit = tenmodel.fit(data, mask) # FA print('Computing FA...') FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 # RGB print('Computing RGB...') FA = np.clip(FA, 0, 1) RGB = color_fa(FA, tenfit.evecs) if args.all : print('Computing Diffusivities...') # diffusivities MD = mean_diffusivity(tenfit.evals) AD = axial_diffusivity(tenfit.evals) RD = radial_diffusivity(tenfit.evals) print('Computing Mode...') MODE = mode(tenfit.quadratic_form) print('Saving tensor coefficients and metrics...') # Get the Tensor values and format them for visualisation in the Fibernavigator. tensor_vals = lower_triangular(tenfit.quadratic_form) correct_order = [0, 1, 3, 2, 4, 5] tensor_vals_reordered = tensor_vals[..., correct_order] fiber_tensors = nib.Nifti1Image(tensor_vals_reordered.astype(np.float32), affine) nib.save(fiber_tensors, filename + 'tensors.nii.gz') # Save - for some reason this is not read properly by the FiberNav md_img = nib.Nifti1Image(MD.astype(np.float32), affine) nib.save(md_img, filename + 'md.nii.gz') ad_img = nib.Nifti1Image(AD.astype(np.float32), affine) nib.save(ad_img, filename + 'ad.nii.gz') rd_img = nib.Nifti1Image(RD.astype(np.float32), affine) nib.save(rd_img, filename + 'rd.nii.gz') mode_img = nib.Nifti1Image(MODE.astype(np.float32), affine) nib.save(mode_img, filename + 'mode.nii.gz') fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) nib.save(fa_img, filename + 'fa.nii.gz') rgb_img = nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine) nib.save(rgb_img, filename + 'rgb.nii.gz')
def gradient_descent(dwis, bvals, bvecs, mask, init_f, init_tensor, niters=50): ''' Optimize the volume fraction and the tensor via gradient descent. ''' dim_x, dim_y, dim_z = mask.shape indices_dwis = (bvals > 0) nb_dwis = np.count_nonzero(indices_dwis) indices_b0 = (bvals == 0) nb_b0 = np.count_nonzero(indices_b0) b = bvals.max() b0 = dwis[..., indices_b0].mean(-1) signal = dwis[..., indices_dwis] / b0[..., None] np.clip(signal, 1.0e-6, 1 - 1.0e-6, signal) bvals = bvals[indices_dwis] bvecs = bvecs[indices_dwis] gtab = gradient_table_from_bvals_bvecs(bvals, bvecs) H = dti.design_matrix(gtab)[:, :6] signal[np.logical_not(mask)] = 0. signal = signal[mask] lower_triangular = init_tensor[mask, 0] volume_fraction = init_f[mask] print(" - Begin gradient descent.") mask_nvoxels = np.count_nonzero(mask) step_size = 1.0e-7 weight = 100.0 l_min_loop, l_max_loop = 0.1e-3, 2.5e-3 for i in range(niters): print(" - Iteration {0:d} out of {1:d}.".format(i + 1, niters)) grad1, predicted_signal_tissue, predicted_signal_water = \ grad_data_fit_tensor(lower_triangular, signal, H, bvals, volume_fraction) print("\tgrad1 avg: {0:0.4e}".format(np.mean(np.abs(grad1)))) predicted_signal = volume_fraction[..., None] * predicted_signal_tissue + \ (1-volume_fraction[..., None]) * predicted_signal_water prediction_error = np.sqrt(((predicted_signal - signal)**2).mean(-1)) print("\tpref error avg: {0:0.4e}".format(np.mean(prediction_error))) gradf = (bvals * (predicted_signal - signal) \ * (predicted_signal_tissue - predicted_signal_water)).sum(-1) print("\tgradf avg: {0:0.4e}".format(np.mean(np.abs(gradf)))) volume_fraction -= weight * step_size * gradf grad1[np.isnan(grad1)] = 0 # np.clip(grad1, -1.e5, 1.e5, grad1) np.clip(volume_fraction, 0.01, 0.99, volume_fraction) lower_triangular -= step_size * grad1 lower_triangular[np.isnan(lower_triangular)] = 0 dti_params = dti.eig_from_lo_tri(lower_triangular).reshape( (mask_nvoxels, 4, 3)) evals = dti_params[..., 0, :] evecs = dti_params[..., 1:, :] lower_triangular = clip_tensor_evals(evals, evecs, l_min_loop, l_max_loop) del dti_params, evals, evecs final_tensor = np.zeros((dim_x, dim_y, dim_z, 1, 6), dtype=np.float32) final_tensor[mask, 0] = lower_triangular final_f = np.zeros((dim_x, dim_y, dim_z), dtype=np.float32) final_f[mask] = 1 - volume_fraction return final_f, final_tensor
def test_TensorModel(): data, gtab = dsi_voxels() dm = dti.TensorModel(gtab, 'LS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.5, True) dm = dti.TensorModel(gtab, 'WLS') dtifit = dm.fit(data[0, 0, 0]) assert_equal(dtifit.fa < 0.5, True) sphere = create_unit_sphere(4) assert_equal(len(dtifit.odf(sphere)), len(sphere.vertices)) assert_almost_equal(dtifit.fa, gfa(dtifit.odf(sphere)), 1) # Check that the multivoxel case works: dtifit = dm.fit(data) # And smoke-test that all these operations return sensibly-shaped arrays: assert_equal(dtifit.fa.shape, data.shape[:3]) assert_equal(dtifit.ad.shape, data.shape[:3]) assert_equal(dtifit.md.shape, data.shape[:3]) assert_equal(dtifit.rd.shape, data.shape[:3]) assert_equal(dtifit.trace.shape, data.shape[:3]) assert_equal(dtifit.mode.shape, data.shape[:3]) assert_equal(dtifit.linearity.shape, data.shape[:3]) assert_equal(dtifit.planarity.shape, data.shape[:3]) assert_equal(dtifit.sphericity.shape, data.shape[:3]) # Test for the shape of the mask assert_raises(ValueError, dm.fit, np.ones((10, 10, 3)), np.ones((3, 3))) # Make some synthetic data b0 = 1000. bvecs, bvals = read_bvec_file(get_data('55dir_grad.bvec')) gtab = grad.gradient_table_from_bvals_bvecs(bvals, bvecs.T) # The first b value is 0., so we take the second one: B = bvals[1] # Scale the eigenvalues and tensor by the B value so the units match D = np.array([1., 1., 1., 0., 0., 1., -np.log(b0) * B]) / B evals = np.array([2., 1., 0.]) / B md = evals.mean() tensor = from_lower_triangular(D) A_squiggle = tensor - (1 / 3.0) * np.trace(tensor) * np.eye(3) mode = 3 * np.sqrt(6) * np.linalg.det( A_squiggle / np.linalg.norm(A_squiggle)) evecs = np.linalg.eigh(tensor)[1] # Design Matrix X = dti.design_matrix(gtab) # Signals Y = np.exp(np.dot(X, D)) assert_almost_equal(Y[0], b0) Y.shape = (-1, ) + Y.shape # Test fitting with different methods: for fit_method in ['OLS', 'WLS', 'NLLS']: tensor_model = dti.TensorModel(gtab, fit_method=fit_method) tensor_fit = tensor_model.fit(Y) assert_true(tensor_fit.model is tensor_model) assert_equal(tensor_fit.shape, Y.shape[:-1]) assert_array_almost_equal(tensor_fit.evals[0], evals) assert_array_almost_equal(tensor_fit.quadratic_form[0], tensor, err_msg=\ "Calculation of tensor from Y does not compare to analytical solution") assert_almost_equal(tensor_fit.md[0], md) assert_array_almost_equal(tensor_fit.mode, mode, decimal=5) assert_equal(tensor_fit.directions.shape[-2], 1) assert_equal(tensor_fit.directions.shape[-1], 3) # Test error-handling: assert_raises(ValueError, dti.TensorModel, gtab, fit_method='crazy_method')
def reconstruct( diffusion_file, bvals_file, bvecs_file, warped_segmentation_file, fa_file, adc_file, evecs_file ): ''' Reconstruct a diffusion image by fitting a tensor model. diffusion_file the diffusion image file path to reconstruct bvals_file the b-value file path bvecs_file the b-vector file path warped_segmentation_file the segmentation/mask file path fa_file the fa map output file path adc_file the adc map output file path evecs_file the evecs map output file path ''' # load the input image input_image = nibabel.load( diffusion_file ) # grab the input data data = input_image.get_data() # create a simple mask #mask = data[..., 0] > 50 # create a mask from the segmentation file mask_image = nibabel.load( warped_segmentation_file ).get_data() mask = mask_image[...] > 0 # load the bval and bvec files b_values, b_vectors = dipy.io.read_bvals_bvecs( bvals_file, bvecs_file ) # create a gradient table gradient_table = gradienter.gradient_table_from_bvals_bvecs( b_values, b_vectors, 0, 10 ) # instantiate tensor model tensor_model = reconstructer.TensorModel( gradient_table ) print 'starting fiting..' # perform tensor fitting tensor_fitting = tensor_model.fit( data, mask ) print 'ending fiting..' # create FA map fa_map = tensor_fitting.fa # clean-up FA map fa_map[numpy.isnan( fa_map )] = 0 # create ADC map adc_map = tensor_fitting.md # clean-up ADC map adc_map[numpy.isnan( adc_map )] = 0 # create eigenvector map evecs_map = tensor_fitting.evecs # store the maps nibabel.save( nibabel.Nifti1Image( fa_map, input_image.get_affine() ), fa_file ) nibabel.save( nibabel.Nifti1Image( adc_map, input_image.get_affine() ), adc_file ) nibabel.save( nibabel.Nifti1Image( evecs_map, input_image.get_affine() ), evecs_file )
def image_gradient_consistency_check(dwi_file, bvecs, bvals, b0_threshold=B0_THRESHOLD): """ Check that gradient encoding patterns found in the b-values correspond to those found in the signal intensity variance across volumes of the dwi image. Parameters ---------- dwi_file : str Optionally provide a file path to the diffusion-weighted image series to which the bvecs/bvals should correspond. bvecs : m x n 2d array B-vectors array. bvals : 1d array B-values float array. b0_threshold : float Gradient threshold below which volumes and vectors are considered B0's. """ import nibabel as nib from dipy.core.gradients import gradient_table_from_bvals_bvecs from sklearn.cluster import MeanShift, estimate_bandwidth # Build gradient table object gtab = gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=b0_threshold) # Check that number of image volumes corresponds to the number of gradient encodings. volume_num = nib.load(dwi_file).shape[-1] if not len(bvals) == volume_num: raise Exception( "Expected %d total image samples but found %d total gradient encoding values", volume_num, len(bvals)) # Evaluate B0 locations relative to mean signal variation. data = np.array(nib.load(dwi_file).dataobj) signal_means = [] for vol in range(data.shape[-1]): signal_means.append(np.mean(data[:, :, :, vol])) signal_b0_indices = np.where( signal_means > np.mean(signal_means) + 3 * np.std(signal_means))[0] # Check B0 locations first if not np.allclose(np.where(gtab.b0s_mask == True)[0], signal_b0_indices): raise UserWarning( 'B0 indices in vectors do not correspond to relative high-signal contrast volumes ' 'detected in dwi image.') # Next check number of unique b encodings (i.e. shells) and their indices X = np.array(signal_means).reshape(-1, 1) ms = MeanShift(bandwidth=estimate_bandwidth(X, quantile=0.1), bin_seeding=True) ms.fit(X) labs, idx = np.unique(ms.labels_, return_index=True) ix_img = [] i = -1 for val in range(len(ms.labels_)): if val in np.sort(idx[::-1]): i = i + 1 ix_img.append(labs[i]) ix_img = np.array(ix_img) ix_vec = [] i = -1 for val in range(len(bvals)): if bvals[val] != bvals[val - 1]: i = i + 1 ix_vec.append(i) ix_vec = np.array(ix_vec) if len(ms.cluster_centers_) != len(np.unique(bvals)): raise UserWarning( 'Number of unique b-values does not correspond to number of unique signal gradient ' 'encoding intensities in the dwi image.') if np.all(np.isclose(ix_img, ix_vec)) is True: raise UserWarning( 'Positions of b-value B0\'s and shell(s) do not correspond to signal intensity ' 'fluctuation patterns in the dwi image.') return
def main(): # parse command line parameters parser = argparse.ArgumentParser(description='Compute the anisotropic \ power map for the given dMRI data set. Computes spherical deconvolution \ from the highest b-balue. ') parser.add_argument('data', help='[IN] 4D input data volume') parser.add_argument('bval', help='[IN] text file with b-values (FSL format)') parser.add_argument('bvec', help='[IN] text file with b-vectors (FSL format)') parser.add_argument('mask', help='[IN] 3D mask volume') parser.add_argument( '--power', default=2, type=float, help='[IN] The degree to which power maps are calculated') parser.add_argument('out', help='[OUT] name of file for anisotropic power map') parser.add_argument( '--attenuation', '-a', action='store_true', default=False, help='use signal attenuation instead of full signal amplitude') parser.add_argument('--n_processes', '-j', default=8, type=int, help='Number of parallel processes') parser.add_argument('--verbose', '-v', action='store_true', default=False, help='be verbose') args = parser.parse_args() data_file_name = args.data bval_file_name = args.bval bvec_file_name = args.bvec mask_file_name = args.mask power = args.power out_file_name = args.out signal_attenuation = args.attenuation nbr_processes = args.n_processes verbose = args.verbose # load b-values and b-vectors if verbose: print('loading b-values and b-vectors') bvals, bvecs = read_bvals_bvecs(bval_file_name, bvec_file_name) # round b-values to 50s bvals = np.round(bvals / 50.0) * 50 b_thresh = np.max(bvals) - 10 selected_volumes = np.asarray( [np.any(tup) for tup in zip(bvals > b_thresh, bvals < 50)]) gtab = gradient_table_from_bvals_bvecs(bvals[selected_volumes], bvecs[selected_volumes], atol=0.1) if verbose: print(gtab.bvals) # load the diffusion data if verbose: print('loading mask') img = nib.load(mask_file_name) mask = img.get_data() # load the diffusion data if verbose: print('loading diffusion data') img = nib.load(data_file_name) affine = img.get_affine() data = img.get_data(caching='unchanged')[..., selected_volumes] if signal_attenuation: # compute signal attenuation if verbose: print('computing signal attenuation') mean_b0 = np.mean(data[..., gtab.b0s_mask], axis=3) # vectorize ! for vol in range(data.shape[3]): data[..., vol] = data[..., vol] / mean_b0 if verbose: print('ensuring valid numbers') data = np.nan_to_num(data) data[data < 0] = 0 # compute anisotropic power map if verbose: print('compute anisotropic power map') apm = anisotropic_power_map(data, mask, gtab, power=power, nbr_processes=nbr_processes, verbose=verbose) # save anisotropic power map img = nib.Nifti1Image(apm, affine) nib.save(img, out_file_name)
def main(): params = readArgs() # read in from the command line read_args = params.collect_args() params.check_args(read_args) # get img obj dwi_img = nib.load(params.dwi_) mask_img = nib.load(params.mask_) from dipy.io import read_bvals_bvecs bvals, bvecs = read_bvals_bvecs(params.bval_, params.bvec_) # need to create the gradient table yo from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs, b0_threshold=25) # get the data from image objects dwi_data = dwi_img.get_data() mask_data = mask_img.get_data() # and get affine img_affine = dwi_img.affine from dipy.data import get_sphere sphere = get_sphere('repulsion724') from dipy.segment.mask import applymask dwi_data = applymask(dwi_data, mask_data) printfl('dwi_data.shape (%d, %d, %d, %d)' % dwi_data.shape) printfl('\nYour bvecs look like this:{0}'.format(bvecs)) printfl('\nYour bvals look like this:{0}\n'.format(bvals)) from dipy.reconst.shm import anisotropic_power, sph_harm_lookup, smooth_pinv, normalize_data from dipy.core.sphere import HemiSphere smooth = 0.0 normed_data = normalize_data(dwi_data, gtab.b0s_mask) normed_data = normed_data[..., np.where(1 - gtab.b0s_mask)[0]] from dipy.core.gradients import gradient_table_from_bvals_bvecs gtab2 = gradient_table_from_bvals_bvecs( gtab.bvals[np.where(1 - gtab.b0s_mask)[0]], gtab.bvecs[np.where(1 - gtab.b0s_mask)[0]]) signal_native_pts = HemiSphere(xyz=gtab2.bvecs) sph_harm_basis = sph_harm_lookup.get(None) Ba, m, n = sph_harm_basis(params.sh_order_, signal_native_pts.theta, signal_native_pts.phi) L = -n * (n + 1) invB = smooth_pinv(Ba, np.sqrt(smooth) * L) # fit SH basis to DWI signal normed_data_sh = np.dot(normed_data, invB.T) # power map call printfl("fitting power map") pow_map = anisotropic_power(normed_data_sh, norm_factor=0.00001, power=2, non_negative=True) pow_map_img = nib.Nifti1Image(pow_map.astype(np.float32), img_affine) # make output name out_name = ''.join( [params.output_, '_powMap_sh', str(params.sh_order_), '.nii.gz']) printfl("writing power map to: {}".format(out_name)) nib.save(pow_map_img, out_name)
def test_reorient_bvecs(): sq2 = np.sqrt(2) / 2 bvals = np.concatenate([[0], np.ones(6) * 1000]) bvecs = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1], [sq2, sq2, 0], [sq2, 0, sq2], [0, sq2, sq2]]) gt = gradient_table_from_bvals_bvecs(bvals, bvecs, b0_threshold=0) # The simple case: all affines are identity affs = np.zeros((6, 4, 4)) for i in range(4): affs[:, i, i] = 1 # We should get back the same b-vectors new_gt = reorient_bvecs(gt, affs) npt.assert_equal(gt.bvecs, new_gt.bvecs) # Now apply some rotations rotation_affines = [] rotated_bvecs = bvecs[:] for i in np.where(~gt.b0s_mask)[0]: rot_ang = np.random.rand() cos_rot = np.cos(rot_ang) sin_rot = np.sin(rot_ang) rotation_affines.append( np.array([[1, 0, 0, 0], [0, cos_rot, -sin_rot, 0], [0, sin_rot, cos_rot, 0], [0, 0, 0, 1]])) rotated_bvecs[i] = np.dot(rotation_affines[-1][:3, :3], bvecs[i]) # Copy over the rotation affines full_affines = rotation_affines[:] # And add some scaling and translation to each one to make this harder for i in range(len(full_affines)): full_affines[i] = np.dot( full_affines[i], np.array([[2.5, 0, 0, -10], [0, 2.2, 0, 20], [0, 0, 1, 0], [0, 0, 0, 1]])) gt_rot = gradient_table_from_bvals_bvecs(bvals, rotated_bvecs, b0_threshold=0) new_gt = reorient_bvecs(gt_rot, full_affines) # At the end of all this, we should be able to recover the original # vectors npt.assert_almost_equal(gt.bvecs, new_gt.bvecs) # We should be able to pass just the 3-by-3 rotation components to the same # effect new_gt = reorient_bvecs(gt_rot, np.array(rotation_affines)[:, :3, :3]) npt.assert_almost_equal(gt.bvecs, new_gt.bvecs) # Verify that giving the wrong number of affines raises an error: full_affines.append(np.zeros((4, 4))) npt.assert_raises(ValueError, reorient_bvecs, gt_rot, full_affines) # Shear components in the matrix need to be decomposed into rotation only, # and should not lead to scaling of the bvecs shear_affines = [] for i in np.where(~gt.b0s_mask)[0]: shear_affines.append( np.array([[1, 0, 1, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])) # atol is set to 1 here to do the scaling verification here, # so that the reorient_bvecs function does not throw an error itself new_gt = reorient_bvecs(gt, np.array(shear_affines)[:, :3, :3], atol=1) bvecs_close_to_1 = abs(vector_norm(new_gt.bvecs[~gt.b0s_mask]) - 1) <= 0.001 npt.assert_(np.all(bvecs_close_to_1))