def test_split_dki_param(): dkiM = dki.DiffusionKurtosisModel(gtab_2s, fit_method="OLS") dkiF = dkiM.fit(DWI) evals, evecs, kt = dki.split_dki_param(dkiF.model_params) assert_array_almost_equal(evals, dkiF.evals) assert_array_almost_equal(evecs, dkiF.evecs) assert_array_almost_equal(kt, dkiF.kt)
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 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
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 dki_prediction(dki_params, gtab, S0=150): """ In Dipy versions < 0.12, there is a bug in DKI prediction, that doesn't allow using volumes of S0. This is temporary fix until the bug is fixed upstream. See: https://github.com/nipy/dipy/pull/1028 For now, we provide this as a fix, to monkey-patch into dipy.reconst.dki 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 gtab : a GradientTable class instance The gradient table for this prediction S0 : float or ndarray (optional) The non diffusion-weighted signal in every voxel, or across all voxels. Default: 150 Returns -------- S : (..., N) ndarray Simulated signal based on the DKI model: .. math:: S=S_{0}e^{-bD+\frac{1}{6}b^{2}D^{2}K} """ evals, evecs, kt = dki.split_dki_param(dki_params) # Define DKI design matrix according to given gtab A = dki.design_matrix(gtab) # Flat parameters and initialize pred_sig fevals = evals.reshape((-1, evals.shape[-1])) fevecs = evecs.reshape((-1,) + evecs.shape[-2:]) fkt = kt.reshape((-1, kt.shape[-1])) pred_sig = np.zeros((len(fevals), len(gtab.bvals))) if isinstance(S0, np.ndarray): S0_vol = np.reshape(S0, (len(fevals))) else: S0_vol = S0 # looping for all voxels for v in range(len(pred_sig)): DT = np.dot(np.dot(fevecs[v], np.diag(fevals[v])), fevecs[v].T) dt = dki.lower_triangular(DT) MD = (dt[0] + dt[2] + dt[5]) / 3 if isinstance(S0_vol, np.ndarray): this_S0 = S0_vol[v] else: this_S0 = S0_vol X = np.concatenate((dt, fkt[v] * MD * MD, np.array([np.log(this_S0)])), axis=0) pred_sig[v] = np.exp(np.dot(A, X)) # Reshape data according to the shape of dki_params pred_sig = pred_sig.reshape(dki_params.shape[:-1] + (pred_sig.shape[-1],)) return pred_sig
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