def test_check_multi_b(): bvals = np.array([1000, 1000, 1000, 1000, 2000, 2000, 2000, 2000, 0]) bvecs = generate_bvecs(bvals.shape[-1]) gtab = gradient_table(bvals, bvecs) npt.assert_(check_multi_b(gtab, 2, non_zero=False)) # We don't consider differences this small to be sufficient: bvals = np.array([1995, 1995, 1995, 1995, 2005, 2005, 2005, 2005, 0]) bvecs = generate_bvecs(bvals.shape[-1]) gtab = gradient_table(bvals, bvecs) npt.assert_(not check_multi_b(gtab, 2, non_zero=True)) # Unless you specify that you are interested in this magnitude of changes: npt.assert_(check_multi_b(gtab, 2, non_zero=True, bmag=1)) # Or if you consider zero to be one of your b-values: npt.assert_(check_multi_b(gtab, 2, non_zero=False))
def test_check_multi_b(): bvals = np.array([1000, 1000, 1000, 1000, 2000, 2000, 2000, 2000, 0]) bvecs = generate_bvecs(bvals.shape[-1]) gtab = gradient_table(bvals, bvecs) npt.assert_(check_multi_b(gtab, 2, non_zero=False)) # We don't consider differences this small to be sufficient: bvals = np.array([1995, 1995, 1995, 1995, 2005, 2005, 2005, 2005, 0]) bvecs = generate_bvecs(bvals.shape[-1]) gtab = gradient_table(bvals, bvecs) npt.assert_(not check_multi_b(gtab, 2, non_zero=True)) # Unless you specify that you are interested in this magnitude of changes: npt.assert_(check_multi_b(gtab, 2, non_zero=True, bmag=1)) # Or if you consider zero to be one of your b-values: npt.assert_(check_multi_b(gtab, 2, non_zero=False))
def __init__(self, gtab, fit_method="NLS", *args, **kwargs): """ Free Water Diffusion Tensor Model [1]_. Parameters ---------- gtab : GradientTable class instance fit_method : str or callable str can be one of the following: 'WLS' for weighted linear least square fit according to [1]_ :func:`fwdti.wls_iter` 'NLS' for non-linear least square fit according to [1]_ :func:`fwdti.nls_iter` callable has to have the signature: fit_method(design_matrix, data, *args, **kwargs) args, kwargs : arguments and key-word arguments passed to the fit_method. See fwdti.wls_iter, fwdti.nls_iter for details References ---------- .. [1] Henriques, R.N., Rokem, A., Garyfallidis, E., St-Jean, S., Peterson E.T., Correia, M.M., 2017. [Re] Optimization of a free water elimination two-compartment model for diffusion tensor imaging. ReScience volume 3, issue 1, article number 2 """ ReconstModel.__init__(self, gtab) if not callable(fit_method): try: fit_method = common_fit_methods[fit_method] except KeyError: e_s = '"' + str(fit_method) + '" is not a known fit ' e_s += 'method, the fit method should either be a ' e_s += 'function or one of the common fit methods' raise ValueError(e_s) self.fit_method = fit_method self.design_matrix = design_matrix(self.gtab) self.args = args self.kwargs = kwargs # Check if at least three b-values are given enough_b = check_multi_b(self.gtab, 3, non_zero=False) if not enough_b: mes = "fwDTI requires at least 3 b-values (which can include b=0)" raise ValueError(mes)
def __init__(self, gtab, fit_method="NLS", *args, **kwargs): """ Free Water Diffusion Tensor Model [1]_. Parameters ---------- gtab : GradientTable class instance fit_method : str or callable str can be one of the following: 'WLS' for weighted linear least square fit according to [1]_ :func:`fwdti.wls_iter` 'NLS' for non-linear least square fit according to [1]_ :func:`fwdti.nls_iter` callable has to have the signature: fit_method(design_matrix, data, *args, **kwargs) args, kwargs : arguments and key-word arguments passed to the fit_method. See fwdti.wls_iter, fwdti.nls_iter for details References ---------- .. [1] Hoy, A.R., Koay, C.G., Kecskemeti, S.R., Alexander, A.L., 2014. Optimization of a free water elimination two-compartmental model for diffusion tensor imaging. NeuroImage 103, 323-333. doi: 10.1016/j.neuroimage.2014.09.053 """ ReconstModel.__init__(self, gtab) if not callable(fit_method): try: fit_method = common_fit_methods[fit_method] except KeyError: e_s = '"' + str(fit_method) + '" is not a known fit ' e_s += 'method, the fit method should either be a ' e_s += 'function or one of the common fit methods' raise ValueError(e_s) self.fit_method = fit_method self.design_matrix = design_matrix(self.gtab) self.args = args self.kwargs = kwargs # Check if at least three b-values are given enough_b = check_multi_b(self.gtab, 3, non_zero=False) if not enough_b: mes = "fwDTI requires at least 3 b-values (which can include b=0)" raise ValueError(mes)
def __init__(self, gtab, fit_method="NLS", *args, **kwargs): """ Free Water Diffusion Tensor Model [1]_. Parameters ---------- gtab : GradientTable class instance fit_method : str or callable str can be one of the following: 'WLS' for weighted linear least square fit according to [1]_ :func:`fwdti.wls_iter` 'NLS' for non-linear least square fit according to [1]_ :func:`fwdti.nls_iter` callable has to have the signature: fit_method(design_matrix, data, *args, **kwargs) args, kwargs : arguments and key-word arguments passed to the fit_method. See fwdti.wls_iter, fwdti.nls_iter for details References ---------- .. [1] Hoy, A.R., Koay, C.G., Kecskemeti, S.R., Alexander, A.L., 2014. Optimization of a free water elimination two-compartmental model for diffusion tensor imaging. NeuroImage 103, 323-333. doi: 10.1016/j.neuroimage.2014.09.053 """ ReconstModel.__init__(self, gtab) if not callable(fit_method): try: fit_method = common_fit_methods[fit_method] except KeyError: e_s = '"' + str(fit_method) + '" is not a known fit ' e_s += 'method, the fit method should either be a ' e_s += 'function or one of the common fit methods' raise ValueError(e_s) self.fit_method = fit_method self.design_matrix = design_matrix(self.gtab) self.args = args self.kwargs = kwargs # Check if at least three b-values are given enough_b = check_multi_b(self.gtab, 3, non_zero=False) if not enough_b: mes = "fwDTI requires at least 3 b-values (which can include b=0)" raise ValueError(mes)
def __init__(self, gtab, bmag=None, return_S0_hat=False, *args, **kwargs): """ Mean Signal Diffusion Kurtosis Model [1]_. Parameters ---------- gtab : GradientTable class instance bmag : int The order of magnitude that the bvalues have to differ to be considered an unique b-value. Default: derive this value from the maximal b-value provided: $bmag=log_{10}(max(bvals)) - 1$. return_S0_hat : bool If True, also return S0 values for the fit. args, kwargs : arguments and keyword arguments passed to the fit_method. See msdki.wls_fit_msdki for details References ---------- .. [1] Henriques, R.N., 2018. Advanced Methods for Diffusion MRI Data Analysis and their Application to the Healthy Ageing Brain (Doctoral thesis). Downing College, University of Cambridge. https://doi.org/10.17863/CAM.29356 """ ReconstModel.__init__(self, gtab) self.return_S0_hat = return_S0_hat self.ubvals = unique_bvals_magnitude(gtab.bvals, bmag=bmag) self.design_matrix = design_matrix(self.ubvals) self.bmag = bmag self.args = args self.kwargs = kwargs self.min_signal = self.kwargs.pop('min_signal', MIN_POSITIVE_SIGNAL) if self.min_signal is not None and self.min_signal <= 0: e_s = "The `min_signal` key-word argument needs to be strictly" e_s += " positive." raise ValueError(e_s) # Check if at least three b-values are given enough_b = check_multi_b(self.gtab, 3, non_zero=False, bmag=bmag) if not enough_b: mes = "MSDKI requires at least 3 b-values (which can include b=0)" raise ValueError(mes)
def __init__(self, gtab, bmag=None, return_S0_hat=False, *args, **kwargs): """ Mean Signal Diffusion Kurtosis Model [1]_. Parameters ---------- gtab : GradientTable class instance bmag : int The order of magnitude that the bvalues have to differ to be considered an unique b-value. Default: derive this value from the maximal b-value provided: $bmag=log_{10}(max(bvals)) - 1$. return_S0_hat : bool If True, also return S0 values for the fit. args, kwargs : arguments and keyword arguments passed to the fit_method. See msdki.wls_fit_msdki for details References ---------- .. [1] Henriques, R.N., 2018. Advanced Methods for Diffusion MRI Data Analysis and their Application to the Healthy Ageing Brain (Doctoral thesis). Downing College, University of Cambridge. https://doi.org/10.17863/CAM.29356 """ ReconstModel.__init__(self, gtab) self.return_S0_hat = return_S0_hat self.ubvals = unique_bvals(gtab.bvals, bmag=bmag) self.design_matrix = design_matrix(self.ubvals) self.bmag = bmag self.args = args self.kwargs = kwargs self.min_signal = self.kwargs.pop('min_signal', MIN_POSITIVE_SIGNAL) if self.min_signal is not None and self.min_signal <= 0: e_s = "The `min_signal` key-word argument needs to be strictly" e_s += " positive." raise ValueError(e_s) # Check if at least three b-values are given enough_b = check_multi_b(self.gtab, 3, non_zero=False, bmag=bmag) if not enough_b: mes = "MSDKI requires at least 3 b-values (which can include b=0)" raise ValueError(mes)
def init(gtab, model="DTI", **kwargs): """ Instatiate a diffusion model. Parameters ---------- gtab : :obj:`numpy.ndarray` An array representing the gradient table in RAS+B format. model : :obj:`str` Diffusion model. Options: ``"3DShore"``, ``"SFM"``, ``"GP"``, ``"DTI"``, ``"DKI"``, ``"S0"`` Return ------ model : :obj:`~dipy.reconst.ReconstModel` An model object compliant with DIPY's interface. """ if model.lower() in ("s0", "b0"): return TrivialB0Model(gtab=gtab, S0=kwargs.pop("S0")) if model.lower() in ("avg", "average", "mean"): return AverageDWModel(gtab=gtab, **kwargs) # Generate a GradientTable object for DIPY gtab = _rasb2dipy(gtab) param = {} if model.lower().startswith("3dshore"): from dipy.reconst.shore import ShoreModel as Model param = { "radial_order": 6, "zeta": 700, "lambdaN": 1e-8, "lambdaL": 1e-8, } elif model.lower() in ("sfm", "gp"): Model = SparseFascicleModel param = {"solver": "ElasticNet"} if model.lower() == "gp": from sklearn.gaussian_process import GaussianProcessRegressor param = {"solver": GaussianProcessRegressor} multi_b = check_multi_b(gtab, 2, non_zero=False) if multi_b: from dipy.reconst.sfm import ExponentialIsotropicModel param.update({"isotropic": ExponentialIsotropicModel}) elif model.lower() in ("dti", "dki"): Model = DTIModel if model.lower() == "dti" else DKIModel else: raise NotImplementedError(f"Unsupported model <{model}>.") omp_nthreads = kwargs.pop("omp_nthreads", None) param.update(kwargs) return get_run_cls(Model(gtab, **param), omp_nthreads)
def main(self): self.imgFile = str(self.imgFile) self.maskFile = str(self.maskFile) self.mk_low_high = [ float(x) for x in self.mk_low_high[1:-1].split(',') ] self.mk_low_high.sort() self.fa_low_high = [ float(x) for x in self.fa_low_high[1:-1].split(',') ] self.fa_low_high.sort() self.md_low_high = [ float(x) for x in self.md_low_high[1:-1].split(',') ] self.md_low_high.sort() hdr = None affine = None if not self.out_dir: self.out_dir = os.path.dirname(self.imgFile) outPrefix = os.path.join(self.out_dir, os.path.basename(self.imgFile).split('.')[0]) if self.imgFile.endswith('.nii.gz') or self.imgFile.endswith('.nii'): bvals, bvecs = read_bvals_bvecs(self.bvalFile, self.bvecFile) outFormat = '.nii.gz' img = nib.load(self.imgFile) data = img.get_data() affine = img.affine grad_axis = 3 if len(data.shape) != 4: raise AttributeError('Not a valid dwi, check dimension') elif self.imgFile.endswith('.nrrd') or self.imgFile.endswith('.nhdr'): img = nrrd.read(self.imgFile) data = img[0] hdr = img[1] outFormat = '.nrrd' bvals, bvecs, b_max, grad_axis, N = nrrd_bvals_bvecs(hdr) # put the gradients along last axis if grad_axis != 3: data = np.moveaxis(data, grad_axis, 3) # provide the user a liberty to specify different file formats for dwi and mask if self.maskFile.endswith('.nii.gz') or self.maskFile.endswith('.nii'): mask_data = nib.load(self.maskFile).get_data() elif self.maskFile.endswith('.nrrd') or self.maskFile.endswith( '.nhdr'): mask_data = nrrd.read(self.maskFile)[0] data = applymask(data, mask_data) gtab = gradient_table(bvals, bvecs) dtimodel = dti.TensorModel(gtab) dtifit = dtimodel.fit(data, mask_data) evals = dtifit.evals fa = dtifit.fa md = dtifit.md ad = dtifit.ad rd = dtifit.rd evals_zero = evals < 0. evals_zero_mask = (evals_zero[..., 0] | evals_zero[..., 1] | evals_zero[..., 2]) * 1 mkFlag = check_multi_b(gtab, n_bvals=3) if mkFlag: dkimodel = dki.DiffusionKurtosisModel(gtab) dkifit = dkimodel.fit(data, mask_data) mk = dkifit.mk( 0, 3) # http://nipy.org/dipy/examples_built/reconst_dki.html else: warnings.warn( "DIPY DKI requires at least 3 b-shells (which can include b=0), " "kurtosis quality cannot be computed.") fa_mask = mask_calc(fa, self.fa_low_high) md_mask = mask_calc(md, self.md_low_high) where_b0s = np.where(bvals == 0)[0] where_dwi = np.where(bvals != 0)[0] bse_data = data[..., where_b0s].mean(-1) b0File = outPrefix + '_b0' + outFormat save_map(b0File, bse_data, affine, hdr) # prevent division by zero during normalization bse_data[bse_data < 1] = 1. extend_bse = np.expand_dims(bse_data, grad_axis) extend_bse = np.repeat(extend_bse, len(where_dwi), grad_axis) curtail_dwi = np.take(data, where_dwi, axis=grad_axis) # 1 / b0 * min(b0 - Gi) minOverGrads = np.min(extend_bse - curtail_dwi, axis=grad_axis) / bse_data # another way to prevent division by zero: 1/b0 * min(b0-Gi) with condition at b0~eps # minOverGrads = np.min(extend_bse - curtail_dwi, axis=grad_axis) / (bse_data + eps) # minOverGrads[(bse_data < eps) & (minOverGrads < 5 * eps)] = 0. # minOverGrads[(bse_data < eps) & (minOverGrads > 5 * eps)] = 10. minOverGradsNegativeMask = (minOverGrads < 0) * 1 # compute histograms print('\nminOverGrads (b0-Gi)/b0 histogram') bins = [-inf, 0, inf] negative, _ = hist_calc(minOverGrads, bins) print('\nevals<0 histogram') bins = [-inf, 0, inf] hist_calc(evals, bins) print('\nfractional anisotropy histogram') bins = form_bins(self.fa_low_high) hist_calc(fa, bins) print('\nmean diffusivity histogram') bins = form_bins(self.md_low_high) hist_calc(md, bins) if mkFlag: print('\nmean kurtosis mask') bins = form_bins(self.mk_low_high) hist_calc(mk, bins) # save histograms print('\nCreating minOverGrads image ...') save_map(outPrefix + '_minOverGrads' + outFormat, minOverGrads, affine, hdr) print('\nCreating minOverGrads<0 mask ...') save_map(outPrefix + '_minOverGradsMask' + outFormat, minOverGradsNegativeMask.astype('short'), affine, hdr) print('\nCreating evals<0 mask ...') save_map(outPrefix + '_evalsZeroMask' + outFormat, evals_zero_mask.astype('short'), affine, hdr) if mkFlag: mk_mask = mask_calc(mk, self.mk_low_high) print('\nCreating mk image ...') save_map(outPrefix + '_MK' + outFormat, mk, affine, hdr) print('Creating mk out of range mask ...') save_map(outPrefix + '_MK_mask' + outFormat, mk_mask.astype('short'), affine, hdr) else: mk = np.zeros(fa.shape) print('\nCreating fa image ...') save_map(outPrefix + '_FA' + outFormat, fa, affine, hdr) print('Creating fa out of range mask ...') save_map(outPrefix + '_FA_mask' + outFormat, fa_mask.astype('short'), affine, hdr) print('\nCreating md image ....') save_map(outPrefix + '_MD' + outFormat, md, affine, hdr) print('Creating md out of range mask ...') save_map(outPrefix + '_MD_mask' + outFormat, md_mask.astype('short'), affine, hdr) # conclusion N_mask = mask_data.size print('\n\nConclusion: ') print('The masked dwi has %.5f%% voxels with values less than b0' % (negative * 100)) print('The masked dwi has %.5f%% voxels with negative eigen value' % (evals_zero_mask.sum() / N_mask * 100)) print('The masked dwi has %.5f%% voxels with FA out of [%f,%f]' % (fa_mask.sum() / N_mask * 100, self.fa_low_high[0], self.fa_low_high[1])) print('The masked dwi has %.5f%% voxels with MD out of [%f,%f]' % (md_mask.sum() / N_mask * 100, self.md_low_high[0], self.md_low_high[1])) if mkFlag: print('The masked dwi has %.5f%% voxels with MK out of [%f,%f]' % (mk_mask.sum() / N_mask * 100, self.mk_low_high[0], self.mk_low_high[1])) # perform roi based analysis if self.template and self.labelMap: antsReg(b0File, self.maskFile, self.template, outPrefix) warp = outPrefix + '1Warp.nii.gz' trans = outPrefix + '0GenericAffine.mat' outLabelMapFile = outPrefix + '_labelMap.nii.gz' applyXform(self.labelMap, b0File, warp, trans, outLabelMapFile, interp='NearestNeighbor') rm = local['rm'] rm(warp, trans, outPrefix + 'Warped.nii.gz', outPrefix + '1InverseWarp.nii.gz', outPrefix + 'InverseWarped.nii.gz') outLabelMap = nib.load(outLabelMapFile).get_data() labels = np.unique(outLabelMap)[1:] label2name = parse_labels(labels, self.lut._path if self.lut else None) print('Creating ROI based statistics ...') stat_file = outPrefix + f'_{self.name}_stat.csv' df = pd.DataFrame(columns=[ 'region', 'FA_mean', 'FA_std', 'MD_mean', 'MD_std', 'AD_mean', 'AD_std', 'RD_mean', 'RD_std', 'total_{min_i(b0-Gi)<0}', 'total_evals<0', 'MK_mean', 'MK_std', ]) for i, label in enumerate(label2name.keys()): roi = outLabelMap == int(label) properties = [ num2str(x) for x in [ fa[roi > 0].mean(), fa[roi > 0].std(), md[roi > 0]. mean(), md[roi > 0].std(), ad[roi > 0].mean(), ad[ roi > 0].std(), rd[roi > 0].mean(), rd[roi > 0].std(), minOverGradsNegativeMask[ roi > 0].sum(), evals_zero_mask[roi > 0].sum(), mk[ roi > 0].mean(), mk[roi > 0].std() ] ] df.loc[i] = [label2name[label]] + properties df = df.set_index('region') # print(df) df.to_csv(stat_file) print('See ', os.path.abspath(stat_file))