def test_spdot(): n = 100 m = 20 k = 10 A = np.random.randn(n, m) B = np.random.randn(m, k) A_sparse = sps.csr_matrix(A) B_sparse = sps.csr_matrix(B) dense_dot = np.dot(A, B) # Try all the different variations: npt.assert_array_almost_equal(dense_dot, spdot(A_sparse, B_sparse).todense()) npt.assert_array_almost_equal(dense_dot, spdot(A, B_sparse)) npt.assert_array_almost_equal(dense_dot, spdot(A_sparse, B))
def test_FiberFit(): data_file, bval_file, bvec_file = dpd.get_fnames('small_64D') data_ni = nib.load(data_file) data = data_ni.get_data() bvals, bvecs = read_bvals_bvecs(bval_file, bvec_file) gtab = grad.gradient_table(bvals, bvecs) FM = life.FiberModel(gtab) evals = [0.0015, 0.0005, 0.0005] streamline = [[[1, 2, 3], [4, 5, 3], [5, 6, 3], [6, 7, 3]], [[1, 2, 3], [4, 5, 3], [5, 6, 3]]] fiber_matrix, vox_coords = FM.setup(streamline, np.eye(4), evals) w = np.array([0.5, 0.5]) sig = opt.spdot(fiber_matrix, w) + 1.0 # Add some isotropic stuff S0 = data[..., gtab.b0s_mask] this_data = np.zeros((10, 10, 10, 64)) this_data[vox_coords[:, 0], vox_coords[:, 1], vox_coords[:, 2]] =\ (sig.reshape((4, 64)) * S0[vox_coords[:, 0], vox_coords[:, 1], vox_coords[:, 2]]) # Grab some realistic S0 values: this_data = np.concatenate([data[..., gtab.b0s_mask], this_data], -1) fit = FM.fit(this_data, streamline, np.eye(4)) npt.assert_almost_equal(fit.predict()[1], fit.data[1], decimal=-1) # Predict with an input GradientTable npt.assert_almost_equal(fit.predict(gtab)[1], fit.data[1], decimal=-1) npt.assert_almost_equal( this_data[vox_coords[:, 0], vox_coords[:, 1], vox_coords[:, 2]], fit.data)
def eval_method(name=None, method=None, track_path=None, data_path=None): if track_path == None: track_path = './Result/Track/tractogram_' + method + '_' + name + '.trk' if data_path == None: data_path = './data/DWI/' + name + '/' if not op.exists(track_path): print('no tracking') return 0 else: from dipy.io.gradients import read_bvals_bvecs from dipy.io.image import load_nifti_data, load_nifti from dipy.core.gradients import gradient_table data, affine, hardi_img = load_nifti(data_path + 'norm.nii.gz', return_img=True) print(data.shape) labels = load_nifti_data(data_path + 'seg.nii.gz') # t1_data = load_nifti_data('./data/tanenci_20170601/b0.nii.gz') bvals, bvecs = read_bvals_bvecs(data_path + 'DWI.bval', data_path + 'DWI.bvec') gtab = gradient_table(bvals, bvecs) # Read the candidates from file in voxel space: candidate_sl_sft = load_trk(track_path, 'same', bbox_valid_check=False) candidate_sl_sft.to_vox() candidate_sl = candidate_sl_sft.streamlines print('loading finished, begin weighting') fiber_model = life.FiberModel(gtab) inv_affine = np.linalg.inv(hardi_img.affine) fiber_fit = fiber_model.fit(data, reduct(candidate_sl, data[:, :, :, 0]), affine=np.eye(4)) print('weighting finished, begin prediction') beta_baseline = np.zeros(fiber_fit.beta.shape[0]) pred_weighted = np.reshape( opt.spdot(fiber_fit.life_matrix, beta_baseline), (fiber_fit.vox_coords.shape[0], np.sum(~gtab.b0s_mask))) model_predict = fiber_fit.predict() model_error = model_predict - fiber_fit.data model_rmse = np.sqrt(np.mean(model_error[:, 10:]**2, -1)) #print('model_rmse:', model_rmse.shape) vol_model = np.zeros(data.shape[:3]) * np.nan vol_model[fiber_fit.vox_coords[:, 0], fiber_fit.vox_coords[:, 1], fiber_fit.vox_coords[:, 2]] = model_rmse #print('error:', np.sum(vol_model) / model_rmse.shape[0]) return np.sum(model_rmse) / model_rmse.shape[0], vol_model, affine
def run_LIFE_all(data, gtab, streamlines): import dipy.tracking.life as life import dipy.core.optimize as opt fiber_model = life.FiberModel(gtab) fiber_fit = fiber_model.fit(data, streamlines, affine=np.eye(4)) streamlines_filt = list(np.array(streamlines)[np.where(fiber_fit.beta > 0)[0]]) beta_baseline = np.zeros(fiber_fit.beta.shape[0]) pred_weighted = np.reshape(opt.spdot(fiber_fit.life_matrix, beta_baseline), (fiber_fit.vox_coords.shape[0], np.sum(~gtab.b0s_mask))) mean_pred = np.empty((fiber_fit.vox_coords.shape[0], gtab.bvals.shape[0])) S0 = fiber_fit.b0_signal mean_pred[..., gtab.b0s_mask] = S0[:, None] mean_pred[..., ~gtab.b0s_mask] = (pred_weighted + fiber_fit.mean_signal[:, None]) * S0[:, None] mean_error = mean_pred - fiber_fit.data mean_rmse = np.sqrt(np.mean(mean_error ** 2, -1)) return streamlines_filt, mean_rmse
def predict(self, gtab=None, S0=None): """ Predict the signal Parameters ---------- gtab : GradientTable Default: use self.gtab S0 : float or array The non-diffusion-weighted signal in the voxels for which a prediction is made. Default: use self.b0_signal Returns ------- prediction : ndarray of shape (voxels, bvecs) An array with a prediction of the signal in each voxel/direction """ # We generate the prediction and in each voxel, we add the # offset, according to the isotropic part of the signal, which was # removed prior to fitting: if gtab is None: _matrix = self.life_matrix gtab = self.model.gtab else: _model = FiberModel(gtab) _matrix, _ = _model.setup(self.streamline, self.affine, self.evals) pred_weighted = np.reshape(opt.spdot(_matrix, self.beta), (self.vox_coords.shape[0], np.sum(~gtab.b0s_mask))) pred = np.empty((self.vox_coords.shape[0], gtab.bvals.shape[0])) if S0 is None: S0 = self.b0_signal pred[..., gtab.b0s_mask] = S0[:, None] pred[..., ~gtab.b0s_mask] =\ (pred_weighted + self.mean_signal[:, None]) * S0[:, None] return pred
def run_LIFE_all(data, gtab, streamlines): ''' Filters tractography streamlines using Linear Fascicle Evaluation (LiFE). Parameters ---------- data : array 4D numpy array of diffusion image data. gtab : Obj DiPy object storing diffusion gradient information. streamlines : ArraySequence DiPy list/array-like object of streamline points from tractography. Returns ------- streamlines_filt : ArraySequence DiPy list/array-like object of filtered streamline fibers with positive beta-coefficients after fitting LiFE model. mean_rmse : float Root Mean-Squared Error (RMSE) when using LiFE-filtered fibers to predict diffusion data. ''' import dipy.tracking.life as life import dipy.core.optimize as opt fiber_model = life.FiberModel(gtab) fiber_fit = fiber_model.fit(data, streamlines, affine=np.eye(4)) streamlines_filt = list( np.array(streamlines)[np.where(fiber_fit.beta > 0)[0]]) beta_baseline = np.zeros(fiber_fit.beta.shape[0]) pred_weighted = np.reshape( opt.spdot(fiber_fit.life_matrix, beta_baseline), (fiber_fit.vox_coords.shape[0], np.sum(~gtab.b0s_mask))) mean_pred = np.empty((fiber_fit.vox_coords.shape[0], gtab.bvals.shape[0])) S0 = fiber_fit.b0_signal mean_pred[..., gtab.b0s_mask] = S0[:, None] mean_pred[..., ~gtab.b0s_mask] = (pred_weighted + fiber_fit.mean_signal[:, None]) * S0[:, None] mean_error = mean_pred - fiber_fit.data mean_rmse = np.sqrt(np.mean(mean_error**2, -1)) return streamlines_filt, mean_rmse
def test_FiberFit(): data_file, bval_file, bvec_file = dpd.get_data('small_64D') data_ni = nib.load(data_file) data = data_ni.get_data() data_aff = data_ni.affine bvals, bvecs = (np.load(f) for f in (bval_file, bvec_file)) gtab = dpg.gradient_table(bvals, bvecs) FM = life.FiberModel(gtab) evals = [0.0015, 0.0005, 0.0005] streamline = [[[1, 2, 3], [4, 5, 3], [5, 6, 3], [6, 7, 3]], [[1, 2, 3], [4, 5, 3], [5, 6, 3]]] fiber_matrix, vox_coords = FM.setup(streamline, None, evals) w = np.array([0.5, 0.5]) sig = opt.spdot(fiber_matrix, w) + 1.0 # Add some isotropic stuff S0 = data[..., gtab.b0s_mask] rel_sig = data[..., ~gtab.b0s_mask]/data[..., gtab.b0s_mask] this_data = np.zeros((10, 10, 10, 64)) this_data[vox_coords[:, 0], vox_coords[:, 1], vox_coords[:, 2]] =\ (sig.reshape((4, 64)) * S0[vox_coords[:, 0], vox_coords[:, 1], vox_coords[:, 2]]) # Grab some realistic S0 values: this_data = np.concatenate([data[..., gtab.b0s_mask], this_data], -1) fit = FM.fit(this_data, streamline) npt.assert_almost_equal(fit.predict()[1], fit.data[1], decimal=-1) # Predict with an input GradientTable npt.assert_almost_equal(fit.predict(gtab)[1], fit.data[1], decimal=-1) npt.assert_almost_equal( this_data[vox_coords[:, 0], vox_coords[:, 1], vox_coords[:, 2]], fit.data)
""" model_error = model_predict - fiber_fit.data model_rmse = np.sqrt(np.mean(model_error[:, 10:]**2, -1)) """ As a baseline against which we can compare, we calculate another error term. In this case, we assume that the weight for each streamline is equal to zero. This produces the naive prediction of the mean of the signal in each voxel. """ beta_baseline = np.zeros(fiber_fit.beta.shape[0]) pred_weighted = np.reshape( opt.spdot(fiber_fit.life_matrix, beta_baseline), (fiber_fit.vox_coords.shape[0], np.sum(~gtab.b0s_mask))) mean_pred = np.empty((fiber_fit.vox_coords.shape[0], gtab.bvals.shape[0])) S0 = fiber_fit.b0_signal """ Since the fitting is done in the demeaned S/S0 domain, we need to add back the mean and then multiply by S0 in every voxel: """ mean_pred[..., gtab.b0s_mask] = S0[:, None] mean_pred[..., ~gtab.b0s_mask] =\ (pred_weighted + fiber_fit.mean_signal[:, None]) * S0[:, None] mean_error = mean_pred - fiber_fit.data mean_rmse = np.sqrt(np.mean(mean_error**2, -1))
""" model_error = model_predict - fiber_fit.data model_rmse = np.sqrt(np.mean(model_error[:, 10:] ** 2, -1)) """ As a baseline against which we can compare, we calculate another error term. In this case, we assume that the weight for each streamline is equal to zero. This produces the naive prediction of the mean of the signal in each voxel. """ beta_baseline = np.zeros(fiber_fit.beta.shape[0]) pred_weighted = np.reshape(opt.spdot(fiber_fit.life_matrix, beta_baseline), (fiber_fit.vox_coords.shape[0], np.sum(~gtab.b0s_mask))) mean_pred = np.empty((fiber_fit.vox_coords.shape[0], gtab.bvals.shape[0])) S0 = fiber_fit.b0_signal """ Since the fitting is done in the demeaned S/S0 domain, we need to add back the mean and then multiply by S0 in every voxel: """ mean_pred[..., gtab.b0s_mask] = S0[:, None] mean_pred[..., ~gtab.b0s_mask] =\ (pred_weighted + fiber_fit.mean_signal[:, None]) * S0[:, None]
def evaluate_streamline_plausibility(dwi_data, gtab, mask_data, streamlines, affine=np.eye(4), sphere='repulsion724'): """ Linear Fascicle Evaluation (LiFE) takes any connectome and uses a forward modelling approach to predict diffusion measurements in the same brain. Parameters ---------- dwi_data : array 4D array of dwi data. gtab : Obj DiPy object storing diffusion gradient information. mask_data : array 3D Brain mask. streamlines : ArraySequence DiPy list/array-like object of streamline points from tractography. Returns ------- streamlines : ArraySequence DiPy list/array-like object of streamline points from tractography. References ---------- .. [1] Pestilli, F., Yeatman, J, Rokem, A. Kay, K. and Wandell B.A. (2014). Validation and statistical inference in living connectomes. Nature Methods 11: 1058-1063. doi:10.1038/nmeth.3098 """ import dipy.tracking.life as life import dipy.core.optimize as opt from dipy.tracking._utils import _mapping_to_voxel # from dipy.data import get_sphere from dipy.tracking import utils from dipy.tracking.streamline import Streamlines original_count = len(streamlines) streamlines_long = nib.streamlines. \ array_sequence.ArraySequence( [ s for s in streamlines if len(s) >= float(10) ] ) print('Removing streamlines with negative voxel indices...') # Remove any streamlines with negative voxel indices lin_T, offset = _mapping_to_voxel(np.eye(4)) streamlines_positive = [] for sl in streamlines_long: inds = np.dot(sl, lin_T) inds += offset if not inds.min().round(decimals=6) < 0: streamlines_positive.append(sl) del streamlines_long # Filter resulting streamlines by those that stay entirely # inside the ROI of interest mask_data = np.array(mask_data, dtype=bool, copy=False) streamlines_in_brain = Streamlines( utils.target(streamlines_positive, np.eye(4), mask_data, include=True)) streamlines_in_brain = [i for i in streamlines_in_brain] del streamlines_positive print('Fitting fiber model...') # ! Remember this 4d masking function ! data_in_mask = np.nan_to_num( np.broadcast_to(mask_data[..., None], dwi_data.shape).astype('bool') * dwi_data) # ! Remember this 4d masking function ! fiber_model = life.FiberModel(gtab) fiber_fit = fiber_model.fit(data_in_mask, streamlines_in_brain, affine=affine, sphere=False) # sphere = get_sphere(sphere) # fiber_fit = fiber_model.fit(data_in_mask, streamlines_in_brain, # affine=affine, # sphere=sphere) streamlines = list( np.array(streamlines_in_brain)[np.where(fiber_fit.beta > 0)[0]]) pruned_count = len(streamlines) if pruned_count == 0: print( UserWarning('\nWarning LiFE skipped due to implausible values ' 'detected in model betas. This does not ' 'necessarily invalidate the ' 'tractography. Rather it could indicate that ' 'you\'ve sampled too few streamlines, or that the ' 'sampling scheme is simply incompatible with the ' 'LiFE model. Is your acquisition hemispheric? ' 'Also check the gradient table for errors. \n')) return streamlines_in_brain else: del streamlines_in_brain model_predict = fiber_fit.predict() model_error = model_predict - fiber_fit.data model_rmse = np.sqrt(np.mean(model_error[:, 10:]**2, -1)) beta_baseline = np.zeros(fiber_fit.beta.shape[0]) pred_weighted = np.reshape( opt.spdot(fiber_fit.life_matrix, beta_baseline), (fiber_fit.vox_coords.shape[0], np.sum(~gtab.b0s_mask))) mean_pred = np.empty((fiber_fit.vox_coords.shape[0], gtab.bvals.shape[0])) S0 = fiber_fit.b0_signal mean_pred[..., gtab.b0s_mask] = S0[:, None] mean_pred[..., ~gtab.b0s_mask] = (pred_weighted + fiber_fit.mean_signal[:, None]) * S0[:, None] mean_error = mean_pred - fiber_fit.data mean_rmse = np.sqrt(np.mean(mean_error**2, -1)) print(f"Original # Streamlines: {original_count}") print(f"Final # Streamlines: {pruned_count}") print(f"Streamlines removed: {pruned_count - original_count}") print(f"Mean RMSE: {np.mean(mean_rmse)}") print(f"Mean Model RMSE: {np.mean(model_rmse)}") print(f"Mean Reduction RMSE: {np.mean(mean_rmse - model_rmse)}") return streamlines
model_error = model_predict - fiber_fit.data model_rmse = np.sqrt(np.mean(model_error[:, 10:] ** 2, -1)) """ As a baseline against which we can compare, we calculate another error term. In this case, we assume that the weight for each streamline is equal to zero. This produces the naive prediction of the mean of the signal in each voxel. """ beta_baseline = np.zeros(fiber_fit.beta.shape[0]) pred_weighted = np.reshape( opt.spdot(fiber_fit.life_matrix, beta_baseline), (fiber_fit.vox_coords.shape[0], np.sum(~gtab.b0s_mask)) ) mean_pred = np.empty((fiber_fit.vox_coords.shape[0], gtab.bvals.shape[0])) S0 = fiber_fit.b0_signal """ Since the fitting is done in the demeaned S/S0 domain, we need to add back the mean and then multiply by S0 in every voxel: """ mean_pred[..., gtab.b0s_mask] = S0[:, None] mean_pred[..., ~gtab.b0s_mask] = (pred_weighted + fiber_fit.mean_signal[:, None]) * S0[:, None] mean_error = mean_pred - fiber_fit.data mean_rmse = np.sqrt(np.mean(mean_error ** 2, -1))
def LiFEvaluation(dwidata, trk_streamlines, gtab, subject="lifesubj", header=None, roimask=None, affine=None, display=True, outpathpickle=None, outpathtrk=None, processes=1, outpathfig=None, strproperty="", verbose=None): """ Implementation of Linear Fascicle Evaluation, outputs histograms, evals Parameters ---------- dwidata : array output trk filename trkdata : array gtab : array og bval & bvec table outpath: string folder location for resulting values and figures display : boolean, optional Condition to display the results (default = False) savefig: boolean, optional Condition to save the results in outpath (default = True) Defined by Pestilli, F., Yeatman, J, Rokem, A. Kay, K. and Wandell B.A. (2014). Validation and statistical inference in living connectomes and recreated by Dipy :param dwidata: array of diffusion data :param trkdata: array of tractography data obtained from dwi :param gtab: bval & bvec table :param outpath: location to save analysis outputs :param display: :param savefig: :return: """ """""" """ if not op.exists('lr-superiorfrontal.trk'): else: # We'll need to know where the corpus callosum is from these variables: from dipy.data import (read_stanford_labels, fetch_stanford_t1, read_stanford_t1) hardi_img, gtab, labels_img = read_stanford_labels() labels = labels_img.get_data() cc_slice = labels == 2 fetch_stanford_t1() t1 = read_stanford_t1() t1_data = t1.get_data() data = hardi_img.get_data() """ """""" # Read the candidates from file in voxel space: if roimask is None: roimask = dwidata > 0 else: dwidataroi = dwidata * np.repeat( roimask[:, :, :, None], np.shape(dwidata)[3], axis=3) print("verbose: " + str(verbose) + " outpathpickle: " + str(outpathpickle)) fiber_model = life.FiberModel(gtab) # inv_affine must be used if the streamlines are in the world space, and thus we must useapply the inverse affine of dwi #when comparing the diffusion directions gtab and the voxels of trk #inv_affine = np.linalg.inv(hardi_img.affine) #fiber_fit will fit the streamlines to the original diffusion data and if verbose: txt = "Begin the evaluation over " + str( np.size(trk_streamlines)) + " streamlines" print(txt) send_mail(txt, subject="LifE start msg ") fiber_fit = fiber_model.fit(dwidata, trk_streamlines, affine=np.eye(4), processes=processes, verbose=verbose) #fiber_fit_roi = fiber_model.fit(dwidataroi, trk_streamlines, affine=np.eye(4), processes=processes, verbose=verbose) optimized_sl = list( np.array(trk_streamlines)[np.where(fiber_fit.beta > 0)[0]]) plt.ioff() if verbose: txt = "End of the evaluation over " + str(np.size(trk_streamlines)) print(txt) send_mail(txt, subject="LifE status msg ") if outpathtrk is not None: outpathfile = str( outpathtrk) + subject + strproperty + "_lifeopt_test.trk" myheader = create_tractogram_header(outpathfile, *header) optimized_sl_gen = lambda: (s for s in optimized_sl) save_trk_heavy_duty(outpathfile, streamlines=optimized_sl_gen, affine=affine, header=myheader) txt = ("Saved final trk at " + outpathfile) print(txt) send_mail(txt, subject="LifE save msg ") """ except TypeError: txt=('Could not save new tractogram file, header of original trk file not properly implemented into ' 'LifEvaluation') print(txt) send_mail(txt,subject="LifE error msg ") """ """ if interactive: ren = window.Renderer() ren.add(actor.streamtube(optimized_sl, cmap.line_colors(optimized_sl))) ren.add(ROI_actor) #ren.add(vol_actor) if interactive: window.show(ren) if outpathfig is not None: print("reached windowrecord") window.record(ren, n_frames=1, out_path=outpathfig +'_life_optimized.png', size=(800, 800)) print("did window record") """ maxsize_var = 20525023825 sizebeta = getsize(fiber_fit.beta) if sizebeta < maxsize_var: picklepath = outpathpickle + subject + strproperty + '_beta.p' txt = ("fiber_fit.beta saved at " + picklepath) pickle.dump(fiber_fit.beta, open(picklepath, "wb")) if verbose: print(txt) send_mail(txt, subject="LifE save msg ") else: txt = ( "Object fiber_fit.beta exceeded the imposed the 20GB limit with a size of: " + str(sizebeta / (10 ^ 9)) + "GB") print(txt) send_mail(txt, subject="LifE error msg") sizecoords = getsize(fiber_fit.vox_coords) if sizecoords < maxsize_var: picklepath = outpathpickle + subject + strproperty + '_voxcoords.p' txt = ("fiber_fit.voxcoords saved at " + picklepath) pickle.dump(fiber_fit.vox_coords, open(picklepath, "wb")) if verbose: print(txt) send_mail(txt, subject="LifE save msg ") else: txt = ( "Object fiber_fit.beta exceeded the imposed the 20GB limit with a size of: " + str(sizebeta / (10 ^ 9)) + "GB") print(txt) send_mail(txt, subject="LifE error msg") #predict diffusion data based on new model model_predict = fiber_fit.predict( ) #possible to predict based on different gtab or base signal (change gtab, S0) model_error = model_predict - fiber_fit.data #compare original dwi data and the model fit, calculate error model_rmse = np.sqrt( np.mean(model_error[:, 10:]**2, -1)) #this is good, but must check ways to avoid overfitting #how does the model get built? add lasso? JS beta_baseline = np.zeros( fiber_fit.beta.shape[0] ) #baseline assumption where the streamlines weight is 0 pred_weighted = np.reshape( opt.spdot(fiber_fit.life_matrix, beta_baseline), (fiber_fit.vox_coords.shape[0], np.sum(~gtab.b0s_mask))) mean_pred = np.empty((fiber_fit.vox_coords.shape[0], gtab.bvals.shape[0])) S0 = fiber_fit.b0_signal mean_pred[..., gtab.b0s_mask] = S0[:, None] mean_pred[..., ~gtab.b0s_mask] = \ (pred_weighted + fiber_fit.mean_signal[:, None]) * S0[:, None] mean_error = mean_pred - fiber_fit.data mean_rmse = np.sqrt(np.mean(mean_error**2, -1)) size_meanrmse = getsize(mean_rmse) if size_meanrmse < maxsize_var: picklepath = outpathpickle + subject + strproperty + '_mean_rmse.p' txt = ("mean_rmse saved at " + picklepath) pickle.dump(mean_rmse, open(picklepath, "wb")) if verbose: print(txt) send_mail(txt, subject="LifE save msg ") else: txt = ( "Object mean_rmse exceeded the imposed the 20GB limit with a size of: " + str(size_meanrmse / (10 ^ 9)) + " GB") print(txt) send_mail(txt, subject="LifE error msg") size_modelrmse = getsize(model_rmse) if size_modelrmse < maxsize_var: picklepath = outpathpickle + subject + strproperty + '_model_rmse.p' txt = ("model_rmse saved at " + picklepath) pickle.dump(model_rmse, open(picklepath, "wb")) if verbose: print(txt) send_mail(txt, subject="LifE save msg ") else: txt = ( "Object model_rmse exceeded the imposed the 20GB limit with a size of: " + str(size_modelrmse / (10 ^ 9)) + " GB") print(txt) send_mail(txt, subject="LifE error msg") if outpathfig is not None: try: import matplotlib.pyplot as myplot fig, ax = plt.subplots(1) ax.hist(fiber_fit.beta, bins=100, histtype='step') LifEcreate_fig(fiber_fit.beta, mean_rmse, model_rmse, fiber_fit.vox_coords, dwidata, subject, t1_data=dwidata[:, :, :, 0], outpathfig=outpathfig, interactive=False, strproperty=strproperty, verbose=verbose) except: print( "Coult not launch life create fig, possibly qsub location (this is a template warning, to be improved upon" ) return model_error, mean_error
def life(dwifile, bvecsfile, bvalsfile, tractogramfile, outdir, display_tracks=False, verbose=0): """ Linear fascicle evaluation (LiFE) Evaluating the results of tractography algorithms is one of the biggest challenges for diffusion MRI. One proposal for evaluation of tractography results is to use a forward model that predicts the signal from each of a set of streamlines, and then fit a linear model to these simultaneous prediction. Parameters ---------- dwifile: str the path to the diffusion dataset. bvecsfile: str the path to the diffusion b-vectors. bvalsfile: str the path to the diffusion b-values. tractogramfile: str the path to the tractogram. outdir: str the destination folder. display_tracks: bool, default False if True render the tracks. verbose: int, default 0 the verbosity level. Returns ------- life_weights_file: str a file containing the fiber track weights. life_weights_snap: str a snap with the distrubution of weights. spatial_error_file: str the model root mean square error. tracks_snap: str a snap with the tracks. """ # Load diffusion data and tractogram bvecs = numpy.loadtxt(bvecsfile) bvals = numpy.loadtxt(bvalsfile) gtab = gradient_table(bvals, bvecs) im = nibabel.load(dwifile) data = im.get_data() trk = nibabel.streamlines.load(tractogramfile) if verbose > 0: print("[info] Diffusion shape: {0}".format(data.shape)) print("[info] Number of tracks: {0}".format(len(trk.streamlines))) # Express the tractogram in voxel coordiantes inv_affine = numpy.linalg.inv(trk.affine) trk = [ numpy.dot( numpy.concatenate( ( streamline, numpy.ones( (len(streamline), 1) ) ), axis=1 ), inv_affine) for streamline in trk.streamlines] trk = [track[..., :3] for track in trk] # Create a viewer tracks_snap = None if display_tracks: nb_tracks = len(trk) if nb_tracks < 5000: downsampling = 1 else: downsampling = nb_tracks // 5000 tracks_snap = os.path.join(outdir, "tracks.png") streamlines_actor = fvtk.line(trk[::downsampling], line_colors(trk[::downsampling])) vol_actor = fvtk.slicer(data[..., 0]) vol_actor.display(data.shape[0] // 2, None, None) ren = fvtk.ren() fvtk.add(ren, streamlines_actor) fvtk.add(ren, vol_actor) fvtk.record(ren, n_frames=1, out_path=tracks_snap, size=(800, 800)) if verbose > 1: fvtk.show(ren) # Fit the Life model and save the associated weights fiber_model = dpilife.FiberModel(gtab) fiber_fit = fiber_model.fit(data, trk, affine=numpy.eye(4)) life_weights = fiber_fit.beta life_weights_file = os.path.join(outdir, "life_weights.txt") numpy.savetxt(life_weights_file, life_weights) life_weights_snap = os.path.join(outdir, "life_weights.png") fig, ax = plt.subplots(1) ax.hist(life_weights, bins=100, histtype="step") ax.set_xlabel("Fiber weights") ax.set_ylabel("# Fibers") fig.savefig(life_weights_snap) # Invert the model and predict back either the data that was used to fit # the model: compute the prediction error of the diffusion-weighted data # and calculate the root of the mean squared error. model_predict = fiber_fit.predict() model_error = model_predict - fiber_fit.data model_rmse = numpy.sqrt(numpy.mean(model_error[:, 10:] ** 2, -1)) data_rmse = numpy.ones(data.shape[:3]) * numpy.nan data_rmse[fiber_fit.vox_coords[:, 0], fiber_fit.vox_coords[:, 1], fiber_fit.vox_coords[:, 2]] = model_rmse model_error_file = os.path.join(outdir, "model_rmse.nii.gz") error_im = nibabel.Nifti1Image(data_rmse, im.affine) nibabel.save(error_im, model_error_file) # As a baseline against which we can compare, we assume that the weight # for each streamline is equal to zero. This produces the naive prediction # of the mean of the signal in each voxel. life_weights_baseline = numpy.zeros(life_weights.shape[0]) pred_weighted = numpy.reshape( opt.spdot(fiber_fit.life_matrix, life_weights_baseline), (fiber_fit.vox_coords.shape[0], numpy.sum(~gtab.b0s_mask))) mean_pred = numpy.empty( (fiber_fit.vox_coords.shape[0], gtab.bvals.shape[0])) S0 = fiber_fit.b0_signal # Since the fitting is done in the demeaned S/S0 domain, we need to add # back the mean and then multiply by S0 in every voxels. mean_pred[..., gtab.b0s_mask] = S0[:, None] mean_pred[..., ~gtab.b0s_mask] = ( (pred_weighted + fiber_fit.mean_signal[:, None]) * S0[:, None]) mean_error = mean_pred - fiber_fit.data mean_rmse = numpy.sqrt(numpy.mean(mean_error ** 2, -1)) data_rmse = numpy.ones(data.shape[:3]) * numpy.nan data_rmse[fiber_fit.vox_coords[:, 0], fiber_fit.vox_coords[:, 1], fiber_fit.vox_coords[:, 2]] = mean_rmse mean_error_file = os.path.join(outdir, "mean_rmse.nii.gz") error_im = nibabel.Nifti1Image(data_rmse, im.affine) nibabel.save(error_im, mean_error_file) # Compute the improvment array data_rmse = numpy.ones(data.shape[:3]) * numpy.nan data_rmse[fiber_fit.vox_coords[:, 0], fiber_fit.vox_coords[:, 1], fiber_fit.vox_coords[:, 2]] = mean_rmse - model_rmse improvment_error_file = os.path.join(outdir, "improvment_rmse.nii.gz") error_im = nibabel.Nifti1Image(data_rmse, im.affine) nibabel.save(error_im, improvment_error_file) return (life_weights_file, life_weights_snap, model_error_file, mean_error_file, improvment_error_file, tracks_snap)