def run(self, pam_files, stopping_files, seeding_files, stopping_thr=0.2, seed_density=1, use_sh=False, out_dir='', out_tractogram='tractogram.trk'): """ Workflow for deterministic using a saved peaks and metrics (PAM) file as input. Parameters ---------- pam_files : string Path to the peaks and metrics files. This path may contain wildcards to use multiple masks at once. stopping_files : string Path of FA or other images used for stopping criteria for tracking. seeding_files : string A binary image showing where we need to seed for tracking. stopping_thr : float, optional Threshold applied to stopping volume's data to identify where tracking has to stop (default 0.25). seed_density : int, optional Number of seeds per dimension inside voxel (default 1). For example, seed_density of 2 means 8 regularly distributed points in the voxel. And seed density of 1 means 1 point at the center of the voxel. use_sh : bool, optional Use spherical harmonics saved in peaks to find the maximum peak cone. (default False) out_dir : string, optional Output directory (default input file directory) out_tractogram : string, optional Name of the tractogram file to be saved (default 'tractogram.trk') References ---------- Garyfallidis, University of Cambridge, PhD thesis 2012. Amirbekian, University of California San Francisco, PhD thesis 2017. """ io_it = self.get_io_iterator() for pams_path, stopping_path, seeding_path, out_tract in io_it: logging.info('Deterministic tracking on {0}'.format(pams_path)) pam = load_peaks(pams_path, verbose=False) self._core_run(stopping_path, stopping_thr, seeding_path, seed_density, use_sh, pam, out_tract)
def reconst_flow_core(flow): with TemporaryDirectory() as out_dir: data_path, bval_path, bvec_path = get_data('small_64D') vol_img = nib.load(data_path) volume = vol_img.get_data() mask = np.ones_like(volume[:, :, :, 0]) mask_img = nib.Nifti1Image(mask.astype(np.uint8), vol_img.get_affine()) mask_path = join(out_dir, 'tmp_mask.nii.gz') nib.save(mask_img, mask_path) reconst_flow = flow() reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, extract_pam_values=True) gfa_path = reconst_flow.last_generated_outputs['out_gfa'] gfa_data = nib.load(gfa_path).get_data() assert_equal(gfa_data.shape, volume.shape[:-1]) peaks_dir_path = reconst_flow.last_generated_outputs['out_peaks_dir'] peaks_dir_data = nib.load(peaks_dir_path).get_data() assert_equal(peaks_dir_data.shape[-1], 15) assert_equal(peaks_dir_data.shape[:-1], volume.shape[:-1]) peaks_idx_path = \ reconst_flow.last_generated_outputs['out_peaks_indices'] peaks_idx_data = nib.load(peaks_idx_path).get_data() assert_equal(peaks_idx_data.shape[-1], 5) assert_equal(peaks_idx_data.shape[:-1], volume.shape[:-1]) peaks_vals_path = \ reconst_flow.last_generated_outputs['out_peaks_values'] peaks_vals_data = nib.load(peaks_vals_path).get_data() assert_equal(peaks_vals_data.shape[-1], 5) assert_equal(peaks_vals_data.shape[:-1], volume.shape[:-1]) shm_path = reconst_flow.last_generated_outputs['out_shm'] shm_data = nib.load(shm_path).get_data() assert_equal(shm_data.shape[-1], 45) assert_equal(shm_data.shape[:-1], volume.shape[:-1]) pam = load_peaks(reconst_flow.last_generated_outputs['out_pam']) npt.assert_allclose(pam.peak_dirs.reshape(peaks_dir_data.shape), peaks_dir_data) npt.assert_allclose(pam.peak_values, peaks_vals_data) npt.assert_allclose(pam.peak_indices, peaks_idx_data) npt.assert_allclose(pam.shm_coeff, shm_data) npt.assert_allclose(pam.gfa, gfa_data)
def run(self, pam_files, stopping_files, seeding_files, stopping_thr=0.2, seed_density=1, use_sh=False, out_dir='', out_tractogram='tractogram.trk'): """ Workflow for deterministic using a saved peaks and metrics (PAM) file as input. Parameters ---------- pam_files : string Path to the peaks and metrics files. This path may contain wildcards to use multiple masks at once. stopping_files : string Path of FA or other images used for stopping criteria for tracking. seeding_files : string A binary image showing where we need to seed for tracking. stopping_thr : float, optional Threshold applied to stopping volume's data to identify where tracking has to stop (default 0.25). seed_density : int, optional Number of seeds per dimension inside voxel (default 1). For example, seed_density of 2 means 8 regularly distributed points in the voxel. And seed density of 1 means 1 point at the center of the voxel. use_sh : bool, optional Use spherical harmonics saved in peaks to find the maximum peak cone. (default False) out_dir : string, optional Output directory (default input file directory) out_tractogram : string, optional Name of the tractogram file to be saved (default 'tractogram.trk') References ---------- Garyfallidis, University of Cambridge, PhD thesis 2012. Amirbekian, University of California San Francisco, PhD thesis 2017. """ io_it = self.get_io_iterator() for pams_path, stopping_path, seeding_path, out_tract in io_it: logging.info('Deterministic tracking on {0}' .format(pams_path)) pam = load_peaks(pams_path, verbose=False) self._core_run(stopping_path, stopping_thr, seeding_path, seed_density, use_sh, pam, out_tract)
def test_io_peaks(): with InTemporaryDirectory(): fname = 'test.pam5' pam = PeaksAndMetrics() pam.affine = np.eye(4) pam.peak_dirs = np.random.rand(10, 10, 10, 5, 3) pam.peak_values = np.zeros((10, 10, 10, 5)) pam.peak_indices = np.zeros((10, 10, 10, 5)) pam.shm_coeff = np.zeros((10, 10, 10, 45)) pam.sphere = default_sphere pam.B = np.zeros((45, default_sphere.vertices.shape[0])) pam.total_weight = 0.5 pam.ang_thr = 60 pam.gfa = np.zeros((10, 10, 10)) pam.qa = np.zeros((10, 10, 10, 5)) pam.odf = np.zeros((10, 10, 10, default_sphere.vertices.shape[0])) save_peaks(fname, pam) pam2 = load_peaks(fname, verbose=True) npt.assert_array_equal(pam.peak_dirs, pam2.peak_dirs) pam2.affine = None fname2 = 'test2.pam5' save_peaks(fname2, pam2, np.eye(4)) pam2_res = load_peaks(fname2, verbose=True) npt.assert_array_equal(pam.peak_dirs, pam2_res.peak_dirs) pam3 = load_peaks(fname2, verbose=False) for attr in [ 'peak_dirs', 'peak_values', 'peak_indices', 'gfa', 'qa', 'shm_coeff', 'B', 'odf' ]: npt.assert_array_equal(getattr(pam3, attr), getattr(pam, attr)) npt.assert_equal(pam3.total_weight, pam.total_weight) npt.assert_equal(pam3.ang_thr, pam.ang_thr) npt.assert_array_almost_equal(pam3.sphere.vertices, pam.sphere.vertices) fname3 = 'test3.pam5' pam4 = PeaksAndMetrics() npt.assert_raises(ValueError, save_peaks, fname3, pam4) fname4 = 'test4.pam5' del pam.affine save_peaks(fname4, pam, affine=None) fname5 = 'test5.pkm' npt.assert_raises(IOError, save_peaks, fname5, pam) pam.affine = np.eye(4) fname6 = 'test6.pam5' save_peaks(fname6, pam, verbose=True) del pam.shm_coeff save_peaks(fname6, pam, verbose=False) pam.shm_coeff = np.zeros((10, 10, 10, 45)) del pam.odf save_peaks(fname6, pam) pam_tmp = load_peaks(fname6, True) npt.assert_equal(pam_tmp.odf, None) fname7 = 'test7.paw' npt.assert_raises(IOError, load_peaks, fname7) del pam.shm_coeff save_peaks(fname6, pam, verbose=True) fname_shm = 'shm.nii.gz' fname_dirs = 'dirs.nii.gz' fname_values = 'values.nii.gz' fname_indices = 'indices.nii.gz' fname_gfa = 'gfa.nii.gz' pam.shm_coeff = np.ones((10, 10, 10, 45)) peaks_to_niftis(pam, fname_shm, fname_dirs, fname_values, fname_indices, fname_gfa, reshape_dirs=False) os.path.isfile(fname_shm) os.path.isfile(fname_dirs) os.path.isfile(fname_values) os.path.isfile(fname_indices) os.path.isfile(fname_gfa)
def reconst_flow_core(flow): with TemporaryDirectory() as out_dir: data_path, bval_path, bvec_path = get_fnames('small_64D') volume, affine = load_nifti(data_path) mask = np.ones_like(volume[:, :, :, 0]) mask_path = pjoin(out_dir, 'tmp_mask.nii.gz') save_nifti(mask_path, mask.astype(np.uint8), affine) reconst_flow = flow() for sh_order in [4, 6, 8]: if flow.get_short_name() == 'csd': reconst_flow.run(data_path, bval_path, bvec_path, mask_path, sh_order=sh_order, out_dir=out_dir, extract_pam_values=True) elif flow.get_short_name() == 'csa': reconst_flow.run(data_path, bval_path, bvec_path, mask_path, sh_order=sh_order, odf_to_sh_order=sh_order, out_dir=out_dir, extract_pam_values=True) gfa_path = reconst_flow.last_generated_outputs['out_gfa'] gfa_data = load_nifti_data(gfa_path) npt.assert_equal(gfa_data.shape, volume.shape[:-1]) peaks_dir_path =\ reconst_flow.last_generated_outputs['out_peaks_dir'] peaks_dir_data = load_nifti_data(peaks_dir_path) npt.assert_equal(peaks_dir_data.shape[-1], 15) npt.assert_equal(peaks_dir_data.shape[:-1], volume.shape[:-1]) peaks_idx_path = \ reconst_flow.last_generated_outputs['out_peaks_indices'] peaks_idx_data = load_nifti_data(peaks_idx_path) npt.assert_equal(peaks_idx_data.shape[-1], 5) npt.assert_equal(peaks_idx_data.shape[:-1], volume.shape[:-1]) peaks_vals_path = \ reconst_flow.last_generated_outputs['out_peaks_values'] peaks_vals_data = load_nifti_data(peaks_vals_path) npt.assert_equal(peaks_vals_data.shape[-1], 5) npt.assert_equal(peaks_vals_data.shape[:-1], volume.shape[:-1]) shm_path = reconst_flow.last_generated_outputs['out_shm'] shm_data = load_nifti_data(shm_path) # Test that the number of coefficients is what you would expect # given the order of the sh basis: npt.assert_equal(shm_data.shape[-1], sph_harm_ind_list(sh_order)[0].shape[0]) npt.assert_equal(shm_data.shape[:-1], volume.shape[:-1]) pam = load_peaks(reconst_flow.last_generated_outputs['out_pam']) npt.assert_allclose(pam.peak_dirs.reshape(peaks_dir_data.shape), peaks_dir_data) npt.assert_allclose(pam.peak_values, peaks_vals_data) npt.assert_allclose(pam.peak_indices, peaks_idx_data) npt.assert_allclose(pam.shm_coeff, shm_data) npt.assert_allclose(pam.gfa, gfa_data) bvals, bvecs = read_bvals_bvecs(bval_path, bvec_path) bvals[0] = 5. bvecs = generate_bvecs(len(bvals)) tmp_bval_path = pjoin(out_dir, "tmp.bval") tmp_bvec_path = pjoin(out_dir, "tmp.bvec") np.savetxt(tmp_bval_path, bvals) np.savetxt(tmp_bvec_path, bvecs.T) reconst_flow._force_overwrite = True if flow.get_short_name() == 'csd': reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf=[15, 5, 5]) reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf='15, 5, 5') reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf=None) reconst_flow2 = flow() reconst_flow2._force_overwrite = True reconst_flow2.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf=None, roi_center=[5, 5, 5]) else: with npt.assert_raises(BaseException): npt.assert_warns(UserWarning, reconst_flow.run, data_path, tmp_bval_path, tmp_bvec_path, mask_path, out_dir=out_dir, extract_pam_values=True) # test parallel implementation reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, parallel=True, nbr_processes=None) reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, parallel=True, nbr_processes=2)
def buan_bundle_profiles(model_bundle_folder, bundle_folder, orig_bundle_folder, metric_folder, group_id, subject, no_disks=100, out_dir=''): """ Applies statistical analysis on bundles and saves the results in a directory specified by ``out_dir``. Parameters ---------- model_bundle_folder : string Path to the input model bundle files. This path may contain wildcards to process multiple inputs at once. bundle_folder : string Path to the input bundle files in common space. This path may contain wildcards to process multiple inputs at once. orig_folder : string Path to the input bundle files in native space. This path may contain wildcards to process multiple inputs at once. metric_folder : string Path to the input dti metric or/and peak files. It will be used as metric for statistical analysis of bundles. group_id : integer what group subject belongs to either 0 for control or 1 for patient. subject : string subject id e.g. 10001. no_disks : integer, optional Number of disks used for dividing bundle into disks. out_dir : string, optional Output directory. (default current directory) References ---------- .. [Chandio2020] Chandio, B.Q., Risacher, S.L., Pestilli, F., Bullock, D., Yeh, FC., Koudoro, S., Rokem, A., Harezlak, J., and Garyfallidis, E. Bundle analytics, a computational framework for investigating the shapes and profiles of brain pathways across populations. Sci Rep 10, 17149 (2020) """ t = time() dt = dict() mb = glob(os.path.join(model_bundle_folder, "*.trk")) print(mb) mb.sort() bd = glob(os.path.join(bundle_folder, "*.trk")) bd.sort() print(bd) org_bd = glob(os.path.join(orig_bundle_folder, "*.trk")) org_bd.sort() print(org_bd) n = len(org_bd) n = len(mb) for io in range(n): mbundles = load_tractogram(mb[io], reference='same', bbox_valid_check=False).streamlines bundles = load_tractogram(bd[io], reference='same', bbox_valid_check=False).streamlines orig_bundles = load_tractogram(org_bd[io], reference='same', bbox_valid_check=False).streamlines if len(orig_bundles) > 5: indx = assignment_map(bundles, mbundles, no_disks) ind = np.array(indx) metric_files_names_dti = glob( os.path.join(metric_folder, "*.nii.gz")) metric_files_names_csa = glob(os.path.join(metric_folder, "*.pam5")) _, affine = load_nifti(metric_files_names_dti[0]) affine_r = np.linalg.inv(affine) transformed_orig_bundles = transform_streamlines( orig_bundles, affine_r) for mn in range(len(metric_files_names_dti)): ab = os.path.split(metric_files_names_dti[mn]) metric_name = ab[1] fm = metric_name[:-7] bm = os.path.split(mb[io])[1][:-4] logging.info("bm = " + bm) dt = dict() logging.info("metric = " + metric_files_names_dti[mn]) metric, _ = load_nifti(metric_files_names_dti[mn]) anatomical_measures(transformed_orig_bundles, metric, dt, fm, bm, subject, group_id, ind, out_dir) for mn in range(len(metric_files_names_csa)): ab = os.path.split(metric_files_names_csa[mn]) metric_name = ab[1] fm = metric_name[:-5] bm = os.path.split(mb[io])[1][:-4] logging.info("bm = " + bm) logging.info("metric = " + metric_files_names_csa[mn]) dt = dict() metric = load_peaks(metric_files_names_csa[mn]) peak_values(transformed_orig_bundles, metric, dt, fm, bm, subject, group_id, ind, out_dir) print("total time taken in minutes = ", (-t + time()) / 60)
def buan_bundle_profiles(model_bundle_folder, bundle_folder, orig_bundle_folder, metric_folder, group_id, subject, no_disks=100, out_dir=''): """ Applies statistical analysis on bundles and saves the results in a directory specified by ``out_dir``. Parameters ---------- model_bundle_folder : string Path to the input model bundle files. This path may contain wildcards to process multiple inputs at once. bundle_folder : string Path to the input bundle files in common space. This path may contain wildcards to process multiple inputs at once. orig_folder : string Path to the input bundle files in native space. This path may contain wildcards to process multiple inputs at once. metric_folder : string Path to the input dti metric or/and peak files. It will be used as metric for statistical analysis of bundles. group_id : integer what group subject belongs to either 0 for control or 1 for patient subject : string subject id e.g. 10001 no_disks : integer, optional Number of disks used for dividing bundle into disks. (Default 100) out_dir : string, optional Output directory (default input file directory) References ---------- .. [Chandio19] Chandio, B.Q., S. Koudoro, D. Reagan, J. Harezlak, E. Garyfallidis, Bundle Analytics: a computational and statistical analyses framework for tractometric studies, Proceedings of: International Society of Magnetic Resonance in Medicine (ISMRM), Montreal, Canada, 2019. """ t = time() dt = dict() mb = glob(os.path.join(model_bundle_folder, "*.trk")) print(mb) mb.sort() bd = glob(os.path.join(bundle_folder, "*.trk")) bd.sort() print(bd) org_bd = glob(os.path.join(orig_bundle_folder, "*.trk")) org_bd.sort() print(org_bd) n = len(org_bd) n = len(mb) for io in range(n): mbundles = load_tractogram(mb[io], reference='same', bbox_valid_check=False).streamlines bundles = load_tractogram(bd[io], reference='same', bbox_valid_check=False).streamlines orig_bundles = load_tractogram(org_bd[io], reference='same', bbox_valid_check=False).streamlines if len(orig_bundles) > 5: indx = assignment_map(bundles, mbundles, no_disks) ind = np.array(indx) metric_files_names_dti = glob(os.path.join(metric_folder, "*.nii.gz")) metric_files_names_csa = glob(os.path.join(metric_folder, "*.pam5")) _, affine = load_nifti(metric_files_names_dti[0]) affine_r = np.linalg.inv(affine) transformed_orig_bundles = transform_streamlines(orig_bundles, affine_r) for mn in range(len(metric_files_names_dti)): ab = os.path.split(metric_files_names_dti[mn]) metric_name = ab[1] fm = metric_name[:-7] bm = os.path.split(mb[io])[1][:-4] logging.info("bm = " + bm) dt = dict() logging.info("metric = " + metric_files_names_dti[mn]) metric, _ = load_nifti(metric_files_names_dti[mn]) anatomical_measures(transformed_orig_bundles, metric, dt, fm, bm, subject, group_id, ind, out_dir) for mn in range(len(metric_files_names_csa)): ab = os.path.split(metric_files_names_csa[mn]) metric_name = ab[1] fm = metric_name[:-5] bm = os.path.split(mb[io])[1][:-4] logging.info("bm = " + bm) logging.info("metric = " + metric_files_names_csa[mn]) dt = dict() metric = load_peaks(metric_files_names_csa[mn]) peak_values(transformed_orig_bundles, metric, dt, fm, bm, subject, group_id, ind, out_dir) print("total time taken in minutes = ", (-t + time())/60)
def reconst_flow_core(flow): with TemporaryDirectory() as out_dir: data_path, bval_path, bvec_path = get_fnames('small_64D') vol_img = nib.load(data_path) volume = vol_img.get_data() mask = np.ones_like(volume[:, :, :, 0]) mask_img = nib.Nifti1Image(mask.astype(np.uint8), vol_img.affine) mask_path = pjoin(out_dir, 'tmp_mask.nii.gz') nib.save(mask_img, mask_path) reconst_flow = flow() for sh_order in [4, 6, 8]: if flow.get_short_name() == 'csd': reconst_flow.run(data_path, bval_path, bvec_path, mask_path, sh_order=sh_order, out_dir=out_dir, extract_pam_values=True) elif flow.get_short_name() == 'csa': reconst_flow.run(data_path, bval_path, bvec_path, mask_path, sh_order=sh_order, odf_to_sh_order=sh_order, out_dir=out_dir, extract_pam_values=True) gfa_path = reconst_flow.last_generated_outputs['out_gfa'] gfa_data = nib.load(gfa_path).get_data() npt.assert_equal(gfa_data.shape, volume.shape[:-1]) peaks_dir_path =\ reconst_flow.last_generated_outputs['out_peaks_dir'] peaks_dir_data = nib.load(peaks_dir_path).get_data() npt.assert_equal(peaks_dir_data.shape[-1], 15) npt.assert_equal(peaks_dir_data.shape[:-1], volume.shape[:-1]) peaks_idx_path = \ reconst_flow.last_generated_outputs['out_peaks_indices'] peaks_idx_data = nib.load(peaks_idx_path).get_data() npt.assert_equal(peaks_idx_data.shape[-1], 5) npt.assert_equal(peaks_idx_data.shape[:-1], volume.shape[:-1]) peaks_vals_path = \ reconst_flow.last_generated_outputs['out_peaks_values'] peaks_vals_data = nib.load(peaks_vals_path).get_data() npt.assert_equal(peaks_vals_data.shape[-1], 5) npt.assert_equal(peaks_vals_data.shape[:-1], volume.shape[:-1]) shm_path = reconst_flow.last_generated_outputs['out_shm'] shm_data = nib.load(shm_path).get_data() # Test that the number of coefficients is what you would expect # given the order of the sh basis: npt.assert_equal(shm_data.shape[-1], sph_harm_ind_list(sh_order)[0].shape[0]) npt.assert_equal(shm_data.shape[:-1], volume.shape[:-1]) pam = load_peaks(reconst_flow.last_generated_outputs['out_pam']) npt.assert_allclose(pam.peak_dirs.reshape(peaks_dir_data.shape), peaks_dir_data) npt.assert_allclose(pam.peak_values, peaks_vals_data) npt.assert_allclose(pam.peak_indices, peaks_idx_data) npt.assert_allclose(pam.shm_coeff, shm_data) npt.assert_allclose(pam.gfa, gfa_data) bvals, bvecs = read_bvals_bvecs(bval_path, bvec_path) bvals[0] = 5. bvecs = generate_bvecs(len(bvals)) tmp_bval_path = pjoin(out_dir, "tmp.bval") tmp_bvec_path = pjoin(out_dir, "tmp.bvec") np.savetxt(tmp_bval_path, bvals) np.savetxt(tmp_bvec_path, bvecs.T) reconst_flow._force_overwrite = True with npt.assert_raises(BaseException): npt.assert_warns(UserWarning, reconst_flow.run, data_path, tmp_bval_path, tmp_bvec_path, mask_path, out_dir=out_dir, extract_pam_values=True) if flow.get_short_name() == 'csd': reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf=[15, 5, 5]) reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf='15, 5, 5') reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf=None) reconst_flow2 = flow() reconst_flow2._force_overwrite = True reconst_flow2.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf=None, roi_center=[10, 10, 10])
def run(self, input_files, cluster=False, cluster_thr=15., random_colors=False, length_gt=0, length_lt=1000, clusters_gt=0, clusters_lt=10**8, native_coords=False, stealth=False, emergency_header='icbm_2009a', bg_color=(0, 0, 0), disable_order_transparency=False, buan=False, buan_thr=0.5, buan_highlight=(1, 0, 0), out_dir='', out_stealth_png='tmp.png'): """ Interactive medical visualization - Invert the Horizon! Interact with any number of .trk, .tck or .dpy tractograms and anatomy files .nii or .nii.gz. Cluster streamlines on loading. Parameters ---------- input_files : variable string cluster : bool, optional Enable QuickBundlesX clustering. cluster_thr : float, optional Distance threshold used for clustering. Default value 15.0 for small animal brains you may need to use something smaller such as 2.0. The distance is in mm. For this parameter to be active ``cluster`` should be enabled. random_colors : bool, optional Given multiple tractograms have been included then each tractogram will be shown with different color. length_gt : float, optional Clusters with average length greater than ``length_gt`` amount in mm will be shown. length_lt : float, optional Clusters with average length less than ``length_lt`` amount in mm will be shown. clusters_gt : int, optional Clusters with size greater than ``clusters_gt`` will be shown. clusters_lt : int, optional Clusters with size less than ``clusters_gt`` will be shown. native_coords : bool, optional Show results in native coordinates. stealth : bool, optional Do not use interactive mode just save figure. emergency_header : str, optional If no anatomy reference is provided an emergency header is provided. Current options 'icbm_2009a' and 'icbm_2009c'. bg_color : variable float, optional Define the background color of the scene. Colors can be defined with 1 or 3 values and should be between [0-1]. disable_order_transparency : bool, optional Use depth peeling to sort transparent objects. If True also enables anti-aliasing. buan : bool, optional Enables BUAN framework visualization. buan_thr : float, optional Uses the threshold value to highlight segments on the bundle which have pvalues less than this threshold. buan_highlight : variable float, optional Define the bundle highlight area color. Colors can be defined with 1 or 3 values and should be between [0-1]. For example, a value of (1, 0, 0) would mean the red color. out_dir : str, optional Output directory. (default current directory) out_stealth_png : str, optional Filename of saved picture. References ---------- .. [Horizon_ISMRM19] Garyfallidis E., M-A. Cote, B.Q. Chandio, S. Fadnavis, J. Guaje, R. Aggarwal, E. St-Onge, K.S. Juneja, S. Koudoro, D. Reagan, DIPY Horizon: fast, modular, unified and adaptive visualization, Proceedings of: International Society of Magnetic Resonance in Medicine (ISMRM), Montreal, Canada, 2019. """ verbose = True tractograms = [] images = [] pams = [] numpy_files = [] interactive = not stealth world_coords = not native_coords bundle_colors = None mni_2009a = {} mni_2009a['affine'] = np.array([[1., 0., 0., -98.], [0., 1., 0., -134.], [0., 0., 1., -72.], [0., 0., 0., 1.]]) mni_2009a['dims'] = (197, 233, 189) mni_2009a['vox_size'] = (1., 1., 1.) mni_2009a['vox_space'] = 'RAS' mni_2009c = {} mni_2009c['affine'] = np.array([[1., 0., 0., -96.], [0., 1., 0., -132.], [0., 0., 1., -78.], [0., 0., 0., 1.]]) mni_2009c['dims'] = (193, 229, 193) mni_2009c['vox_size'] = (1., 1., 1.) mni_2009c['vox_space'] = 'RAS' if emergency_header == 'icbm_2009a': hdr = mni_2009c else: hdr = mni_2009c emergency_ref = create_nifti_header(hdr['affine'], hdr['dims'], hdr['vox_size']) io_it = self.get_io_iterator() for input_output in io_it: fname = input_output[0] if verbose: print('Loading file ...') print(fname) print('\n') fl = fname.lower() ends = fl.endswith if ends('.trk'): sft = load_tractogram(fname, 'same', bbox_valid_check=False) tractograms.append(sft) if ends('.dpy') or ends('.tck'): sft = load_tractogram(fname, emergency_ref) tractograms.append(sft) if ends('.nii.gz') or ends('.nii'): data, affine = load_nifti(fname) images.append((data, affine)) if verbose: print('Affine to RAS') np.set_printoptions(3, suppress=True) print(affine) np.set_printoptions() if ends(".pam5"): pam = load_peaks(fname) pams.append(pam) if verbose: print('Peak_dirs shape') print(pam.peak_dirs.shape) if ends(".npy"): data = np.load(fname) numpy_files.append(data) if verbose: print('numpy array length') print(len(data)) if buan: bundle_colors = [] for i in range(len(numpy_files)): n = len(numpy_files[i]) pvalues = numpy_files[i] bundle = tractograms[i].streamlines indx = assignment_map(bundle, bundle, n) ind = np.array(indx) nb_lines = len(bundle) lines_range = range(nb_lines) points_per_line = [len(bundle[i]) for i in lines_range] points_per_line = np.array(points_per_line, np.intp) cols_arr = line_colors(bundle) colors_mapper = np.repeat(lines_range, points_per_line, axis=0) vtk_colors = numpy_to_vtk_colors(255 * cols_arr[colors_mapper]) colors = numpy_support.vtk_to_numpy(vtk_colors) colors = (colors - np.min(colors)) / np.ptp(colors) for i in range(n): if pvalues[i] < buan_thr: colors[ind == i] = buan_highlight bundle_colors.append(colors) if len(bg_color) == 1: bg_color *= 3 elif len(bg_color) != 3: raise ValueError('You need 3 values to set up backgound color. ' 'e.g --bg_color 0.5 0.5 0.5') order_transparent = not disable_order_transparency horizon(tractograms=tractograms, images=images, pams=pams, cluster=cluster, cluster_thr=cluster_thr, random_colors=random_colors, bg_color=bg_color, order_transparent=order_transparent, length_gt=length_gt, length_lt=length_lt, clusters_gt=clusters_gt, clusters_lt=clusters_lt, world_coords=world_coords, interactive=interactive, buan=buan, buan_colors=bundle_colors, out_png=pjoin(out_dir, out_stealth_png))
def run(self, pam_files, wm_files, gm_files, csf_files, seeding_files, step_size=0.2, back_tracking_dist=2, front_tracking_dist=1, max_trial=20, particle_count=15, seed_density=1, pmf_threshold=0.1, max_angle=30., out_dir='', out_tractogram='tractogram.trk'): """Workflow for Particle Filtering Tracking. This workflow use a saved peaks and metrics (PAM) file as input. Parameters ---------- pam_files : string Path to the peaks and metrics files. This path may contain wildcards to use multiple masks at once. wm_files : string Path of White matter for stopping criteria for tracking. gm_files : string Path of grey matter for stopping criteria for tracking. csf_files : string Path of cerebrospinal fluid for stopping criteria for tracking. seeding_files : string A binary image showing where we need to seed for tracking. step_size : float, optional Step size used for tracking. back_tracking_dist : float, optional Distance in mm to back track before starting the particle filtering tractography. The total particle filtering tractography distance is equal to back_tracking_dist + front_tracking_dist. By default this is set to 2 mm. front_tracking_dist : float, optional Distance in mm to run the particle filtering tractography after the the back track distance. The total particle filtering tractography distance is equal to back_tracking_dist + front_tracking_dist. By default this is set to 1 mm. max_trial : int, optional Maximum number of trial for the particle filtering tractography (Prevents infinite loops, default=20). particle_count : int, optional Number of particles to use in the particle filter. (default 15) seed_density : int, optional Number of seeds per dimension inside voxel (default 1). For example, seed_density of 2 means 8 regularly distributed points in the voxel. And seed density of 1 means 1 point at the center of the voxel. pmf_threshold : float, optional Threshold for ODF functions. (default 0.1) max_angle : float, optional Maximum angle between tract segments. This angle can be more generous (larger) than values typically used with probabilistic direction getters. The angle range is (0, 90) out_dir : string, optional Output directory (default input file directory) out_tractogram : string, optional Name of the tractogram file to be saved (default 'tractogram.trk') References ---------- Girard, G., Whittingstall, K., Deriche, R., & Descoteaux, M. Towards quantitative connectivity analysis: reducing tractography biases. NeuroImage, 98, 266-278, 2014.. """ io_it = self.get_io_iterator() for pams_path, wm_path, gm_path, csf_path, seeding_path, out_tract \ in io_it: logging.info( 'Particle Filtering tracking on {0}'.format(pams_path)) pam = load_peaks(pams_path, verbose=False) wm, affine, voxel_size = load_nifti(wm_path, return_voxsize=True) gm, _ = load_nifti(gm_path) csf, _ = load_nifti(csf_path) avs = sum(voxel_size) / len(voxel_size) # average_voxel_size classifier = CmcTissueClassifier.from_pve(wm, gm, csf, step_size=step_size, average_voxel_size=avs) logging.info('classifier done') seed_mask, _ = load_nifti(seeding_path) seeds = utils.seeds_from_mask( seed_mask, density=[seed_density, seed_density, seed_density], affine=affine) logging.info('seeds done') dg = ProbabilisticDirectionGetter direction_getter = dg.from_shcoeff(pam.shm_coeff, max_angle=max_angle, sphere=pam.sphere, pmf_threshold=pmf_threshold) streamlines = ParticleFilteringTracking( direction_getter, classifier, seeds, affine, step_size=step_size, pft_back_tracking_dist=back_tracking_dist, pft_front_tracking_dist=front_tracking_dist, pft_max_trial=max_trial, particle_count=particle_count) logging.info('ParticleFilteringTracking initiated') tractogram = Tractogram(streamlines, affine_to_rasmm=np.eye(4)) save(tractogram, out_tract) logging.info('Saved {0}'.format(out_tract))
def run(self, pam_files, stopping_files, seeding_files, stopping_thr=0.2, seed_density=1, tracking_method="deterministic", pmf_threshold=0.1, max_angle=30., out_dir='', out_tractogram='tractogram.trk'): """Workflow for Local Fiber Tracking. This workflow use a saved peaks and metrics (PAM) file as input. Parameters ---------- pam_files : string Path to the peaks and metrics files. This path may contain wildcards to use multiple masks at once. stopping_files : string Path of FA or other images used for stopping criteria for tracking. seeding_files : string A binary image showing where we need to seed for tracking. stopping_thr : float, optional Threshold applied to stopping volume's data to identify where tracking has to stop (default 0.25). seed_density : int, optional Number of seeds per dimension inside voxel (default 1). For example, seed_density of 2 means 8 regularly distributed points in the voxel. And seed density of 1 means 1 point at the center of the voxel. tracking_method : string, optional Select direction getter strategy: - "eudx" (Uses the peaks saved in the pam_files) - "deterministic" or "det" for a deterministic tracking (Uses the sh saved in the pam_files, default) - "probabilistic" or "prob" for a Probabilistic tracking (Uses the sh saved in the pam_files) - "closestpeaks" or "cp" for a ClosestPeaks tracking (Uses the sh saved in the pam_files) pmf_threshold : float, optional Threshold for ODF functions. (default 0.1) max_angle : float, optional Maximum angle between tract segments. This angle can be more generous (larger) than values typically used with probabilistic direction getters. The angle range is (0, 90) out_dir : string, optional Output directory (default input file directory) out_tractogram : string, optional Name of the tractogram file to be saved (default 'tractogram.trk') References ---------- Garyfallidis, University of Cambridge, PhD thesis 2012. Amirbekian, University of California San Francisco, PhD thesis 2017. """ io_it = self.get_io_iterator() for pams_path, stopping_path, seeding_path, out_tract in io_it: logging.info('Local tracking on {0}'.format(pams_path)) pam = load_peaks(pams_path, verbose=False) dg = self._get_direction_getter(tracking_method, pam, pmf_threshold=pmf_threshold, max_angle=max_angle) self._core_run(stopping_path, stopping_thr, seeding_path, seed_density, dg, out_tract)
def tracking(folder): print('Tracking in ' + folder) output_folder = folder + 'dipy_out/' # make a folder to save new data into try: Path(output_folder).mkdir(parents=True, exist_ok=True) except OSError: print('Could not create output dir. Aborting...') return # load data print('Loading data...') img = nib.load(folder + 'data.nii.gz') dmri = np.asarray(img.dataobj) affine = img.affine mask, _ = load_nifti(folder + 'nodif_brain_mask.nii.gz') bvals, bvecs = read_bvals_bvecs(folder + 'bvals', folder + 'bvecs') gtab = gradient_table(bvals, bvecs) # extract peaksoutput_folder + 'peak_vals.nii.gz' if Path(output_folder + 'peaks.pam5').exists(): peaks = load_peaks(output_folder + 'peaks.pam5') else: print('Extracting peaks...') response, ration = auto_response(gtab, dmri, roi_radius=10, fa_thr=.7) csd_model = ConstrainedSphericalDeconvModel(gtab, response) peaks = peaks_from_model(model=csd_model, data=dmri, sphere=default_sphere, relative_peak_threshold=.5, min_separation_angle=25, parallel=True) save_peaks(output_folder + 'peaks.pam5', peaks, affine) scaled = peaks.peak_dirs * np.repeat( np.expand_dims(peaks.peak_values, -1), 3, -1) cropped = scaled[:, :, :, :3, :].reshape(dmri.shape[:3] + (9, )) save_nifti(output_folder + 'peaks.nii.gz', cropped, affine) #save_nifti(output_folder + 'peak_dirs.nii.gz', peaks.peak_dirs, affine) #save_nifti(output_folder + 'peak_vals.nii.gz', peaks.peak_values, affine) # tracking print('Tracking...') maskdata, mask = median_otsu(dmri, vol_idx=range(0, dmri.shape[3]), median_radius=3, numpass=1, autocrop=True, dilate=2) tensor_model = TensorModel(gtab, fit_method='WLS') tensor_fit = tensor_model.fit(maskdata) fa = fractional_anisotropy(tensor_fit.evals) fa[np.isnan(fa)] = 0 bla = np.average(fa) tissue_classifier = ThresholdStoppingCriterion(fa, .1) seeds = random_seeds_from_mask(fa > 1e-5, affine, seeds_count=1) streamline_generator = LocalTracking(direction_getter=peaks, stopping_criterion=tissue_classifier, seeds=seeds, affine=affine, step_size=.5) streamlines = Streamlines(streamline_generator) save_trk(StatefulTractogram(streamlines, img, Space.RASMM), output_folder + 'whole_brain.trk')
def run(self, input_files, cluster=False, cluster_thr=15., random_colors=False, length_gt=0, length_lt=1000, clusters_gt=0, clusters_lt=10**8, native_coords=False, stealth=False, out_dir='', out_stealth_png='tmp.png'): """ Highly interactive visualization - invert the Horizon! Interact with any number of .trk, .tck or .dpy tractograms and anatomy files .nii or .nii.gz. Cluster streamlines on loading. Parameters ---------- input_files : variable string cluster : bool cluster_thr : float random_colors : bool length_gt : float length_lt : float clusters_gt : int clusters_lt : int native_coords : bool stealth : bool out_dir : string out_stealth_png : string References ---------- .. [Horizon_ISMRM19] Garyfallidis E., M-A. Cote, B.Q. Chandio, S. Fadnavis, J. Guaje, R. Aggarwal, E. St-Onge, K.S. Juneja, S. Koudoro, D. Reagan, DIPY Horizon: fast, modular, unified and adaptive visualization, Proceedings of: International Society of Magnetic Resonance in Medicine (ISMRM), Montreal, Canada, 2019. """ verbose = True tractograms = [] images = [] pams = [] interactive = not stealth world_coords = not native_coords io_it = self.get_io_iterator() for input_output in io_it: fname = input_output[0] if verbose: print('Loading file ...') print(fname) print('\n') fl = fname.lower() ends = fl.endswith if ends('.trk') or ends('.tck'): streamlines = nib.streamlines.load(fname).streamlines tractograms.append(streamlines) elif ends('dpy'): dpy_obj = Dpy(fname, mode='r') streamlines = list(dpy_obj.read_tracks()) dpy_obj.close() if ends('.nii.gz') or ends('.nii'): data, affine = load_nifti(fname) images.append((data, affine)) if verbose: print('Affine to RAS') np.set_printoptions(3, suppress=True) print(affine) np.set_printoptions() if ends(".pam5"): pam = load_peaks(fname) pams.append(pam) if verbose: print('Peak_dirs shape') print(pam.peak_dirs.shape) horizon(tractograms=tractograms, images=images, pams=pams, cluster=cluster, cluster_thr=cluster_thr, random_colors=random_colors, length_gt=length_gt, length_lt=length_lt, clusters_gt=clusters_gt, clusters_lt=clusters_lt, world_coords=world_coords, interactive=interactive, out_png=pjoin(out_dir, out_stealth_png))
def reconst_flow_core(flow): with TemporaryDirectory() as out_dir: data_path, bval_path, bvec_path = get_data('small_64D') vol_img = nib.load(data_path) volume = vol_img.get_data() mask = np.ones_like(volume[:, :, :, 0]) mask_img = nib.Nifti1Image(mask.astype(np.uint8), vol_img.affine) mask_path = join(out_dir, 'tmp_mask.nii.gz') nib.save(mask_img, mask_path) reconst_flow = flow() reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, extract_pam_values=True) gfa_path = reconst_flow.last_generated_outputs['out_gfa'] gfa_data = nib.load(gfa_path).get_data() assert_equal(gfa_data.shape, volume.shape[:-1]) peaks_dir_path = reconst_flow.last_generated_outputs['out_peaks_dir'] peaks_dir_data = nib.load(peaks_dir_path).get_data() assert_equal(peaks_dir_data.shape[-1], 15) assert_equal(peaks_dir_data.shape[:-1], volume.shape[:-1]) peaks_idx_path = \ reconst_flow.last_generated_outputs['out_peaks_indices'] peaks_idx_data = nib.load(peaks_idx_path).get_data() assert_equal(peaks_idx_data.shape[-1], 5) assert_equal(peaks_idx_data.shape[:-1], volume.shape[:-1]) peaks_vals_path = \ reconst_flow.last_generated_outputs['out_peaks_values'] peaks_vals_data = nib.load(peaks_vals_path).get_data() assert_equal(peaks_vals_data.shape[-1], 5) assert_equal(peaks_vals_data.shape[:-1], volume.shape[:-1]) shm_path = reconst_flow.last_generated_outputs['out_shm'] shm_data = nib.load(shm_path).get_data() assert_equal(shm_data.shape[-1], 45) assert_equal(shm_data.shape[:-1], volume.shape[:-1]) pam = load_peaks(reconst_flow.last_generated_outputs['out_pam']) npt.assert_allclose(pam.peak_dirs.reshape(peaks_dir_data.shape), peaks_dir_data) npt.assert_allclose(pam.peak_values, peaks_vals_data) npt.assert_allclose(pam.peak_indices, peaks_idx_data) npt.assert_allclose(pam.shm_coeff, shm_data) npt.assert_allclose(pam.gfa, gfa_data) if flow.get_short_name() == 'csd': reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf=[15, 5, 5]) reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf='15, 5, 5') reconst_flow = flow() reconst_flow._force_overwrite = True reconst_flow.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf=None) reconst_flow2 = flow() reconst_flow2._force_overwrite = True reconst_flow2.run(data_path, bval_path, bvec_path, mask_path, out_dir=out_dir, frf=None, roi_center=[10, 10, 10])
def run(self, pam_files, stopping_files, seeding_files, use_binary_mask=False, stopping_thr=0.2, seed_density=1, step_size=0.5, tracking_method="eudx", pmf_threshold=0.1, max_angle=30., out_dir='', out_tractogram='tractogram.trk', save_seeds=False): """Workflow for Local Fiber Tracking. This workflow use a saved peaks and metrics (PAM) file as input. Parameters ---------- pam_files : string Path to the peaks and metrics files. This path may contain wildcards to use multiple masks at once. stopping_files : string Path to images (e.g. FA) used for stopping criterion for tracking. seeding_files : string A binary image showing where we need to seed for tracking. use_binary_mask : bool, optional If True, uses a binary stopping criterion. If the provided `stopping_files` are not binary, `stopping_thr` will be used to binarize the images. stopping_thr : float, optional Threshold applied to stopping volume's data to identify where tracking has to stop (default 0.2). seed_density : int, optional Number of seeds per dimension inside voxel (default 1). For example, seed_density of 2 means 8 regularly distributed points in the voxel. And seed density of 1 means 1 point at the center of the voxel. step_size : float, optional Step size used for tracking (default 0.5mm). tracking_method : string, optional Select direction getter strategy : - "eudx" (Uses the peaks saved in the pam_files) - "deterministic" or "det" for a deterministic tracking (Uses the sh saved in the pam_files, default) - "probabilistic" or "prob" for a Probabilistic tracking (Uses the sh saved in the pam_files) - "closestpeaks" or "cp" for a ClosestPeaks tracking (Uses the sh saved in the pam_files) pmf_threshold : float, optional Threshold for ODF functions (default 0.1). max_angle : float, optional Maximum angle between streamline segments (range [0, 90], default 30). out_dir : string, optional Output directory (default input file directory). out_tractogram : string, optional Name of the tractogram file to be saved (default 'tractogram.trk'). save_seeds : bool, optional If true, save the seeds associated to their streamline in the 'data_per_streamline' Tractogram dictionary using 'seeds' as the key. References ---------- Garyfallidis, University of Cambridge, PhD thesis 2012. Amirbekian, University of California San Francisco, PhD thesis 2017. """ io_it = self.get_io_iterator() for pams_path, stopping_path, seeding_path, out_tract in io_it: logging.info('Local tracking on {0}'.format(pams_path)) pam = load_peaks(pams_path, verbose=False) dg = self._get_direction_getter(tracking_method, pam, pmf_threshold=pmf_threshold, max_angle=max_angle) self._core_run(stopping_path, use_binary_mask, stopping_thr, seeding_path, seed_density, step_size, dg, out_tract, save_seeds)
def run(self, pam_files, stopping_files, seeding_files, stopping_thr=0.2, seed_density=1, step_size=0.5, tracking_method="eudx", pmf_threshold=0.1, max_angle=30., out_dir='', out_tractogram='tractogram.trk'): """Workflow for Local Fiber Tracking. This workflow use a saved peaks and metrics (PAM) file as input. Parameters ---------- pam_files : string Path to the peaks and metrics files. This path may contain wildcards to use multiple masks at once. stopping_files : string Path to images (e.g. FA) used for stopping criteria for tracking. seeding_files : string A binary image showing where we need to seed for tracking. stopping_thr : float, optional Threshold applied to stopping volume's data to identify where tracking has to stop (default 0.25). seed_density : int, optional Number of seeds per dimension inside voxel (default 1). For example, seed_density of 2 means 8 regularly distributed points in the voxel. And seed density of 1 means 1 point at the center of the voxel. step_size : float, optional Step size used for tracking (default 0.5mm). tracking_method : string, optional Select direction getter strategy: - "eudx" (Uses the peaks saved in the pam_files) - "deterministic" or "det" for a deterministic tracking (Uses the sh saved in the pam_files, default) - "probabilistic" or "prob" for a Probabilistic tracking (Uses the sh saved in the pam_files) - "closestpeaks" or "cp" for a ClosestPeaks tracking (Uses the sh saved in the pam_files) pmf_threshold : float, optional Threshold for ODF functions (default 0.1). max_angle : float, optional Maximum angle between streamline segments (range [0, 90], default 30). out_dir : string, optional Output directory (default input file directory). out_tractogram : string, optional Name of the tractogram file to be saved (default 'tractogram.trk'). References ---------- Garyfallidis, University of Cambridge, PhD thesis 2012. Amirbekian, University of California San Francisco, PhD thesis 2017. """ io_it = self.get_io_iterator() for pams_path, stopping_path, seeding_path, out_tract in io_it: logging.info('Local tracking on {0}' .format(pams_path)) pam = load_peaks(pams_path, verbose=False) dg = self._get_direction_getter(tracking_method, pam, pmf_threshold=pmf_threshold, max_angle=max_angle) self._core_run(stopping_path, stopping_thr, seeding_path, seed_density, step_size, dg, out_tract)
def run(self, pam_files, wm_files, gm_files, csf_files, seeding_files, step_size=0.2, seed_density=1, pmf_threshold=0.1, max_angle=20., pft_back=2, pft_front=1, pft_count=15, out_dir='', out_tractogram='tractogram.trk', save_seeds=False): """Workflow for Particle Filtering Tracking. This workflow use a saved peaks and metrics (PAM) file as input. Parameters ---------- pam_files : string Path to the peaks and metrics files. This path may contain wildcards to use multiple masks at once. wm_files : string Path to white matter partial volume estimate for tracking (CMC). gm_files : string Path to grey matter partial volume estimate for tracking (CMC). csf_files : string Path to cerebrospinal fluid partial volume estimate for tracking (CMC). seeding_files : string A binary image showing where we need to seed for tracking. step_size : float, optional Step size used for tracking (default 0.2mm). seed_density : int, optional Number of seeds per dimension inside voxel (default 1). For example, seed_density of 2 means 8 regularly distributed points in the voxel. And seed density of 1 means 1 point at the center of the voxel. pmf_threshold : float, optional Threshold for ODF functions (default 0.1). max_angle : float, optional Maximum angle between streamline segments (range [0, 90], default 20). pft_back : float, optional Distance in mm to back track before starting the particle filtering tractography (default 2mm). The total particle filtering tractography distance is equal to back_tracking_dist + front_tracking_dist. pft_front : float, optional Distance in mm to run the particle filtering tractography after the the back track distance (default 1mm). The total particle filtering tractography distance is equal to back_tracking_dist + front_tracking_dist. pft_count : int, optional Number of particles to use in the particle filter (default 15). out_dir : string, optional Output directory (default input file directory) out_tractogram : string, optional Name of the tractogram file to be saved (default 'tractogram.trk') save_seeds : bool, optional If true, save the seeds associated to their streamline in the 'data_per_streamline' Tractogram dictionary using 'seeds' as the key References ---------- Girard, G., Whittingstall, K., Deriche, R., & Descoteaux, M. Towards quantitative connectivity analysis: reducing tractography biases. NeuroImage, 98, 266-278, 2014. """ io_it = self.get_io_iterator() for pams_path, wm_path, gm_path, csf_path, seeding_path, out_tract \ in io_it: logging.info( 'Particle Filtering tracking on {0}'.format(pams_path)) pam = load_peaks(pams_path, verbose=False) wm, affine, voxel_size = load_nifti(wm_path, return_voxsize=True) gm, _ = load_nifti(gm_path) csf, _ = load_nifti(csf_path) avs = sum(voxel_size) / len(voxel_size) # average_voxel_size stopping_criterion = CmcStoppingCriterion.from_pve( wm, gm, csf, step_size=step_size, average_voxel_size=avs) logging.info('stopping criterion done') seed_mask, _ = load_nifti(seeding_path) seeds = utils.seeds_from_mask( seed_mask, density=[seed_density, seed_density, seed_density], affine=affine) logging.info('seeds done') dg = ProbabilisticDirectionGetter direction_getter = dg.from_shcoeff(pam.shm_coeff, max_angle=max_angle, sphere=pam.sphere, pmf_threshold=pmf_threshold) tracking_result = ParticleFilteringTracking( direction_getter, stopping_criterion, seeds, affine, step_size=step_size, pft_back_tracking_dist=pft_back, pft_front_tracking_dist=pft_front, pft_max_trial=20, particle_count=pft_count, save_seeds=save_seeds) logging.info('ParticleFilteringTracking initiated') if save_seeds: streamlines, seeds = zip(*tracking_result) seeds = {'seeds': seeds} else: streamlines = list(tracking_result) seeds = {} sft = StatefulTractogram(streamlines, seeding_path, Space.RASMM, data_per_streamline=seeds) save_tractogram(sft, out_tract, bbox_valid_check=False) logging.info('Saved {0}'.format(out_tract))
def run(self, pam_files, wm_files, gm_files, csf_files, seeding_files, step_size=0.2, seed_density=1, pmf_threshold=0.1, max_angle=20., pft_back=2, pft_front=1, pft_count=15, out_dir='', out_tractogram='tractogram.trk'): """Workflow for Particle Filtering Tracking. This workflow use a saved peaks and metrics (PAM) file as input. Parameters ---------- pam_files : string Path to the peaks and metrics files. This path may contain wildcards to use multiple masks at once. wm_files : string Path to white matter partial volume estimate for tracking (CMC). gm_files : string Path to grey matter partial volume estimate for tracking (CMC). csf_files : string Path to cerebrospinal fluid partial volume estimate for tracking (CMC). seeding_files : string A binary image showing where we need to seed for tracking. step_size : float, optional Step size used for tracking (default 0.2mm). seed_density : int, optional Number of seeds per dimension inside voxel (default 1). For example, seed_density of 2 means 8 regularly distributed points in the voxel. And seed density of 1 means 1 point at the center of the voxel. pmf_threshold : float, optional Threshold for ODF functions (default 0.1). max_angle : float, optional Maximum angle between streamline segments (range [0, 90], default 20). pft_back : float, optional Distance in mm to back track before starting the particle filtering tractography (defaul 2mm). The total particle filtering tractography distance is equal to back_tracking_dist + front_tracking_dist. pft_front : float, optional Distance in mm to run the particle filtering tractography after the the back track distance (default 1mm). The total particle filtering tractography distance is equal to back_tracking_dist + front_tracking_dist. pft_count : int, optional Number of particles to use in the particle filter (default 15). out_dir : string, optional Output directory (default input file directory) out_tractogram : string, optional Name of the tractogram file to be saved (default 'tractogram.trk') References ---------- Girard, G., Whittingstall, K., Deriche, R., & Descoteaux, M. Towards quantitative connectivity analysis: reducing tractography biases. NeuroImage, 98, 266-278, 2014. """ io_it = self.get_io_iterator() for pams_path, wm_path, gm_path, csf_path, seeding_path, out_tract \ in io_it: logging.info('Particle Filtering tracking on {0}' .format(pams_path)) pam = load_peaks(pams_path, verbose=False) wm, affine, voxel_size = load_nifti(wm_path, return_voxsize=True) gm, _ = load_nifti(gm_path) csf, _ = load_nifti(csf_path) avs = sum(voxel_size) / len(voxel_size) # average_voxel_size classifier = CmcTissueClassifier.from_pve(wm, gm, csf, step_size=step_size, average_voxel_size=avs) logging.info('classifier done') seed_mask, _ = load_nifti(seeding_path) seeds = utils.seeds_from_mask(seed_mask, density=[seed_density, seed_density, seed_density], affine=affine) logging.info('seeds done') dg = ProbabilisticDirectionGetter direction_getter = dg.from_shcoeff(pam.shm_coeff, max_angle=max_angle, sphere=pam.sphere, pmf_threshold=pmf_threshold) streamlines_generator = ParticleFilteringTracking( direction_getter, classifier, seeds, affine, step_size=step_size, pft_back_tracking_dist=pft_back, pft_front_tracking_dist=pft_front, pft_max_trial=20, particle_count=pft_count) logging.info('ParticleFilteringTracking initiated') tractogram = Tractogram(streamlines_generator, affine_to_rasmm=np.eye(4)) save(tractogram, out_tract) logging.info('Saved {0}'.format(out_tract))
def bundle_analysis(model_bundle_folder, bundle_folder, orig_bundle_folder, metric_folder, group, subject, no_disks=100, out_dir=''): """ Applies statistical analysis on bundles and saves the results in a directory specified by ``out_dir``. Parameters ---------- model_bundle_folder : string Path to the input model bundle files. This path may contain wildcards to process multiple inputs at once. bundle_folder : string Path to the input bundle files in common space. This path may contain wildcards to process multiple inputs at once. orig_folder : string Path to the input bundle files in native space. This path may contain wildcards to process multiple inputs at once. metric_folder : string Path to the input dti metric or/and peak files. It will be used as metric for statistical analysis of bundles. group : string what group subject belongs to e.g. control or patient subject : string subject id e.g. 10001 no_disks : integer, optional Number of disks used for dividing bundle into disks. (Default 100) out_dir : string, optional Output directory (default input file directory) References ---------- .. [Chandio19] Chandio, B.Q., S. Koudoro, D. Reagan, J. Harezlak, E. Garyfallidis, Bundle Analytics: a computational and statistical analyses framework for tractometric studies, Proceedings of: International Society of Magnetic Resonance in Medicine (ISMRM), Montreal, Canada, 2019. """ dt = dict() mb = os.listdir(model_bundle_folder) mb.sort() bd = os.listdir(bundle_folder) bd.sort() org_bd = os.listdir(orig_bundle_folder) org_bd.sort() n = len(org_bd) for io in range(n): mbundles, _ = load_trk(os.path.join(model_bundle_folder, mb[io])) bundles, _ = load_trk(os.path.join(bundle_folder, bd[io])) orig_bundles, _ = load_trk(os.path.join(orig_bundle_folder, org_bd[io])) mbundle_streamlines = set_number_of_points(mbundles, nb_points=no_disks) metric = AveragePointwiseEuclideanMetric() qb = QuickBundles(threshold=25., metric=metric) clusters = qb.cluster(mbundle_streamlines) centroids = Streamlines(clusters.centroids) print('Number of centroids ', len(centroids.data)) print('Model bundle ', mb[io]) print('Number of streamlines in bundle in common space ', len(bundles)) print('Number of streamlines in bundle in original space ', len(orig_bundles)) _, indx = cKDTree(centroids.data, 1, copy_data=True).query(bundles.data, k=1) metric_files_names = os.listdir(metric_folder) _, affine = load_nifti(os.path.join(metric_folder, "fa.nii.gz")) affine_r = np.linalg.inv(affine) transformed_orig_bundles = transform_streamlines( orig_bundles, affine_r) for mn in range(0, len(metric_files_names)): ind = np.array(indx) fm = metric_files_names[mn][:2] bm = mb[io][:-4] dt = dict() metric_name = os.path.join(metric_folder, metric_files_names[mn]) if metric_files_names[mn][2:] == '.nii.gz': metric, _ = load_nifti(metric_name) dti_measures(transformed_orig_bundles, metric, dt, fm, bm, subject, group, ind, out_dir) else: fm = metric_files_names[mn][:3] metric = load_peaks(metric_name) peak_values(bundles, metric, dt, fm, bm, subject, group, ind, out_dir)
def test_io_peaks(): with InTemporaryDirectory(): fname = 'test.pam5' sphere = get_sphere('repulsion724') pam = PeaksAndMetrics() pam.affine = np.eye(4) pam.peak_dirs = np.random.rand(10, 10, 10, 5, 3) pam.peak_values = np.zeros((10, 10, 10, 5)) pam.peak_indices = np.zeros((10, 10, 10, 5)) pam.shm_coeff = np.zeros((10, 10, 10, 45)) pam.sphere = sphere pam.B = np.zeros((45, sphere.vertices.shape[0])) pam.total_weight = 0.5 pam.ang_thr = 60 pam.gfa = np.zeros((10, 10, 10)) pam.qa = np.zeros((10, 10, 10, 5)) pam.odf = np.zeros((10, 10, 10, sphere.vertices.shape[0])) save_peaks(fname, pam) pam2 = load_peaks(fname, verbose=True) npt.assert_array_equal(pam.peak_dirs, pam2.peak_dirs) pam2.affine = None fname2 = 'test2.pam5' save_peaks(fname2, pam2, np.eye(4)) pam2_res = load_peaks(fname2, verbose=True) npt.assert_array_equal(pam.peak_dirs, pam2_res.peak_dirs) pam3 = load_peaks(fname2, verbose=False) for attr in ['peak_dirs', 'peak_values', 'peak_indices', 'gfa', 'qa', 'shm_coeff', 'B', 'odf']: npt.assert_array_equal(getattr(pam3, attr), getattr(pam, attr)) npt.assert_equal(pam3.total_weight, pam.total_weight) npt.assert_equal(pam3.ang_thr, pam.ang_thr) npt.assert_array_almost_equal(pam3.sphere.vertices, pam.sphere.vertices) fname3 = 'test3.pam5' pam4 = PeaksAndMetrics() npt.assert_raises(ValueError, save_peaks, fname3, pam4) fname4 = 'test4.pam5' del pam.affine save_peaks(fname4, pam, affine=None) fname5 = 'test5.pkm' npt.assert_raises(IOError, save_peaks, fname5, pam) pam.affine = np.eye(4) fname6 = 'test6.pam5' save_peaks(fname6, pam, verbose=True) del pam.shm_coeff save_peaks(fname6, pam, verbose=False) pam.shm_coeff = np.zeros((10, 10, 10, 45)) del pam.odf save_peaks(fname6, pam) pam_tmp = load_peaks(fname6, True) npt.assert_equal(pam_tmp.odf, None) fname7 = 'test7.paw' npt.assert_raises(IOError, load_peaks, fname7) del pam.shm_coeff save_peaks(fname6, pam, verbose=True) fname_shm = 'shm.nii.gz' fname_dirs = 'dirs.nii.gz' fname_values = 'values.nii.gz' fname_indices = 'indices.nii.gz' fname_gfa = 'gfa.nii.gz' pam.shm_coeff = np.ones((10, 10, 10, 45)) peaks_to_niftis(pam, fname_shm, fname_dirs, fname_values, fname_indices, fname_gfa, reshape_dirs=False) os.path.isfile(fname_shm) os.path.isfile(fname_dirs) os.path.isfile(fname_values) os.path.isfile(fname_indices) os.path.isfile(fname_gfa)
def bundle_analysis(model_bundle_folder, bundle_folder, orig_bundle_folder, metric_folder, group, subject, no_disks=100, out_dir=''): """ Applies statistical analysis on bundles and saves the results in a directory specified by ``out_dir``. Parameters ---------- model_bundle_folder : string Path to the input model bundle files. This path may contain wildcards to process multiple inputs at once. bundle_folder : string Path to the input bundle files in common space. This path may contain wildcards to process multiple inputs at once. orig_folder : string Path to the input bundle files in native space. This path may contain wildcards to process multiple inputs at once. metric_folder : string Path to the input dti metric or/and peak files. It will be used as metric for statistical analysis of bundles. group : string what group subject belongs to e.g. control or patient subject : string subject id e.g. 10001 no_disks : integer, optional Number of disks used for dividing bundle into disks. (Default 100) out_dir : string, optional Output directory (default input file directory) References ---------- .. [Chandio19] Chandio, B.Q., S. Koudoro, D. Reagan, J. Harezlak, E. Garyfallidis, Bundle Analytics: a computational and statistical analyses framework for tractometric studies, Proceedings of: International Society of Magnetic Resonance in Medicine (ISMRM), Montreal, Canada, 2019. """ dt = dict() mb = os.listdir(model_bundle_folder) mb.sort() bd = os.listdir(bundle_folder) bd.sort() org_bd = os.listdir(orig_bundle_folder) org_bd.sort() n = len(org_bd) for io in range(n): mbundles, _ = load_trk(os.path.join(model_bundle_folder, mb[io])) bundles, _ = load_trk(os.path.join(bundle_folder, bd[io])) orig_bundles, _ = load_trk(os.path.join(orig_bundle_folder, org_bd[io])) mbundle_streamlines = set_number_of_points(mbundles, nb_points=no_disks) metric = AveragePointwiseEuclideanMetric() qb = QuickBundles(threshold=25., metric=metric) clusters = qb.cluster(mbundle_streamlines) centroids = Streamlines(clusters.centroids) print('Number of centroids ', len(centroids.data)) print('Model bundle ', mb[io]) print('Number of streamlines in bundle in common space ', len(bundles)) print('Number of streamlines in bundle in original space ', len(orig_bundles)) _, indx = cKDTree(centroids.data, 1, copy_data=True).query(bundles.data, k=1) metric_files_names = os.listdir(metric_folder) _, affine = load_nifti(os.path.join(metric_folder, "fa.nii.gz")) affine_r = np.linalg.inv(affine) transformed_orig_bundles = transform_streamlines(orig_bundles, affine_r) for mn in range(0, len(metric_files_names)): ind = np.array(indx) fm = metric_files_names[mn][:2] bm = mb[io][:-4] dt = dict() metric_name = os.path.join(metric_folder, metric_files_names[mn]) if metric_files_names[mn][2:] == '.nii.gz': metric, _ = load_nifti(metric_name) dti_measures(transformed_orig_bundles, metric, dt, fm, bm, subject, group, ind, out_dir) else: fm = metric_files_names[mn][:3] metric = load_peaks(metric_name) peak_values(bundles, metric, dt, fm, bm, subject, group, ind, out_dir)
def run(self, input_files, cluster=False, cluster_thr=15., random_colors=False, length_gt=0, length_lt=1000, clusters_gt=0, clusters_lt=10**8, native_coords=False, stealth=False, emergency_header='icbm_2009a', bg_color=(0, 0, 0), disable_order_transparency=False, out_dir='', out_stealth_png='tmp.png'): """ Interactive medical visualization - Invert the Horizon! Interact with any number of .trk, .tck or .dpy tractograms and anatomy files .nii or .nii.gz. Cluster streamlines on loading. Parameters ---------- input_files : variable string cluster : bool, optional Enable QuickBundlesX clustering cluster_thr : float, optional Distance threshold used for clustering. Default value 15.0 for small animal brains you may need to use something smaller such as 2.0. The distance is in mm. For this parameter to be active ``cluster`` should be enabled random_colors : bool, optional Given multiple tractograms have been included then each tractogram will be shown with different color length_gt : float, optional Clusters with average length greater than ``length_gt`` amount in mm will be shown length_lt : float, optional Clusters with average length less than ``length_lt`` amount in mm will be shown clusters_gt : int, optional Clusters with size greater than ``clusters_gt`` will be shown. clusters_lt : int, optional Clusters with size less than ``clusters_gt`` will be shown. native_coords : bool, optional Show results in native coordinates stealth : bool, optional Do not use interactive mode just save figure. emergency_header : str, optional If no anatomy reference is provided an emergency header is provided. Current options 'icbm_2009a' and 'icbm_2009c'. bg_color : variable float, optional Define the background color of the scene. Colors can be defined with 1 or 3 values and should be between [0-1]. Default is black (e.g --bg_color 0 0 0 or --bg_color 0). disable_order_transparency : bool, optional Default False. Use depth peeling to sort transparent objects. If True also enables anti-aliasing. out_dir : str, optional Output directory. Default current directory. out_stealth_png : str, optional Filename of saved picture. References ---------- .. [Horizon_ISMRM19] Garyfallidis E., M-A. Cote, B.Q. Chandio, S. Fadnavis, J. Guaje, R. Aggarwal, E. St-Onge, K.S. Juneja, S. Koudoro, D. Reagan, DIPY Horizon: fast, modular, unified and adaptive visualization, Proceedings of: International Society of Magnetic Resonance in Medicine (ISMRM), Montreal, Canada, 2019. """ verbose = True tractograms = [] images = [] pams = [] interactive = not stealth world_coords = not native_coords mni_2009a = {} mni_2009a['affine'] = np.array([[1., 0., 0., -98.], [0., 1., 0., -134.], [0., 0., 1., -72.], [0., 0., 0., 1.]]) mni_2009a['dims'] = (197, 233, 189) mni_2009a['vox_size'] = (1., 1., 1.) mni_2009a['vox_space'] = 'RAS' mni_2009c = {} mni_2009c['affine'] = np.array([[1., 0., 0., -96.], [0., 1., 0., -132.], [0., 0., 1., -78.], [0., 0., 0., 1.]]) mni_2009c['dims'] = (193, 229, 193) mni_2009c['vox_size'] = (1., 1., 1.) mni_2009c['vox_space'] = 'RAS' if emergency_header == 'icbm_2009a': hdr = mni_2009c else: hdr = mni_2009c emergency_ref = create_nifti_header(hdr['affine'], hdr['dims'], hdr['vox_size']) io_it = self.get_io_iterator() for input_output in io_it: fname = input_output[0] if verbose: print('Loading file ...') print(fname) print('\n') fl = fname.lower() ends = fl.endswith if ends('.trk'): sft = load_tractogram(fname, 'same', bbox_valid_check=False) tractograms.append(sft) if ends('.dpy') or ends('.tck'): sft = load_tractogram(fname, emergency_ref) tractograms.append(sft) if ends('.nii.gz') or ends('.nii'): data, affine = load_nifti(fname) images.append((data, affine)) if verbose: print('Affine to RAS') np.set_printoptions(3, suppress=True) print(affine) np.set_printoptions() if ends(".pam5"): pam = load_peaks(fname) pams.append(pam) if verbose: print('Peak_dirs shape') print(pam.peak_dirs.shape) if len(bg_color) == 1: bg_color *= 3 elif len(bg_color) != 3: raise ValueError('You need 3 values to set up backgound color. ' 'e.g --bg_color 0.5 0.5 0.5') order_transparent = not disable_order_transparency horizon(tractograms=tractograms, images=images, pams=pams, cluster=cluster, cluster_thr=cluster_thr, random_colors=random_colors, bg_color=bg_color, order_transparent=order_transparent, length_gt=length_gt, length_lt=length_lt, clusters_gt=clusters_gt, clusters_lt=clusters_lt, world_coords=world_coords, interactive=interactive, out_png=pjoin(out_dir, out_stealth_png))