def test_peaks_shm_coeff(): SNR = 100 S0 = 100 _, fbvals, fbvecs = get_data('small_64D') from dipy.data import get_sphere sphere = get_sphere('repulsion724') bvals = np.load(fbvals) bvecs = np.load(fbvecs) gtab = gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) data, _ = multi_tensor(gtab, mevals, S0, angles=[(0, 0), (60, 0)], fractions=[50, 50], snr=SNR) from dipy.reconst.shm import CsaOdfModel model = CsaOdfModel(gtab, 4) pam = peaks_from_model(model, data[None, :], sphere, .5, 45, return_odf=True, return_sh=True) # Test that spherical harmonic coefficients return back correctly odf2 = np.dot(pam.shm_coeff, pam.B) assert_array_almost_equal(pam.odf, odf2) assert_equal(pam.shm_coeff.shape[-1], 45) pam = peaks_from_model(model, data[None, :], sphere, .5, 45, return_odf=True, return_sh=False) assert_equal(pam.shm_coeff, None) pam = peaks_from_model(model, data[None, :], sphere, .5, 45, return_odf=True, return_sh=True, sh_basis_type='mrtrix') odf2 = np.dot(pam.shm_coeff, pam.B) assert_array_almost_equal(pam.odf, odf2)
def test_peaksFromModelParallel(): SNR = 100 S0 = 100 _, fbvals, fbvecs = get_data('small_64D') bvals = np.load(fbvals) bvecs = np.load(fbvecs) gtab = gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) data, _ = multi_tensor(gtab, mevals, S0, angles=[(0, 0), (60, 0)], fractions=[50, 50], snr=SNR) # test equality with/without multiprocessing model = SimpleOdfModel(gtab) pam_multi = peaks_from_model(model, data, _sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=True) pam_single = peaks_from_model(model, data, _sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=False) assert_equal(pam_multi.gfa.dtype, pam_single.gfa.dtype) assert_equal(pam_multi.gfa.shape, pam_single.gfa.shape) assert_array_almost_equal(pam_multi.gfa, pam_single.gfa) assert_equal(pam_multi.qa.dtype, pam_single.qa.dtype) assert_equal(pam_multi.qa.shape, pam_single.qa.shape) assert_array_almost_equal(pam_multi.qa, pam_single.qa) assert_equal(pam_multi.peak_values.dtype, pam_single.peak_values.dtype) assert_equal(pam_multi.peak_values.shape, pam_single.peak_values.shape) assert_array_almost_equal(pam_multi.peak_values, pam_single.peak_values) assert_equal(pam_multi.peak_indices.dtype, pam_single.peak_indices.dtype) assert_equal(pam_multi.peak_indices.shape, pam_single.peak_indices.shape) assert_array_equal(pam_multi.peak_indices, pam_single.peak_indices) assert_equal(pam_multi.peak_dirs.dtype, pam_single.peak_dirs.dtype) assert_equal(pam_multi.peak_dirs.shape, pam_single.peak_dirs.shape) assert_array_almost_equal(pam_multi.peak_dirs, pam_single.peak_dirs) assert_equal(pam_multi.shm_coeff.dtype, pam_single.shm_coeff.dtype) assert_equal(pam_multi.shm_coeff.shape, pam_single.shm_coeff.shape) assert_array_almost_equal(pam_multi.shm_coeff, pam_single.shm_coeff) assert_equal(pam_multi.odf.dtype, pam_single.odf.dtype) assert_equal(pam_multi.odf.shape, pam_single.odf.shape) assert_array_almost_equal(pam_multi.odf, pam_single.odf)
def get_fiber_direction(self): # Getting fiber direction csamodel = shm.CsaOdfModel(self.gtab, 6) if self.atlas == "stanford": white_matter = binary_dilation((self.labels == 1) | (self.labels == 2)) elif self.atlas == "desikan": white_matter = binary_dilation((self.labels == 41) | (self.labels == 2)) if self.data.shape[:3] == white_matter.shape: csapeaks = peaks.peaks_from_model(model=csamodel, data=self.data, sphere=peaks.default_sphere, relative_peak_threshold=.8, min_separation_angle=45, mask=white_matter) # mask, b0_mask = self.create_mask() # self.make_tensor(b0_mask) # csd_peaks = self.make_csd(mask, b0_mask) else: print(f"{LogLVL.lvl1}ERR: dimensions are different:") print(f"{LogLVL.lvl2}data shape: {self.data.shape}") print(f"{LogLVL.lvl2}white matter shape: {white_matter.shape}") print(f"{LogLVL.lvl1}ERR: cannot continue") csapeaks = None return csapeaks, white_matter
def test_peaksFromModel(): data = np.zeros((10, 2)) # Test basic case model = SimpleOdfModel(_gtab) odf_argmax = _odf.argmax() pam = peaks_from_model(model, data, _sphere, .5, 45, normalize_peaks=True) assert_array_equal(pam.gfa, gfa(_odf)) assert_array_equal(pam.peak_values[:, 0], 1.) assert_array_equal(pam.peak_values[:, 1:], 0.) mn, mx = _odf.min(), _odf.max() assert_array_equal(pam.qa[:, 0], (mx - mn) / mx) assert_array_equal(pam.qa[:, 1:], 0.) assert_array_equal(pam.peak_indices[:, 0], odf_argmax) assert_array_equal(pam.peak_indices[:, 1:], -1) # Test that odf array matches and is right shape pam = peaks_from_model(model, data, _sphere, .5, 45, return_odf=True) expected_shape = (len(data), len(_odf)) assert_equal(pam.odf.shape, expected_shape) assert_((_odf == pam.odf).all()) assert_array_equal(pam.peak_values[:, 0], _odf.max()) # Test mask mask = (np.arange(10) % 2) == 1 pam = peaks_from_model(model, data, _sphere, .5, 45, mask=mask, normalize_peaks=True) assert_array_equal(pam.gfa[~mask], 0) assert_array_equal(pam.qa[~mask], 0) assert_array_equal(pam.peak_values[~mask], 0) assert_array_equal(pam.peak_indices[~mask], -1) assert_array_equal(pam.gfa[mask], gfa(_odf)) assert_array_equal(pam.peak_values[mask, 0], 1.) assert_array_equal(pam.peak_values[mask, 1:], 0.) mn, mx = _odf.min(), _odf.max() assert_array_equal(pam.qa[mask, 0], (mx - mn) / mx) assert_array_equal(pam.qa[mask, 1:], 0.) assert_array_equal(pam.peak_indices[mask, 0], odf_argmax) assert_array_equal(pam.peak_indices[mask, 1:], -1)
def test_PeaksAndMetricsDirectionGetter(): class SillyModel(object): def fit(self, data, mask=None): return SillyFit(self) class SillyFit(object): def __init__(self, model): self.model = model def odf(self, sphere): odf = np.zeros(sphere.theta.shape) r = np.random.randint(0, len(odf)) odf[r] = 1 return odf def get_direction(dg, point, dir): newdir = dir.copy() state = dg.get_direction(point, newdir) return (state, np.array(newdir)) data = np.random.random((3, 4, 5, 2)) peaks = peaks_from_model(SillyModel(), data, default_sphere, relative_peak_threshold=0.5, min_separation_angle=25) peaks._initialize() up = np.zeros(3) up[2] = 1.0 down = -up for i in range(3 - 1): for j in range(4 - 1): for k in range(5 - 1): point = np.array([i, j, k], dtype=float) # Test that the angle threshold rejects points peaks.ang_thr = 0.0 state, nd = get_direction(peaks, point, up) npt.assert_equal(state, 1) # Here we leverage the fact that we know Hemispheres project # all their vertices into the z >= 0 half of the sphere. peaks.ang_thr = 90.0 state, nd = get_direction(peaks, point, up) npt.assert_equal(state, 0) expected_dir = peaks.peak_dirs[i, j, k, 0] npt.assert_array_almost_equal(nd, expected_dir) state, nd = get_direction(peaks, point, down) npt.assert_array_almost_equal(nd, -expected_dir) # Check that we can get directions at non-integer points point += np.random.random(3) state, nd = get_direction(peaks, point, up) npt.assert_equal(state, 0) # Check that points are rounded to get initial direction point -= 0.5 id = peaks.initial_direction(point) # id should be a (1, 3) array npt.assert_array_almost_equal(id, [expected_dir])
def make_csd(self, mask, b0_mask): """ Constrained Spherical Deconvolution (CSD) https://dipy.org/documentation/0.16.0./examples_built/tracking_quick_start/ https://dipy.org/documentation/0.16.0./examples_built/introduction_to_basic_tracking/ Another kind of tracking : https://dipy.org/documentation/1.2.0./examples_built/tracking_introduction_eudx/#example-tracking-introduction-eudx https://github.com/dipy/dipy/blob/master/doc/examples/tracking_deterministic.py """ # we need to estimate the response function and create a model response, ratio = auto_response(self.gtab, self.data, roi_radius=10, fa_thr=0.7) csd_model = ConstrainedSphericalDeconvModel(self.gtab, response) # Using peaks sphere = get_sphere('symmetric724') csd_peaks = peaks.peaks_from_model(model=csd_model, data=b0_mask, sphere=peaks.default_sphere, mask=mask, relative_peak_threshold=.5, min_separation_angle=25, parallel=True) self.save_plot(csd_peaks.gfa[:, :, 35].T, f"{self.subj_id}_csd") # Note:The GFA values of the FODs don’t classify gray matter and white matter well # View csd_peaks # from dipy.viz import window, actor, has_fury, colormap as cmap # interactive = True # if has_fury: # scene = window.Scene() # scene.add(actor.peak_slicer(csd_peaks.peak_dirs, # csd_peaks.peak_values, # colors=None)) # window.record(scene, out_path='csd_direction_field.png', size=(900, 900)) # if interactive: # window.show(scene, size=(800, 800)) ## Restrict the fiber tracking to areas with good directionality information using tensor model # - with cropped data return csd_peaks
def run(self, input_files, bvalues_files, bvectors_files, mask_files, b0_threshold=50.0, bvecs_tol=0.01, roi_center=None, roi_radii=10, fa_thr=0.7, frf=None, extract_pam_values=False, sh_order=8, odf_to_sh_order=8, parallel=False, nbr_processes=None, out_dir='', out_pam='peaks.pam5', out_shm='shm.nii.gz', out_peaks_dir='peaks_dirs.nii.gz', out_peaks_values='peaks_values.nii.gz', out_peaks_indices='peaks_indices.nii.gz', out_gfa='gfa.nii.gz'): """ Constrained spherical deconvolution 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 b0 volumes. bvecs_tol : float, optional Bvecs should be unit vectors. roi_center : variable int, optional 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, optional radii of cuboid ROI in voxels. fa_thr : float, optional FA threshold for calculating the response function. frf : variable float, optional Fiber response function can be for example inputed as 15 4 4 (from the command line) or [15, 4, 4] from a Python script to be converted to float and multiplied by 10**-4 . If None the fiber response function will be computed automatically. extract_pam_values : bool, optional Save or not to save pam volumes as single nifti files. sh_order : int, optional Spherical harmonics order used in the CSA fit. odf_to_sh_order : int, optional Spherical harmonics order used for peak_from_model to compress the ODF to spherical harmonics coefficients. parallel : bool, optional Whether to use parallelization in peak-finding during the calibration procedure. nbr_processes : int, optional If `parallel` is True, the number of subprocesses to use (default multiprocessing.cpu_count()). out_dir : string, optional Output directory. (default current directory) out_pam : string, optional Name of the peaks volume to be saved. out_shm : string, optional Name of the spherical harmonics volume to be saved. out_peaks_dir : string, optional Name of the peaks directions volume to be saved. out_peaks_values : string, optional Name of the peaks values volume to be saved. out_peaks_indices : string, optional Name of the peaks indices volume to be saved. out_gfa : string, optional Name of the generalized FA volume to be saved. References ---------- .. [1] Tournier, J.D., et al. NeuroImage 2007. Robust determination of the fibre orientation distribution in diffusion MRI: Non-negativity constrained super-resolved spherical deconvolution. """ io_it = self.get_io_iterator() for (dwi, bval, bvec, maskfile, opam, oshm, opeaks_dir, opeaks_values, opeaks_indices, ogfa) in io_it: logging.info('Loading {0}'.format(dwi)) data, affine = load_nifti(dwi) bvals, bvecs = read_bvals_bvecs(bval, bvec) print(b0_threshold, bvals.min()) if b0_threshold < bvals.min(): warn( "b0_threshold (value: {0}) is too low, increase your " "b0_threshold. It should be higher than the first b0 value " "({1}).".format(b0_threshold, bvals.min())) gtab = gradient_table(bvals, bvecs, b0_threshold=b0_threshold, atol=bvecs_tol) mask_vol = load_nifti_data(maskfile).astype(bool) n_params = ((sh_order + 1) * (sh_order + 2)) / 2 if data.shape[-1] < n_params: raise ValueError('You need at least {0} unique DWI volumes to ' 'compute fiber odfs. You currently have: {1}' ' DWI volumes.'.format( n_params, data.shape[-1])) if frf is None: logging.info('Computing response function') if roi_center is not None: logging.info( 'Response ROI center:\n{0}'.format(roi_center)) logging.info('Response ROI radii:\n{0}'.format(roi_radii)) response, ratio = auto_response_ssst(gtab, data, roi_center=roi_center, roi_radii=roi_radii, fa_thr=fa_thr) response = list(response) else: logging.info('Using response function') if isinstance(frf, str): l01 = np.array(literal_eval(frf), dtype=np.float64) else: l01 = np.array(frf, dtype=np.float64) l01 *= 10**-4 response = np.array([l01[0], l01[1], l01[1]]) ratio = l01[1] / l01[0] response = (response, ratio) logging.info("Eigenvalues for the frf of the input" " data are :{0}".format(response[0])) logging.info( 'Ratio for smallest to largest eigen value is {0}'.format( ratio)) peaks_sphere = default_sphere logging.info('CSD computation started.') csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order=sh_order) peaks_csd = peaks_from_model(model=csd_model, data=data, sphere=peaks_sphere, relative_peak_threshold=.5, min_separation_angle=25, mask=mask_vol, return_sh=True, sh_order=sh_order, normalize_peaks=True, parallel=parallel, nbr_processes=nbr_processes) peaks_csd.affine = affine save_peaks(opam, peaks_csd) logging.info('CSD computation completed.') if extract_pam_values: peaks_to_niftis(peaks_csd, oshm, opeaks_dir, opeaks_values, opeaks_indices, ogfa, reshape_dirs=True) dname_ = os.path.dirname(opam) if dname_ == '': logging.info('Pam5 file saved in current directory') else: logging.info('Pam5 file saved in {0}'.format(dname_)) return io_it
def run(self, input_files, bvalues_files, bvectors_files, mask_files, sh_order=6, odf_to_sh_order=8, b0_threshold=50.0, bvecs_tol=0.01, extract_pam_values=False, parallel=False, nbr_processes=None, out_dir='', out_pam='peaks.pam5', out_shm='shm.nii.gz', out_peaks_dir='peaks_dirs.nii.gz', out_peaks_values='peaks_values.nii.gz', out_peaks_indices='peaks_indices.nii.gz', out_gfa='gfa.nii.gz'): """ Constant Solid Angle. 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) sh_order : int, optional Spherical harmonics order used in the CSA fit. odf_to_sh_order : int, optional Spherical harmonics order used for peak_from_model to compress the ODF to spherical harmonics coefficients. b0_threshold : float, optional Threshold used to find b0 volumes. bvecs_tol : float, optional Threshold used so that norm(bvec)=1. extract_pam_values : bool, optional Whether or not to save pam volumes as single nifti files. parallel : bool, optional Whether to use parallelization in peak-finding during the calibration procedure. nbr_processes : int, optional If `parallel` is True, the number of subprocesses to use (default multiprocessing.cpu_count()). out_dir : string, optional Output directory. (default current directory) out_pam : string, optional Name of the peaks volume to be saved. out_shm : string, optional Name of the spherical harmonics volume to be saved. out_peaks_dir : string, optional Name of the peaks directions volume to be saved. out_peaks_values : string, optional Name of the peaks values volume to be saved. out_peaks_indices : string, optional Name of the peaks indices volume to be saved. out_gfa : string, optional Name of the generalized FA volume to be saved. References ---------- .. [1] Aganj, I., et al. 2009. ODF Reconstruction in Q-Ball Imaging with Solid Angle Consideration. """ io_it = self.get_io_iterator() for (dwi, bval, bvec, maskfile, opam, oshm, opeaks_dir, opeaks_values, opeaks_indices, ogfa) in io_it: logging.info('Loading {0}'.format(dwi)) data, affine = load_nifti(dwi) bvals, bvecs = read_bvals_bvecs(bval, bvec) if b0_threshold < bvals.min(): warn( "b0_threshold (value: {0}) is too low, increase your " "b0_threshold. It should be higher than the first b0 value " "({1}).".format(b0_threshold, bvals.min())) gtab = gradient_table(bvals, bvecs, b0_threshold=b0_threshold, atol=bvecs_tol) mask_vol = load_nifti_data(maskfile).astype(bool) peaks_sphere = default_sphere logging.info('Starting CSA computations {0}'.format(dwi)) csa_model = CsaOdfModel(gtab, sh_order) peaks_csa = peaks_from_model(model=csa_model, data=data, sphere=peaks_sphere, relative_peak_threshold=.5, min_separation_angle=25, mask=mask_vol, return_sh=True, sh_order=odf_to_sh_order, normalize_peaks=True, parallel=parallel, nbr_processes=nbr_processes) peaks_csa.affine = affine save_peaks(opam, peaks_csa) logging.info('Finished CSA {0}'.format(dwi)) if extract_pam_values: peaks_to_niftis(peaks_csa, oshm, opeaks_dir, opeaks_values, opeaks_indices, ogfa, reshape_dirs=True) dname_ = os.path.dirname(opam) if dname_ == '': logging.info('Pam5 file saved in current directory') else: logging.info('Pam5 file saved in {0}'.format(dname_)) return io_it
def test_peaksFromModel(): data = np.zeros((10, 2)) for sphere in [_sphere, get_sphere('symmetric642')]: # Test basic case model = SimpleOdfModel(_gtab) _odf = (sphere.vertices * [1, 2, 3]).sum(-1) odf_argmax = _odf.argmax() pam = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True) assert_array_equal(pam.gfa, gfa(_odf)) assert_array_equal(pam.peak_values[:, 0], 1.) assert_array_equal(pam.peak_values[:, 1:], 0.) mn, mx = _odf.min(), _odf.max() assert_array_equal(pam.qa[:, 0], (mx - mn) / mx) assert_array_equal(pam.qa[:, 1:], 0.) assert_array_equal(pam.peak_indices[:, 0], odf_argmax) assert_array_equal(pam.peak_indices[:, 1:], -1) # Test that odf array matches and is right shape pam = peaks_from_model(model, data, sphere, .5, 45, return_odf=True) expected_shape = (len(data), len(_odf)) assert_equal(pam.odf.shape, expected_shape) assert_((_odf == pam.odf).all()) assert_array_equal(pam.peak_values[:, 0], _odf.max()) # Test mask mask = (np.arange(10) % 2) == 1 pam = peaks_from_model(model, data, sphere, .5, 45, mask=mask, normalize_peaks=True) assert_array_equal(pam.gfa[~mask], 0) assert_array_equal(pam.qa[~mask], 0) assert_array_equal(pam.peak_values[~mask], 0) assert_array_equal(pam.peak_indices[~mask], -1) assert_array_equal(pam.gfa[mask], gfa(_odf)) assert_array_equal(pam.peak_values[mask, 0], 1.) assert_array_equal(pam.peak_values[mask, 1:], 0.) mn, mx = _odf.min(), _odf.max() assert_array_equal(pam.qa[mask, 0], (mx - mn) / mx) assert_array_equal(pam.qa[mask, 1:], 0.) assert_array_equal(pam.peak_indices[mask, 0], odf_argmax) assert_array_equal(pam.peak_indices[mask, 1:], -1) # Test serialization and deserialization: for normalize_peaks in [True, False]: for return_odf in [True, False]: for return_sh in [True, False]: pam = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=normalize_peaks, return_odf=return_odf, return_sh=return_sh) b = BytesIO() pickle.dump(pam, b) b.seek(0) new_pam = pickle.load(b) b.close() for attr in [ 'peak_dirs', 'peak_values', 'peak_indices', 'gfa', 'qa', 'shm_coeff', 'B', 'odf' ]: assert_array_equal(getattr(pam, attr), getattr(new_pam, attr)) assert_array_equal(pam.sphere.vertices, new_pam.sphere.vertices)
def test_peaksFromModelParallel(): SNR = 100 S0 = 100 _, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) data, _ = multi_tensor(gtab, mevals, S0, angles=[(0, 0), (60, 0)], fractions=[50, 50], snr=SNR) for sphere in [_sphere, get_sphere('symmetric724')]: # test equality with/without multiprocessing model = SimpleOdfModel(gtab) pam_multi = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=True) pam_single = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=False) pam_multi_inv1 = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=True, nbr_processes=0) pam_multi_inv2 = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=True, nbr_processes=-2) for pam in [pam_multi, pam_multi_inv1, pam_multi_inv2]: assert_equal(pam.gfa.dtype, pam_single.gfa.dtype) assert_equal(pam.gfa.shape, pam_single.gfa.shape) assert_array_almost_equal(pam.gfa, pam_single.gfa) assert_equal(pam.qa.dtype, pam_single.qa.dtype) assert_equal(pam.qa.shape, pam_single.qa.shape) assert_array_almost_equal(pam.qa, pam_single.qa) assert_equal(pam.peak_values.dtype, pam_single.peak_values.dtype) assert_equal(pam.peak_values.shape, pam_single.peak_values.shape) assert_array_almost_equal(pam.peak_values, pam_single.peak_values) assert_equal(pam.peak_indices.dtype, pam_single.peak_indices.dtype) assert_equal(pam.peak_indices.shape, pam_single.peak_indices.shape) assert_array_equal(pam.peak_indices, pam_single.peak_indices) assert_equal(pam.peak_dirs.dtype, pam_single.peak_dirs.dtype) assert_equal(pam.peak_dirs.shape, pam_single.peak_dirs.shape) assert_array_almost_equal(pam.peak_dirs, pam_single.peak_dirs) assert_equal(pam.shm_coeff.dtype, pam_single.shm_coeff.dtype) assert_equal(pam.shm_coeff.shape, pam_single.shm_coeff.shape) assert_array_almost_equal(pam.shm_coeff, pam_single.shm_coeff) assert_equal(pam.odf.dtype, pam_single.odf.dtype) assert_equal(pam.odf.shape, pam_single.odf.shape) assert_array_almost_equal(pam.odf, pam_single.odf)
def compute_fodf(data, bvals, bvecs, full_frf, sh_order=8, nbr_processes=None, mask=None, sh_basis='descoteaux07', return_sh=True, n_peaks=5, force_b0_threshold=False): """ Script to compute Constrained Spherical Deconvolution (CSD) fiber ODFs. By default, will output all possible files, using default names. Specific names can be specified using the file flags specified in the "File flags" section. If --not_all is set, only the files specified explicitly by the flags will be output. See [Tournier et al. NeuroImage 2007] and [Cote et al Tractometer MedIA 2013] for quantitative comparisons with Sharpening Deconvolution Transform (SDT). Parameters ---------- data: ndarray 4D Input diffusion volume with shape (X, Y, Z, N) bvals: ndarray 1D bvals array with shape (N,) bvecs: ndarray 2D (normalized) bvecs array with shape (N, 3) full_frf: ndarray frf data, ex, loaded from a frf_file, with shape (4,). sh_order: int, optional SH order used for the CSD. (Default: 8) nbr_processes: int, optional Number of sub processes to start. Default = none, i.e use the cpu count. If 0, use all processes. mask: ndarray, optional 3D mask with shape (X,Y,Z) Binary mask. Only the data inside the mask will be used for computations and reconstruction. Useful if no white matter mask is available. sh_basis: str, optional Spherical harmonics basis used for the SH coefficients.Must be either 'descoteaux07' or 'tournier07' (default 'descoteaux07') - 'descoteaux07': SH basis from the Descoteaux et al. MRM 2007 paper - 'tournier07': SH basis from the Tournier et al. NeuroImage 2007 paper. return_sh: bool, optional If true, returns the sh. n_peaks: int, optional Nb of peaks for the fodf. Default: copied dipy's default, i.e. 5. force_b0_threshold: bool, optional If True, will continue even if the minimum bvalue is suspiciously high. Returns ------- peaks_csd: PeaksAndMetrics An object with ``gfa``, ``peak_directions``, ``peak_values``, ``peak_indices``, ``odf``, ``shm_coeffs`` as attributes """ # Checking data and sh_order check_b0_threshold(force_b0_threshold, bvals.min()) if data.shape[-1] < (sh_order + 1) * (sh_order + 2) / 2: logging.warning( 'We recommend having at least {} unique DWI volumes, but you ' 'currently have {} volumes. Try lowering the parameter sh_order ' 'in case of non convergence.'.format( (sh_order + 1) * (sh_order + 2) / 2, data.shape[-1])) # Checking bvals, bvecs values and loading gtab if not is_normalized_bvecs(bvecs): logging.warning('Your b-vectors do not seem normalized...') bvecs = normalize_bvecs(bvecs) gtab = gradient_table(bvals, bvecs, b0_threshold=bvals.min()) # Checking full_frf and separating it if not full_frf.shape[0] == 4: raise ValueError('FRF file did not contain 4 elements. ' 'Invalid or deprecated FRF format') frf = full_frf[0:3] mean_b0_val = full_frf[3] # Checking if we will use parallel processing parallel = True if nbr_processes is not None: if nbr_processes == 0: # Will use all processed nbr_processes = None elif nbr_processes == 1: parallel = False elif nbr_processes < 0: raise ValueError('nbr_processes should be positive.') # Checking sh basis validate_sh_basis_choice(sh_basis) # Loading the spheres reg_sphere = get_sphere('symmetric362') peaks_sphere = get_sphere('symmetric724') # Computing CSD csd_model = ConstrainedSphericalDeconvModel(gtab, (frf, mean_b0_val), reg_sphere=reg_sphere, sh_order=sh_order) # Computing peaks. Run in parallel, using the default number of processes # (default: CPU count) peaks_csd = peaks_from_model(model=csd_model, data=data, sphere=peaks_sphere, relative_peak_threshold=.5, min_separation_angle=25, mask=mask, return_sh=return_sh, sh_basis_type=sh_basis, sh_order=sh_order, normalize_peaks=True, npeaks=n_peaks, parallel=parallel, nbr_processes=nbr_processes) return peaks_csd
""" Now we will use the denoised array (``denoised_arr``) obtained from ``mppca`` in the rest of the steps in the tutorial. As for the next step, we generate the anisotropic powermap introduced by [DellAcqua2014]_. To do so, we make use of the Q-ball Model as follows: """ qball_model = shm.QballModel(gtab, 8) """ We generate the peaks from the ``qball_model`` as follows: """ peaks = dp.peaks_from_model(model=qball_model, data=denoised_arr, relative_peak_threshold=.5, min_separation_angle=25, sphere=sphere, mask=mask) ap = shm.anisotropic_power(peaks.shm_coeff) plt.matshow(np.rot90(ap[:, :, 10]), cmap=plt.cm.bone) #plt.savefig("anisotropic_power_map.png") plt.show() """ .. figure:: anisotropic_power_map.png :align: center Anisotropic Power Map (Axial Slice) """
ren = window.Renderer() ren.add(fodf_spheres) print('Saving illustration as sf_odfs.png') window.record(ren, out_path='sf_odfs.png', size=(1000, 1000)) if interactive: window.show(ren) """ We can extract the peaks from the ODF, and plot these as well """ sf_peaks = dpp.peaks_from_model(sf_model, data_small, sphere, relative_peak_threshold=.5, min_separation_angle=25, return_sh=False) window.clear(ren) fodf_peaks = actor.peak_slicer(sf_peaks.peak_dirs, sf_peaks.peak_values, scale=1.3) ren.add(fodf_peaks) print('Saving illustration as sf_peaks.png') window.record(ren, out_path='sf_peaks.png', size=(1000, 1000)) if interactive: window.show(ren) """ Finally, we plot both the peaks and the ODFs, overlayed:
colormap='plasma') ren = window.Renderer() ren.add(fodf_spheres) print('Saving illustration as sf_odfs.png') window.record(ren, out_path='sf_odfs.png', size=(1000, 1000)) if interactive: window.show(ren) """ We can extract the peaks from the ODF, and plot these as well """ sf_peaks = dpp.peaks_from_model(sf_model, data_small, sphere, relative_peak_threshold=.5, min_separation_angle=25, return_sh=False) window.clear(ren) fodf_peaks = actor.peak_slicer(sf_peaks.peak_dirs, sf_peaks.peak_values) ren.add(fodf_peaks) print('Saving illustration as sf_peaks.png') window.record(ren, out_path='sf_peaks.png', size=(1000, 1000)) if interactive: window.show(ren) """ Finally, we plot both the peaks and the ODFs, overlayed: """
def run(self, input_files, bvalues_files, bvectors_files, mask_files, sh_order=6, odf_to_sh_order=8, b0_threshold=50.0, bvecs_tol=0.01, extract_pam_values=False, parallel=False, nbr_processes=None, out_dir='', out_pam='peaks.pam5', out_shm='shm.nii.gz', out_peaks_dir='peaks_dirs.nii.gz', out_peaks_values='peaks_values.nii.gz', out_peaks_indices='peaks_indices.nii.gz', out_gfa='gfa.nii.gz'): """ Constant Solid Angle. 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) sh_order : int, optional Spherical harmonics order (default 6) used in the CSA fit. odf_to_sh_order : int, optional Spherical harmonics order used for peak_from_model to compress the ODF to spherical harmonics coefficients (default 8) b0_threshold : float, optional Threshold used to find b=0 directions bvecs_tol : float, optional Threshold used so that norm(bvec)=1 (default 0.01) extract_pam_values : bool, optional Wheter or not to save pam volumes as single nifti files. parallel : bool, optional Whether to use parallelization in peak-finding during the calibration procedure. Default: False nbr_processes : int, optional If `parallel` is True, the number of subprocesses to use (default multiprocessing.cpu_count()). out_dir : string, optional Output directory (default input file directory) out_pam : string, optional Name of the peaks volume to be saved (default 'peaks.pam5') out_shm : string, optional Name of the shperical harmonics volume to be saved (default 'shm.nii.gz') out_peaks_dir : string, optional Name of the peaks directions volume to be saved (default 'peaks_dirs.nii.gz') out_peaks_values : string, optional Name of the peaks values volume to be saved (default 'peaks_values.nii.gz') out_peaks_indices : string, optional Name of the peaks indices volume to be saved (default 'peaks_indices.nii.gz') out_gfa : string, optional Name of the generalise fa volume to be saved (default 'gfa.nii.gz') References ---------- .. [1] Aganj, I., et al. 2009. ODF Reconstruction in Q-Ball Imaging with Solid Angle Consideration. """ io_it = self.get_io_iterator() for (dwi, bval, bvec, maskfile, opam, oshm, opeaks_dir, opeaks_values, opeaks_indices, ogfa) in io_it: logging.info('Loading {0}'.format(dwi)) data, affine = load_nifti(dwi) bvals, bvecs = read_bvals_bvecs(bval, bvec) if b0_threshold < bvals.min(): warn("b0_threshold (value: {0}) is too low, increase your " "b0_threshold. It should higher than the first b0 value " "({1}).".format(b0_threshold, bvals.min())) gtab = gradient_table(bvals, bvecs, b0_threshold=b0_threshold, atol=bvecs_tol) mask_vol = nib.load(maskfile).get_data().astype(np.bool) peaks_sphere = get_sphere('repulsion724') logging.info('Starting CSA computations {0}'.format(dwi)) csa_model = CsaOdfModel(gtab, sh_order) peaks_csa = peaks_from_model(model=csa_model, data=data, sphere=peaks_sphere, relative_peak_threshold=.5, min_separation_angle=25, mask=mask_vol, return_sh=True, sh_order=odf_to_sh_order, normalize_peaks=True, parallel=parallel, nbr_processes=nbr_processes) peaks_csa.affine = affine save_peaks(opam, peaks_csa) logging.info('Finished CSA {0}'.format(dwi)) if extract_pam_values: peaks_to_niftis(peaks_csa, oshm, opeaks_dir, opeaks_values, opeaks_indices, ogfa, reshape_dirs=True) dname_ = os.path.dirname(opam) if dname_ == '': logging.info('Pam5 file saved in current directory') else: logging.info( 'Pam5 file saved in {0}'.format(dname_)) return io_it
from dipy.segment.mask import median_otsu bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs) data, affine = load_nifti(fdwi) maskdata, mask = median_otsu(data, 3, 1, False, vol_idx=range(1, 20)) from dipy.reconst.dti import TensorModel tensor_model = TensorModel(gtab, fit_method='WLS') tensor_fit = tensor_model.fit(data, mask) FA = tensor_fit.fa from dipy.reconst.shm import CsaOdfModel from dipy.data import get_sphere from dipy.direction.peaks import peaks_from_model sphere = get_sphere('repulsion724') csamodel = CsaOdfModel(gtab, 4) pam = peaks_from_model(model=csamodel, data=maskdata, sphere=sphere, relative_peak_threshold=.5, min_separation_angle=25, mask=mask, return_odf=False, normalize_peaks=True)
def run(self, input_files, bvalues_files, bvectors_files, mask_files, b0_threshold=50.0, bvecs_tol=0.01, roi_center=None, roi_radius=10, fa_thr=0.7, frf=None, extract_pam_values=False, sh_order=8, odf_to_sh_order=8, parallel=False, nbr_processes=None, out_dir='', out_pam='peaks.pam5', out_shm='shm.nii.gz', out_peaks_dir='peaks_dirs.nii.gz', out_peaks_values='peaks_values.nii.gz', out_peaks_indices='peaks_indices.nii.gz', out_gfa='gfa.nii.gz'): """ Constrained spherical deconvolution 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 bvecs_tol : float, optional Bvecs should be unit vectors. (default:0.01) roi_center : variable int, optional 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]` (default None) roi_radius : int, optional radius of cubic ROI in voxels (default 10) fa_thr : float, optional FA threshold for calculating the response function (default 0.7) frf : variable float, optional Fiber response function can be for example inputed as 15 4 4 (from the command line) or [15, 4, 4] from a Python script to be converted to float and mutiplied by 10**-4 . If None the fiber response function will be computed automatically (default: None). extract_pam_values : bool, optional Save or not to save pam volumes as single nifti files. sh_order : int, optional Spherical harmonics order (default 6) used in the CSA fit. odf_to_sh_order : int, optional Spherical harmonics order used for peak_from_model to compress the ODF to spherical harmonics coefficients (default 8) parallel : bool, optional Whether to use parallelization in peak-finding during the calibration procedure. Default: False nbr_processes : int, optional If `parallel` is True, the number of subprocesses to use (default multiprocessing.cpu_count()). out_dir : string, optional Output directory (default input file directory) out_pam : string, optional Name of the peaks volume to be saved (default 'peaks.pam5') out_shm : string, optional Name of the shperical harmonics volume to be saved (default 'shm.nii.gz') out_peaks_dir : string, optional Name of the peaks directions volume to be saved (default 'peaks_dirs.nii.gz') out_peaks_values : string, optional Name of the peaks values volume to be saved (default 'peaks_values.nii.gz') out_peaks_indices : string, optional Name of the peaks indices volume to be saved (default 'peaks_indices.nii.gz') out_gfa : string, optional Name of the generalise fa volume to be saved (default 'gfa.nii.gz') References ---------- .. [1] Tournier, J.D., et al. NeuroImage 2007. Robust determination of the fibre orientation distribution in diffusion MRI: Non-negativity constrained super-resolved spherical deconvolution. """ io_it = self.get_io_iterator() for (dwi, bval, bvec, maskfile, opam, oshm, opeaks_dir, opeaks_values, opeaks_indices, ogfa) in io_it: logging.info('Loading {0}'.format(dwi)) data, affine = load_nifti(dwi) bvals, bvecs = read_bvals_bvecs(bval, bvec) print(b0_threshold, bvals.min()) if b0_threshold < bvals.min(): warn("b0_threshold (value: {0}) is too low, increase your " "b0_threshold. It should higher than the first b0 value " "({1}).".format(b0_threshold, bvals.min())) gtab = gradient_table(bvals, bvecs, b0_threshold=b0_threshold, atol=bvecs_tol) mask_vol = nib.load(maskfile).get_data().astype(np.bool) n_params = ((sh_order + 1) * (sh_order + 2)) / 2 if data.shape[-1] < n_params: raise ValueError( 'You need at least {0} unique DWI volumes to ' 'compute fiber odfs. You currently have: {1}' ' DWI volumes.'.format(n_params, data.shape[-1])) if frf is None: logging.info('Computing response function') if roi_center is not None: logging.info('Response ROI center:\n{0}' .format(roi_center)) logging.info('Response ROI radius:\n{0}' .format(roi_radius)) response, ratio, nvox = auto_response( gtab, data, roi_center=roi_center, roi_radius=roi_radius, fa_thr=fa_thr, return_number_of_voxels=True) response = list(response) else: logging.info('Using response function') if isinstance(frf, str): l01 = np.array(literal_eval(frf), dtype=np.float64) else: l01 = np.array(frf, dtype=np.float64) l01 *= 10 ** -4 response = np.array([l01[0], l01[1], l01[1]]) ratio = l01[1] / l01[0] response = (response, ratio) logging.info("Eigenvalues for the frf of the input" " data are :{0}".format(response[0])) logging.info('Ratio for smallest to largest eigen value is {0}' .format(ratio)) peaks_sphere = get_sphere('repulsion724') logging.info('CSD computation started.') csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order=sh_order) peaks_csd = peaks_from_model(model=csd_model, data=data, sphere=peaks_sphere, relative_peak_threshold=.5, min_separation_angle=25, mask=mask_vol, return_sh=True, sh_order=sh_order, normalize_peaks=True, parallel=parallel, nbr_processes=nbr_processes) peaks_csd.affine = affine save_peaks(opam, peaks_csd) logging.info('CSD computation completed.') if extract_pam_values: peaks_to_niftis(peaks_csd, oshm, opeaks_dir, opeaks_values, opeaks_indices, ogfa, reshape_dirs=True) dname_ = os.path.dirname(opam) if dname_ == '': logging.info('Pam5 file saved in current directory') else: logging.info( 'Pam5 file saved in {0}'.format(dname_)) return io_it
def fiber_tracking(subject): # declare the type of algorithm, \in [deterministic, probabilitic] algo = 'deterministic' # algo = 'probabilitic' ''' @param subject: string represents the subject name @param algo: the name for the algorithms, \in ['deterministic', 'probabilitic'] @return streamlines: for saving the final results and visualization ''' print('processing for', subject) fname, bval_fname, bvec_fname, label_fname = get_file_names(subject) data, sub_affine, img = load_nifti(fname, return_img=True) bvals, bvecs = read_bvals_bvecs(bval_fname, bvec_fname) gtab = gradient_table(bvals, bvecs) labels = load_nifti_data(label_fname) print('data loading complete.\n') ################################################################## # set mask(s) and seed(s) # global_mask = binary_dilation((data[:, :, :, 0] != 0)) global_mask = binary_dilation((labels == 1) | (labels == 2)) # global_mask = binary_dilation((labels == 2) | (labels == 32) | (labels == 76)) affine = np.eye(4) seeds = utils.seeds_from_mask(global_mask, affine, density=1) print('mask(s) and seed(s) set complete.\n') ################################################################## print('getting directions from diffusion dataset...') # define tracking mask with Constant Solid Angle (CSA) csamodel = CsaOdfModel(gtab, 6) stopping_criterion = BinaryStoppingCriterion(global_mask) # define direction criterion direction_criterion = None print('Compute directions...') if algo == "deterministic": # EuDX direction_criterion = peaks.peaks_from_model( model=csamodel, data=data, sphere=peaks.default_sphere, relative_peak_threshold=.8, min_separation_angle=45, mask=global_mask) # # Deterministic Algorithm (select direction with max probability) # direction_criterion = DeterministicMaximumDirectionGetter.from_shcoeff( # csd_fit.shm_coeff, # max_angle=30., # sphere=default_sphere) else: response, ratio = auto_response(gtab, data, roi_radius=10, fa_thr=0.7) # fit the reconstruction model with Constrained Spherical Deconvolusion (CSD) csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order=6) csd_fit = csd_model.fit(data, mask=global_mask) # gfa = csamodel.fit(data, mask=global_mask).gfa # stopping_criterion = ThresholdStoppingCriterion(gfa, .25) # Probabilitic Algorithm direction_criterion = ProbabilisticDirectionGetter.from_shcoeff( csd_fit.shm_coeff, max_angle=30., sphere=default_sphere) print('direction computation complete.\n') ################################################################## print('start tracking process...') # start tracking streamline_generator = LocalTracking(direction_criterion, stopping_criterion, seeds, affine=affine, step_size=0.5) # Generate streamlines object streamlines = Streamlines(streamline_generator) sft = StatefulTractogram(streamlines, img, Space.RASMM) print('traking complete.\n') ################################################################## return { "subject": subject, "streamlines": streamlines, "sft": sft, "affine": sub_affine, "data": data, "img": img, "labels": labels }
def main(): parser = _build_arg_parser() args = parser.parse_args() if not args.not_all: args.gfa = args.gfa or 'gfa.nii.gz' args.peaks = args.peaks or 'peaks.nii.gz' args.peak_indices = args.peak_indices or 'peaks_indices.nii.gz' args.sh = args.sh or 'sh.nii.gz' args.nufo = args.nufo or 'nufo.nii.gz' args.a_power = args.a_power or 'anisotropic_power.nii.gz' arglist = [ args.gfa, args.peaks, args.peak_indices, args.sh, args.nufo, args.a_power ] if args.not_all and not any(arglist): parser.error('When using --not_all, you need to specify at least ' + 'one file to output.') assert_inputs_exist(parser, [args.in_dwi, args.in_bval, args.in_bvec]) assert_outputs_exist(parser, args, arglist) validate_nbr_processes(parser, args) nbr_processes = args.nbr_processes parallel = nbr_processes > 1 # Load data img = nib.load(args.in_dwi) data = img.get_fdata(dtype=np.float32) bvals, bvecs = read_bvals_bvecs(args.in_bval, args.in_bvec) 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()) sphere = get_sphere('symmetric724') mask = None if args.mask: mask = get_data_as_mask(nib.load(args.mask)) # Sanity check on shape of mask if mask.shape != data.shape[:-1]: raise ValueError('Mask shape does not match data shape.') if args.use_qball: model = QballModel(gtab, sh_order=args.sh_order, smooth=DEFAULT_SMOOTH) else: model = CsaOdfModel(gtab, sh_order=args.sh_order, smooth=DEFAULT_SMOOTH) odfpeaks = peaks_from_model(model=model, data=data, sphere=sphere, relative_peak_threshold=.5, min_separation_angle=25, mask=mask, return_odf=False, normalize_peaks=True, return_sh=True, sh_order=int(args.sh_order), sh_basis_type=args.sh_basis, npeaks=5, parallel=parallel, nbr_processes=nbr_processes) if args.gfa: nib.save(nib.Nifti1Image(odfpeaks.gfa.astype(np.float32), img.affine), args.gfa) if args.peaks: nib.save( nib.Nifti1Image(reshape_peaks_for_visualization(odfpeaks), img.affine), args.peaks) if args.peak_indices: nib.save(nib.Nifti1Image(odfpeaks.peak_indices, img.affine), args.peak_indices) if args.sh: nib.save( nib.Nifti1Image(odfpeaks.shm_coeff.astype(np.float32), img.affine), args.sh) if args.nufo: peaks_count = (odfpeaks.peak_indices > -1).sum(3) nib.save(nib.Nifti1Image(peaks_count.astype(np.int32), img.affine), args.nufo) if args.a_power: odf_a_power = anisotropic_power(odfpeaks.shm_coeff) nib.save(nib.Nifti1Image(odf_a_power.astype(np.float32), img.affine), args.a_power)
sh_image.SetPixel(x, y, z, sh_coeffs[z, y, x, :]) sh_image.SetOrigin(in_image.GetOrigin()) sh_image.SetSpacing(in_image.GetSpacing()) sh_image.SetDirection(in_image.GetDirection()) if num_peaks > 0: print('Calculating peaks') sys.stdout.flush() # calculate peak image data = np.nan_to_num(data) sf_peaks = dpp.peaks_from_model( model, data, sphere, relative_peak_threshold=relative_peak_threshold, min_separation_angle=min_separation_angle, return_sh=False, npeaks=num_peaks, parallel=True, mask=mask) # reshape to be MITK/MRtrix compliant s = sf_peaks.peak_dirs.shape peaks = sf_peaks.peak_dirs.reshape((s[0], s[1], s[2], num_peaks * 3), order='C') peaks = np.nan_to_num(peaks) peak_image = sitk.Image([data.shape[2], data.shape[1], data.shape[0]], sitk.sitkVectorFloat32, num_peaks * 3) # scale peaks max_peak = 1.0
for i_par in tqdm(range(len(par_vec)), ascii=True): par = par_vec[i_par] sf_model = sfm.SparseFascicleModel(gtab_test, sphere=sphere, l1_ratio=0.5, alpha=par, response=response[0]) '''sf_fit = sf_model.fit( hardi_d_img_test_np, mask ) sf_odf = sf_fit.odf(sphere)''' sf_peaks = dpp.peaks_from_model(sf_model, hardi_d_img_test_np, sphere, relative_peak_threshold=0.5, min_separation_angle=30, return_sh=False) peak_vals = sf_peaks.peak_values peak_dirs = sf_peaks.peak_dirs img_mos_dipy = np.sum(peak_vals > 0.1, axis=-1) img_mos_dipy[mask == 0] = 0 mose_temp = img_mos_dipy.astype(np.int) Mose_CM = np.zeros((6, 6)) for i in range(len(ind_1)):
""" We've loaded an image called ``labels_img`` which is a map of tissue types such that every integer value in the array ``labels`` represents an anatomical structure or tissue type [#]_. For this example, the image was created so that white matter voxels have values of either 1 or 2. We'll use ``peaks_from_model`` to apply the ``CsaOdfModel`` to each white matter voxel and estimate fiber orientations which we can use for tracking. We will also dilate this mask by 1 voxel to ensure streamlines reach the grey matter. """ white_matter = binary_dilation((labels == 1) | (labels == 2)) csamodel = shm.CsaOdfModel(gtab, 6) csapeaks = peaks.peaks_from_model(model=csamodel, data=data, sphere=peaks.default_sphere, relative_peak_threshold=.8, min_separation_angle=45, mask=white_matter) """ Now we can use EuDX to track all of the white matter. To keep things reasonably fast we use ``density=1`` which will result in 1 seeds per voxel. The stopping criterion, determining when the tracking stops, is set to stop when the tracking exit the white matter. """ seeds = utils.seeds_from_mask(white_matter, density=1) stopping_criterion = BinaryStoppingCriterion(white_matter) affine = np.eye(4) streamline_generator = LocalTracking(csapeaks, stopping_criterion, seeds,
continue sh_image.SetPixel(x, y, z, sh_coeffs[z, y, x, :]) sh_image.SetOrigin(in_image.GetOrigin()) sh_image.SetSpacing(in_image.GetSpacing()) sh_image.SetDirection(in_image.GetDirection()) if num_peaks > 0: print('Calculating peaks') sys.stdout.flush() # calculate peak image data = np.nan_to_num(data) sf_peaks = dpp.peaks_from_model(model, data, sphere, relative_peak_threshold=relative_peak_threshold, min_separation_angle=min_separation_angle, return_sh=False, npeaks=num_peaks, parallel=True, mask=mask) # reshape to be MITK/MRtrix compliant s = sf_peaks.peak_dirs.shape peaks = sf_peaks.peak_dirs.reshape((s[0], s[1], s[2], num_peaks * 3), order='C') peaks = np.nan_to_num(peaks) peak_image = sitk.Image([data.shape[2], data.shape[1], data.shape[0]], sitk.sitkVectorFloat32, num_peaks * 3) # scale peaks max_peak = 1.0 if normalize_peaks: max_peak = np.max(sf_peaks.peak_values) if max_peak <= 0:
def test_PeaksAndMetricsDirectionGetter(): class SillyModel(object): def fit(self, data, mask=None): return SillyFit(self) class SillyFit(object): def __init__(self, model): self.model = model def odf(self, sphere): odf = np.zeros(sphere.theta.shape) r = np.random.randint(0, len(odf)) odf[r] = 1 return odf def get_direction(dg, point, dir): newdir = dir.copy() state = dg.get_direction(point, newdir) return (state, np.array(newdir)) data = np.random.random((3, 4, 5, 2)) peaks = peaks_from_model(SillyModel(), data, default_sphere, relative_peak_threshold=.5, min_separation_angle=25) peaks._initialize() up = np.zeros(3) up[2] = 1. down = -up for i in range(3-1): for j in range(4-1): for k in range(5-1): point = np.array([i, j, k], dtype=float) # Test that the angle threshold rejects points peaks.ang_thr = 0. state, nd = get_direction(peaks, point, up) npt.assert_equal(state, 1) # Here we leverage the fact that we know Hemispheres project # all their vertices into the z >= 0 half of the sphere. peaks.ang_thr = 90. state, nd = get_direction(peaks, point, up) npt.assert_equal(state, 0) expected_dir = peaks.peak_dirs[i, j, k, 0] npt.assert_array_almost_equal(nd, expected_dir) state, nd = get_direction(peaks, point, down) npt.assert_array_almost_equal(nd, -expected_dir) # Check that we can get directions at non-integer points point += np.random.random(3) state, nd = get_direction(peaks, point, up) npt.assert_equal(state, 0) # Check that points are rounded to get initial direction point -= .5 initial_dir = peaks.initial_direction(point) # It should be a (1, 3) array npt.assert_array_almost_equal(initial_dir, [expected_dir]) peaks1 = peaks_from_model(SillyModel(), data, default_sphere, relative_peak_threshold=.5, min_separation_angle=25, npeaks=1) peaks1._initialize() point = np.array([1, 1, 1], dtype=float) # it should have one direction npt.assert_array_almost_equal(len(peaks1.initial_direction(point)), 1) npt.assert_array_almost_equal(len(peaks.initial_direction(point)), 1)
def test_peaksFromModel(): data = np.zeros((10, 2)) for sphere in [_sphere, get_sphere('symmetric642')]: # Test basic case model = SimpleOdfModel(_gtab) _odf = (sphere.vertices * [1, 2, 3]).sum(-1) odf_argmax = _odf.argmax() pam = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True) assert_array_equal(pam.gfa, gfa(_odf)) assert_array_equal(pam.peak_values[:, 0], 1.) assert_array_equal(pam.peak_values[:, 1:], 0.) mn, mx = _odf.min(), _odf.max() assert_array_equal(pam.qa[:, 0], (mx - mn) / mx) assert_array_equal(pam.qa[:, 1:], 0.) assert_array_equal(pam.peak_indices[:, 0], odf_argmax) assert_array_equal(pam.peak_indices[:, 1:], -1) # Test that odf array matches and is right shape pam = peaks_from_model(model, data, sphere, .5, 45, return_odf=True) expected_shape = (len(data), len(_odf)) assert_equal(pam.odf.shape, expected_shape) assert_((_odf == pam.odf).all()) assert_array_equal(pam.peak_values[:, 0], _odf.max()) # Test mask mask = (np.arange(10) % 2) == 1 pam = peaks_from_model(model, data, sphere, .5, 45, mask=mask, normalize_peaks=True) assert_array_equal(pam.gfa[~mask], 0) assert_array_equal(pam.qa[~mask], 0) assert_array_equal(pam.peak_values[~mask], 0) assert_array_equal(pam.peak_indices[~mask], -1) assert_array_equal(pam.gfa[mask], gfa(_odf)) assert_array_equal(pam.peak_values[mask, 0], 1.) assert_array_equal(pam.peak_values[mask, 1:], 0.) mn, mx = _odf.min(), _odf.max() assert_array_equal(pam.qa[mask, 0], (mx - mn) / mx) assert_array_equal(pam.qa[mask, 1:], 0.) assert_array_equal(pam.peak_indices[mask, 0], odf_argmax) assert_array_equal(pam.peak_indices[mask, 1:], -1) # Test serialization and deserialization: for normalize_peaks in [True, False]: for return_odf in [True, False]: for return_sh in [True, False]: pam = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=normalize_peaks, return_odf=return_odf, return_sh=return_sh) b = BytesIO() pickle.dump(pam, b) b.seek(0) new_pam = pickle.load(b) b.close() for attr in ['peak_dirs', 'peak_values', 'peak_indices', 'gfa', 'qa', 'shm_coeff', 'B', 'odf']: assert_array_equal(getattr(pam, attr), getattr(new_pam, attr)) assert_array_equal(pam.sphere.vertices, new_pam.sphere.vertices)
def recursive_response(gtab, data, mask=None, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=True, nbr_processes=None, sphere=default_sphere): """ Recursive calibration of response function using peak threshold Parameters ---------- gtab : GradientTable data : ndarray diffusion data mask : ndarray, optional mask for recursive calibration, for example a white matter mask. It has shape `data.shape[0:3]` and dtype=bool. Default: use the entire data array. sh_order : int, optional maximal spherical harmonics order. Default: 8 peak_thr : float, optional peak threshold, how large the second peak can be relative to the first peak in order to call it a single fiber population [1]. Default: 0.01 init_fa : float, optional FA of the initial 'fat' response function (tensor). Default: 0.08 init_trace : float, optional trace of the initial 'fat' response function (tensor). Default: 0.0021 iter : int, optional maximum number of iterations for calibration. Default: 8. convergence : float, optional convergence criterion, maximum relative change of SH coefficients. Default: 0.001. parallel : bool, optional Whether to use parallelization in peak-finding during the calibration procedure. Default: True nbr_processes: int If `parallel` is True, the number of subprocesses to use (default multiprocessing.cpu_count()). sphere : Sphere, optional. The sphere used for peak finding. Default: default_sphere. Returns ------- response : ndarray response function in SH coefficients Notes ----- In CSD there is an important pre-processing step: the estimation of the fiber response function. Using an FA threshold is not a very robust method. It is dependent on the dataset (non-informed used subjectivity), and still depends on the diffusion tensor (FA and first eigenvector), which has low accuracy at high b-value. This function recursively calibrates the response function, for more information see [1]. References ---------- .. [1] Tax, C.M.W., et al. NeuroImage 2014. Recursive calibration of the fiber response function for spherical deconvolution of diffusion MRI data. """ S0 = 1. evals = fa_trace_to_lambdas(init_fa, init_trace) res_obj = (evals, S0) if mask is None: data = data.reshape(-1, data.shape[-1]) else: data = data[mask] n = np.arange(0, sh_order + 1, 2) where_dwi = lazy_index(~gtab.b0s_mask) response_p = np.ones(len(n)) for _ in range(iter): r_sh_all = np.zeros(len(n)) csd_model = ConstrainedSphericalDeconvModel(gtab, res_obj, sh_order=sh_order) csd_peaks = peaks_from_model(model=csd_model, data=data, sphere=sphere, relative_peak_threshold=peak_thr, min_separation_angle=25, parallel=parallel, nbr_processes=nbr_processes) dirs = csd_peaks.peak_dirs vals = csd_peaks.peak_values single_peak_mask = (vals[:, 1] / vals[:, 0]) < peak_thr data = data[single_peak_mask] dirs = dirs[single_peak_mask] for num_vox in range(data.shape[0]): rotmat = vec2vec_rotmat(dirs[num_vox, 0], np.array([0, 0, 1])) rot_gradients = np.dot(rotmat, gtab.gradients.T).T x, y, z = rot_gradients[where_dwi].T r, theta, phi = cart2sphere(x, y, z) # for the gradient sphere B_dwi = real_sph_harm(0, n, theta[:, None], phi[:, None]) r_sh_all += np.linalg.lstsq(B_dwi, data[num_vox, where_dwi], rcond=-1)[0] response = r_sh_all / data.shape[0] res_obj = AxSymShResponse(data[:, gtab.b0s_mask].mean(), response) change = abs((response_p - response) / response_p) if all(change < convergence): break response_p = response return res_obj
def recursive_response(gtab, data, mask=None, sh_order=8, peak_thr=0.01, init_fa=0.08, init_trace=0.0021, iter=8, convergence=0.001, parallel=True, nbr_processes=None, sphere=default_sphere): """ Recursive calibration of response function using peak threshold Parameters ---------- gtab : GradientTable data : ndarray diffusion data mask : ndarray, optional mask for recursive calibration, for example a white matter mask. It has shape `data.shape[0:3]` and dtype=bool. Default: use the entire data array. sh_order : int, optional maximal spherical harmonics order. Default: 8 peak_thr : float, optional peak threshold, how large the second peak can be relative to the first peak in order to call it a single fiber population [1]. Default: 0.01 init_fa : float, optional FA of the initial 'fat' response function (tensor). Default: 0.08 init_trace : float, optional trace of the initial 'fat' response function (tensor). Default: 0.0021 iter : int, optional maximum number of iterations for calibration. Default: 8. convergence : float, optional convergence criterion, maximum relative change of SH coefficients. Default: 0.001. parallel : bool, optional Whether to use parallelization in peak-finding during the calibration procedure. Default: True nbr_processes: int If `parallel` is True, the number of subprocesses to use (default multiprocessing.cpu_count()). sphere : Sphere, optional. The sphere used for peak finding. Default: default_sphere. Returns ------- response : ndarray response function in SH coefficients Notes ----- In CSD there is an important pre-processing step: the estimation of the fiber response function. Using an FA threshold is not a very robust method. It is dependent on the dataset (non-informed used subjectivity), and still depends on the diffusion tensor (FA and first eigenvector), which has low accuracy at high b-value. This function recursively calibrates the response function, for more information see [1]. References ---------- .. [1] Tax, C.M.W., et al. NeuroImage 2014. Recursive calibration of the fiber response function for spherical deconvolution of diffusion MRI data. """ S0 = 1. evals = fa_trace_to_lambdas(init_fa, init_trace) res_obj = (evals, S0) if mask is None: data = data.reshape(-1, data.shape[-1]) else: data = data[mask] n = np.arange(0, sh_order + 1, 2) where_dwi = lazy_index(~gtab.b0s_mask) response_p = np.ones(len(n)) for num_it in range(iter): r_sh_all = np.zeros(len(n)) csd_model = ConstrainedSphericalDeconvModel(gtab, res_obj, sh_order=sh_order) csd_peaks = peaks_from_model(model=csd_model, data=data, sphere=sphere, relative_peak_threshold=peak_thr, min_separation_angle=25, parallel=parallel, nbr_processes=nbr_processes) dirs = csd_peaks.peak_dirs vals = csd_peaks.peak_values single_peak_mask = (vals[:, 1] / vals[:, 0]) < peak_thr data = data[single_peak_mask] dirs = dirs[single_peak_mask] for num_vox in range(data.shape[0]): rotmat = vec2vec_rotmat(dirs[num_vox, 0], np.array([0, 0, 1])) rot_gradients = np.dot(rotmat, gtab.gradients.T).T x, y, z = rot_gradients[where_dwi].T r, theta, phi = cart2sphere(x, y, z) # for the gradient sphere B_dwi = real_sph_harm(0, n, theta[:, None], phi[:, None]) r_sh_all += np.linalg.lstsq(B_dwi, data[num_vox, where_dwi])[0] response = r_sh_all / data.shape[0] res_obj = AxSymShResponse(data[:, gtab.b0s_mask].mean(), response) change = abs((response_p - response) / response_p) if all(change < convergence): break response_p = response return res_obj
sphere = get_sphere() from dipy.reconst import sfm sf_model = sfm.SparseFascicleModel(gtab, sphere=sphere, l1_ratio=0.5, alpha=0.001, response=response[0]) """ We fit this model to the data in each voxel in the white-matter mask, so that we can use these directions in tracking: """ from dipy.direction.peaks import peaks_from_model pnm = peaks_from_model(sf_model, data, sphere, relative_peak_threshold=.5, min_separation_angle=25, mask=white_matter, parallel=True ) """ A ThresholdTissueClassifier object is used to segment the data to track only through areas in which the Generalized Fractional Anisotropy (GFA) is sufficiently high. """ from dipy.tracking.local import ThresholdTissueClassifier classifier = ThresholdTissueClassifier(pnm.gfa, .25) """ Tracking will be started from a set of seeds evenly distributed in the white matter:
def test_peaksFromModelParallel(): SNR = 100 S0 = 100 _, fbvals, fbvecs = get_fnames('small_64D') bvals, bvecs = read_bvals_bvecs(fbvals, fbvecs) gtab = gradient_table(bvals, bvecs) mevals = np.array(([0.0015, 0.0003, 0.0003], [0.0015, 0.0003, 0.0003])) data, _ = multi_tensor(gtab, mevals, S0, angles=[(0, 0), (60, 0)], fractions=[50, 50], snr=SNR) for sphere in [_sphere, default_sphere]: # test equality with/without multiprocessing model = SimpleOdfModel(gtab) pam_multi = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=True) pam_single = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=False) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always", category=UserWarning) pam_multi_inv1 = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=True, nbr_processes=0) pam_multi_inv2 = peaks_from_model(model, data, sphere, .5, 45, normalize_peaks=True, return_odf=True, return_sh=True, parallel=True, nbr_processes=-2) assert_(len(w) == 2) assert_(issubclass(w[0].category, UserWarning)) assert_(issubclass(w[1].category, UserWarning)) assert_("Invalid number of processes " in str(w[0].message)) assert_("Invalid number of processes " in str(w[1].message)) for pam in [pam_multi, pam_multi_inv1, pam_multi_inv2]: assert_equal(pam.gfa.dtype, pam_single.gfa.dtype) assert_equal(pam.gfa.shape, pam_single.gfa.shape) assert_array_almost_equal(pam.gfa, pam_single.gfa) assert_equal(pam.qa.dtype, pam_single.qa.dtype) assert_equal(pam.qa.shape, pam_single.qa.shape) assert_array_almost_equal(pam.qa, pam_single.qa) assert_equal(pam.peak_values.dtype, pam_single.peak_values.dtype) assert_equal(pam.peak_values.shape, pam_single.peak_values.shape) assert_array_almost_equal(pam.peak_values, pam_single.peak_values) assert_equal(pam.peak_indices.dtype, pam_single.peak_indices.dtype) assert_equal(pam.peak_indices.shape, pam_single.peak_indices.shape) assert_array_equal(pam.peak_indices, pam_single.peak_indices) assert_equal(pam.peak_dirs.dtype, pam_single.peak_dirs.dtype) assert_equal(pam.peak_dirs.shape, pam_single.peak_dirs.shape) assert_array_almost_equal(pam.peak_dirs, pam_single.peak_dirs) assert_equal(pam.shm_coeff.dtype, pam_single.shm_coeff.dtype) assert_equal(pam.shm_coeff.shape, pam_single.shm_coeff.shape) assert_array_almost_equal(pam.shm_coeff, pam_single.shm_coeff) assert_equal(pam.odf.dtype, pam_single.odf.dtype) assert_equal(pam.odf.shape, pam_single.odf.shape) assert_array_almost_equal(pam.odf, pam_single.odf)
def main(): parser = _build_arg_parser() args = parser.parse_args() logging.basicConfig(level=logging.INFO) if not args.not_all: args.fodf = args.fodf or 'fodf.nii.gz' args.peaks = args.peaks or 'peaks.nii.gz' args.peak_indices = args.peak_indices or 'peak_indices.nii.gz' arglist = [args.fodf, args.peaks, args.peak_indices] if args.not_all and not any(arglist): parser.error('When using --not_all, you need to specify at least ' 'one file to output.') assert_inputs_exist(parser, [args.input, args.bvals, args.bvecs, args.frf_file]) assert_outputs_exist(parser, args, arglist) nbr_processes = args.nbr_processes parallel = True if nbr_processes is not None: if nbr_processes <= 0: nbr_processes = None elif nbr_processes == 1: parallel = False full_frf = np.loadtxt(args.frf_file) if not full_frf.shape[0] == 4: raise ValueError('FRF file did not contain 4 elements. ' 'Invalid or deprecated FRF format') frf = full_frf[0:3] mean_b0_val = full_frf[3] vol = nib.load(args.input) data = vol.get_data() 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()) if args.mask is None: mask = None else: mask = nib.load(args.mask).get_data().astype(np.bool) # Raise warning for sh order if there is not enough DWIs if data.shape[-1] < (args.sh_order + 1) * (args.sh_order + 2) / 2: warnings.warn( 'We recommend having at least {} unique DWIs volumes, but you ' 'currently have {} volumes. Try lowering the parameter --sh_order ' 'in case of non convergence.'.format( (args.sh_order + 1) * (args.sh_order + 2) / 2, data.shape[-1])) reg_sphere = get_sphere('symmetric362') peaks_sphere = get_sphere('symmetric724') csd_model = ConstrainedSphericalDeconvModel(gtab, (frf, mean_b0_val), reg_sphere=reg_sphere, sh_order=args.sh_order) peaks_csd = peaks_from_model(model=csd_model, data=data, sphere=peaks_sphere, relative_peak_threshold=.5, min_separation_angle=25, mask=mask, return_sh=True, sh_basis_type=args.sh_basis, sh_order=args.sh_order, normalize_peaks=True, parallel=parallel, nbr_processes=nbr_processes) if args.fodf: nib.save( nib.Nifti1Image(peaks_csd.shm_coeff.astype(np.float32), vol.affine), args.fodf) if args.peaks: nib.save( nib.Nifti1Image(reshape_peaks_for_visualization(peaks_csd), vol.affine), args.peaks) if args.peak_indices: nib.save(nib.Nifti1Image(peaks_csd.peak_indices, vol.affine), args.peak_indices)