def tens_mod_fa_est(gtab_file, dwi_file, B0_mask): """ Estimate a tensor FA image to use for registrations. Parameters ---------- gtab_file : str File path to pickled DiPy gradient table object. dwi_file : str File path to diffusion weighted image. B0_mask : str File path to B0 brain mask. Returns ------- fa_path : str File path to FA Nifti1Image. B0_mask : str File path to B0 brain mask Nifti1Image. gtab_file : str File path to pickled DiPy gradient table object. dwi_file : str File path to diffusion weighted Nifti1Image. fa_md_path : str File path to FA/MD mask Nifti1Image. """ import os from dipy.io import load_pickle from dipy.reconst.dti import TensorModel from dipy.reconst.dti import fractional_anisotropy, mean_diffusivity gtab = load_pickle(gtab_file) data = nib.load(dwi_file).get_fdata() print("Generating tensor FA image to use for registrations...") nodif_B0_img = nib.load(B0_mask) nodif_B0_mask_data = np.nan_to_num(np.asarray( nodif_B0_img.dataobj)).astype("bool") model = TensorModel(gtab) mod = model.fit(data, nodif_B0_mask_data) FA = fractional_anisotropy(mod.evals) MD = mean_diffusivity(mod.evals) FA_MD = np.logical_or(FA >= 0.2, (np.logical_and(FA >= 0.08, MD >= 0.0011))) FA[np.isnan(FA)] = 0 FA_MD[np.isnan(FA_MD)] = 0 fa_path = f"{os.path.dirname(B0_mask)}{'/tensor_fa.nii.gz'}" nib.save(nib.Nifti1Image(FA.astype(np.float32), nodif_B0_img.affine), fa_path) fa_md_path = f"{os.path.dirname(B0_mask)}{'/tensor_fa_md.nii.gz'}" nib.save(nib.Nifti1Image(FA_MD.astype(np.float32), nodif_B0_img.affine), fa_md_path) nodif_B0_img.uncache() del FA, FA_MD return fa_path, B0_mask, gtab_file, dwi_file
def get_FA_MD(): time0 = time.time() data, affine = load_nifti('normalized_pDWI.nii.gz') bvals, bvecs = read_bvals_bvecs('DWI.bval', 'DWI.bvec') gtab = gradient_table(bvals, bvecs) #head_mask = load_nifti_data(data_path + '/' + brain_mask) print(data.shape) print('begin modeling!, time:', time.time() - time0) tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(data) from dipy.reconst.dti import fractional_anisotropy print('begin calculating FA!, time:', time.time() - time0) FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 #FA = FA * head_mask save_nifti('FA.nii.gz', FA.astype(np.float32), affine) # print('begin calculating MD!, time:', time.time() - time0) MD1 = dti.mean_diffusivity(tenfit.evals) #MD1 = MD1*head_mask save_nifti('MD.nii.gz', MD1.astype(np.float32), affine) print('Over!, time:', time.time() - time0) return FA, MD1
def test_diffusivities(): psphere = get_sphere('symmetric362') bvecs = np.concatenate(([[0, 0, 0]], psphere.vertices)) bvals = np.zeros(len(bvecs)) + 1000 bvals[0] = 0 gtab = grad.gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0001], [0.0015, 0.0003, 0.0003])) mevecs = [np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]), np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]])] S = single_tensor(gtab, 100, mevals[0], mevecs[0], snr=None) dm = dti.TensorModel(gtab, 'LS') dmfit = dm.fit(S) md = mean_diffusivity(dmfit.evals) Trace = trace(dmfit.evals) rd = radial_diffusivity(dmfit.evals) ad = axial_diffusivity(dmfit.evals) lin = linearity(dmfit.evals) plan = planarity(dmfit.evals) spher = sphericity(dmfit.evals) assert_almost_equal(md, (0.0015 + 0.0003 + 0.0001) / 3) assert_almost_equal(Trace, (0.0015 + 0.0003 + 0.0001)) assert_almost_equal(ad, 0.0015) assert_almost_equal(rd, (0.0003 + 0.0001) / 2) assert_almost_equal(lin, (0.0015 - 0.0003)/Trace) assert_almost_equal(plan, 2 * (0.0003 - 0.0001)/Trace) assert_almost_equal(spher, (3 * 0.0001)/Trace)
def compute_dti(fname_in, fname_bvals, fname_bvecs, prefix): """ Compute DTI. :param fname_in: input 4d file. :param bvals: bvals txt file :param bvecs: bvecs txt file :param prefix: output prefix. Example: "dti_" :return: True/False """ # Open file. from msct_image import Image nii = Image(fname_in) data = nii.data print('data.shape (%d, %d, %d, %d)' % data.shape) # open bvecs/bvals from dipy.io import read_bvals_bvecs bvals, bvecs = read_bvals_bvecs(fname_bvals, fname_bvecs) from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs) # # mask and crop the data. This is a quick way to avoid calculating Tensors on the background of the image. # from dipy.segment.mask import median_otsu # maskdata, mask = median_otsu(data, 3, 1, True, vol_idx=range(10, 50), dilate=2) # print('maskdata.shape (%d, %d, %d, %d)' % maskdata.shape) # fit tensor model import dipy.reconst.dti as dti tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(data) # Compute metrics printv('Computing metrics...', param.verbose) # FA from dipy.reconst.dti import fractional_anisotropy nii.data = fractional_anisotropy(tenfit.evals) nii.setFileName(prefix+'FA.nii.gz') nii.save('float32') # MD from dipy.reconst.dti import mean_diffusivity nii.data = mean_diffusivity(tenfit.evals) nii.setFileName(prefix+'MD.nii.gz') nii.save('float32') # RD from dipy.reconst.dti import radial_diffusivity nii.data = radial_diffusivity(tenfit.evals) nii.setFileName(prefix+'RD.nii.gz') nii.save('float32') # AD from dipy.reconst.dti import axial_diffusivity nii.data = axial_diffusivity(tenfit.evals) nii.setFileName(prefix+'AD.nii.gz') nii.save('float32') return True
def compute_dti(fname_in, fname_bvals, fname_bvecs, prefix): """ Compute DTI. :param fname_in: input 4d file. :param bvals: bvals txt file :param bvecs: bvecs txt file :param prefix: output prefix. Example: "dti_" :return: True/False """ # Open file. from msct_image import Image nii = Image(fname_in) data = nii.data print('data.shape (%d, %d, %d, %d)' % data.shape) # open bvecs/bvals from dipy.io import read_bvals_bvecs bvals, bvecs = read_bvals_bvecs(fname_bvals, fname_bvecs) from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs) # # mask and crop the data. This is a quick way to avoid calculating Tensors on the background of the image. # from dipy.segment.mask import median_otsu # maskdata, mask = median_otsu(data, 3, 1, True, vol_idx=range(10, 50), dilate=2) # print('maskdata.shape (%d, %d, %d, %d)' % maskdata.shape) # fit tensor model import dipy.reconst.dti as dti tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(data) # Compute metrics printv('Computing metrics...', param.verbose) # FA from dipy.reconst.dti import fractional_anisotropy nii.data = fractional_anisotropy(tenfit.evals) nii.setFileName(prefix + 'FA.nii.gz') nii.save('float32') # MD from dipy.reconst.dti import mean_diffusivity nii.data = mean_diffusivity(tenfit.evals) nii.setFileName(prefix + 'MD.nii.gz') nii.save('float32') # RD from dipy.reconst.dti import radial_diffusivity nii.data = radial_diffusivity(tenfit.evals) nii.setFileName(prefix + 'RD.nii.gz') nii.save('float32') # AD from dipy.reconst.dti import axial_diffusivity nii.data = axial_diffusivity(tenfit.evals) nii.setFileName(prefix + 'AD.nii.gz') nii.save('float32') return True
def compute_anisotropy(self, tenfit: dti.TensorFit): print("Computing anisotropy measures (FA, MD, RGB)") mother_dir = os.path.dirname(self.denoised) FA = dti.fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 fa_img = nib.Nifti1Image(FA.astype(np.float32), self.img.affine) nib.save(fa_img, f"{mother_dir}/tensor_fa.nii.gz") evecs_img = nib.Nifti1Image(tenfit.evecs.astype(np.float32), self.img.affine) nib.save(evecs_img, f"{mother_dir}/tensor_evecs.nii.gz") MD1 = dti.mean_diffusivity(tenfit.evals) MD1_img = nib.Nifti1Image(MD1.astype(np.float32), self.img.affine) nib.save(MD1_img, f"{mother_dir}/tensors_md.nii.gz") FA = np.clip(FA, 0, 1) RGB = dti.color_fa(FA, tenfit.evecs) RGB_img = nib.Nifti1Image(np.array(255 * RGB, "uint8"), self.img.affine) nib.save(RGB_img, f"{mother_dir}/tensor_rgb.nii.gz")
def DTImaps(ImgPath, Bvalpath, Bvecpath, tracto=True): data, affine = resli(ImgPath) data = Nonlocal(data, affine) b0_mask, mask = otsu(data, affine) #maask binary evals, evecs = DTImodel(b0_mask, affine, mask, gtab(Bvalpath, Bvecpath)) print('--> Calculando el mapa de anisotropia fraccional') FA = fractional_anisotropy(evals) FA[np.isnan(FA)] = 0 nib.save(nib.Nifti1Image(FA.astype(np.float32), affine), "Mapa_anisotropia_fraccional") print('--> Calculando el mapa de anisotropia fraccional RGB') FA2 = np.clip(FA, 0, 1) RGB = color_fa(FA2, evecs) nib.save(nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine), "Mapa_anisotropia_fraccional RGB") print('--> Calculando el mapa de difusividad media') MD1 = dti.mean_diffusivity(evals) nib.save(nib.Nifti1Image(MD1.astype(np.float32), affine), "Mapa_difusividad_media") if tracto: sphere = get_sphere('symmetric724') peak_indices = quantize_evecs(evecs, sphere.vertices) eu = EuDX(FA.astype('f8'), peak_indices, seeds=500000, odf_vertices=sphere.vertices, a_low=0.15) tensor_streamlines = [streamline for streamline in eu] new_vox_sz = (2., 2., 2.) hdr = nib.trackvis.empty_header() hdr['voxel_size'] = new_vox_sz hdr['voxel_order'] = 'LAS' hdr['dim'] = FA.shape tensor_streamlines_trk = ((sl, None, None) for sl in tensor_streamlines) ten_sl_fname = "Tracto.trk" nib.trackvis.write(ten_sl_fname, tensor_streamlines_trk, hdr, points_space='voxel') return FA
def csd_response(gtab, data): """ Estimate response function for given HARDI data. Unfortunately, does not work for 2d (synthetic) data (why?). """ tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(data, mask=data[..., 0] > 200) FA = fractional_anisotropy(tenfit.evals) MD = dti.mean_diffusivity(tenfit.evals) wm_mask = (np.logical_or(FA >= 0.4, (np.logical_and(FA >= 0.15, MD >= 0.0011)))) response = recursive_response(gtab, data, mask=wm_mask, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=True) return response
def calculate_scalars(tensor_data, mask): ''' Calculate the scalar images from the tensor returns: FA, MD, TR, AX, RAD ''' mask = np.asarray(mask, dtype=np.bool) shape = mask.shape data = dti.from_lower_triangular(tensor_data[mask]) w, v = dti.decompose_tensor(data) w = np.squeeze(w) v = np.squeeze(v) md = np.zeros(shape) md[mask] = dti.mean_diffusivity(w, axis=-1) fa = np.zeros(shape) fa[mask] = dti.fractional_anisotropy(w, axis=-1) tr = np.zeros(shape) tr[mask] = dti.trace(w, axis=-1) ax = np.zeros(shape) ax[mask] = dti.axial_diffusivity(w, axis=-1) rad = np.zeros(shape) rad[mask] = dti.radial_diffusivity(w, axis=-1) return fa, md, tr, ax, rad
def dodata(f_name, data_path): dipy_home = pjoin(os.path.expanduser('~'), 'dipy_data') folder = pjoin(dipy_home, data_path) fraw = pjoin(folder, f_name + '.nii.gz') fbval = pjoin(folder, f_name + '.bval') fbvec = pjoin(folder, f_name + '.bvec') flabels = pjoin(folder, f_name + '.nii-label.nii.gz') bvals, bvecs = read_bvals_bvecs(fbval, fbvec) gtab = gradient_table(bvals, bvecs) img = nib.load(fraw) data = img.get_data() affine = img.get_affine() label_img = nib.load(flabels) labels = label_img.get_data() lap = through_label_sl.label_position(labels, labelValue=1) dataslice = data[40:80, 20:80, lap[2][2] / 2] #print lap[2][2]/2 #get_csd_gfa(f_name,data,gtab,dataslice) maskdata, mask = median_otsu(data, 2, 1, False, vol_idx=range(10, 50), dilate=2) #不去背景 """ get fa and tensor evecs and ODF""" from dipy.reconst.dti import TensorModel, mean_diffusivity tenmodel = TensorModel(gtab) tenfit = tenmodel.fit(data, mask) sphere = get_sphere('symmetric724') FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 np.save(os.getcwd() + '\zhibiao' + f_name + '_FA.npy', FA) fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) nib.save(fa_img, os.getcwd() + '\zhibiao' + f_name + '_FA.nii.gz') print('Saving "DTI_tensor_fa.nii.gz" sucessful.') evecs_img = nib.Nifti1Image(tenfit.evecs.astype(np.float32), affine) nib.save(evecs_img, os.getcwd() + '\zhibiao' + f_name + '_DTI_tensor_evecs.nii.gz') print('Saving "DTI_tensor_evecs.nii.gz" sucessful.') MD1 = mean_diffusivity(tenfit.evals) nib.save(nib.Nifti1Image(MD1.astype(np.float32), img.get_affine()), os.getcwd() + '\zhibiao' + f_name + '_MD.nii.gz') #tensor_odfs = tenmodel.fit(data[20:50, 55:85, 38:39]).odf(sphere) #from dipy.reconst.odf import gfa #dti_gfa=gfa(tensor_odfs) wm_mask = (np.logical_or(FA >= 0.4, (np.logical_and(FA >= 0.15, MD >= 0.0011)))) response = recursive_response(gtab, data, mask=wm_mask, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=False) from dipy.reconst.csdeconv import ConstrainedSphericalDeconvModel csd_model = ConstrainedSphericalDeconvModel(gtab, response) #csd_fit = csd_model.fit(data) from dipy.direction import peaks_from_model csd_peaks = peaks_from_model(model=csd_model, data=data, sphere=sphere, relative_peak_threshold=.5, min_separation_angle=25, parallel=False) GFA = csd_peaks.gfa nib.save(GFA, os.getcwd() + '\zhibiao' + f_name + '_MSD.nii.gz') print('Saving "GFA.nii.gz" sucessful.') from dipy.reconst.shore import ShoreModel asm = ShoreModel(gtab) print('Calculating...SHORE msd') asmfit = asm.fit(data, mask) msd = asmfit.msd() msd[np.isnan(msd)] = 0 #print GFA[:,:,slice].T print('Saving msd_img.png') nib.save(msd, os.getcwd() + '\zhibiao' + f_name + '_GFA.nii.gz')
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 _run_interface(self, runtime): from dipy.core.gradients import GradientTable from dipy.reconst.dti import fractional_anisotropy, mean_diffusivity from dipy.reconst.csdeconv import recursive_response, auto_response img = nb.load(self.inputs.in_file) imref = nb.four_to_three(img)[0] affine = img.affine if isdefined(self.inputs.in_mask): msk = nb.load(self.inputs.in_mask).get_data() msk[msk > 0] = 1 msk[msk < 0] = 0 else: msk = np.ones(imref.shape) data = img.get_data().astype(np.float32) gtab = self._get_gradient_table() evals = np.nan_to_num(nb.load(self.inputs.in_evals).get_data()) FA = np.nan_to_num(fractional_anisotropy(evals)) * msk indices = np.where(FA > self.inputs.fa_thresh) S0s = data[indices][:, np.nonzero(gtab.b0s_mask)[0]] S0 = np.mean(S0s) if self.inputs.auto: response, ratio = auto_response(gtab, data, roi_radius=self.inputs.roi_radius, fa_thr=self.inputs.fa_thresh) response = response[0].tolist() + [S0] elif self.inputs.recursive: MD = np.nan_to_num(mean_diffusivity(evals)) * msk indices = np.logical_or(FA >= 0.4, (np.logical_and(FA >= 0.15, MD >= 0.0011))) data = nb.load(self.inputs.in_file).get_data() response = recursive_response(gtab, data, mask=indices, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=True) ratio = abs(response[1] / response[0]) else: lambdas = evals[indices] l01 = np.sort(np.mean(lambdas, axis=0)) response = np.array([l01[-1], l01[-2], l01[-2], S0]) ratio = abs(response[1] / response[0]) if ratio > 0.25: IFLOGGER.warn( 'Estimated response is not prolate enough. ' 'Ratio=%0.3f.', ratio) elif ratio < 1.e-5 or np.any(np.isnan(response)): response = np.array([1.8e-3, 3.6e-4, 3.6e-4, S0]) IFLOGGER.warn( 'Estimated response is not valid, using a default one') else: IFLOGGER.info('Estimated response: %s', str(response[:3])) np.savetxt(op.abspath(self.inputs.response), response) wm_mask = np.zeros_like(FA) wm_mask[indices] = 1 nb.Nifti1Image(wm_mask.astype(np.uint8), affine, None).to_filename(op.abspath(self.inputs.out_mask)) return runtime
def run_to_estimate_dti_maps(path_input, path_output, file_tensor_fitevals="", file_tensor_fitevecs="", fbval="", fbvec=""): folder = os.path.dirname(path_input) if fbval == "" or fbvec == "": folder_sujeto = path_output for l in os.listdir(folder_sujeto): if "TENSOR" in l and "bval" in l: fbval = os.path.join(folder_sujeto, l) if "TENSOR" in l and "bvec" in l: fbvec = os.path.join(folder_sujeto, l) if file_tensor_fitevals == "" or file_tensor_fitevecs == "": for i in os.listdir(folder): if "DTIEvals" in i: file_tensor_fitevals = os.path.join(folder, i) for i in os.listdir(folder): if "DTIEvecs" in i: file_tensor_fitevecs = os.path.join(folder, i) if not os.path.exists(os.path.join(folder, "list_maps.txt")): # def to_estimate_dti_maps(path_dwi_input, path_output, file_tensor_fitevecs, file_tensor_fitevals): ref_name_only = utils.to_extract_filename(file_tensor_fitevecs) ref_name_only = ref_name_only[:-9] list_maps = [] img_tensorFitevecs = nib.load(file_tensor_fitevecs) img_tensorFitevals = nib.load(file_tensor_fitevals) evecs = img_tensorFitevecs.get_data() evals = img_tensorFitevals.get_data() affine = img_tensorFitevecs.affine print(d.separador + d.separador + 'computing of FA map') FA = fractional_anisotropy(evals) FA[np.isnan(FA)] = 0 nib.save( nib.Nifti1Image(FA.astype(np.float32), affine), os.path.join(path_output, ref_name_only + '_FA' + d.extension)) list_maps.append( os.path.join(path_output, ref_name_only + '_FA' + d.extension)) print(d.separador + d.separador + 'computing of Color FA map') FA2 = np.clip(FA, 0, 1) RGB = color_fa(FA2, evecs) nib.save( nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine), os.path.join(path_output, ref_name_only + '_FA_RGB' + d.extension)) print(d.separador + d.separador + 'computing of MD map') MD = dti.mean_diffusivity(evals) nib.save( nib.Nifti1Image(MD.astype(np.float32), affine), os.path.join(path_output, ref_name_only + '_MD' + d.extension)) list_maps.append( os.path.join(path_output, ref_name_only + '_MD' + d.extension)) print(d.separador + d.separador + 'computing of AD map') AD = dti.axial_diffusivity(evals) nib.save( nib.Nifti1Image(AD.astype(np.float32), affine), os.path.join(path_output, ref_name_only + '_AD' + d.extension)) list_maps.append( os.path.join(path_output, ref_name_only + '_AD' + d.extension)) print(d.separador + d.separador + 'computing of RD map') RD = dti.radial_diffusivity(evals) nib.save( nib.Nifti1Image(RD.astype(np.float32), affine), os.path.join(path_output, ref_name_only + '_RD' + d.extension)) list_maps.append( os.path.join(path_output, ref_name_only + '_RD' + d.extension)) sphere = get_sphere('symmetric724') peak_indices = quantize_evecs(evecs, sphere.vertices) eu = EuDX(FA.astype('f8'), peak_indices, seeds=300000, odf_vertices=sphere.vertices, a_low=0.15) tensor_streamlines = [streamline for streamline in eu] hdr = nib.trackvis.empty_header() hdr['voxel_size'] = nib.load(path_input).header.get_zooms()[:3] hdr['voxel_order'] = 'LAS' hdr['dim'] = FA.shape tensor_streamlines_trk = ((sl, None, None) for sl in tensor_streamlines) nib.trackvis.write(os.path.join( path_output, ref_name_only + '_tractography_EuDx.trk'), tensor_streamlines_trk, hdr, points_space='voxel') print(list_maps) with open(os.path.join(path_output, "list_maps.txt"), "w") as f: for s in list_maps: f.write(str(s) + "\n") return path_input
def nonlinfit_fn(dwi, bvecs, bvals, base_name): import nibabel as nb import numpy as np import os.path as op import dipy.reconst.dti as dti from dipy.core.gradients import GradientTable dwi_img = nb.load(dwi) dwi_data = dwi_img.get_data() dwi_affine = dwi_img.get_affine() from dipy.segment.mask import median_otsu b0_mask, mask = median_otsu(dwi_data, 2, 4) # Mask the data so that tensors are not fit for # unnecessary voxels mask_img = nb.Nifti1Image(mask.astype(np.float32), dwi_affine) b0_imgs = nb.Nifti1Image(b0_mask.astype(np.float32), dwi_affine) b0_img = nb.four_to_three(b0_imgs)[0] out_mask_name = op.abspath(base_name + '_binary_mask.nii.gz') out_b0_name = op.abspath(base_name + '_b0_mask.nii.gz') nb.save(mask_img, out_mask_name) nb.save(b0_img, out_b0_name) # Load the gradient strengths and directions bvals = np.loadtxt(bvals) gradients = np.loadtxt(bvecs) # Dipy wants Nx3 arrays if gradients.shape[0] == 3: gradients = gradients.T assert(gradients.shape[1] == 3) # Place in Dipy's preferred format gtab = GradientTable(gradients) gtab.bvals = bvals # Fit the tensors to the data tenmodel = dti.TensorModel(gtab, fit_method="NLLS") tenfit = tenmodel.fit(dwi_data, mask) # Calculate the fit, fa, and md of each voxel's tensor tensor_data = tenfit.lower_triangular() print('Computing anisotropy measures (FA, MD, RGB)') from dipy.reconst.dti import fractional_anisotropy, color_fa evals = tenfit.evals.astype(np.float32) FA = fractional_anisotropy(np.abs(evals)) FA = np.clip(FA, 0, 1) MD = dti.mean_diffusivity(np.abs(evals)) norm = dti.norm(tenfit.quadratic_form) RGB = color_fa(FA, tenfit.evecs) evecs = tenfit.evecs.astype(np.float32) mode = tenfit.mode.astype(np.float32) mode = np.nan_to_num(mode) # Write tensor as a 4D Nifti image with the original affine tensor_fit_img = nb.Nifti1Image(tensor_data.astype(np.float32), dwi_affine) mode_img = nb.Nifti1Image(mode.astype(np.float32), dwi_affine) norm_img = nb.Nifti1Image(norm.astype(np.float32), dwi_affine) FA_img = nb.Nifti1Image(FA.astype(np.float32), dwi_affine) evecs_img = nb.Nifti1Image(evecs, dwi_affine) evals_img = nb.Nifti1Image(evals, dwi_affine) rgb_img = nb.Nifti1Image(np.array(255 * RGB, 'uint8'), dwi_affine) MD_img = nb.Nifti1Image(MD.astype(np.float32), dwi_affine) out_tensor_file = op.abspath(base_name + "_tensor.nii.gz") out_mode_file = op.abspath(base_name + "_mode.nii.gz") out_fa_file = op.abspath(base_name + "_fa.nii.gz") out_norm_file = op.abspath(base_name + "_norm.nii.gz") out_evals_file = op.abspath(base_name + "_evals.nii.gz") out_evecs_file = op.abspath(base_name + "_evecs.nii.gz") out_rgb_fa_file = op.abspath(base_name + "_rgb_fa.nii.gz") out_md_file = op.abspath(base_name + "_md.nii.gz") nb.save(rgb_img, out_rgb_fa_file) nb.save(norm_img, out_norm_file) nb.save(mode_img, out_mode_file) nb.save(tensor_fit_img, out_tensor_file) nb.save(evecs_img, out_evecs_file) nb.save(evals_img, out_evals_file) nb.save(FA_img, out_fa_file) nb.save(MD_img, out_md_file) print('Tensor fit image saved as {i}'.format(i=out_tensor_file)) print('FA image saved as {i}'.format(i=out_fa_file)) print('MD image saved as {i}'.format(i=out_md_file)) return out_tensor_file, out_fa_file, out_md_file, \ out_evecs_file, out_evals_file, out_rgb_fa_file, out_norm_file, \ out_mode_file, out_mask_name, out_b0_name
def compute_dti(fname_in, fname_bvals, fname_bvecs, prefix, method, file_mask): """ Compute DTI. :param fname_in: input 4d file. :param bvals: bvals txt file :param bvecs: bvecs txt file :param prefix: output prefix. Example: "dti_" :param method: algo for computing dti :return: True/False """ # Open file. from msct_image import Image nii = Image(fname_in) data = nii.data print('data.shape (%d, %d, %d, %d)' % data.shape) # open bvecs/bvals from dipy.io import read_bvals_bvecs bvals, bvecs = read_bvals_bvecs(fname_bvals, fname_bvecs) from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs) # mask and crop the data. This is a quick way to avoid calculating Tensors on the background of the image. if not file_mask == '': printv('Open mask file...', param.verbose) # open mask file nii_mask = Image(file_mask) mask = nii_mask.data # fit tensor model printv('Computing tensor using "'+method+'" method...', param.verbose) import dipy.reconst.dti as dti if method == 'standard': tenmodel = dti.TensorModel(gtab) if file_mask == '': tenfit = tenmodel.fit(data) else: tenfit = tenmodel.fit(data, mask) elif method == 'restore': import dipy.denoise.noise_estimate as ne sigma = ne.estimate_sigma(data) dti_restore = dti.TensorModel(gtab, fit_method='RESTORE', sigma=sigma) if file_mask == '': tenfit = dti_restore.fit(data) else: tenfit = dti_restore.fit(data, mask) # Compute metrics printv('Computing metrics...', param.verbose) # FA from dipy.reconst.dti import fractional_anisotropy nii.data = fractional_anisotropy(tenfit.evals) nii.setFileName(prefix+'FA.nii.gz') nii.save('float32') # MD from dipy.reconst.dti import mean_diffusivity nii.data = mean_diffusivity(tenfit.evals) nii.setFileName(prefix+'MD.nii.gz') nii.save('float32') # RD from dipy.reconst.dti import radial_diffusivity nii.data = radial_diffusivity(tenfit.evals) nii.setFileName(prefix+'RD.nii.gz') nii.save('float32') # AD from dipy.reconst.dti import axial_diffusivity nii.data = axial_diffusivity(tenfit.evals) nii.setFileName(prefix+'AD.nii.gz') nii.save('float32') return True
def diffusion_components(dki_params, sphere='repulsion100', awf=None, mask=None): """ Extracts the restricted and hindered diffusion tensors of well aligned fibers from diffusion kurtosis imaging parameters [1]_. Parameters ---------- dki_params : ndarray (x, y, z, 27) or (n, 27) All parameters estimated from the diffusion kurtosis model. Parameters are ordered as follows: 1) Three diffusion tensor's eigenvalues 2) Three lines of the eigenvector matrix each containing the first, second and third coordinates of the eigenvector 3) Fifteen elements of the kurtosis tensor sphere : Sphere class instance, optional The sphere providing sample directions to sample the restricted and hindered cellular diffusion tensors. For more details see Fieremans et al., 2011. awf : ndarray (optional) Array containing values of the axonal water fraction that has the shape dki_params.shape[:-1]. If not given this will be automatically computed using :func:`axonal_water_fraction`" with function's default precision. mask : ndarray (optional) A boolean array used to mark the coordinates in the data that should be analyzed that has the shape dki_params.shape[:-1] Returns ------- edt : ndarray (x, y, z, 6) or (n, 6) Parameters of the hindered diffusion tensor. idt : ndarray (x, y, z, 6) or (n, 6) Parameters of the restricted diffusion tensor. Notes ----- In the original article of DKI microstructural model [1]_, the hindered and restricted tensors were definde as the intra-cellular and extra-cellular diffusion compartments respectively. References ---------- .. [1] Fieremans E, Jensen JH, Helpern JA, 2011. White matter characterization with diffusional kurtosis imaging. Neuroimage 58(1):177-88. doi: 10.1016/j.neuroimage.2011.06.006 """ shape = dki_params.shape[:-1] # load gradient directions if not isinstance(sphere, dps.Sphere): sphere = get_sphere(sphere) # select voxels where to apply the single fiber model if mask is None: mask = np.ones(shape, dtype='bool') else: if mask.shape != shape: raise ValueError("Mask is not the same shape as dki_params.") else: mask = np.array(mask, dtype=bool, copy=False) # check or compute awf values if awf is None: awf = axonal_water_fraction(dki_params, sphere=sphere, mask=mask) else: if awf.shape != shape: raise ValueError("awf array is not the same shape as dki_params.") # Initialize hindered and restricted diffusion tensors edt_all = np.zeros(shape + (6, )) idt_all = np.zeros(shape + (6, )) # Generate matrix that converts apparent diffusion coefficients to tensors B = np.zeros((sphere.x.size, 6)) B[:, 0] = sphere.x * sphere.x # Bxx B[:, 1] = sphere.x * sphere.y * 2. # Bxy B[:, 2] = sphere.y * sphere.y # Byy B[:, 3] = sphere.x * sphere.z * 2. # Bxz B[:, 4] = sphere.y * sphere.z * 2. # Byz B[:, 5] = sphere.z * sphere.z # Bzz pinvB = np.linalg.pinv(B) # Compute hindered and restricted diffusion tensors for all voxels evals, evecs, kt = split_dki_param(dki_params) dt = lower_triangular(vec_val_vect(evecs, evals)) md = mean_diffusivity(evals) index = ndindex(mask.shape) for idx in index: if not mask[idx]: continue # sample apparent diffusion and kurtosis values di = directional_diffusion(dt[idx], sphere.vertices) ki = directional_kurtosis(dt[idx], md[idx], kt[idx], sphere.vertices, adc=di, min_kurtosis=0) edi = di * (1 + np.sqrt(ki * awf[idx] / (3.0 - 3.0 * awf[idx]))) edt = np.dot(pinvB, edi) edt_all[idx] = edt # We only move on if there is an axonal water fraction. # Otherwise, remaining params are already zero, so move on if awf[idx] == 0: continue # Convert apparent diffusion and kurtosis values to apparent diffusion # values of the hindered and restricted diffusion idi = di * (1 - np.sqrt(ki * (1.0 - awf[idx]) / (3.0 * awf[idx]))) # generate hindered and restricted diffusion tensors idt = np.dot(pinvB, idi) idt_all[idx] = idt return edt_all, idt_all
# --------------------------------------------------------------- print('Fitting the free water DTI model...') # --------------------------------------------------------------- t0 = time.time() fw_params = nls_fit_tensor(gtab, data, mask) dt = time.time() - t0 print("This step took %f seconds to run" % dt) # ---------------------------------------------------------------- print('Compute tensor statistic from the fitted parameters...') # ---------------------------------------------------------------- evals = fw_params[..., :3] FA = dti.fractional_anisotropy(evals) MD = dti.mean_diffusivity(evals) F = fw_params[..., 12] # ---------------------------------------------------------------- print('Compute standard DTI for comparison...') # ---------------------------------------------------------------- dtimodel = dti.TensorModel(gtab) dtifit = dtimodel.fit(data, mask=mask) dti_FA = dtifit.fa dti_MD = dtifit.md # ---------------------------------------------------------------- print('Plot data for a axial slice of the data ...')
def run(self, input_files, bvalues, bvectors, mask_files, b0_threshold=0.0, save_metrics=[], out_dir='', out_tensor='tensors.nii.gz', out_fa='fa.nii.gz', out_ga='ga.nii.gz', out_rgb='rgb.nii.gz', out_md='md.nii.gz', out_ad='ad.nii.gz', out_rd='rd.nii.gz', out_mode='mode.nii.gz', out_evec='evecs.nii.gz', out_eval='evals.nii.gz'): """ Workflow for tensor reconstruction and for computing DTI metrics. Performs a tensor reconstruction on the files by 'globing' ``input_files`` and saves the DTI metrics in a directory specified by ``out_dir``. Parameters ---------- input_files : string Path to the input volumes. This path may contain wildcards to process multiple inputs at once. bvalues : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. bvectors : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. mask_files : string Path to the input masks. This path may contain wildcards to use multiple masks at once. (default: No mask used) b0_threshold : float, optional Threshold used to find b=0 directions (default 0.0) save_metrics : variable string, optional List of metrics to save. Possible values: fa, ga, rgb, md, ad, rd, mode, tensor, evec, eval (default [] (all)) out_dir : string, optional Output directory (default input file directory) out_tensor : string, optional Name of the tensors volume to be saved (default 'tensors.nii.gz') out_fa : string, optional Name of the fractional anisotropy volume to be saved (default 'fa.nii.gz') out_ga : string, optional Name of the geodesic anisotropy volume to be saved (default 'ga.nii.gz') out_rgb : string, optional Name of the color fa volume to be saved (default 'rgb.nii.gz') out_md : string, optional Name of the mean diffusivity volume to be saved (default 'md.nii.gz') out_ad : string, optional Name of the axial diffusivity volume to be saved (default 'ad.nii.gz') out_rd : string, optional Name of the radial diffusivity volume to be saved (default 'rd.nii.gz') out_mode : string, optional Name of the mode volume to be saved (default 'mode.nii.gz') out_evec : string, optional Name of the eigenvectors volume to be saved (default 'evecs.nii.gz') out_eval : string, optional Name of the eigenvalues to be saved (default 'evals.nii.gz') """ io_it = self.get_io_iterator() for dwi, bval, bvec, mask, otensor, ofa, oga, orgb, omd, oad, orad, \ omode, oevecs, oevals in io_it: logging.info('Computing DTI metrics for {0}'.format(dwi)) img = nib.load(dwi) data = img.get_data() affine = img.get_affine() if mask is None: mask = None else: mask = nib.load(mask).get_data().astype(np.bool) tenfit, _ = self.get_fitted_tensor(data, mask, bval, bvec, b0_threshold) if not save_metrics: save_metrics = ['fa', 'md', 'rd', 'ad', 'ga', 'rgb', 'mode', 'evec', 'eval', 'tensor'] FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) if 'tensor' in save_metrics: 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, otensor) if 'fa' in save_metrics: fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) nib.save(fa_img, ofa) if 'ga' in save_metrics: GA = geodesic_anisotropy(tenfit.evals) ga_img = nib.Nifti1Image(GA.astype(np.float32), affine) nib.save(ga_img, oga) if 'rgb' in save_metrics: RGB = color_fa(FA, tenfit.evecs) rgb_img = nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine) nib.save(rgb_img, orgb) if 'md' in save_metrics: MD = mean_diffusivity(tenfit.evals) md_img = nib.Nifti1Image(MD.astype(np.float32), affine) nib.save(md_img, omd) if 'ad' in save_metrics: AD = axial_diffusivity(tenfit.evals) ad_img = nib.Nifti1Image(AD.astype(np.float32), affine) nib.save(ad_img, oad) if 'rd' in save_metrics: RD = radial_diffusivity(tenfit.evals) rd_img = nib.Nifti1Image(RD.astype(np.float32), affine) nib.save(rd_img, orad) if 'mode' in save_metrics: MODE = get_mode(tenfit.quadratic_form) mode_img = nib.Nifti1Image(MODE.astype(np.float32), affine) nib.save(mode_img, omode) if 'evec' in save_metrics: evecs_img = nib.Nifti1Image(tenfit.evecs.astype(np.float32), affine) nib.save(evecs_img, oevecs) if 'eval' in save_metrics: evals_img = nib.Nifti1Image(tenfit.evals.astype(np.float32), affine) nib.save(evals_img, oevals) logging.info('DTI metrics saved in {0}'. format(os.path.dirname(oevals)))
def main(): #Argparse Stuff parser = argparse.ArgumentParser(description='subject_id') parser.add_argument('--subject_id', type=str, default='135124') args = parser.parse_args() # Method Saving Paths # TODO KARTHIK base_save_path = r'/root/hcp_results' base_save_path = os.path.normpath(base_save_path) if os.path.exists(base_save_path) == False: os.mkdir(base_save_path) # Create base saving path for Method # TODO The Method name can be made an argument later on method_name = 'DTI' # Base HCP Data Path # TODO KARTHIK This is where we hard set HCP's Data Path base_data_path = r'/root/local_mount/data' base_data_path = os.path.normpath(base_data_path) # Subject ID's list #subj_ID_List = ['115017', '114823', '116726', '118225', '115825', '125525'] #subj_ID_List = ['100610', '102311', '102816', '104416', '105923', '108323', '109123', '111312', '111514'] # Subject ID subj_ID = args.subject_id # Subject Save Path print('Working on subject ID: {}'.format(subj_ID)) subj_save_path = os.path.join(base_save_path, subj_ID) if os.path.exists(subj_save_path) == False: os.mkdir(subj_save_path) # TODO For later the subject data, bval and bvec reading part can be put inside a function subj_data_path = os.path.join(base_data_path, subj_ID, 'T1w', 'Diffusion') # Read the Nifti file, bvals and bvecs subj_bvals = os.path.join(subj_data_path, 'bvals') subj_bvecs = os.path.join(subj_data_path, 'bvecs') subj_babel_object = nib.load(os.path.join(subj_data_path, 'data.nii.gz')) subj_data = subj_babel_object.get_fdata() # Load the mask mask_babel_object = nib.load( os.path.join(subj_data_path, 'nodif_brain_mask.nii.gz')) mask_data = mask_babel_object.get_data() # Prepping Bvals, Bvecs and forming the gradient table using dipy bvals, bvecs = read_bvals_bvecs(subj_bvals, subj_bvecs) gtab = gradient_table(bvals, bvecs) print('Gradient Table formed ...') #maskdata, mask = median_otsu(subj_data, vol_idx=range(10, 50), median_radius=3, # numpass=1, autocrop=True, dilate=2) #print('maskdata.shape (%d, %d, %d, %d)' % maskdata.shape) # Form the tensor model tenmodel = dti.TensorModel(gtab) ## Loop over the data and mask data_dims = subj_data.shape fa_vol = np.zeros((data_dims[0], data_dims[1], data_dims[2])) md_vol = np.zeros((data_dims[0], data_dims[1], data_dims[2])) for x in range(0, data_dims[0]): print(x) for y in range(0, data_dims[1]): for z in range(0, data_dims[2]): if mask_data[x, y, z] == 1: # Fit the tensor and calculate FA and MD tenfit = tenmodel.fit(subj_data[x, y, z, :]) # Eval FA and MD and assign to empty vols FA = fractional_anisotropy(tenfit.evals) MD1 = dti.mean_diffusivity(tenfit.evals) fa_vol[x, y, z] = FA md_vol[x, y, z] = MD1 ### Nifti Saving Part # Create a directory per subject subj_method_save_path = os.path.join(subj_save_path, method_name) if os.path.exists(subj_method_save_path) == False: os.mkdir(subj_method_save_path) # Retrieve the affine from already Read Nifti file to form the header affine = subj_babel_object.affine # Form the file path fa_file_path = os.path.join(subj_method_save_path, 'tensor_fa.nii.gz') md_file_path = os.path.join(subj_method_save_path, 'tensor_md.nii.gz') print('Computing anisotropy measures (FA, MD, RGB)') save_nifti(fa_file_path, fa_vol.astype(np.float32), affine) save_nifti(md_file_path, md_vol.astype(np.float32), affine) return None
def nls_fit_fwdki(design_matrix, design_matrix_dki, data, S0, params=None, Diso=3e-3, f_transform=True, mdreg=2.7e-3): """ Fit the water elimination DKI model using the non-linear least-squares. Parameters ---------- design_matrix : array (g, 22) Design matrix holding the covariants used to solve for the regression coefficients. data : ndarray ([X, Y, Z, ...], g) Data or response variables holding the data. Note that the last dimension should contain the data. It makes no copies of data. S0 : ndarray ([X, Y, Z]) A first guess of the non-diffusion signal S0. params : ndarray ([X, Y, Z, ...], 28), optional A first model parameters guess (3 eigenvalues, 3 coordinates of 3 eigenvalues, 15 elements of the kurtosis tensor and the volume fraction of the free water compartment). If the initial params are not given, for the diffusion and kurtosis tensor parameters, its initial guess is obtain from the standard DKI model, while for the free water fraction its value is estimated using the fwDTI model. Default: None Diso : float, optional Value of the free water isotropic diffusion. Default is set to 3e-3 $mm^{2}.s^{-1}$. Please ajust this value if you are assuming different units of diffusion. f_transform : bool, optional If true, the water volume fractions is converted during the convergence procedure to ft = arcsin(2*f - 1) + pi/2, insuring f estimates between 0 and 1. Default: True mdreg : float, optimal DTI's mean diffusivity regularization threshold. If standard DTI diffusion tensor's mean diffusivity is almost near the free water diffusion value, the diffusion signal is assumed to be only free water diffusion (i.e. volume fraction will be set to 1 and tissue's diffusion parameters are set to zero). Default md_reg is 2.7e-3 $mm^{2}.s^{-1}$ (corresponding to 90% of the free water diffusion value). Returns ------- fw_params : ndarray (x, y, z, 28) Matrix containing in the dimention the free water model parameters in the following order: 1) Three diffusion tensor's eigenvalues 2) Three lines of the eigenvector matrix each containing the first, second and third coordinates of the eigenvector 3) Fifteen elements of the kurtosis tensor 4) The volume fraction of the free water compartment S0 : ndarray (x, y, z) The models estimate of the non diffusion-weighted signal S0. """ # preparing data and initializing parameters data = np.asarray(data) data_flat = np.reshape(data, (-1, data.shape[-1])) S0out = S0.copy() S0out = S0out.ravel() # Computing WLS DTI solution for MD regularization dtiparams = dti.wls_fit_tensor(design_matrix, data_flat) md = dti.mean_diffusivity(dtiparams[..., :3]) cond = md > mdreg # removal condition data_cond = data_flat[~cond, :] # Initializing fw_params according to selected initial guess if np.any(params) is None: params_out = np.zeros((len(data_flat), 28)) dkiparams = dki.wls_fit_dki(design_matrix_dki, data_flat) fweparams, sd = fwdti.wls_fit_tensor(design_matrix, data_flat, S0=S0, Diso=Diso, mdreg=2.7e-3) params_out[:, 0:27] = dkiparams params_out[:, 27] = fweparams[:, 12] else: params_out = params.copy() params_out = np.reshape(params_out, (-1, params_out.shape[-1])) params_cond = params_out[~cond, :] S0_cond = S0out[~cond] for vox in range(data_cond.shape[0]): if np.all(data_cond[vox] == 0): raise ValueError("The data in this voxel contains only zeros") params = params_cond[vox] # converting evals and evecs to diffusion tensor elements evals = params[:3] evecs = params[3:12].reshape((3, 3)) dt = lower_triangular(vec_val_vect(evecs, evals)) kt = params[..., 12:27] s0 = S0_cond[vox] MD = evals.mean() # f transformation if requested if f_transform: f = np.arcsin(2*params[27] - 1) + np.pi/2 else: f = params[27] # Use the Levenberg-Marquardt algorithm wrapped in opt.leastsq start_params = np.concatenate((dt, kt*MD*MD, [np.log(s0), f]), axis=0) this_tensor, status = opt.leastsq(_nls_err_func, start_params, args=(design_matrix_dki, data_cond[vox], Diso, f_transform)) # Invert f transformation if this was requested if f_transform: this_tensor[22] = 0.5 * (1 + np.sin(this_tensor[22] - np.pi/2)) # The parameters are the evals and the evecs: evals, evecs = decompose_tensor(from_lower_triangular(this_tensor[:6])) MD = evals.mean() params_cond[vox, :3] = evals params_cond[vox, 3:12] = evecs.ravel() params_cond[vox, 12:27] = this_tensor[6:21] / (MD ** 2) params_cond[vox, 27] = this_tensor[22] S0_cond[vox] = np.exp(-this_tensor[21]) params_out[~cond, :] = params_cond params_out[cond, 27] = 1 # Only free water params_out = np.reshape(params_out, (data.shape[:-1]) + (28,)) S0out[~cond] = S0_cond S0out[cond] = \ np.mean(data_flat[cond, :] / \ np.exp(np.dot(design_matrix[..., :6], np.array([Diso, 0, Diso, 0, 0, Diso]))), -1) # Only free water S0out = S0out.reshape(data.shape[:-1]) return params_out, S0out
def apparent_kurtosis_coef(dki_params, sphere, min_diffusivity=0, min_kurtosis=-1): r""" Calculate the apparent kurtosis coefficient (AKC) in each direction of a sphere. Parameters ---------- dki_params : ndarray (x, y, z, 27) or (n, 27) All parameters estimated from the diffusion kurtosis model. Parameters are ordered as follow: 1) Three diffusion tensor's eingenvalues 2) Three lines of the eigenvector matrix each containing the first, second and third coordinates of the eigenvectors respectively 3) Fifteen elements of the kurtosis tensor sphere : a Sphere class instance The AKC will be calculated for each of the vertices in the sphere min_diffusivity : float (optional) Because negative eigenvalues are not physical and small eigenvalues cause quite a lot of noise in diffusion based metrics, diffusivity values smaller than `min_diffusivity` are replaced with `min_diffusivity`. defaut = 0 min_kurtosis : float (optional) Because high amplitude negative values of kurtosis are not physicaly and biologicaly pluasible, and these causes huge artefacts in kurtosis based measures, directional kurtosis values than `min_kurtosis` are replaced with `min_kurtosis`. defaut = -1 Returns -------- AKC : ndarray (x, y, z, g) or (n, g) Apparent kurtosis coefficient (AKC) for all g directions of a sphere. Notes ----- For each sphere direction with coordinates $(n_{1}, n_{2}, n_{3})$, the calculation of AKC is done using formula: .. math :: AKC(n)=\frac{MD^{2}}{ADC(n)^{2}}\sum_{i=1}^{3}\sum_{j=1}^{3} \sum_{k=1}^{3}\sum_{l=1}^{3}n_{i}n_{j}n_{k}n_{l}W_{ijkl} where $W_{ijkl}$ are the elements of the kurtosis tensor, MD the mean diffusivity and ADC the apparent diffusion coefficent computed as: .. math :: ADC(n)=\sum_{i=1}^{3}\sum_{j=1}^{3}n_{i}n_{j}D_{ij} where $D_{ij}$ are the elements of the diffusion tensor. """ # Flat parameters outshape = dki_params.shape[:-1] dki_params = dki_params.reshape((-1, dki_params.shape[-1])) # Split data evals, evecs, kt = split_dki_param(dki_params) # Compute MD MD = mean_diffusivity(evals) # Initialize AKC matrix V = sphere.vertices AKC = np.zeros((len(kt), len(V))) # loop over all voxels for vox in range(len(kt)): R = evecs[vox] dt = lower_triangular(np.dot(np.dot(R, np.diag(evals[vox])), R.T)) AKC[vox] = _directional_kurtosis(dt, MD[vox], kt[vox], V, min_diffusivity=min_diffusivity, min_kurtosis=min_kurtosis) # reshape data according to input data AKC = AKC.reshape((outshape + (len(V), ))) return AKC
def dodata(f_name,data_path): dipy_home = pjoin(os.path.expanduser('~'), 'dipy_data') folder = pjoin(dipy_home, data_path) fraw = pjoin(folder, f_name+'.nii.gz') fbval = pjoin(folder, f_name+'.bval') fbvec = pjoin(folder, f_name+'.bvec') flabels = pjoin(folder, f_name+'.nii-label.nii.gz') bvals, bvecs = read_bvals_bvecs(fbval, fbvec) gtab = gradient_table(bvals, bvecs) img = nib.load(fraw) data = img.get_data() affine = img.get_affine() label_img = nib.load(flabels) labels=label_img.get_data() lap=through_label_sl.label_position(labels, labelValue=1) dataslice = data[40:80, 20:80, lap[2][2] / 2] #print lap[2][2]/2 #get_csd_gfa(f_name,data,gtab,dataslice) maskdata, mask = median_otsu(data, 2, 1, False, vol_idx=range(10, 50), dilate=2) #不去背景 """ get fa and tensor evecs and ODF""" from dipy.reconst.dti import TensorModel,mean_diffusivity tenmodel = TensorModel(gtab) tenfit = tenmodel.fit(data, mask) sphere = get_sphere('symmetric724') FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 np.save(os.getcwd()+'\zhibiao'+f_name+'_FA.npy',FA) fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) nib.save(fa_img,os.getcwd()+'\zhibiao'+f_name+'_FA.nii.gz') print('Saving "DTI_tensor_fa.nii.gz" sucessful.') evecs_img = nib.Nifti1Image(tenfit.evecs.astype(np.float32), affine) nib.save(evecs_img, os.getcwd()+'\zhibiao'+f_name+'_DTI_tensor_evecs.nii.gz') print('Saving "DTI_tensor_evecs.nii.gz" sucessful.') MD1 = mean_diffusivity(tenfit.evals) nib.save(nib.Nifti1Image(MD1.astype(np.float32), img.get_affine()), os.getcwd()+'\zhibiao'+f_name+'_MD.nii.gz') #tensor_odfs = tenmodel.fit(data[20:50, 55:85, 38:39]).odf(sphere) #from dipy.reconst.odf import gfa #dti_gfa=gfa(tensor_odfs) wm_mask = (np.logical_or(FA >= 0.4, (np.logical_and(FA >= 0.15, MD >= 0.0011)))) response = recursive_response(gtab, data, mask=wm_mask, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=False) from dipy.reconst.csdeconv import ConstrainedSphericalDeconvModel csd_model = ConstrainedSphericalDeconvModel(gtab, response) #csd_fit = csd_model.fit(data) from dipy.direction import peaks_from_model csd_peaks = peaks_from_model(model=csd_model, data=data, sphere=sphere, relative_peak_threshold=.5, min_separation_angle=25, parallel=False) GFA = csd_peaks.gfa nib.save(GFA, os.getcwd()+'\zhibiao'+f_name+'_MSD.nii.gz') print('Saving "GFA.nii.gz" sucessful.') from dipy.reconst.shore import ShoreModel asm = ShoreModel(gtab) print('Calculating...SHORE msd') asmfit = asm.fit(data,mask) msd = asmfit.msd() msd[np.isnan(msd)] = 0 #print GFA[:,:,slice].T print('Saving msd_img.png') nib.save(msd, os.getcwd()+'\zhibiao'+f_name+'_GFA.nii.gz')
tenfit = tenmodel.fit(data, mask) sphere = get_sphere('symmetric724') FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 np.save(os.getcwd()+'\zhibiao'+f_name+'_FA.npy',FA) fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) print FA.shape nib.save(fa_img,os.getcwd()+'/zhibiao/'+f_name+'_FA.nii.gz') print('Saving "DTI_tensor_fa.nii.gz" sucessful.') evecs_img = nib.Nifti1Image(tenfit.evecs.astype(np.float32), affine) nib.save(evecs_img, os.getcwd()+'/zhibiao/'+f_name+'_DTI_tensor_evecs.nii.gz') print('Saving "DTI_tensor_evecs.nii.gz" sucessful.') MD = mean_diffusivity(tenfit.evals) print MD.shape print('Saving "MD.nii.gz" sucessful.') nib.save(nib.Nifti1Image(MD.astype(np.float32), img.get_affine()), os.getcwd()+'/zhibiao/'+f_name+'_MD.nii.gz') tensor_odfs = tenmodel.fit(data[20:50, 55:85, 38:39]).odf(sphere) from dipy.reconst.odf import gfa dti_gfa=gfa(tensor_odfs) """ wm_mask = (np.logical_or(FA >= 0.4, (np.logical_and(FA >= 0.15, MD >= 0.0011)))) response = recursive_response(gtab, data, mask=wm_mask, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=False)
def run(self, input_files, bvalues, bvectors, mask_files, b0_threshold=0.0, save_metrics=[], out_dir='', out_tensor='tensors.nii.gz', out_fa='fa.nii.gz', out_ga='ga.nii.gz', out_rgb='rgb.nii.gz', out_md='md.nii.gz', out_ad='ad.nii.gz', out_rd='rd.nii.gz', out_mode='mode.nii.gz', out_evec='evecs.nii.gz', out_eval='evals.nii.gz'): """ Workflow for tensor reconstruction and for computing DTI metrics. Performs a tensor reconstruction on the files by 'globing' ``input_files`` and saves the DTI metrics in a directory specified by ``out_dir``. Parameters ---------- input_files : string Path to the input volumes. This path may contain wildcards to process multiple inputs at once. bvalues : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. bvectors : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. mask_files : string Path to the input masks. This path may contain wildcards to use multiple masks at once. (default: No mask used) b0_threshold : float, optional Threshold used to find b=0 directions (default 0.0) save_metrics : variable string, optional List of metrics to save. Possible values: fa, ga, rgb, md, ad, rd, mode, tensor, evec, eval (default [] (all)) out_dir : string, optional Output directory (default input file directory) out_tensor : string, optional Name of the tensors volume to be saved (default 'tensors.nii.gz') out_fa : string, optional Name of the fractional anisotropy volume to be saved (default 'fa.nii.gz') out_ga : string, optional Name of the geodesic anisotropy volume to be saved (default 'ga.nii.gz') out_rgb : string, optional Name of the color fa volume to be saved (default 'rgb.nii.gz') out_md : string, optional Name of the mean diffusivity volume to be saved (default 'md.nii.gz') out_ad : string, optional Name of the axial diffusivity volume to be saved (default 'ad.nii.gz') out_rd : string, optional Name of the radial diffusivity volume to be saved (default 'rd.nii.gz') out_mode : string, optional Name of the mode volume to be saved (default 'mode.nii.gz') out_evec : string, optional Name of the eigenvectors volume to be saved (default 'evecs.nii.gz') out_eval : string, optional Name of the eigenvalues to be saved (default 'evals.nii.gz') """ io_it = self.get_io_iterator() for dwi, bval, bvec, mask, otensor, ofa, oga, orgb, omd, oad, orad, \ omode, oevecs, oevals in io_it: logging.info('Computing DTI metrics for {0}'.format(dwi)) img = nib.load(dwi) data = img.get_data() affine = img.get_affine() if mask is None: mask = None else: mask = nib.load(mask).get_data().astype(np.bool) tenfit, _ = self.get_fitted_tensor(data, mask, bval, bvec, b0_threshold) if not save_metrics: save_metrics = [ 'fa', 'md', 'rd', 'ad', 'ga', 'rgb', 'mode', 'evec', 'eval', 'tensor' ] FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) if 'tensor' in save_metrics: 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, otensor) if 'fa' in save_metrics: fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) nib.save(fa_img, ofa) if 'ga' in save_metrics: GA = geodesic_anisotropy(tenfit.evals) ga_img = nib.Nifti1Image(GA.astype(np.float32), affine) nib.save(ga_img, oga) if 'rgb' in save_metrics: RGB = color_fa(FA, tenfit.evecs) rgb_img = nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine) nib.save(rgb_img, orgb) if 'md' in save_metrics: MD = mean_diffusivity(tenfit.evals) md_img = nib.Nifti1Image(MD.astype(np.float32), affine) nib.save(md_img, omd) if 'ad' in save_metrics: AD = axial_diffusivity(tenfit.evals) ad_img = nib.Nifti1Image(AD.astype(np.float32), affine) nib.save(ad_img, oad) if 'rd' in save_metrics: RD = radial_diffusivity(tenfit.evals) rd_img = nib.Nifti1Image(RD.astype(np.float32), affine) nib.save(rd_img, orad) if 'mode' in save_metrics: MODE = get_mode(tenfit.quadratic_form) mode_img = nib.Nifti1Image(MODE.astype(np.float32), affine) nib.save(mode_img, omode) if 'evec' in save_metrics: evecs_img = nib.Nifti1Image(tenfit.evecs.astype(np.float32), affine) nib.save(evecs_img, oevecs) if 'eval' in save_metrics: evals_img = nib.Nifti1Image(tenfit.evals.astype(np.float32), affine) nib.save(evals_img, oevals) logging.info('DTI metrics saved in {0}'.format( os.path.dirname(oevals)))
def to_estimate_dti_maps(path_dwi_input, path_output, file_tensor_fitevecs, file_tensor_fitevals): ref_name_only = utils.to_extract_filename(file_tensor_fitevecs) ref_name_only = ref_name_only[:-9] list_maps = [] img_tensorFitevecs = nib.load(file_tensor_fitevecs) img_tensorFitevals = nib.load(file_tensor_fitevals) evecs = img_tensorFitevecs.get_data() evals = img_tensorFitevals.get_data() affine = img_tensorFitevecs.affine print(d.separador + d.separador + 'computing of FA map') FA = fractional_anisotropy(evals) FA[np.isnan(FA)] = 0 nib.save(nib.Nifti1Image(FA.astype(np.float32), affine), path_output + ref_name_only + '_FA' + d.extension) list_maps.append(path_output + ref_name_only + '_FA' + d.extension) print(d.separador + d.separador + 'computing of Color FA map') FA2 = np.clip(FA, 0, 1) RGB = color_fa(FA2, evecs) nib.save(nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine), path_output + ref_name_only + '_FA_RGB' + d.extension) print(d.separador + d.separador + 'computing of MD map') MD = dti.mean_diffusivity(evals) nib.save(nib.Nifti1Image(MD.astype(np.float32), affine), path_output + ref_name_only + '_MD' + d.extension) list_maps.append(path_output + ref_name_only + '_MD' + d.extension) print(d.separador + d.separador + 'computing of AD map') AD = dti.axial_diffusivity(evals) nib.save(nib.Nifti1Image(AD.astype(np.float32), affine), path_output + ref_name_only + '_AD' + d.extension) list_maps.append(path_output + ref_name_only + '_AD' + d.extension) print(d.separador + d.separador + 'computing of RD map') RD = dti.radial_diffusivity(evals) nib.save(nib.Nifti1Image(RD.astype(np.float32), affine), path_output + ref_name_only + '_RD' + d.extension) list_maps.append(path_output + ref_name_only + '_RD' + d.extension) sphere = get_sphere('symmetric724') peak_indices = quantize_evecs(evecs, sphere.vertices) eu = EuDX(FA.astype('f8'), peak_indices, seeds=300000, odf_vertices=sphere.vertices, a_low=0.15) tensor_streamlines = [streamline for streamline in eu] hdr = nib.trackvis.empty_header() hdr['voxel_size'] = nib.load(path_dwi_input).get_header().get_zooms()[:3] hdr['voxel_order'] = 'LAS' hdr['dim'] = FA.shape tensor_streamlines_trk = ((sl, None, None) for sl in tensor_streamlines) nib.trackvis.write(path_output + ref_name_only + '_tractography_EuDx.trk', tensor_streamlines_trk, hdr, points_space='voxel') return list_maps
def run(self, input_files, bvalues_files, bvectors_files, mask_files, b0_threshold=0.0, bvecs_tol=0.01, save_metrics=[], out_dir='', out_tensor='tensors.nii.gz', out_fa='fa.nii.gz', out_ga='ga.nii.gz', out_rgb='rgb.nii.gz', out_md='md.nii.gz', out_ad='ad.nii.gz', out_rd='rd.nii.gz', out_mode='mode.nii.gz', out_evec='evecs.nii.gz', out_eval='evals.nii.gz'): """ Workflow for tensor reconstruction and for computing DTI metrics. using Weighted Least-Squares. Performs a tensor reconstruction on the files by 'globing' ``input_files`` and saves the DTI metrics in a directory specified by ``out_dir``. Parameters ---------- input_files : string Path to the input volumes. This path may contain wildcards to process multiple inputs at once. bvalues_files : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. bvectors_files : string Path to the bvectors files. This path may contain wildcards to use multiple bvectors files at once. mask_files : string Path to the input masks. This path may contain wildcards to use multiple masks at once. (default: No mask used) b0_threshold : float, optional Threshold used to find b=0 directions (default 0.0) bvecs_tol : float, optional Threshold used to check that norm(bvec) = 1 +/- bvecs_tol b-vectors are unit vectors (default 0.01) save_metrics : variable string, optional List of metrics to save. Possible values: fa, ga, rgb, md, ad, rd, mode, tensor, evec, eval (default [] (all)) out_dir : string, optional Output directory (default input file directory) out_tensor : string, optional Name of the tensors volume to be saved (default 'tensors.nii.gz') out_fa : string, optional Name of the fractional anisotropy volume to be saved (default 'fa.nii.gz') out_ga : string, optional Name of the geodesic anisotropy volume to be saved (default 'ga.nii.gz') out_rgb : string, optional Name of the color fa volume to be saved (default 'rgb.nii.gz') out_md : string, optional Name of the mean diffusivity volume to be saved (default 'md.nii.gz') out_ad : string, optional Name of the axial diffusivity volume to be saved (default 'ad.nii.gz') out_rd : string, optional Name of the radial diffusivity volume to be saved (default 'rd.nii.gz') out_mode : string, optional Name of the mode volume to be saved (default 'mode.nii.gz') out_evec : string, optional Name of the eigenvectors volume to be saved (default 'evecs.nii.gz') out_eval : string, optional Name of the eigenvalues to be saved (default 'evals.nii.gz') References ---------- .. [1] Basser, P.J., Mattiello, J., LeBihan, D., 1994. Estimation of the effective self-diffusion tensor from the NMR spin echo. J Magn Reson B 103, 247-254. .. [2] Basser, P., Pierpaoli, C., 1996. Microstructural and physiological features of tissues elucidated by quantitative diffusion-tensor MRI. Journal of Magnetic Resonance 111, 209-219. .. [3] Lin-Ching C., Jones D.K., Pierpaoli, C. 2005. RESTORE: Robust estimation of tensors by outlier rejection. MRM 53: 1088-1095 .. [4] hung, SW., Lu, Y., Henry, R.G., 2006. Comparison of bootstrap approaches for estimation of uncertainties of DTI parameters. NeuroImage 33, 531-541. """ io_it = self.get_io_iterator() for dwi, bval, bvec, mask, otensor, ofa, oga, orgb, omd, oad, orad, \ omode, oevecs, oevals in io_it: logging.info('Computing DTI metrics for {0}'.format(dwi)) img = nib.load(dwi) data = img.get_data() affine = img.affine if mask is not None: mask = nib.load(mask).get_data().astype(np.bool) tenfit, _ = self.get_fitted_tensor(data, mask, bval, bvec, b0_threshold, bvecs_tol) if not save_metrics: save_metrics = [ 'fa', 'md', 'rd', 'ad', 'ga', 'rgb', 'mode', 'evec', 'eval', 'tensor' ] FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) if 'tensor' in save_metrics: 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, otensor) if 'fa' in save_metrics: fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) nib.save(fa_img, ofa) if 'ga' in save_metrics: GA = geodesic_anisotropy(tenfit.evals) ga_img = nib.Nifti1Image(GA.astype(np.float32), affine) nib.save(ga_img, oga) if 'rgb' in save_metrics: RGB = color_fa(FA, tenfit.evecs) rgb_img = nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine) nib.save(rgb_img, orgb) if 'md' in save_metrics: MD = mean_diffusivity(tenfit.evals) md_img = nib.Nifti1Image(MD.astype(np.float32), affine) nib.save(md_img, omd) if 'ad' in save_metrics: AD = axial_diffusivity(tenfit.evals) ad_img = nib.Nifti1Image(AD.astype(np.float32), affine) nib.save(ad_img, oad) if 'rd' in save_metrics: RD = radial_diffusivity(tenfit.evals) rd_img = nib.Nifti1Image(RD.astype(np.float32), affine) nib.save(rd_img, orad) if 'mode' in save_metrics: MODE = get_mode(tenfit.quadratic_form) mode_img = nib.Nifti1Image(MODE.astype(np.float32), affine) nib.save(mode_img, omode) if 'evec' in save_metrics: evecs_img = nib.Nifti1Image(tenfit.evecs.astype(np.float32), affine) nib.save(evecs_img, oevecs) if 'eval' in save_metrics: evals_img = nib.Nifti1Image(tenfit.evals.astype(np.float32), affine) nib.save(evals_img, oevals) dname_ = os.path.dirname(oevals) if dname_ == '': logging.info('DTI metrics saved in current directory') else: logging.info('DTI metrics saved in {0}'.format(dname_))
def compute_dti(fname_in, fname_bvals, fname_bvecs, prefix, method, file_mask): """ Compute DTI. :param fname_in: input 4d file. :param bvals: bvals txt file :param bvecs: bvecs txt file :param prefix: output prefix. Example: "dti_" :param method: algo for computing dti :return: True/False """ # Open file. from msct_image import Image nii = Image(fname_in) data = nii.data sct.printv('data.shape (%d, %d, %d, %d)' % data.shape) # open bvecs/bvals from dipy.io import read_bvals_bvecs bvals, bvecs = read_bvals_bvecs(fname_bvals, fname_bvecs) from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs) # mask and crop the data. This is a quick way to avoid calculating Tensors on the background of the image. if not file_mask == '': sct.printv('Open mask file...', param.verbose) # open mask file nii_mask = Image(file_mask) mask = nii_mask.data # fit tensor model sct.printv('Computing tensor using "' + method + '" method...', param.verbose) import dipy.reconst.dti as dti if method == 'standard': tenmodel = dti.TensorModel(gtab) if file_mask == '': tenfit = tenmodel.fit(data) else: tenfit = tenmodel.fit(data, mask) elif method == 'restore': import dipy.denoise.noise_estimate as ne sigma = ne.estimate_sigma(data) dti_restore = dti.TensorModel(gtab, fit_method='RESTORE', sigma=sigma) if file_mask == '': tenfit = dti_restore.fit(data) else: tenfit = dti_restore.fit(data, mask) # Compute metrics sct.printv('Computing metrics...', param.verbose) # FA from dipy.reconst.dti import fractional_anisotropy nii.data = fractional_anisotropy(tenfit.evals) nii.setFileName(prefix + 'FA.nii.gz') nii.save('float32') # MD from dipy.reconst.dti import mean_diffusivity nii.data = mean_diffusivity(tenfit.evals) nii.setFileName(prefix + 'MD.nii.gz') nii.save('float32') # RD from dipy.reconst.dti import radial_diffusivity nii.data = radial_diffusivity(tenfit.evals) nii.setFileName(prefix + 'RD.nii.gz') nii.save('float32') # AD from dipy.reconst.dti import axial_diffusivity nii.data = axial_diffusivity(tenfit.evals) nii.setFileName(prefix + 'AD.nii.gz') nii.save('float32') return True
def apparent_kurtosis_coef(dki_params, sphere, min_diffusivity=0, min_kurtosis=-1): r""" Calculate the apparent kurtosis coefficient (AKC) in each direction of a sphere. Parameters ---------- dki_params : ndarray (x, y, z, 27) or (n, 27) All parameters estimated from the diffusion kurtosis model. Parameters are ordered as follow: 1) Three diffusion tensor's eingenvalues 2) Three lines of the eigenvector matrix each containing the first, second and third coordinates of the eigenvectors respectively 3) Fifteen elements of the kurtosis tensor sphere : a Sphere class instance The AKC will be calculated for each of the vertices in the sphere min_diffusivity : float (optional) Because negative eigenvalues are not physical and small eigenvalues cause quite a lot of noise in diffusion based metrics, diffusivity values smaller than `min_diffusivity` are replaced with `min_diffusivity`. defaut = 0 min_kurtosis : float (optional) Because high amplitude negative values of kurtosis are not physicaly and biologicaly pluasible, and these causes huge artefacts in kurtosis based measures, directional kurtosis values than `min_kurtosis` are replaced with `min_kurtosis`. defaut = -1 Returns -------- AKC : ndarray (x, y, z, g) or (n, g) Apparent kurtosis coefficient (AKC) for all g directions of a sphere. Notes ----- For each sphere direction with coordinates $(n_{1}, n_{2}, n_{3})$, the calculation of AKC is done using formula: .. math :: AKC(n)=\frac{MD^{2}}{ADC(n)^{2}}\sum_{i=1}^{3}\sum_{j=1}^{3} \sum_{k=1}^{3}\sum_{l=1}^{3}n_{i}n_{j}n_{k}n_{l}W_{ijkl} where $W_{ijkl}$ are the elements of the kurtosis tensor, MD the mean diffusivity and ADC the apparent diffusion coefficent computed as: .. math :: ADC(n)=\sum_{i=1}^{3}\sum_{j=1}^{3}n_{i}n_{j}D_{ij} where $D_{ij}$ are the elements of the diffusion tensor. """ # Flat parameters outshape = dki_params.shape[:-1] dki_params = dki_params.reshape((-1, dki_params.shape[-1])) # Split data evals, evecs, kt = split_dki_param(dki_params) # Compute MD MD = mean_diffusivity(evals) # Initialize AKC matrix V = sphere.vertices AKC = np.zeros((len(kt), len(V))) # loop over all voxels for vox in range(len(kt)): R = evecs[vox] dt = lower_triangular(np.dot(np.dot(R, np.diag(evals[vox])), R.T)) AKC[vox] = _directional_kurtosis(dt, MD[vox], kt[vox], V, min_diffusivity=min_diffusivity, min_kurtosis=min_kurtosis) # reshape data according to input data AKC = AKC.reshape((outshape + (len(V),))) return AKC
def run(self, input_files, bvalues_files, bvectors_files, mask_files, b0_threshold=50, bvecs_tol=0.01, save_metrics=[], out_dir='', out_tensor='tensors.nii.gz', out_fa='fa.nii.gz', out_ga='ga.nii.gz', out_rgb='rgb.nii.gz', out_md='md.nii.gz', out_ad='ad.nii.gz', out_rd='rd.nii.gz', out_mode='mode.nii.gz', out_evec='evecs.nii.gz', out_eval='evals.nii.gz', nifti_tensor=True): """ Workflow for tensor reconstruction and for computing DTI metrics. using Weighted Least-Squares. Performs a tensor reconstruction on the files by 'globing' ``input_files`` and saves the DTI metrics in a directory specified by ``out_dir``. Parameters ---------- input_files : string Path to the input volumes. This path may contain wildcards to process multiple inputs at once. bvalues_files : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. bvectors_files : string Path to the bvectors files. This path may contain wildcards to use multiple bvectors files at once. mask_files : string Path to the input masks. This path may contain wildcards to use multiple masks at once. b0_threshold : float, optional Threshold used to find b0 volumes. bvecs_tol : float, optional Threshold used to check that norm(bvec) = 1 +/- bvecs_tol b-vectors are unit vectors. save_metrics : variable string, optional List of metrics to save. Possible values: fa, ga, rgb, md, ad, rd, mode, tensor, evec, eval out_dir : string, optional Output directory. (default current directory) out_tensor : string, optional Name of the tensors volume to be saved. Per default, this will be saved following the nifti standard: with the tensor elements as Dxx, Dxy, Dyy, Dxz, Dyz, Dzz on the last (5th) dimension of the volume (shape: (i, j, k, 1, 6)). If `nifti_tensor` is False, this will be saved in an alternate format that is used by other software (e.g., FSL): a 4-dimensional volume (shape (i, j, k, 6)) with Dxx, Dxy, Dxz, Dyy, Dyz, Dzz on the last dimension. out_fa : string, optional Name of the fractional anisotropy volume to be saved. out_ga : string, optional Name of the geodesic anisotropy volume to be saved. out_rgb : string, optional Name of the color fa volume to be saved. out_md : string, optional Name of the mean diffusivity volume to be saved. out_ad : string, optional Name of the axial diffusivity volume to be saved. out_rd : string, optional Name of the radial diffusivity volume to be saved. out_mode : string, optional Name of the mode volume to be saved. out_evec : string, optional Name of the eigenvectors volume to be saved. out_eval : string, optional Name of the eigenvalues to be saved. nifti_tensor : bool, optional Whether the tensor is saved in the standard Nifti format or in an alternate format that is used by other software (e.g., FSL): a 4-dimensional volume (shape (i, j, k, 6)) with Dxx, Dxy, Dxz, Dyy, Dyz, Dzz on the last dimension. References ---------- .. [1] Basser, P.J., Mattiello, J., LeBihan, D., 1994. Estimation of the effective self-diffusion tensor from the NMR spin echo. J Magn Reson B 103, 247-254. .. [2] Basser, P., Pierpaoli, C., 1996. Microstructural and physiological features of tissues elucidated by quantitative diffusion-tensor MRI. Journal of Magnetic Resonance 111, 209-219. .. [3] Lin-Ching C., Jones D.K., Pierpaoli, C. 2005. RESTORE: Robust estimation of tensors by outlier rejection. MRM 53: 1088-1095 .. [4] hung, SW., Lu, Y., Henry, R.G., 2006. Comparison of bootstrap approaches for estimation of uncertainties of DTI parameters. NeuroImage 33, 531-541. """ io_it = self.get_io_iterator() for dwi, bval, bvec, mask, otensor, ofa, oga, orgb, omd, oad, orad, \ omode, oevecs, oevals in io_it: logging.info('Computing DTI metrics for {0}'.format(dwi)) data, affine = load_nifti(dwi) if mask is not None: mask = load_nifti_data(mask).astype(bool) tenfit, _ = self.get_fitted_tensor(data, mask, bval, bvec, b0_threshold, bvecs_tol) if not save_metrics: save_metrics = [ 'fa', 'md', 'rd', 'ad', 'ga', 'rgb', 'mode', 'evec', 'eval', 'tensor' ] FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) if 'tensor' in save_metrics: tensor_vals = lower_triangular(tenfit.quadratic_form) if nifti_tensor: ten_img = nifti1_symmat(tensor_vals, affine=affine) else: alt_order = [0, 1, 3, 2, 4, 5] ten_img = nib.Nifti1Image( tensor_vals[..., alt_order].astype(np.float32), affine) nib.save(ten_img, otensor) if 'fa' in save_metrics: save_nifti(ofa, FA.astype(np.float32), affine) if 'ga' in save_metrics: GA = geodesic_anisotropy(tenfit.evals) save_nifti(oga, GA.astype(np.float32), affine) if 'rgb' in save_metrics: RGB = color_fa(FA, tenfit.evecs) save_nifti(orgb, np.array(255 * RGB, 'uint8'), affine) if 'md' in save_metrics: MD = mean_diffusivity(tenfit.evals) save_nifti(omd, MD.astype(np.float32), affine) if 'ad' in save_metrics: AD = axial_diffusivity(tenfit.evals) save_nifti(oad, AD.astype(np.float32), affine) if 'rd' in save_metrics: RD = radial_diffusivity(tenfit.evals) save_nifti(orad, RD.astype(np.float32), affine) if 'mode' in save_metrics: MODE = get_mode(tenfit.quadratic_form) save_nifti(omode, MODE.astype(np.float32), affine) if 'evec' in save_metrics: save_nifti(oevecs, tenfit.evecs.astype(np.float32), affine) if 'eval' in save_metrics: save_nifti(oevals, tenfit.evals.astype(np.float32), affine) dname_ = os.path.dirname(oevals) if dname_ == '': logging.info('DTI metrics saved in current directory') else: logging.info('DTI metrics saved in {0}'.format(dname_))
def main(): parser = _build_args_parser() args = parser.parse_args() if not args.not_all: args.fa = args.fa or 'fa.nii.gz' args.ga = args.ga or 'ga.nii.gz' args.rgb = args.rgb or 'rgb.nii.gz' args.md = args.md or 'md.nii.gz' args.ad = args.ad or 'ad.nii.gz' args.rd = args.rd or 'rd.nii.gz' args.mode = args.mode or 'mode.nii.gz' args.norm = args.norm or 'tensor_norm.nii.gz' args.tensor = args.tensor or 'tensor.nii.gz' args.evecs = args.evecs or 'tensor_evecs.nii.gz' args.evals = args.evals or 'tensor_evals.nii.gz' args.residual = args.residual or 'dti_residual.nii.gz' args.p_i_signal =\ args.p_i_signal or 'physically_implausible_signals_mask.nii.gz' args.pulsation = args.pulsation or 'pulsation_and_misalignment.nii.gz' outputs = [args.fa, args.ga, args.rgb, args.md, args.ad, args.rd, args.mode, args.norm, args.tensor, args.evecs, args.evals, args.residual, args.p_i_signal, args.pulsation] if args.not_all and not any(outputs): parser.error('When using --not_all, you need to specify at least ' + 'one metric to output.') assert_inputs_exist( parser, [args.input, args.bvals, args.bvecs], [args.mask]) assert_outputs_exists(parser, args, outputs) img = nib.load(args.input) data = img.get_data() affine = img.get_affine() if args.mask is None: mask = None else: mask = nib.load(args.mask).get_data().astype(np.bool) # Validate bvals and bvecs logging.info('Tensor estimation with the %s method...', args.method) bvals, bvecs = read_bvals_bvecs(args.bvals, args.bvecs) if not is_normalized_bvecs(bvecs): logging.warning('Your b-vectors do not seem normalized...') bvecs = normalize_bvecs(bvecs) check_b0_threshold(args, bvals.min()) gtab = gradient_table(bvals, bvecs, b0_threshold=bvals.min()) # Get tensors if args.method == 'restore': sigma = ne.estimate_sigma(data) tenmodel = TensorModel(gtab, fit_method=args.method, sigma=sigma, min_signal=_get_min_nonzero_signal(data)) else: tenmodel = TensorModel(gtab, fit_method=args.method, min_signal=_get_min_nonzero_signal(data)) tenfit = tenmodel.fit(data, mask) FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) if args.tensor: # 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, args.tensor) if args.fa: fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) nib.save(fa_img, args.fa) if args.ga: GA = geodesic_anisotropy(tenfit.evals) GA[np.isnan(GA)] = 0 ga_img = nib.Nifti1Image(GA.astype(np.float32), affine) nib.save(ga_img, args.ga) if args.rgb: RGB = color_fa(FA, tenfit.evecs) rgb_img = nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine) nib.save(rgb_img, args.rgb) if args.md: MD = mean_diffusivity(tenfit.evals) md_img = nib.Nifti1Image(MD.astype(np.float32), affine) nib.save(md_img, args.md) if args.ad: AD = axial_diffusivity(tenfit.evals) ad_img = nib.Nifti1Image(AD.astype(np.float32), affine) nib.save(ad_img, args.ad) if args.rd: RD = radial_diffusivity(tenfit.evals) rd_img = nib.Nifti1Image(RD.astype(np.float32), affine) nib.save(rd_img, args.rd) if args.mode: # Compute tensor mode inter_mode = dipy_mode(tenfit.quadratic_form) # Since the mode computation can generate NANs when not masked, # we need to remove them. non_nan_indices = np.isfinite(inter_mode) mode = np.zeros(inter_mode.shape) mode[non_nan_indices] = inter_mode[non_nan_indices] mode_img = nib.Nifti1Image(mode.astype(np.float32), affine) nib.save(mode_img, args.mode) if args.norm: NORM = norm(tenfit.quadratic_form) norm_img = nib.Nifti1Image(NORM.astype(np.float32), affine) nib.save(norm_img, args.norm) if args.evecs: evecs = tenfit.evecs.astype(np.float32) evecs_img = nib.Nifti1Image(evecs, affine) nib.save(evecs_img, args.evecs) # save individual e-vectors also e1_img = nib.Nifti1Image(evecs[..., 0], affine) e2_img = nib.Nifti1Image(evecs[..., 1], affine) e3_img = nib.Nifti1Image(evecs[..., 2], affine) nib.save(e1_img, add_filename_suffix(args.evecs, '_v1')) nib.save(e2_img, add_filename_suffix(args.evecs, '_v2')) nib.save(e3_img, add_filename_suffix(args.evecs, '_v3')) if args.evals: evals = tenfit.evals.astype(np.float32) evals_img = nib.Nifti1Image(evals, affine) nib.save(evals_img, args.evals) # save individual e-values also e1_img = nib.Nifti1Image(evals[..., 0], affine) e2_img = nib.Nifti1Image(evals[..., 1], affine) e3_img = nib.Nifti1Image(evals[..., 2], affine) nib.save(e1_img, add_filename_suffix(args.evals, '_e1')) nib.save(e2_img, add_filename_suffix(args.evals, '_e2')) nib.save(e3_img, add_filename_suffix(args.evals, '_e3')) if args.p_i_signal: S0 = np.mean(data[..., gtab.b0s_mask], axis=-1, keepdims=True) DWI = data[..., ~gtab.b0s_mask] pis_mask = np.max(S0 < DWI, axis=-1) if args.mask is not None: pis_mask *= mask pis_img = nib.Nifti1Image(pis_mask.astype(np.int16), affine) nib.save(pis_img, args.p_i_signal) if args.pulsation: STD = np.std(data[..., ~gtab.b0s_mask], axis=-1) if args.mask is not None: STD *= mask std_img = nib.Nifti1Image(STD.astype(np.float32), affine) nib.save(std_img, add_filename_suffix(args.pulsation, '_std_dwi')) if np.sum(gtab.b0s_mask) <= 1: logger.info('Not enough b=0 images to output standard ' 'deviation map') else: if len(np.where(gtab.b0s_mask)) == 2: logger.info('Only two b=0 images. Be careful with the ' 'interpretation of this std map') STD = np.std(data[..., gtab.b0s_mask], axis=-1) if args.mask is not None: STD *= mask std_img = nib.Nifti1Image(STD.astype(np.float32), affine) nib.save(std_img, add_filename_suffix(args.pulsation, '_std_b0')) if args.residual: if args.mask is None: logger.info("Outlier detection will not be performed, since no " "mask was provided.") S0 = np.mean(data[..., gtab.b0s_mask], axis=-1) data_p = tenfit.predict(gtab, S0) R = np.mean(np.abs(data_p[..., ~gtab.b0s_mask] - data[..., ~gtab.b0s_mask]), axis=-1) if args.mask is not None: R *= mask R_img = nib.Nifti1Image(R.astype(np.float32), affine) nib.save(R_img, args.residual) R_k = np.zeros(data.shape[-1]) # mean residual per DWI std = np.zeros(data.shape[-1]) # std residual per DWI q1 = np.zeros(data.shape[-1]) # first quartile q3 = np.zeros(data.shape[-1]) # third quartile iqr = np.zeros(data.shape[-1]) # interquartile for i in range(data.shape[-1]): x = np.abs(data_p[..., i] - data[..., i])[mask] R_k[i] = np.mean(x) std[i] = np.std(x) q3[i], q1[i] = np.percentile(x, [75, 25]) iqr[i] = q3[i] - q1[i] # Outliers are observations that fall below Q1 - 1.5(IQR) or # above Q3 + 1.5(IQR) We check if a volume is an outlier only if # we have a mask, else we are biased. if args.mask is not None and R_k[i] < (q1[i] - 1.5 * iqr[i]) \ or R_k[i] > (q3[i] + 1.5 * iqr[i]): logger.warning('WARNING: Diffusion-Weighted Image i=%s is an ' 'outlier', i) residual_basename, _ = split_name_with_nii(args.residual) res_stats_basename = residual_basename + ".npy" np.save(add_filename_suffix( res_stats_basename, "_mean_residuals"), R_k) np.save(add_filename_suffix(res_stats_basename, "_q1_residuals"), q1) np.save(add_filename_suffix(res_stats_basename, "_q3_residuals"), q3) np.save(add_filename_suffix(res_stats_basename, "_iqr_residuals"), iqr) np.save(add_filename_suffix(res_stats_basename, "_std_residuals"), std) # To do: I would like to have an error bar with q1 and q3. # Now, q1 acts as a std dwi = np.arange(R_k[~gtab.b0s_mask].shape[0]) plt.bar(dwi, R_k[~gtab.b0s_mask], 0.75, color='y', yerr=q1[~gtab.b0s_mask]) plt.xlabel('DW image') plt.ylabel('Mean residuals +- q1') plt.title('Residuals') plt.savefig(residual_basename + '_residuals_stats.png')
def nonlinfit_fn(dwi, bvecs, bvals, base_name): import nibabel as nb import numpy as np import os.path as op import dipy.reconst.dti as dti from dipy.core.gradients import GradientTable dwi_img = nb.load(dwi) dwi_data = dwi_img.get_data() dwi_affine = dwi_img.get_affine() from dipy.segment.mask import median_otsu b0_mask, mask = median_otsu(dwi_data, 2, 4) # Mask the data so that tensors are not fit for # unnecessary voxels mask_img = nb.Nifti1Image(mask.astype(np.float32), dwi_affine) b0_imgs = nb.Nifti1Image(b0_mask.astype(np.float32), dwi_affine) b0_img = nb.four_to_three(b0_imgs)[0] out_mask_name = op.abspath(base_name + '_binary_mask.nii.gz') out_b0_name = op.abspath(base_name + '_b0_mask.nii.gz') nb.save(mask_img, out_mask_name) nb.save(b0_img, out_b0_name) # Load the gradient strengths and directions bvals = np.loadtxt(bvals) gradients = np.loadtxt(bvecs).T # Place in Dipy's preferred format gtab = GradientTable(gradients) gtab.bvals = bvals # Fit the tensors to the data tenmodel = dti.TensorModel(gtab, fit_method="NLLS") tenfit = tenmodel.fit(dwi_data, mask) # Calculate the fit, fa, and md of each voxel's tensor tensor_data = tenfit.lower_triangular() print('Computing anisotropy measures (FA, MD, RGB)') from dipy.reconst.dti import fractional_anisotropy, color_fa evals = tenfit.evals.astype(np.float32) FA = fractional_anisotropy(np.abs(evals)) FA = np.clip(FA, 0, 1) MD = dti.mean_diffusivity(np.abs(evals)) norm = dti.norm(tenfit.quadratic_form) RGB = color_fa(FA, tenfit.evecs) evecs = tenfit.evecs.astype(np.float32) mode = tenfit.mode.astype(np.float32) # Write tensor as a 4D Nifti image with the original affine tensor_fit_img = nb.Nifti1Image(tensor_data.astype(np.float32), dwi_affine) mode_img = nb.Nifti1Image(mode.astype(np.float32), dwi_affine) norm_img = nb.Nifti1Image(norm.astype(np.float32), dwi_affine) FA_img = nb.Nifti1Image(FA.astype(np.float32), dwi_affine) evecs_img = nb.Nifti1Image(evecs, dwi_affine) evals_img = nb.Nifti1Image(evals, dwi_affine) rgb_img = nb.Nifti1Image(np.array(255 * RGB, 'uint8'), dwi_affine) MD_img = nb.Nifti1Image(MD.astype(np.float32), dwi_affine) out_tensor_file = op.abspath(base_name + "_tensor.nii.gz") out_mode_file = op.abspath(base_name + "_mode.nii.gz") out_fa_file = op.abspath(base_name + "_fa.nii.gz") out_norm_file = op.abspath(base_name + "_norm.nii.gz") out_evals_file = op.abspath(base_name + "_evals.nii.gz") out_evecs_file = op.abspath(base_name + "_evecs.nii.gz") out_rgb_fa_file = op.abspath(base_name + "_rgb_fa.nii.gz") out_md_file = op.abspath(base_name + "_md.nii.gz") nb.save(rgb_img, out_rgb_fa_file) nb.save(norm_img, out_norm_file) nb.save(mode_img, out_mode_file) nb.save(tensor_fit_img, out_tensor_file) nb.save(evecs_img, out_evecs_file) nb.save(evals_img, out_evals_file) nb.save(FA_img, out_fa_file) nb.save(MD_img, out_md_file) print('Tensor fit image saved as {i}'.format(i=out_tensor_file)) print('FA image saved as {i}'.format(i=out_fa_file)) print('MD image saved as {i}'.format(i=out_md_file)) return out_tensor_file, out_fa_file, out_md_file, \ out_evecs_file, out_evals_file, out_rgb_fa_file, out_norm_file, \ out_mode_file, out_mask_name, out_b0_name
sh_basis = sh_model.sampling_matrix(hsph_initial) print('Debug the Spherical Harmonic Basis Set') # Lets try the SF to SH method fake_data = np.ones((1, 100)) fake_sh_model, fake_sh_basis = sf_to_sh(fake_data, hsph_initial, sh_order=8, basis_type='fibernav') print('Debug Fake SH Model') print('Recreate Fake SH Data') pred_fake_data = np.dot(fake_sh_basis, fake_sh_model.T) pred_fake_data = pred_fake_data.T print('Predictions of Signal Made') # Fit Tensor and Calculate FA and MD of the data tenmodel = dti.TensorModel(gtab_b3000) tenfit = tenmodel.fit(pred_fake_data) FA = fractional_anisotropy(tenfit.evals) MD = mean_diffusivity(tenfit.evals) print('Fractional Anisotropy \n') print(FA) print('Mean Diffusivity \n') print(MD)
""" from dipy.reconst.csdeconv import recursive_response """ A WM mask can shorten computation time for the whole dataset. Here it is created based on the DTI fit. """ import dipy.reconst.dti as dti tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(data, mask=data[..., 0] > 200) from dipy.reconst.dti import fractional_anisotropy FA = fractional_anisotropy(tenfit.evals) MD = dti.mean_diffusivity(tenfit.evals) wm_mask = (np.logical_or(FA >= 0.4, (np.logical_and(FA >= 0.15, MD >= 0.0011)))) response = recursive_response(gtab, data, mask=wm_mask, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=True) """ We can check the shape of the signal of the response function, which should be like a pancake: """ response_signal = response.on_sphere(sphere) response_actor = fvtk.sphere_funcs(response_signal, sphere)
def main(): parser = _build_args_parser() args = parser.parse_args() if not args.not_all: args.fa = args.fa or 'fa.nii.gz' args.ga = args.ga or 'ga.nii.gz' args.rgb = args.rgb or 'rgb.nii.gz' args.md = args.md or 'md.nii.gz' args.ad = args.ad or 'ad.nii.gz' args.rd = args.rd or 'rd.nii.gz' args.mode = args.mode or 'mode.nii.gz' args.norm = args.norm or 'tensor_norm.nii.gz' args.tensor = args.tensor or 'tensor.nii.gz' args.evecs = args.evecs or 'tensor_evecs.nii.gz' args.evals = args.evals or 'tensor_evals.nii.gz' args.residual = args.residual or 'dti_residual.nii.gz' args.p_i_signal =\ args.p_i_signal or 'physically_implausible_signals_mask.nii.gz' args.pulsation = args.pulsation or 'pulsation_and_misalignment.nii.gz' outputs = [args.fa, args.ga, args.rgb, args.md, args.ad, args.rd, args.mode, args.norm, args.tensor, args.evecs, args.evals, args.residual, args.p_i_signal, args.pulsation] if args.not_all and not any(outputs): parser.error('When using --not_all, you need to specify at least ' + 'one metric to output.') assert_inputs_exist( parser, [args.input, args.bvals, args.bvecs], args.mask) assert_outputs_exist(parser, args, outputs) img = nib.load(args.input) data = img.get_data() affine = img.get_affine() if args.mask is None: mask = None else: mask = nib.load(args.mask).get_data().astype(np.bool) # Validate bvals and bvecs logging.info('Tensor estimation with the %s method...', args.method) bvals, bvecs = read_bvals_bvecs(args.bvals, args.bvecs) if not is_normalized_bvecs(bvecs): logging.warning('Your b-vectors do not seem normalized...') bvecs = normalize_bvecs(bvecs) check_b0_threshold(args, bvals.min()) gtab = gradient_table(bvals, bvecs, b0_threshold=bvals.min()) # Get tensors if args.method == 'restore': sigma = ne.estimate_sigma(data) tenmodel = TensorModel(gtab, fit_method=args.method, sigma=sigma, min_signal=_get_min_nonzero_signal(data)) else: tenmodel = TensorModel(gtab, fit_method=args.method, min_signal=_get_min_nonzero_signal(data)) tenfit = tenmodel.fit(data, mask) FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) if args.tensor: # 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, args.tensor) if args.fa: fa_img = nib.Nifti1Image(FA.astype(np.float32), affine) nib.save(fa_img, args.fa) if args.ga: GA = geodesic_anisotropy(tenfit.evals) GA[np.isnan(GA)] = 0 ga_img = nib.Nifti1Image(GA.astype(np.float32), affine) nib.save(ga_img, args.ga) if args.rgb: RGB = color_fa(FA, tenfit.evecs) rgb_img = nib.Nifti1Image(np.array(255 * RGB, 'uint8'), affine) nib.save(rgb_img, args.rgb) if args.md: MD = mean_diffusivity(tenfit.evals) md_img = nib.Nifti1Image(MD.astype(np.float32), affine) nib.save(md_img, args.md) if args.ad: AD = axial_diffusivity(tenfit.evals) ad_img = nib.Nifti1Image(AD.astype(np.float32), affine) nib.save(ad_img, args.ad) if args.rd: RD = radial_diffusivity(tenfit.evals) rd_img = nib.Nifti1Image(RD.astype(np.float32), affine) nib.save(rd_img, args.rd) if args.mode: # Compute tensor mode inter_mode = dipy_mode(tenfit.quadratic_form) # Since the mode computation can generate NANs when not masked, # we need to remove them. non_nan_indices = np.isfinite(inter_mode) mode = np.zeros(inter_mode.shape) mode[non_nan_indices] = inter_mode[non_nan_indices] mode_img = nib.Nifti1Image(mode.astype(np.float32), affine) nib.save(mode_img, args.mode) if args.norm: NORM = norm(tenfit.quadratic_form) norm_img = nib.Nifti1Image(NORM.astype(np.float32), affine) nib.save(norm_img, args.norm) if args.evecs: evecs = tenfit.evecs.astype(np.float32) evecs_img = nib.Nifti1Image(evecs, affine) nib.save(evecs_img, args.evecs) # save individual e-vectors also e1_img = nib.Nifti1Image(evecs[..., 0], affine) e2_img = nib.Nifti1Image(evecs[..., 1], affine) e3_img = nib.Nifti1Image(evecs[..., 2], affine) nib.save(e1_img, add_filename_suffix(args.evecs, '_v1')) nib.save(e2_img, add_filename_suffix(args.evecs, '_v2')) nib.save(e3_img, add_filename_suffix(args.evecs, '_v3')) if args.evals: evals = tenfit.evals.astype(np.float32) evals_img = nib.Nifti1Image(evals, affine) nib.save(evals_img, args.evals) # save individual e-values also e1_img = nib.Nifti1Image(evals[..., 0], affine) e2_img = nib.Nifti1Image(evals[..., 1], affine) e3_img = nib.Nifti1Image(evals[..., 2], affine) nib.save(e1_img, add_filename_suffix(args.evals, '_e1')) nib.save(e2_img, add_filename_suffix(args.evals, '_e2')) nib.save(e3_img, add_filename_suffix(args.evals, '_e3')) if args.p_i_signal: S0 = np.mean(data[..., gtab.b0s_mask], axis=-1, keepdims=True) DWI = data[..., ~gtab.b0s_mask] pis_mask = np.max(S0 < DWI, axis=-1) if args.mask is not None: pis_mask *= mask pis_img = nib.Nifti1Image(pis_mask.astype(np.int16), affine) nib.save(pis_img, args.p_i_signal) if args.pulsation: STD = np.std(data[..., ~gtab.b0s_mask], axis=-1) if args.mask is not None: STD *= mask std_img = nib.Nifti1Image(STD.astype(np.float32), affine) nib.save(std_img, add_filename_suffix(args.pulsation, '_std_dwi')) if np.sum(gtab.b0s_mask) <= 1: logger.info('Not enough b=0 images to output standard ' 'deviation map') else: if len(np.where(gtab.b0s_mask)) == 2: logger.info('Only two b=0 images. Be careful with the ' 'interpretation of this std map') STD = np.std(data[..., gtab.b0s_mask], axis=-1) if args.mask is not None: STD *= mask std_img = nib.Nifti1Image(STD.astype(np.float32), affine) nib.save(std_img, add_filename_suffix(args.pulsation, '_std_b0')) if args.residual: # Mean residual image S0 = np.mean(data[..., gtab.b0s_mask], axis=-1) data_p = tenfit.predict(gtab, S0) R = np.mean(np.abs(data_p[..., ~gtab.b0s_mask] - data[..., ~gtab.b0s_mask]), axis=-1) if args.mask is not None: R *= mask R_img = nib.Nifti1Image(R.astype(np.float32), affine) nib.save(R_img, args.residual) # Each volume's residual statistics if args.mask is None: logger.info("Outlier detection will not be performed, since no " "mask was provided.") stats = [dict.fromkeys(['label', 'mean', 'iqr', 'cilo', 'cihi', 'whishi', 'whislo', 'fliers', 'q1', 'med', 'q3'], []) for i in range(data.shape[-1])] # stats with format for boxplots # Note that stats will be computed manually and plotted using bxp # but could be computed using stats = cbook.boxplot_stats # or pyplot.boxplot(x) R_k = np.zeros(data.shape[-1]) # mean residual per DWI std = np.zeros(data.shape[-1]) # std residual per DWI q1 = np.zeros(data.shape[-1]) # first quartile per DWI q3 = np.zeros(data.shape[-1]) # third quartile per DWI iqr = np.zeros(data.shape[-1]) # interquartile per DWI percent_outliers = np.zeros(data.shape[-1]) nb_voxels = np.count_nonzero(mask) for k in range(data.shape[-1]): x = np.abs(data_p[..., k] - data[..., k])[mask] R_k[k] = np.mean(x) std[k] = np.std(x) q3[k], q1[k] = np.percentile(x, [75, 25]) iqr[k] = q3[k] - q1[k] stats[k]['med'] = (q1[k] + q3[k]) / 2 stats[k]['mean'] = R_k[k] stats[k]['q1'] = q1[k] stats[k]['q3'] = q3[k] stats[k]['whislo'] = q1[k] - 1.5 * iqr[k] stats[k]['whishi'] = q3[k] + 1.5 * iqr[k] stats[k]['label'] = k # Outliers are observations that fall below Q1 - 1.5(IQR) or # above Q3 + 1.5(IQR) We check if a voxel is an outlier only if # we have a mask, else we are biased. if args.mask is not None: outliers = (x < stats[k]['whislo']) | (x > stats[k]['whishi']) percent_outliers[k] = np.sum(outliers)/nb_voxels*100 # What would be our definition of too many outliers? # Maybe mean(all_means)+-3SD? # Or we let people choose based on the figure. # if percent_outliers[k] > ???? : # logger.warning(' Careful! Diffusion-Weighted Image' # ' i=%s has %s %% outlier voxels', # k, percent_outliers[k]) # Saving all statistics as npy values residual_basename, _ = split_name_with_nii(args.residual) res_stats_basename = residual_basename + ".npy" np.save(add_filename_suffix( res_stats_basename, "_mean_residuals"), R_k) np.save(add_filename_suffix(res_stats_basename, "_q1_residuals"), q1) np.save(add_filename_suffix(res_stats_basename, "_q3_residuals"), q3) np.save(add_filename_suffix(res_stats_basename, "_iqr_residuals"), iqr) np.save(add_filename_suffix(res_stats_basename, "_std_residuals"), std) # Showing results in graph if args.mask is None: fig, axe = plt.subplots(nrows=1, ncols=1, squeeze=False) else: fig, axe = plt.subplots(nrows=1, ncols=2, squeeze=False, figsize=[10, 4.8]) # Default is [6.4, 4.8]. Increasing width to see better. medianprops = dict(linestyle='-', linewidth=2.5, color='firebrick') meanprops = dict(linestyle='-', linewidth=2.5, color='green') axe[0, 0].bxp(stats, showmeans=True, meanline=True, showfliers=False, medianprops=medianprops, meanprops=meanprops) axe[0, 0].set_xlabel('DW image') axe[0, 0].set_ylabel('Residuals per DWI volume. Red is median,\n' 'green is mean. Whiskers are 1.5*interquartile') axe[0, 0].set_title('Residuals') axe[0, 0].set_xticks(range(0, q1.shape[0], 5)) axe[0, 0].set_xticklabels(range(0, q1.shape[0], 5)) if args.mask is not None: axe[0, 1].plot(range(data.shape[-1]), percent_outliers) axe[0, 1].set_xticks(range(0, q1.shape[0], 5)) axe[0, 1].set_xticklabels(range(0, q1.shape[0], 5)) axe[0, 1].set_xlabel('DW image') axe[0, 1].set_ylabel('Percentage of outlier voxels') axe[0, 1].set_title('Outliers') plt.savefig(residual_basename + '_residuals_stats.png')
def run(self, input_files, bvalues_files, bvectors_files, mask_files, b0_threshold=50.0, save_metrics=[], out_dir='', out_dt_tensor='dti_tensors.nii.gz', out_fa='fa.nii.gz', out_ga='ga.nii.gz', out_rgb='rgb.nii.gz', out_md='md.nii.gz', out_ad='ad.nii.gz', out_rd='rd.nii.gz', out_mode='mode.nii.gz', out_evec='evecs.nii.gz', out_eval='evals.nii.gz', out_dk_tensor="dki_tensors.nii.gz", out_mk="mk.nii.gz", out_ak="ak.nii.gz", out_rk="rk.nii.gz"): """ Workflow for Diffusion Kurtosis reconstruction and for computing DKI metrics. Performs a DKI reconstruction on the files by 'globing' ``input_files`` and saves the DKI metrics in a directory specified by ``out_dir``. Parameters ---------- input_files : string Path to the input volumes. This path may contain wildcards to process multiple inputs at once. bvalues_files : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. bvectors_files : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. mask_files : string Path to the input masks. This path may contain wildcards to use multiple masks at once. (default: No mask used) b0_threshold : float, optional Threshold used to find b0 volumes. save_metrics : variable string, optional List of metrics to save. Possible values: fa, ga, rgb, md, ad, rd, mode, tensor, evec, eval out_dir : string, optional Output directory. (default current directory) out_dt_tensor : string, optional Name of the tensors volume to be saved. out_dk_tensor : string, optional Name of the tensors volume to be saved. out_fa : string, optional Name of the fractional anisotropy volume to be saved. out_ga : string, optional Name of the geodesic anisotropy volume to be saved. out_rgb : string, optional Name of the color fa volume to be saved. out_md : string, optional Name of the mean diffusivity volume to be saved. out_ad : string, optional Name of the axial diffusivity volume to be saved. out_rd : string, optional Name of the radial diffusivity volume to be saved. out_mode : string, optional Name of the mode volume to be saved. out_evec : string, optional Name of the eigenvectors volume to be saved. out_eval : string, optional Name of the eigenvalues to be saved. out_mk : string, optional Name of the mean kurtosis to be saved. out_ak : string, optional Name of the axial kurtosis to be saved. out_rk : string, optional Name of the radial kurtosis to be saved. References ---------- .. [1] Tabesh, A., Jensen, J.H., Ardekani, B.A., Helpern, J.A., 2011. Estimation of tensors and tensor-derived measures in diffusional kurtosis imaging. Magn Reson Med. 65(3), 823-836 .. [2] Jensen, Jens H., Joseph A. Helpern, Anita Ramani, Hanzhang Lu, and Kyle Kaczynski. 2005. Diffusional Kurtosis Imaging: The Quantification of Non-Gaussian Water Diffusion by Means of Magnetic Resonance Imaging. MRM 53 (6):1432-40. """ io_it = self.get_io_iterator() for (dwi, bval, bvec, mask, otensor, ofa, oga, orgb, omd, oad, orad, omode, oevecs, oevals, odk_tensor, omk, oak, ork) in io_it: logging.info('Computing DKI metrics for {0}'.format(dwi)) data, affine = load_nifti(dwi) if mask is not None: mask = load_nifti_data(mask).astype(bool) dkfit, _ = self.get_fitted_tensor(data, mask, bval, bvec, b0_threshold) if not save_metrics: save_metrics = [ 'mk', 'rk', 'ak', 'fa', 'md', 'rd', 'ad', 'ga', 'rgb', 'mode', 'evec', 'eval', 'dt_tensor', 'dk_tensor' ] evals, evecs, kt = split_dki_param(dkfit.model_params) FA = fractional_anisotropy(evals) FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) if 'dt_tensor' in save_metrics: tensor_vals = lower_triangular(dkfit.quadratic_form) correct_order = [0, 1, 3, 2, 4, 5] tensor_vals_reordered = tensor_vals[..., correct_order] save_nifti(otensor, tensor_vals_reordered.astype(np.float32), affine) if 'dk_tensor' in save_metrics: save_nifti(odk_tensor, dkfit.kt.astype(np.float32), affine) if 'fa' in save_metrics: save_nifti(ofa, FA.astype(np.float32), affine) if 'ga' in save_metrics: GA = geodesic_anisotropy(dkfit.evals) save_nifti(oga, GA.astype(np.float32), affine) if 'rgb' in save_metrics: RGB = color_fa(FA, dkfit.evecs) save_nifti(orgb, np.array(255 * RGB, 'uint8'), affine) if 'md' in save_metrics: MD = mean_diffusivity(dkfit.evals) save_nifti(omd, MD.astype(np.float32), affine) if 'ad' in save_metrics: AD = axial_diffusivity(dkfit.evals) save_nifti(oad, AD.astype(np.float32), affine) if 'rd' in save_metrics: RD = radial_diffusivity(dkfit.evals) save_nifti(orad, RD.astype(np.float32), affine) if 'mode' in save_metrics: MODE = get_mode(dkfit.quadratic_form) save_nifti(omode, MODE.astype(np.float32), affine) if 'evec' in save_metrics: save_nifti(oevecs, dkfit.evecs.astype(np.float32), affine) if 'eval' in save_metrics: save_nifti(oevals, dkfit.evals.astype(np.float32), affine) if 'mk' in save_metrics: save_nifti(omk, dkfit.mk().astype(np.float32), affine) if 'ak' in save_metrics: save_nifti(oak, dkfit.ak().astype(np.float32), affine) if 'rk' in save_metrics: save_nifti(ork, dkfit.rk().astype(np.float32), affine) logging.info('DKI metrics saved in {0}'.format( os.path.dirname(oevals)))
def dmri_recon(sid, data_dir, out_dir, resolution, recon='csd', num_threads=2): import tempfile #tempfile.tempdir = '/om/scratch/Fri/ksitek/' import os oldval = None if 'MKL_NUM_THREADS' in os.environ: oldval = os.environ['MKL_NUM_THREADS'] os.environ['MKL_NUM_THREADS'] = '%d' % num_threads ompoldval = None if 'OMP_NUM_THREADS' in os.environ: ompoldval = os.environ['OMP_NUM_THREADS'] os.environ['OMP_NUM_THREADS'] = '%d' % num_threads import nibabel as nib import numpy as np from glob import glob if resolution == '0.2mm': filename = 'Reg_S64550_nii4d.nii' fimg = os.path.abspath(glob(os.path.join(data_dir, filename))[0]) else: filename = 'Reg_S64550_nii4d_resamp-%s.nii.gz'%(resolution) fimg = os.path.abspath(glob(os.path.join(data_dir, 'resample', filename))[0]) print("dwi file = %s"%fimg) fbvec = os.path.abspath(glob(os.path.join(data_dir, 'bvecs', 'camino_120_RAS_flipped-xy.bvecs'))[0]) print("bvec file = %s"%fbvec) fbval = os.path.abspath(glob(os.path.join(data_dir, 'bvecs', 'camino_120_RAS.bvals'))[0]) print("bval file = %s"%fbval) img = nib.load(fimg) data = img.get_data() affine = img.get_affine() prefix = sid from dipy.io import read_bvals_bvecs from dipy.core.gradients import vector_norm bvals, bvecs = read_bvals_bvecs(fbval, fbvec) b0idx = [] for idx, val in enumerate(bvals): if val < 1: pass #bvecs[idx] = [1, 0, 0] else: b0idx.append(idx) #print "b0idx=%d"%idx #print "input bvecs:" #print bvecs bvecs[b0idx, :] = bvecs[b0idx, :]/vector_norm(bvecs[b0idx])[:, None] #print "bvecs after normalization:" #print bvecs from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs) gtab.bvecs.shape == bvecs.shape gtab.bvecs gtab.bvals.shape == bvals.shape gtab.bvals from dipy.reconst.csdeconv import auto_response response, ratio = auto_response(gtab, data, roi_radius=10, fa_thr=0.1) # 0.7 #from dipy.segment.mask import median_otsu #b0_mask, mask = median_otsu(data[:, :, :, b0idx].mean(axis=3).squeeze(), 4, 4) if resolution == '0.2mm': mask_name = 'Reg_S64550_nii_b0-slice_mask.nii.gz' fmask1 = os.path.join(data_dir, mask_name) else: mask_name = 'Reg_S64550_nii_b0-slice_mask_resamp-%s.nii.gz'%(resolution) fmask1 = os.path.join(data_dir, 'resample', mask_name) print("fmask file = %s"%fmask1) mask = nib.load(fmask1).get_data() useFA = True print("creating model") if recon == 'csd': from dipy.reconst.csdeconv import ConstrainedSphericalDeconvModel model = ConstrainedSphericalDeconvModel(gtab, response) useFA = True elif recon == 'csa': from dipy.reconst.shm import CsaOdfModel, normalize_data model = CsaOdfModel(gtab, 4) useFA = False else: raise ValueError('only csd, csa supported currently') from dipy.reconst.dsi import (DiffusionSpectrumDeconvModel, DiffusionSpectrumModel) model = DiffusionSpectrumDeconvModel(gtab) fit = model.fit(data) from dipy.data import get_sphere sphere = get_sphere('symmetric724') #odfs = fit.odf(sphere) from dipy.reconst.peaks import peaks_from_model print("running peaks_from_model") peaks = peaks_from_model(model=model, data=data, sphere=sphere, mask=mask, return_sh=True, return_odf=False, normalize_peaks=True, npeaks=5, relative_peak_threshold=.5, min_separation_angle=25, parallel=num_threads > 1, nbr_processes=num_threads) from dipy.reconst.dti import TensorModel print("running tensor model") tenmodel = TensorModel(gtab) tenfit = tenmodel.fit(data, mask) from dipy.reconst.dti import fractional_anisotropy print("running FA") FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 fa_img = nib.Nifti1Image(FA, img.get_affine()) tensor_fa_file = os.path.abspath('%s_tensor_fa.nii.gz' % (prefix)) nib.save(fa_img, tensor_fa_file) from dipy.reconst.dti import axial_diffusivity print("running AD") AD = axial_diffusivity(tenfit.evals) AD[np.isnan(AD)] = 0 ad_img = nib.Nifti1Image(AD, img.get_affine()) tensor_ad_file = os.path.abspath('%s_tensor_ad.nii.gz' % (prefix)) nib.save(ad_img, tensor_ad_file) from dipy.reconst.dti import radial_diffusivity print("running RD") RD = radial_diffusivity(tenfit.evals) RD[np.isnan(RD)] = 0 rd_img = nib.Nifti1Image(RD, img.get_affine()) tensor_rd_file = os.path.abspath('%s_tensor_rd.nii.gz' % (prefix)) nib.save(rd_img, tensor_rd_file) from dipy.reconst.dti import mean_diffusivity print("running MD") MD = mean_diffusivity(tenfit.evals) MD[np.isnan(MD)] = 0 md_img = nib.Nifti1Image(MD, img.get_affine()) tensor_md_file = os.path.abspath('%s_tensor_md.nii.gz' % (prefix)) nib.save(md_img, tensor_md_file) evecs = tenfit.evecs evec_img = nib.Nifti1Image(evecs, img.get_affine()) tensor_evec_file = os.path.abspath('%s_tensor_evec.nii.gz' % (prefix)) nib.save(evec_img, tensor_evec_file) shm_coeff = fit.shm_coeff shm_coeff_file = os.path.abspath('%s_shm_coeff.nii.gz' % (prefix)) nib.save(nib.Nifti1Image(shm_coeff, img.get_affine()), shm_coeff_file) #from dipy.reconst.dti import quantize_evecs #peak_indices = quantize_evecs(tenfit.evecs, sphere.vertices) #eu = EuDX(FA, peak_indices, odf_vertices = sphere.vertices, #a_low=0.2, seeds=10**6, ang_thr=35) fa_img = nib.Nifti1Image(peaks.gfa, img.get_affine()) model_gfa_file = os.path.abspath('%s_%s_gfa.nii.gz' % (prefix, recon)) nib.save(fa_img, model_gfa_file) from dipy.tracking.eudx import EuDX print("reconstructing with EuDX") if useFA: eu = EuDX(FA, peaks.peak_indices[..., 0], odf_vertices = sphere.vertices, #a_low=0.1, seeds=10**6, ang_thr=45) else: eu = EuDX(peaks.gfa, peaks.peak_indices[..., 0], odf_vertices = sphere.vertices, #a_low=0.1, seeds=10**6, ang_thr=45) sl_fname = os.path.abspath('%s_%s_streamline.trk' % (prefix, recon)) """ #import dipy.tracking.metrics as dmetrics streamlines = ((sl, None, None) for sl in eu) # if dmetrics.length(sl) > 15) hdr = nib.trackvis.empty_header() hdr['voxel_size'] = fa_img.get_header().get_zooms()[:3] hdr['voxel_order'] = 'RAS' #LAS hdr['dim'] = FA.shape[:3] nib.trackvis.write(sl_fname, streamlines, hdr, points_space='voxel') """ # trying new dipy.io.streamline module, per email to neuroimaging list # 2018.04.05 from nibabel.streamlines import Field from nibabel.orientations import aff2axcodes affine = img.get_affine() vox_size=fa_img.get_header().get_zooms()[:3] fov_shape=FA.shape[:3] if vox_size is not None and fov_shape is not None: hdr = {} hdr[Field.VOXEL_TO_RASMM] = affine.copy() hdr[Field.VOXEL_SIZES] = vox_size hdr[Field.DIMENSIONS] = fov_shape hdr[Field.VOXEL_ORDER] = "".join(aff2axcodes(affine)) tractogram = nib.streamlines.Tractogram(eu) tractogram.affine_to_rasmm = affine trk_file = nib.streamlines.TrkFile(tractogram, header=hdr) nib.streamlines.save(trk_file, sl_fname) if oldval: os.environ['MKL_NUM_THREADS'] = oldval else: del os.environ['MKL_NUM_THREADS'] if ompoldval: os.environ['OMP_NUM_THREADS'] = ompoldval else: del os.environ['OMP_NUM_THREADS'] assert tensor_fa_file assert tensor_evec_file assert model_gfa_file assert tensor_ad_file assert tensor_rd_file assert tensor_md_file assert shm_coeff_file print('all output files created') return tensor_fa_file, tensor_evec_file, model_gfa_file, sl_fname, affine, tensor_ad_file, tensor_rd_file, tensor_md_file, shm_coeff_file
def mask_for_response_msmt(gtab, data, roi_center=None, roi_radii=10, wm_fa_thr=0.7, gm_fa_thr=0.2, csf_fa_thr=0.1, gm_md_thr=0.0007, csf_md_thr=0.002): """ Computation of masks for multi-shell multi-tissue (msmt) response function using FA and MD. Parameters ---------- gtab : GradientTable data : ndarray diffusion data (4D) roi_center : array-like, (3,) Center of ROI in data. If center is None, it is assumed that it is the center of the volume with shape `data.shape[:3]`. roi_radii : int or array-like, (3,) radii of cuboid ROI wm_fa_thr : float FA threshold for WM. gm_fa_thr : float FA threshold for GM. csf_fa_thr : float FA threshold for CSF. gm_md_thr : float MD threshold for GM. csf_md_thr : float MD threshold for CSF. Returns ------- mask_wm : ndarray Mask of voxels within the ROI and with FA above the FA threshold for WM. mask_gm : ndarray Mask of voxels within the ROI and with FA below the FA threshold for GM and with MD below the MD threshold for GM. mask_csf : ndarray Mask of voxels within the ROI and with FA below the FA threshold for CSF and with MD below the MD threshold for CSF. Notes ----- In msmt-CSD there is an important pre-processing step: the estimation of every tissue's response function. In order to do this, we look for voxels corresponding to WM, GM and CSF. This function aims to accomplish that by returning a mask of voxels within a ROI and who respect some threshold constraints, for each tissue. More precisely, the WM mask must have a FA value above a given threshold. The GM mask and CSF mask must have a FA below given thresholds and a MD below other thresholds. To get the FA and MD, we need to fit a Tensor model to the datasets. """ if len(data.shape) < 4: msg = """Data must be 4D (3D image + directions). To use a 2D image, please reshape it into a (N, N, 1, ndirs) array.""" raise ValueError(msg) if isinstance(roi_radii, numbers.Number): roi_radii = (roi_radii, roi_radii, roi_radii) if roi_center is None: roi_center = np.array(data.shape[:3]) // 2 roi_radii = _roi_in_volume(data.shape, np.asarray(roi_center), np.asarray(roi_radii)) roi_mask = _mask_from_roi(data.shape[:3], roi_center, roi_radii) list_bvals = unique_bvals_tolerance(gtab.bvals) if not np.all(list_bvals <= 1200): msg_bvals = """Some b-values are higher than 1200. The DTI fit might be affected.""" warnings.warn(msg_bvals, UserWarning) ten = TensorModel(gtab) tenfit = ten.fit(data, mask=roi_mask) fa = fractional_anisotropy(tenfit.evals) fa[np.isnan(fa)] = 0 md = mean_diffusivity(tenfit.evals) md[np.isnan(md)] = 0 mask_wm = np.zeros(fa.shape, dtype=np.int64) mask_wm[fa > wm_fa_thr] = 1 mask_wm *= roi_mask md_mask_gm = np.zeros(md.shape, dtype=np.int64) md_mask_gm[(md < gm_md_thr)] = 1 fa_mask_gm = np.zeros(fa.shape, dtype=np.int64) fa_mask_gm[(fa < gm_fa_thr) & (fa > 0)] = 1 mask_gm = md_mask_gm * fa_mask_gm mask_gm *= roi_mask md_mask_csf = np.zeros(md.shape, dtype=np.int64) md_mask_csf[(md < csf_md_thr) & (md > 0)] = 1 fa_mask_csf = np.zeros(fa.shape, dtype=np.int64) fa_mask_csf[(fa < csf_fa_thr) & (fa > 0)] = 1 mask_csf = md_mask_csf * fa_mask_csf mask_csf *= roi_mask msg = """No voxel with a {0} than {1} were found. Try a larger roi or a {2} threshold for {3}.""" if np.sum(mask_wm) == 0: msg_fa = msg.format('FA higher', str(wm_fa_thr), 'lower FA', 'WM') warnings.warn(msg_fa, UserWarning) if np.sum(mask_gm) == 0: msg_fa = msg.format('FA lower', str(gm_fa_thr), 'higher FA', 'GM') msg_md = msg.format('MD lower', str(gm_md_thr), 'higher MD', 'GM') warnings.warn(msg_fa, UserWarning) warnings.warn(msg_md, UserWarning) if np.sum(mask_csf) == 0: msg_fa = msg.format('FA lower', str(csf_fa_thr), 'higher FA', 'CSF') msg_md = msg.format('MD lower', str(csf_md_thr), 'higher MD', 'CSF') warnings.warn(msg_fa, UserWarning) warnings.warn(msg_md, UserWarning) return mask_wm, mask_gm, mask_csf
def dti_processing(base_dir, subject_id, visit_id, output_dir, denoised_data=True, indexes=["FA", "MD"], output_type="NIFTI"): """Function to derive dti indexes from dwi data using dipy functions Parameters ---------- base_dir : absolute path pointing at the root directory of the bids repository subject_id: string, the tag subject (i.e. sub-sublabel) of the subject to treat visit_id: the VALUE of the session tag to be treated (i.e. for ses-02, only "02" need to be entered) output_dir: absolute path of the directories were the indexes will be written denoised_data: boolean, should the denoised (True) or only the eddy current correct and realigned data (False) be used for fitting ? indexes : list, a list of the indexes that will be written, accepted values are ["FA", "MD", "RD", "AD"] ... : other parameters related to the dipy functions used """ if output_type == "NIFTI": ext = ".nii" elif output_type == "NIFTI_GZ": ext = ".nii.gz" else: raise ValueError( "Output file type {} not recognized".format(output_type)) diff_dir = "{}/{}/ses-{}/dwi".format(base_dir, subject_id, visit_id) fbval = glob("{}/*bval".format(diff_dir))[0] fbvec = glob("{}/*bvec".format(diff_dir))[0] dti_preroc_dir = "{}/derivatives/fsl-dipy_dti-preproc/{}/ses-{}".format( base_dir, subject_id, visit_id) if denoised_data: fdwi = glob("{}/*_denoised*".format(dti_preroc_dir))[0] else: fdwi = glob("{}/*_masked.nii*".format(dti_preroc_dir))[0] bvals, bvecs = read_bvals_bvecs(fbval, fbvec) gtab = gradient_table(bvals, bvecs) img = nb.load(fdwi) mask = nb.load(glob("{}/*_mask.nii*".format(dti_preroc_dir))[0]) x_ix, y_ix, z_ix, mask_crop, maskdata_crop = crop_and_indexes(mask, img) tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(maskdata_crop) output_base_name = get_base_name_all_type(fbval) if "FA" in indexes: print("Computing FA") FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 opt = np.zeros(mask.shape) opt[x_ix, y_ix, z_ix] = FA * mask_crop fa_img = nb.Nifti1Image(opt.astype(np.float32), img.affine) nb.save(fa_img, "{}/{}_FA{}".format(output_dir, output_base_name, ext)) if "MD" in indexes: print("Computing MD") MD = mean_diffusivity(tenfit.evals) MD[np.isnan(MD)] = 0 opt = np.zeros(mask.shape) opt[x_ix, y_ix, z_ix] = MD * mask_crop md_img = nb.Nifti1Image(opt.astype(np.float32), img.affine) nb.save(md_img, "{}/{}_MD{}".format(output_dir, output_base_name, ext)) if "RD" in indexes: print("Computing RD") RD = radial_diffusivity(tenfit.evals) RD[np.isnan(RD)] = 0 opt = np.zeros(mask.shape) opt[x_ix, y_ix, z_ix] = RD * mask_crop rd_img = nb.Nifti1Image(opt.astype(np.float32), img.affine) nb.save(rd_img, "{}/{}_RD{}".format(output_dir, output_base_name, ext)) if "AD" in indexes: print("Computing AD") AD = axial_diffusivity(tenfit.evals) AD[np.isnan(AD)] = 0 opt = np.zeros(mask.shape) opt[x_ix, y_ix, z_ix] = AD * mask_crop ad_img = nb.Nifti1Image(opt.astype(np.float32), img.affine) nb.save(ad_img, "{}/{}_AD{}".format(output_dir, output_base_name, ext))
reached. Here we calibrate the response function on a small part of the data. """ from dipy.reconst.csdeconv import recursive_response """ A WM mask can shorten computation time for the whole dataset. Here it is created based on the DTI fit. """ import dipy.reconst.dti as dti tenmodel = dti.TensorModel(gtab) tenfit = tenmodel.fit(data, mask=data[..., 0] > 200) from dipy.reconst.dti import fractional_anisotropy FA = fractional_anisotropy(tenfit.evals) MD = dti.mean_diffusivity(tenfit.evals) wm_mask = (np.logical_or(FA >= 0.4, (np.logical_and(FA >= 0.15, MD >= 0.0011)))) response = recursive_response(gtab, data, mask=wm_mask, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=True) """ We can check the shape of the signal of the response function, which should be
def dmri_recon(sid, data_dir, out_dir, resolution, recon='csd', dirs='', num_threads=2): import tempfile #tempfile.tempdir = '/om/scratch/Fri/ksitek/' import os oldval = None if 'MKL_NUM_THREADS' in os.environ: oldval = os.environ['MKL_NUM_THREADS'] os.environ['MKL_NUM_THREADS'] = '%d' % num_threads ompoldval = None if 'OMP_NUM_THREADS' in os.environ: ompoldval = os.environ['OMP_NUM_THREADS'] os.environ['OMP_NUM_THREADS'] = '%d' % num_threads import nibabel as nib import numpy as np from glob import glob if resolution == '0.2mm': filename = 'Reg_S64550_nii4d.nii' #filename = 'angular_resample/dwi_%s.nii.gz'%dirs fimg = os.path.abspath(glob(os.path.join(data_dir, filename))[0]) else: filename = 'Reg_S64550_nii4d_resamp-%s.nii.gz' % (resolution) fimg = os.path.abspath( glob(os.path.join(data_dir, 'resample', filename))[0]) print("dwi file = %s" % fimg) fbval = os.path.abspath( glob(os.path.join(data_dir, 'bvecs', 'camino_120_RAS.bvals'))[0]) print("bval file = %s" % fbval) fbvec = os.path.abspath( glob(os.path.join(data_dir, 'bvecs', 'camino_120_RAS_flipped-xy.bvecs'))[0]) # 'angular_resample', # 'dwi_%s.bvecs'%dirs))[0]) print("bvec file = %s" % fbvec) img = nib.load(fimg) data = img.get_fdata() affine = img.get_affine() prefix = sid from dipy.io import read_bvals_bvecs bvals, bvecs = read_bvals_bvecs(fbval, fbvec) ''' from dipy.core.gradients import vector_norm b0idx = [] for idx, val in enumerate(bvals): if val < 1: pass #bvecs[idx] = [1, 0, 0] else: b0idx.append(idx) #print "b0idx=%d"%idx #print "input bvecs:" #print bvecs bvecs[b0idx, :] = bvecs[b0idx, :]/vector_norm(bvecs[b0idx])[:, None] #print "bvecs after normalization:" #print bvecs ''' from dipy.core.gradients import gradient_table gtab = gradient_table(bvals, bvecs) gtab.bvecs.shape == bvecs.shape gtab.bvecs gtab.bvals.shape == bvals.shape gtab.bvals #from dipy.segment.mask import median_otsu #b0_mask, mask = median_otsu(data[:, :, :, b0idx].mean(axis=3).squeeze(), 4, 4) if resolution == '0.2mm': mask_name = 'Reg_S64550_nii_b0-slice_mask.nii.gz' fmask1 = os.path.join(data_dir, mask_name) else: mask_name = 'Reg_S64550_nii_b0-slice_mask_resamp-%s.nii.gz' % ( resolution) fmask1 = os.path.join(data_dir, 'resample', mask_name) print("fmask file = %s" % fmask1) mask = nib.load(fmask1).get_fdata() ''' DTI model & save metrics ''' from dipy.reconst.dti import TensorModel print("running tensor model") tenmodel = TensorModel(gtab) tenfit = tenmodel.fit(data, mask) from dipy.reconst.dti import fractional_anisotropy print("running FA") FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 fa_img = nib.Nifti1Image(FA, img.get_affine()) tensor_fa_file = os.path.abspath('%s_tensor_fa.nii.gz' % (prefix)) nib.save(fa_img, tensor_fa_file) from dipy.reconst.dti import axial_diffusivity print("running AD") AD = axial_diffusivity(tenfit.evals) AD[np.isnan(AD)] = 0 ad_img = nib.Nifti1Image(AD, img.get_affine()) tensor_ad_file = os.path.abspath('%s_tensor_ad.nii.gz' % (prefix)) nib.save(ad_img, tensor_ad_file) from dipy.reconst.dti import radial_diffusivity print("running RD") RD = radial_diffusivity(tenfit.evals) RD[np.isnan(RD)] = 0 rd_img = nib.Nifti1Image(RD, img.get_affine()) tensor_rd_file = os.path.abspath('%s_tensor_rd.nii.gz' % (prefix)) nib.save(rd_img, tensor_rd_file) from dipy.reconst.dti import mean_diffusivity print("running MD") MD = mean_diffusivity(tenfit.evals) MD[np.isnan(MD)] = 0 md_img = nib.Nifti1Image(MD, img.get_affine()) tensor_md_file = os.path.abspath('%s_tensor_md.nii.gz' % (prefix)) nib.save(md_img, tensor_md_file) evecs = tenfit.evecs evec_img = nib.Nifti1Image(evecs, img.get_affine()) tensor_evec_file = os.path.abspath('%s_tensor_evec.nii.gz' % (prefix)) nib.save(evec_img, tensor_evec_file) ''' ODF model ''' useFA = True print("creating %s model" % recon) if recon == 'csd': from dipy.reconst.csdeconv import ConstrainedSphericalDeconvModel from dipy.reconst.csdeconv import auto_response response, ratio = auto_response(gtab, data, roi_radius=10, fa_thr=0.5) # 0.7 model = ConstrainedSphericalDeconvModel(gtab, response) useFA = True return_sh = True elif recon == 'csa': from dipy.reconst.shm import CsaOdfModel, normalize_data model = CsaOdfModel(gtab, sh_order=8) useFA = True return_sh = True elif recon == 'gqi': from dipy.reconst.gqi import GeneralizedQSamplingModel model = GeneralizedQSamplingModel(gtab) return_sh = False else: raise ValueError('only csd, csa supported currently') from dipy.reconst.dsi import (DiffusionSpectrumDeconvModel, DiffusionSpectrumModel) model = DiffusionSpectrumDeconvModel(gtab) '''reconstruct ODFs''' from dipy.data import get_sphere sphere = get_sphere('symmetric724') #odfs = fit.odf(sphere) # with CSD/GQI, uses > 50GB per core; don't get greedy with cores! from dipy.reconst.peaks import peaks_from_model print("running peaks_from_model") peaks = peaks_from_model( model=model, data=data, sphere=sphere, mask=mask, return_sh=return_sh, return_odf=False, normalize_peaks=True, npeaks=5, relative_peak_threshold=.5, min_separation_angle=10, #25, parallel=num_threads > 1, nbr_processes=num_threads) # save the peaks from dipy.io.peaks import save_peaks peaks_file = os.path.abspath('%s_peaks.pam5' % (prefix)) save_peaks(peaks_file, peaks) # save the spherical harmonics shm_coeff_file = os.path.abspath('%s_shm_coeff.nii.gz' % (prefix)) if return_sh: shm_coeff = peaks.shm_coeff nib.save(nib.Nifti1Image(shm_coeff, img.get_affine()), shm_coeff_file) else: # if it's not a spherical model, output it as an essentially null file np.savetxt(shm_coeff_file, [0]) # save the generalized fractional anisotropy image gfa_img = nib.Nifti1Image(peaks.gfa, img.get_affine()) model_gfa_file = os.path.abspath('%s_%s_gfa.nii.gz' % (prefix, recon)) nib.save(gfa_img, model_gfa_file) #from dipy.reconst.dti import quantize_evecs #peak_indices = quantize_evecs(tenfit.evecs, sphere.vertices) #eu = EuDX(FA, peak_indices, odf_vertices = sphere.vertices, #a_low=0.2, seeds=10**6, ang_thr=35) ''' probabilistic tracking ''' ''' from dipy.direction import ProbabilisticDirectionGetter from dipy.tracking.local import LocalTracking from dipy.tracking.streamline import Streamlines from dipy.io.streamline import save_trk prob_dg = ProbabilisticDirectionGetter.from_shcoeff(shm_coeff, max_angle=45., sphere=sphere) streamlines_generator = LocalTracking(prob_dg, affine, step_size=.5, max_cross=1) # Generate streamlines object streamlines = Streamlines(streamlines_generator) affine = img.get_affine() vox_size=fa_img.get_header().get_zooms()[:3] fname = os.path.abspath('%s_%s_prob_streamline.trk' % (prefix, recon)) save_trk(fname, streamlines, affine, vox_size=vox_size) ''' ''' deterministic tracking with EuDX method''' from dipy.tracking.eudx import EuDX print("reconstructing with EuDX") if useFA: eu = EuDX( FA, peaks.peak_indices[..., 0], odf_vertices=sphere.vertices, a_low=0.001, # default is 0.0239 seeds=10**6, ang_thr=75) else: eu = EuDX( peaks.gfa, peaks.peak_indices[..., 0], odf_vertices=sphere.vertices, #a_low=0.1, seeds=10**6, ang_thr=45) sl_fname = os.path.abspath('%s_%s_det_streamline.trk' % (prefix, recon)) # trying new dipy.io.streamline module, per email to neuroimaging list # 2018.04.05 from nibabel.streamlines import Field from nibabel.orientations import aff2axcodes affine = img.get_affine() vox_size = fa_img.get_header().get_zooms()[:3] fov_shape = FA.shape[:3] if vox_size is not None and fov_shape is not None: hdr = {} hdr[Field.VOXEL_TO_RASMM] = affine.copy() hdr[Field.VOXEL_SIZES] = vox_size hdr[Field.DIMENSIONS] = fov_shape hdr[Field.VOXEL_ORDER] = "".join(aff2axcodes(affine)) tractogram = nib.streamlines.Tractogram(eu) tractogram.affine_to_rasmm = affine trk_file = nib.streamlines.TrkFile(tractogram, header=hdr) nib.streamlines.save(trk_file, sl_fname) if oldval: os.environ['MKL_NUM_THREADS'] = oldval else: del os.environ['MKL_NUM_THREADS'] if ompoldval: os.environ['OMP_NUM_THREADS'] = ompoldval else: del os.environ['OMP_NUM_THREADS'] print('all output files created') return (tensor_fa_file, tensor_evec_file, model_gfa_file, sl_fname, affine, tensor_ad_file, tensor_rd_file, tensor_md_file, shm_coeff_file, peaks_file)
def run(self, input_files, bvalues_files, bvectors_files, mask_files, b0_threshold=50, bvecs_tol=0.01, save_metrics=[], out_dir='', out_tensor='tensors.nii.gz', out_fa='fa.nii.gz', out_ga='ga.nii.gz', out_rgb='rgb.nii.gz', out_md='md.nii.gz', out_ad='ad.nii.gz', out_rd='rd.nii.gz', out_mode='mode.nii.gz', out_evec='evecs.nii.gz', out_eval='evals.nii.gz'): """ Workflow for tensor reconstruction and for computing DTI metrics. using Weighted Least-Squares. Performs a tensor reconstruction on the files by 'globing' ``input_files`` and saves the DTI metrics in a directory specified by ``out_dir``. Parameters ---------- input_files : string Path to the input volumes. This path may contain wildcards to process multiple inputs at once. bvalues_files : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. bvectors_files : string Path to the bvectors files. This path may contain wildcards to use multiple bvectors files at once. mask_files : string Path to the input masks. This path may contain wildcards to use multiple masks at once. (default: No mask used) b0_threshold : float, optional Threshold used to find b=0 directions (default 0.0) bvecs_tol : float, optional Threshold used to check that norm(bvec) = 1 +/- bvecs_tol b-vectors are unit vectors (default 0.01) save_metrics : variable string, optional List of metrics to save. Possible values: fa, ga, rgb, md, ad, rd, mode, tensor, evec, eval (default [] (all)) out_dir : string, optional Output directory (default input file directory) out_tensor : string, optional Name of the tensors volume to be saved (default 'tensors.nii.gz') out_fa : string, optional Name of the fractional anisotropy volume to be saved (default 'fa.nii.gz') out_ga : string, optional Name of the geodesic anisotropy volume to be saved (default 'ga.nii.gz') out_rgb : string, optional Name of the color fa volume to be saved (default 'rgb.nii.gz') out_md : string, optional Name of the mean diffusivity volume to be saved (default 'md.nii.gz') out_ad : string, optional Name of the axial diffusivity volume to be saved (default 'ad.nii.gz') out_rd : string, optional Name of the radial diffusivity volume to be saved (default 'rd.nii.gz') out_mode : string, optional Name of the mode volume to be saved (default 'mode.nii.gz') out_evec : string, optional Name of the eigenvectors volume to be saved (default 'evecs.nii.gz') out_eval : string, optional Name of the eigenvalues to be saved (default 'evals.nii.gz') References ---------- .. [1] Basser, P.J., Mattiello, J., LeBihan, D., 1994. Estimation of the effective self-diffusion tensor from the NMR spin echo. J Magn Reson B 103, 247-254. .. [2] Basser, P., Pierpaoli, C., 1996. Microstructural and physiological features of tissues elucidated by quantitative diffusion-tensor MRI. Journal of Magnetic Resonance 111, 209-219. .. [3] Lin-Ching C., Jones D.K., Pierpaoli, C. 2005. RESTORE: Robust estimation of tensors by outlier rejection. MRM 53: 1088-1095 .. [4] hung, SW., Lu, Y., Henry, R.G., 2006. Comparison of bootstrap approaches for estimation of uncertainties of DTI parameters. NeuroImage 33, 531-541. """ io_it = self.get_io_iterator() for dwi, bval, bvec, mask, otensor, ofa, oga, orgb, omd, oad, orad, \ omode, oevecs, oevals in io_it: logging.info('Computing DTI metrics for {0}'.format(dwi)) data, affine = load_nifti(dwi) if mask is not None: mask = nib.load(mask).get_data().astype(np.bool) tenfit, _ = self.get_fitted_tensor(data, mask, bval, bvec, b0_threshold, bvecs_tol) if not save_metrics: save_metrics = ['fa', 'md', 'rd', 'ad', 'ga', 'rgb', 'mode', 'evec', 'eval', 'tensor'] FA = fractional_anisotropy(tenfit.evals) FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) if 'tensor' in save_metrics: tensor_vals = lower_triangular(tenfit.quadratic_form) correct_order = [0, 1, 3, 2, 4, 5] tensor_vals_reordered = tensor_vals[..., correct_order] save_nifti(otensor, tensor_vals_reordered.astype(np.float32), affine) if 'fa' in save_metrics: save_nifti(ofa, FA.astype(np.float32), affine) if 'ga' in save_metrics: GA = geodesic_anisotropy(tenfit.evals) save_nifti(oga, GA.astype(np.float32), affine) if 'rgb' in save_metrics: RGB = color_fa(FA, tenfit.evecs) save_nifti(orgb, np.array(255 * RGB, 'uint8'), affine) if 'md' in save_metrics: MD = mean_diffusivity(tenfit.evals) save_nifti(omd, MD.astype(np.float32), affine) if 'ad' in save_metrics: AD = axial_diffusivity(tenfit.evals) save_nifti(oad, AD.astype(np.float32), affine) if 'rd' in save_metrics: RD = radial_diffusivity(tenfit.evals) save_nifti(orad, RD.astype(np.float32), affine) if 'mode' in save_metrics: MODE = get_mode(tenfit.quadratic_form) save_nifti(omode, MODE.astype(np.float32), affine) if 'evec' in save_metrics: save_nifti(oevecs, tenfit.evecs.astype(np.float32), affine) if 'eval' in save_metrics: save_nifti(oevals, tenfit.evals.astype(np.float32), affine) dname_ = os.path.dirname(oevals) if dname_ == '': logging.info('DTI metrics saved in current directory') else: logging.info( 'DTI metrics saved in {0}'.format(dname_))
def run(self, input_files, bvalues_files, bvectors_files, mask_files, b0_threshold=50.0, save_metrics=[], out_dir='', out_dt_tensor='dti_tensors.nii.gz', out_fa='fa.nii.gz', out_ga='ga.nii.gz', out_rgb='rgb.nii.gz', out_md='md.nii.gz', out_ad='ad.nii.gz', out_rd='rd.nii.gz', out_mode='mode.nii.gz', out_evec='evecs.nii.gz', out_eval='evals.nii.gz', out_dk_tensor="dki_tensors.nii.gz", out_mk="mk.nii.gz", out_ak="ak.nii.gz", out_rk="rk.nii.gz"): """ Workflow for Diffusion Kurtosis reconstruction and for computing DKI metrics. Performs a DKI reconstruction on the files by 'globing' ``input_files`` and saves the DKI metrics in a directory specified by ``out_dir``. Parameters ---------- input_files : string Path to the input volumes. This path may contain wildcards to process multiple inputs at once. bvalues_files : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. bvectors_files : string Path to the bvalues files. This path may contain wildcards to use multiple bvalues files at once. mask_files : string Path to the input masks. This path may contain wildcards to use multiple masks at once. (default: No mask used) b0_threshold : float, optional Threshold used to find b=0 directions (default 0.0) save_metrics : variable string, optional List of metrics to save. Possible values: fa, ga, rgb, md, ad, rd, mode, tensor, evec, eval (default [] (all)) out_dir : string, optional Output directory (default input file directory) out_dt_tensor : string, optional Name of the tensors volume to be saved (default: 'dti_tensors.nii.gz') out_dk_tensor : string, optional Name of the tensors volume to be saved (default 'dki_tensors.nii.gz') out_fa : string, optional Name of the fractional anisotropy volume to be saved (default 'fa.nii.gz') out_ga : string, optional Name of the geodesic anisotropy volume to be saved (default 'ga.nii.gz') out_rgb : string, optional Name of the color fa volume to be saved (default 'rgb.nii.gz') out_md : string, optional Name of the mean diffusivity volume to be saved (default 'md.nii.gz') out_ad : string, optional Name of the axial diffusivity volume to be saved (default 'ad.nii.gz') out_rd : string, optional Name of the radial diffusivity volume to be saved (default 'rd.nii.gz') out_mode : string, optional Name of the mode volume to be saved (default 'mode.nii.gz') out_evec : string, optional Name of the eigenvectors volume to be saved (default 'evecs.nii.gz') out_eval : string, optional Name of the eigenvalues to be saved (default 'evals.nii.gz') out_mk : string, optional Name of the mean kurtosis to be saved (default: 'mk.nii.gz') out_ak : string, optional Name of the axial kurtosis to be saved (default: 'ak.nii.gz') out_rk : string, optional Name of the radial kurtosis to be saved (default: 'rk.nii.gz') References ---------- .. [1] Tabesh, A., Jensen, J.H., Ardekani, B.A., Helpern, J.A., 2011. Estimation of tensors and tensor-derived measures in diffusional kurtosis imaging. Magn Reson Med. 65(3), 823-836 .. [2] Jensen, Jens H., Joseph A. Helpern, Anita Ramani, Hanzhang Lu, and Kyle Kaczynski. 2005. Diffusional Kurtosis Imaging: The Quantification of Non-Gaussian Water Diffusion by Means of Magnetic Resonance Imaging. MRM 53 (6):1432-40. """ io_it = self.get_io_iterator() for (dwi, bval, bvec, mask, otensor, ofa, oga, orgb, omd, oad, orad, omode, oevecs, oevals, odk_tensor, omk, oak, ork) in io_it: logging.info('Computing DKI metrics for {0}'.format(dwi)) data, affine = load_nifti(dwi) if mask is not None: mask = nib.load(mask).get_data().astype(np.bool) dkfit, _ = self.get_fitted_tensor(data, mask, bval, bvec, b0_threshold) if not save_metrics: save_metrics = ['mk', 'rk', 'ak', 'fa', 'md', 'rd', 'ad', 'ga', 'rgb', 'mode', 'evec', 'eval', 'dt_tensor', 'dk_tensor'] evals, evecs, kt = split_dki_param(dkfit.model_params) FA = fractional_anisotropy(evals) FA[np.isnan(FA)] = 0 FA = np.clip(FA, 0, 1) if 'dt_tensor' in save_metrics: tensor_vals = lower_triangular(dkfit.quadratic_form) correct_order = [0, 1, 3, 2, 4, 5] tensor_vals_reordered = tensor_vals[..., correct_order] save_nifti(otensor, tensor_vals_reordered.astype(np.float32), affine) if 'dk_tensor' in save_metrics: save_nifti(odk_tensor, dkfit.kt.astype(np.float32), affine) if 'fa' in save_metrics: save_nifti(ofa, FA.astype(np.float32), affine) if 'ga' in save_metrics: GA = geodesic_anisotropy(dkfit.evals) save_nifti(oga, GA.astype(np.float32), affine) if 'rgb' in save_metrics: RGB = color_fa(FA, dkfit.evecs) save_nifti(orgb, np.array(255 * RGB, 'uint8'), affine) if 'md' in save_metrics: MD = mean_diffusivity(dkfit.evals) save_nifti(omd, MD.astype(np.float32), affine) if 'ad' in save_metrics: AD = axial_diffusivity(dkfit.evals) save_nifti(oad, AD.astype(np.float32), affine) if 'rd' in save_metrics: RD = radial_diffusivity(dkfit.evals) save_nifti(orad, RD.astype(np.float32), affine) if 'mode' in save_metrics: MODE = get_mode(dkfit.quadratic_form) save_nifti(omode, MODE.astype(np.float32), affine) if 'evec' in save_metrics: save_nifti(oevecs, dkfit.evecs.astype(np.float32), affine) if 'eval' in save_metrics: save_nifti(oevals, dkfit.evals.astype(np.float32), affine) if 'mk' in save_metrics: save_nifti(omk, dkfit.mk().astype(np.float32), affine) if 'ak' in save_metrics: save_nifti(oak, dkfit.ak().astype(np.float32), affine) if 'rk' in save_metrics: save_nifti(ork, dkfit.rk().astype(np.float32), affine) logging.info('DKI metrics saved in {0}'. format(os.path.dirname(oevals)))
def _run_interface(self, runtime): from dipy.core.gradients import GradientTable from dipy.reconst.dti import fractional_anisotropy, mean_diffusivity from dipy.reconst.csdeconv import recursive_response, auto_response img = nb.load(self.inputs.in_file) affine = img.get_affine() if isdefined(self.inputs.in_mask): msk = nb.load(self.inputs.in_mask).get_data() msk[msk > 0] = 1 msk[msk < 0] = 0 else: msk = np.ones(imref.get_shape()) data = img.get_data().astype(np.float32) gtab = self._get_gradient_table() evals = np.nan_to_num(nb.load(self.inputs.in_evals).get_data()) FA = np.nan_to_num(fractional_anisotropy(evals)) * msk indices = np.where(FA > self.inputs.fa_thresh) S0s = data[indices][:, np.nonzero(gtab.b0s_mask)[0]] S0 = np.mean(S0s) if self.inputs.auto: response, ratio = auto_response(gtab, data, roi_radius=self.inputs.roi_radius, fa_thr=self.inputs.fa_thresh) response = response[0].tolist() + [S0] elif self.inputs.recursive: MD = np.nan_to_num(mean_diffusivity(evals)) * msk indices = np.logical_or( FA >= 0.4, (np.logical_and(FA >= 0.15, MD >= 0.0011))) data = nb.load(self.inputs.in_file).get_data() response = recursive_response(gtab, data, mask=indices, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=True) ratio = abs(response[1] / response[0]) else: lambdas = evals[indices] l01 = np.sort(np.mean(lambdas, axis=0)) response = np.array([l01[-1], l01[-2], l01[-2], S0]) ratio = abs(response[1] / response[0]) if ratio > 0.25: IFLOGGER.warn(('Estimated response is not prolate enough. ' 'Ratio=%0.3f.') % ratio) elif ratio < 1.e-5 or np.any(np.isnan(response)): response = np.array([1.8e-3, 3.6e-4, 3.6e-4, S0]) IFLOGGER.warn( ('Estimated response is not valid, using a default one')) else: IFLOGGER.info(('Estimated response: %s') % str(response[:3])) np.savetxt(op.abspath(self.inputs.response), response) wm_mask = np.zeros_like(FA) wm_mask[indices] = 1 nb.Nifti1Image( wm_mask.astype(np.uint8), affine, None).to_filename(op.abspath(self.inputs.out_mask)) return runtime
def diffusion_components(dki_params, sphere='repulsion100', awf=None, mask=None): """ Extracts the restricted and hindered diffusion tensors of well aligned fibers from diffusion kurtosis imaging parameters [1]_. Parameters ---------- dki_params : ndarray (x, y, z, 27) or (n, 27) All parameters estimated from the diffusion kurtosis model. Parameters are ordered as follows: 1) Three diffusion tensor's eigenvalues 2) Three lines of the eigenvector matrix each containing the first, second and third coordinates of the eigenvector 3) Fifteen elements of the kurtosis tensor sphere : Sphere class instance, optional The sphere providing sample directions to sample the restricted and hindered cellular diffusion tensors. For more details see Fieremans et al., 2011. awf : ndarray (optional) Array containing values of the axonal water fraction that has the shape dki_params.shape[:-1]. If not given this will be automatically computed using :func:`axonal_water_fraction`" with function's default precision. mask : ndarray (optional) A boolean array used to mark the coordinates in the data that should be analyzed that has the shape dki_params.shape[:-1] Returns -------- edt : ndarray (x, y, z, 6) or (n, 6) Parameters of the hindered diffusion tensor. idt : ndarray (x, y, z, 6) or (n, 6) Parameters of the restricted diffusion tensor. Note ---- In the original article of DKI microstructural model [1]_, the hindered and restricted tensors were definde as the intra-cellular and extra-cellular diffusion compartments respectively. References ---------- .. [1] Fieremans E, Jensen JH, Helpern JA, 2011. White matter characterization with diffusional kurtosis imaging. Neuroimage 58(1):177-88. doi: 10.1016/j.neuroimage.2011.06.006 """ shape = dki_params.shape[:-1] # load gradient directions if not isinstance(sphere, dps.Sphere): sphere = get_sphere(sphere) # select voxels where to apply the single fiber model if mask is None: mask = np.ones(shape, dtype='bool') else: if mask.shape != shape: raise ValueError("Mask is not the same shape as dki_params.") else: mask = np.array(mask, dtype=bool, copy=False) # check or compute awf values if awf is None: awf = axonal_water_fraction(dki_params, sphere=sphere, mask=mask) else: if awf.shape != shape: raise ValueError("awf array is not the same shape as dki_params.") # Initialize hindered and restricted diffusion tensors edt_all = np.zeros(shape + (6,)) idt_all = np.zeros(shape + (6,)) # Generate matrix that converts apparant diffusion coefficients to tensors B = np.zeros((sphere.x.size, 6)) B[:, 0] = sphere.x * sphere.x # Bxx B[:, 1] = sphere.x * sphere.y * 2. # Bxy B[:, 2] = sphere.y * sphere.y # Byy B[:, 3] = sphere.x * sphere.z * 2. # Bxz B[:, 4] = sphere.y * sphere.z * 2. # Byz B[:, 5] = sphere.z * sphere.z # Bzz pinvB = np.linalg.pinv(B) # Compute hindered and restricted diffusion tensors for all voxels evals, evecs, kt = split_dki_param(dki_params) dt = lower_triangular(vec_val_vect(evecs, evals)) md = mean_diffusivity(evals) index = ndindex(mask.shape) for idx in index: if not mask[idx]: continue # sample apparent diffusion and kurtosis values di = directional_diffusion(dt[idx], sphere.vertices) ki = directional_kurtosis(dt[idx], md[idx], kt[idx], sphere.vertices, adc=di, min_kurtosis=0) edi = di * (1 + np.sqrt(ki * awf[idx] / (3.0 - 3.0 * awf[idx]))) edt = np.dot(pinvB, edi) edt_all[idx] = edt # We only move on if there is an axonal water fraction. # Otherwise, remaining params are already zero, so move on if awf[idx] == 0: continue # Convert apparent diffusion and kurtosis values to apparent diffusion # values of the hindered and restricted diffusion idi = di * (1 - np.sqrt(ki * (1.0 - awf[idx]) / (3.0 * awf[idx]))) # generate hindered and restricted diffusion tensors idt = np.dot(pinvB, idi) idt_all[idx] = idt return edt_all, idt_all