def test_ProbabilisticOdfWeightedTracker(): """This tests that the Probabalistic Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.5, .5, 0.]]) simple_image = np.array([ [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.])] * 30 mask = (simple_image > 0).astype(float) tc = ThresholdTissueClassifier(mask, .5) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, sphere) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [ np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.], [2., 5., 0.]]), np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]]) ] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) path = [False, False] for sl in streamlines: if allclose(sl, expected[0]): path[0] = True elif allclose(sl, expected[1]): path[1] = True else: raise AssertionError() npt.assert_(all(path)) # The first path is not possible if 90 degree turns are excluded dg = ProbabilisticDirectionGetter.from_pmf(pmf, 80, sphere) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1]))
def test_ProbabilisticDirectionGetter(): # Test the constructors and errors of the ProbabilisticDirectionGetter class SillyModel(SphHarmModel): sh_order = 4 def fit(self, data, mask=None): coeff = np.zeros(data.shape[:-1] + (15, )) return SphHarmFit(self, coeff, mask=None) model = SillyModel(gtab=None) data = np.zeros((3, 3, 3, 7)) fit = model.fit(data) # Sample point and direction point = np.zeros(3) dir = unit_octahedron.vertices[0].copy() # make a dg from a fit dg = ProbabilisticDirectionGetter.from_shcoeff(fit.shm_coeff, 90, unit_octahedron) state = dg.get_direction(point, dir) npt.assert_equal(state, 1) # Make a dg from a pmf N = unit_octahedron.theta.shape[0] pmf = np.zeros((3, 3, 3, N)) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, unit_octahedron) state = dg.get_direction(point, dir) npt.assert_equal(state, 1) # pmf shape must match sphere bad_pmf = pmf[..., 1:] npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_pmf, bad_pmf, 90, unit_octahedron) # pmf must have 4 dimensions bad_pmf = pmf[0, ...] npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_pmf, bad_pmf, 90, unit_octahedron) # pmf cannot have negative values pmf[0, 0, 0, 0] = -1 npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_pmf, pmf, 90, unit_octahedron) # Check basis_type keyword ProbabilisticDirectionGetter.from_shcoeff(fit.shm_coeff, 90, unit_octahedron, pmf_threshold=0.1, basis_type="mrtrix") npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_shcoeff, fit.shm_coeff, 90, unit_octahedron, pmf_threshold=0.1, basis_type="not a basis")
def probal(Threshold=.2, data_list=None, seed='.', one_node=False, two_node=False): time0 = time.time() print("begin loading data, time:", time.time() - time0) data = data_list['DWI'] affine = data_list['affine'] img = data_list['img'] labels = data_list['labels'] gtab = data_list['gtab'] head_mask = data_list['head_mask'] if type(seed) != str: seed_mask = seed else: seed_mask = (labels == 2) * (head_mask == 1) white_matter = (labels == 2) * (head_mask == 1) seeds = utils.seeds_from_mask(seed_mask, affine, density=1) print("begin reconstruction, time:", time.time() - time0) response, ratio = auto_response_ssst(gtab, data, roi_radii=10, fa_thr=0.7) csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order=6) csd_fit = csd_model.fit(data, mask=white_matter) csa_model = CsaOdfModel(gtab, sh_order=6) gfa = csa_model.fit(data, mask=white_matter).gfa stopping_criterion = ThresholdStoppingCriterion(gfa, Threshold) print("begin tracking, time:", time.time() - time0) fod = csd_fit.odf(small_sphere) pmf = fod.clip(min=0) prob_dg = ProbabilisticDirectionGetter.from_pmf(pmf, max_angle=30., sphere=small_sphere) streamline_generator = LocalTracking(prob_dg, stopping_criterion, seeds, affine, step_size=.5) streamlines = Streamlines(streamline_generator) sft = StatefulTractogram(streamlines, img, Space.RASMM) if one_node or two_node: sft.to_vox() streamlines = reduct_seed_ROI(sft.streamlines, seed_mask, one_node, two_node) sft = StatefulTractogram(streamlines, img, Space.VOX) sft._vox_to_rasmm() print("begin saving, time:", time.time() - time0) output = 'tractogram_probabilistic.trk' save_trk(sft, output) print("finished, time:", time.time() - time0)
def test_ProbabilisticDirectionGetter(): # Test the constructors and errors of the ProbabilisticDirectionGetter class SillyModel(SphHarmModel): sh_order = 4 def fit(self, data, mask=None): coeff = np.zeros(data.shape[:-1] + (15,)) return SphHarmFit(self, coeff, mask=None) model = SillyModel(gtab=None) data = np.zeros((3, 3, 3, 7)) # Test if the tracking works on different dtype of the same data. for dtype in [np.float32, np.float64]: fit = model.fit(data.astype(dtype)) # Sample point and direction point = np.zeros(3) dir = unit_octahedron.vertices[0].copy() # make a dg from a fit dg = ProbabilisticDirectionGetter.from_shcoeff(fit.shm_coeff, 90, unit_octahedron) state = dg.get_direction(point, dir) npt.assert_equal(state, 1) # Make a dg from a pmf N = unit_octahedron.theta.shape[0] pmf = np.zeros((3, 3, 3, N)) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, unit_octahedron) state = dg.get_direction(point, dir) npt.assert_equal(state, 1) # pmf shape must match sphere bad_pmf = pmf[..., 1:] npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_pmf, bad_pmf, 90, unit_octahedron) # pmf must have 4 dimensions bad_pmf = pmf[0, ...] npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_pmf, bad_pmf, 90, unit_octahedron) # pmf cannot have negative values pmf[0, 0, 0, 0] = -1 npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_pmf, pmf, 90, unit_octahedron) # Check basis_type keyword dg = ProbabilisticDirectionGetter.from_shcoeff(fit.shm_coeff, 90, unit_octahedron, basis_type="mrtrix") npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_shcoeff, fit.shm_coeff, 90, unit_octahedron, basis_type="not a basis")
def test_save_seeds(): tissue = np.array([[2, 1, 1, 2, 1], [2, 2, 1, 1, 2], [1, 1, 1, 1, 1], [1, 1, 1, 2, 2], [0, 1, 1, 1, 2], [0, 1, 1, 0, 2], [1, 0, 1, 1, 1]]) tissue = tissue[None] sphere = HemiSphere.from_sphere(unit_octahedron) pmf_lookup = np.array([[0., 0., 0., ], [0., 0., 1.]]) pmf = pmf_lookup[(tissue > 0).astype("int")] # Create a seeds along x = np.array([0., 0, 0, 0, 0, 0, 0]) y = np.array([0., 1, 2, 3, 4, 5, 6]) z = np.array([1., 1, 1, 0, 1, 1, 1]) seeds = np.column_stack([x, y, z]) # Set up tracking endpoint_mask = tissue == TissueTypes.ENDPOINT invalidpoint_mask = tissue == TissueTypes.INVALIDPOINT tc = ActTissueClassifier(endpoint_mask, invalidpoint_mask) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 60, sphere) # valid streamlines only streamlines_generator = LocalTracking(direction_getter=dg, tissue_classifier=tc, seeds=seeds, affine=np.eye(4), step_size=1., return_all=False, save_seeds=True) streamlines_not_all = iter(streamlines_generator) # Verifiy that seeds are returned by the LocalTracker _, seed = next(streamlines_not_all) npt.assert_equal(seed, seeds[0]) _, seed = next(streamlines_not_all) npt.assert_equal(seed, seeds[1]) # Verifiy that seeds are returned by the PFTTracker also pft_streamlines = ParticleFilteringTracking(direction_getter=dg, tissue_classifier=tc, seeds=seeds, affine=np.eye(4), step_size=1., max_cross=1, return_all=False, save_seeds=True) streamlines = iter(pft_streamlines) _, seed = next(streamlines) npt.assert_equal(seed, seeds[0]) _, seed = next(streamlines) npt.assert_equal(seed, seeds[1])
distribution of small fiber bundles within each voxel. We can use this distribution for probabilistic fiber tracking. One way to do this is to represent the FOD using a discrete sphere. This discrete FOD can be used by the Probabilistic Direction Getter as a PMF for sampling tracking directions. We need to clip the FOD to use it as a PMF because the latter cannot have negative values. (Ideally the FOD should be strictly positive, but because of noise and/or model failures sometimes it can have negative values). """ from dipy.direction import ProbabilisticDirectionGetter from dipy.data import small_sphere from dipy.io.trackvis import save_trk fod = csd_fit.odf(small_sphere) pmf = fod.clip(min=0) prob_dg = ProbabilisticDirectionGetter.from_pmf(pmf, max_angle=30., sphere=small_sphere) streamlines = LocalTracking(prob_dg, classifier, seeds, affine, step_size=.5) save_trk("probabilistic_small_sphere.trk", streamlines, affine, labels.shape) """ One disadvantage of using a discrete PMF to represent possible tracking directions is that it tends to take up a lot of memory (RAM). The size of the PMF, the FOD in this case, must be equal to the number of possible tracking directions on the hemisphere, and every voxel has a unique PMF. In this case the data is ``(81, 106, 76)`` and ``small_sphere`` has 181 directions so the FOD is ``(81, 106, 76, 181)``. One way to avoid sampling the PMF and holding it in memory is to build the direction getter directly from the spherical harmonic representation of the FOD. By using this approach, we can also use a larger sphere, like ``default_sphere`` which has 362 directions on the hemisphere, without having to worry about memory limitations. """
def Analyze(img_d_path, img_s_path, gtab): # For fiber tracking, 3 things are needed # 1. Method for getting directions # 2. Method for identifying different tissue types # 3. seeds to begin tracking from print_info = False if print_info: print( "============================ Diffusion ============================" ) print(img_d) print( "============================ Structural =============================" ) print(img_s) print("Labels:", np.unique(img_s.get_data()).astype('int')) # Load images img_d = nib.load(img_d_path) img_s = nib.load(img_s_path) # Resize the label (img_s) # 0. create an empty array the shape of diffusion image, without # the 4th dimension # 1. Convert structural voxel coordinates into ref space (affine) # 2. Convert diffusion voxel coordinates into ref space (affine) # 3 For each diffusion ref coordinate, # find the closest structural ref coordinate # 4. find its corresponding label, then input it to the empty array print_info_2 = True if print_info_2: #print(img_d.get_data().shape[]) print(img_d.affine) print(img_s.affine) print(img_s._dataobj._shape) print(img_d._dataobj._shape) img_d_shape_3D = [j for i, j in enumerate(img_d.dataobj._shape) if i < 3] img_s_shape_3D = img_s.dataobj._shape #raise ValueError(" ") img_d_affine = img_d.affine img_s_affine = img_s.affine img_s_data = img_s.get_data() img_d_data = img_d.get_data() Vox_coord_s_i = np.arange(img_s_shape_3D[0]) Vox_coord_s_j = np.arange(img_s_shape_3D[1]) Vox_coord_s_k = np.arange(img_s_shape_3D[2]) Ref_coord_s_i = Vox_coord_s_i * img_s_affine[0, 0] + img_s_affine[0, 3] Ref_coord_s_j = Vox_coord_s_j * img_s_affine[1, 1] + img_s_affine[1, 3] Ref_coord_s_k = Vox_coord_s_k * img_s_affine[2, 2] + img_s_affine[2, 3] #print(Ref_coord_s_j) reduced_size_label = np.zeros(img_d_shape_3D) for i in range(img_d_shape_3D[0]): for j in range(img_d_shape_3D[1]): for k in range(img_d_shape_3D[2]): # convert to reference coordinate ref_coord_i = i * img_d_affine[0, 0] + img_d_affine[0, 3] ref_coord_j = j * img_d_affine[1, 1] + img_d_affine[1, 3] ref_coord_k = k * img_d_affine[2, 2] + img_d_affine[2, 3] min_i_ind = bisect.bisect_left(np.sort(Ref_coord_s_i), ref_coord_i) min_j_ind = bisect.bisect_left(Ref_coord_s_j, ref_coord_j) min_k_ind = bisect.bisect_left(Ref_coord_s_k, ref_coord_k) #print(min_i_ind,min_j_ind,min_k_ind) #print(img_s_data[260-1-min_i_ind][311-1-min_j_ind][260-1-min_k_ind]) #reduced_size_label[i][j][k]=img_s_data[260-1-min_i_ind][311-1-min_j_ind][260-1-min_k_ind] reduced_size_label[i][j][k] = img_s_data[260 - 1 - min_i_ind, min_j_ind, min_k_ind] print("Label image reduction successful") # Divide Brainstem #msk_Midbrain yy, xx, zz = np.meshgrid(np.arange(174), np.arange(145), np.arange(145)) pon_midbrain_msk = yy > (-115 / 78) * zz + 115 midbrain_msk = zz > 48 BS_msk = reduced_size_label == 16 reduced_size_label_BS_seg = np.copy(reduced_size_label) reduced_size_label_BS_seg[BS_msk * pon_midbrain_msk] = 90 reduced_size_label_BS_seg[BS_msk * midbrain_msk] = 120 plt.figure(figsize=[11, 8.5]) msk = reduced_size_label > 200 temp_reduced_size_label = np.copy(reduced_size_label_BS_seg) temp_reduced_size_label[msk] = 0 plt.imshow(temp_reduced_size_label[72, :, :], origin='lower') msk = reduced_size_label == 16 temp_reduced_size_label = np.copy(reduced_size_label) temp_reduced_size_label[~msk] = 0 plt.figure(figsize=[11, 8.5]) plt.imshow(temp_reduced_size_label[72, :, :], origin='lower') #print("image display complete") #input1=raw_input("stopping") T1_path = "C:\\Users\\gham\\Desktop\\Human Brain\\Data\\102109\\102109_3T_Diffusion_preproc\\102109\\T1w\\" T1_file = "T1w_acpc_dc_restore_1.25.nii.gz" T1 = nib.load(T1_path + T1_file) T1_data = T1.get_data() plt.figure(figsize=[11, 8.5]) plt.imshow(T1_data[72, :, :], origin='lower') plt.show() # implement the modified label reduced_size_label = reduced_size_label_BS_seg #raise ValueError("========== Stop =============") #White matter mask left_cerebral_wm = reduced_size_label == 2 right_cerebral_wm = reduced_size_label == 41 cerebral_wm = left_cerebral_wm + right_cerebral_wm left_cerebellum_wm = reduced_size_label == 7 right_cerebellum_wm = reduced_size_label == 46 cerebellum_wm = left_cerebellum_wm + right_cerebellum_wm CC = np.zeros(reduced_size_label.shape) for i in [251, 252, 253, 254, 255]: CC += reduced_size_label == i left_cortex = np.zeros(reduced_size_label.shape) for i in np.arange(1000, 1036): left_cortex += reduced_size_label == i right_cortex = np.zeros(reduced_size_label.shape) for i in np.arange(2000, 2036): right_cortex += reduced_size_label == i extra = np.zeros(reduced_size_label.shape) for i in [ 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 90, 120, 17, 18, 24, 26, 28, 30, 31, 43, 44, 46, 47, 49, 50, 51, 52, 53, 54, 58, 60, 62, 63, 77, 80, 85 ]: extra += reduced_size_label == i #for i in np.arange(1001,1035): # extra+=reduced_size_label==i wm = cerebral_wm + cerebellum_wm + CC + extra + left_cortex + right_cortex #seed_mask1=np.zeros(reduced_size_label.shape) #for i in [16]: # seed_mask1+=reduced_size_label==i #seed_mask2=np.zeros(reduced_size_label.shape) #seed_mask=seed_mask1+seed_mask2 #seed_mask=(reduced_size_label==16)+(reduced_size_label==2)+(reduced_size_label==41) #seeds = utils.seeds_from_mask(seed_mask, density=1, affine=img_d_affine) seeds = utils.seeds_from_mask(wm, density=1, affine=img_d_affine) # Constrained Spherical Deconvolution #reference: https://www.imagilys.com/constrained-spherical-deconvolution-CSD-tractography/ csd_model = ConstrainedSphericalDeconvModel(gtab, None, sh_order=6) csd_fit = csd_model.fit(img_d_data, mask=wm) print("CSD model complete") # reconstruction from dipy.reconst.shm import CsaOdfModel csa_model = CsaOdfModel(gtab, sh_order=6) gfa = csa_model.fit(img_d_data, mask=wm).gfa classifier = ThresholdTissueClassifier(gfa, .25) # ============================================================================= # import dipy.reconst.dti as dti # from dipy.reconst.dti import fractional_anisotropy # tensor_model = dti.TensorModel(gtab) # tenfit=tensor_model.fit(img_d_data,mask=wm) #COMPUTATIONALL INTENSE # FA=fractional_anisotropy(tenfit.evals) # classifier=ThresholdTissueClassifier(FA,.1) # 0.2 enough? # ============================================================================= print("Classifier complete") # Probabilistic direction getter from dipy.direction import ProbabilisticDirectionGetter from dipy.data import small_sphere from dipy.io.streamline import save_trk fod = csd_fit.odf(small_sphere) pmf = fod.clip(min=0) prob_dg = ProbabilisticDirectionGetter.from_pmf(pmf, max_angle=75., sphere=small_sphere) streamlines_generator = LocalTracking(prob_dg, classifier, seeds, img_d_affine, step_size=.5) save_trk("probabilistic_small_sphere.trk", streamlines_generator, img_d_affine, reduced_size_label.shape) astreamlines = np.array(list(streamlines_generator)) endpoints = np.array( [st[0::len(st) - 1] for st in astreamlines if len(st) > 1]) print(endpoints) with open('endpoints-shorder=6-maxangle=75-gfa=0.25-BSdiv-v3.pkl', 'wb') as f: pickle.dump(endpoints, f) with open("reduced_label-shorder=6-maxangle=75-gfa=0.25-BSdiv-v3.pkl", "wb") as g: pickle.dump(reduced_size_label, g)
def run(context): #################################################### # Get the path to input files and other parameter # #################################################### analysis_data = context.fetch_analysis_data() settings = analysis_data['settings'] postprocessing = settings['postprocessing'] dataset = settings['dataset'] if dataset == "HCPL": dwi_file_handle = context.get_files('input', modality='HARDI')[0] dwi_file_path = dwi_file_handle.download('/root/') bvalues_file_handle = context.get_files( 'input', reg_expression='.*prep.bvalues.hcpl.txt')[0] bvalues_file_path = bvalues_file_handle.download('/root/') bvecs_file_handle = context.get_files( 'input', reg_expression='.*prep.gradients.hcpl.txt')[0] bvecs_file_path = bvecs_file_handle.download('/root/') elif dataset == "DSI": dwi_file_handle = context.get_files('input', modality='DSI')[0] dwi_file_path = dwi_file_handle.download('/root/') bvalues_file_handle = context.get_files( 'input', reg_expression='.*prep.bvalues.txt')[0] bvalues_file_path = bvalues_file_handle.download('/root/') bvecs_file_handle = context.get_files( 'input', reg_expression='.*prep.gradients.txt')[0] bvecs_file_path = bvecs_file_handle.download('/root/') else: context.set_progress(message='Wrong dataset parameter') inject_file_handle = context.get_files( 'input', reg_expression='.*prep.inject.nii.gz')[0] inject_file_path = inject_file_handle.download('/root/') VUMC_ROIs_file_handle = context.get_files( 'input', reg_expression='.*VUMC_ROIs.nii.gz')[0] VUMC_ROIs_file_path = VUMC_ROIs_file_handle.download('/root/') ############################### # _____ _____ _______ __ # # | __ \_ _| __ \ \ / / # # | | | || | | |__) \ \_/ / # # | | | || | | ___/ \ / # # | |__| || |_| | | | # # |_____/_____|_| |_| # # # ############################### ######################################################################################## # _______ _ __ __ _______ _ __ # # |__ __| | | | \/ | |__ __| | | / _| # # | |_ __ __ _ ___| | ___ _| \ / | ___| |_ __ __ _ ___| | _| |_ __ _ ___ ___ # # | | '__/ _` |/ __| |/ / | | | |\/| |/ __| | '__/ _` |/ __| |/ / _/ _` |/ __/ _ \ # # | | | | (_| | (__| <| |_| | | | | (__| | | | (_| | (__| <| || (_| | (_| __/ # # |_|_| \__,_|\___|_|\_\\__, |_| |_|\___|_|_| \__,_|\___|_|\_\_| \__,_|\___\___| # # __/ | # # |___/ # # # # # # IronTract Team # ######################################################################################## ################# # Load the data # ################# dwi_img = nib.load(dwi_file_path) bvals, bvecs = read_bvals_bvecs(bvalues_file_path, bvecs_file_path) gtab = gradient_table(bvals, bvecs) ############################################ # Extract the brain mask from the b0 image # ############################################ _, brain_mask = median_otsu(dwi_img.get_data()[:, :, :, 0], median_radius=2, numpass=1) ################################################################## # Fit the tensor model and compute the fractional anisotropy map # ################################################################## context.set_progress(message='Processing voxel-wise DTI metrics.') tenmodel = TensorModel(gtab) tenfit = tenmodel.fit(dwi_img.get_data(), mask=brain_mask) FA = fractional_anisotropy(tenfit.evals) stopping_criterion = ThresholdStoppingCriterion(FA, 0.2) sphere = get_sphere("repulsion724") seed_mask_img = nib.load(inject_file_path) affine = seed_mask_img.affine seeds = utils.random_seeds_from_mask(seed_mask_img.get_data(), affine, seed_count_per_voxel=True, seeds_count=5000) if dataset == "HCPL": ################################################ # Compute Fiber Orientation Distribution (CSD) # ################################################ context.set_progress(message='Processing voxel-wise FOD estimation.') response, _ = auto_response_ssst(gtab, dwi_img.get_data(), roi_radii=10, fa_thr=0.7) csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order=8) csd_fit = csd_model.fit(dwi_img.get_data(), mask=brain_mask) shm = csd_fit.shm_coeff prob_dg = ProbabilisticDirectionGetter.from_shcoeff(shm, max_angle=20., sphere=sphere, pmf_threshold=0.1) elif dataset == "DSI": context.set_progress(message='Processing voxel-wise DSI estimation.') dsmodel = DiffusionSpectrumModel(gtab) dsfit = dsmodel.fit(dwi_img.get_data()) ODFs = dsfit.odf(sphere) prob_dg = ProbabilisticDirectionGetter.from_pmf(ODFs, max_angle=20., sphere=sphere, pmf_threshold=0.01) ########################################### # Compute DIPY Probabilistic Tractography # ########################################### context.set_progress(message='Processing tractography.') streamline_generator = LocalTracking(prob_dg, stopping_criterion, seeds, affine, step_size=.2, max_cross=1) streamlines = Streamlines(streamline_generator) # sft = StatefulTractogram(streamlines, seed_mask_img, Space.RASMM) # streamlines_file_path = "/root/streamlines.trk" # save_trk(sft, streamlines_file_path) ########################################################################### # Compute 3D volumes for the IronTract Challenge. For 'EPFL', we only # # keep streamlines with length > 1mm. We compute the visitation count # # image and apply a small gaussian smoothing. The gaussian smoothing # # is especially usefull to increase voxel coverage of deterministic # # algorithms. The log of the smoothed visitation count map is then # # iteratively thresholded producing 200 volumes/operation points. # # For VUMC, additional streamline filtering is done using anatomical # # priors (keeping only streamlines that intersect with at least one ROI). # ########################################################################### if postprocessing in ["EPFL", "ALL"]: context.set_progress(message='Processing density map (EPFL)') volume_folder = "/root/vol_epfl" output_epfl_zip_file_path = "/root/TrackyMcTrackface_EPFL_example.zip" os.mkdir(volume_folder) lengths = length(streamlines) streamlines = streamlines[lengths > 1] density = utils.density_map(streamlines, affine, seed_mask_img.shape) density = scipy.ndimage.gaussian_filter(density.astype("float32"), 0.5) log_density = np.log10(density + 1) max_density = np.max(log_density) for i, t in enumerate(np.arange(0, max_density, max_density / 200)): nbr = str(i) nbr = nbr.zfill(3) mask = log_density >= t vol_filename = os.path.join(volume_folder, "vol" + nbr + "_t" + str(t) + ".nii.gz") nib.Nifti1Image(mask.astype("int32"), affine, seed_mask_img.header).to_filename(vol_filename) shutil.make_archive(output_epfl_zip_file_path[:-4], 'zip', volume_folder) if postprocessing in ["VUMC", "ALL"]: context.set_progress(message='Processing density map (VUMC)') ROIs_img = nib.load(VUMC_ROIs_file_path) volume_folder = "/root/vol_vumc" output_vumc_zip_file_path = "/root/TrackyMcTrackface_VUMC_example.zip" os.mkdir(volume_folder) lengths = length(streamlines) streamlines = streamlines[lengths > 1] rois = ROIs_img.get_fdata().astype(int) _, grouping = utils.connectivity_matrix(streamlines, affine, rois, inclusive=True, return_mapping=True, mapping_as_streamlines=False) streamlines = streamlines[grouping[(0, 1)]] density = utils.density_map(streamlines, affine, seed_mask_img.shape) density = scipy.ndimage.gaussian_filter(density.astype("float32"), 0.5) log_density = np.log10(density + 1) max_density = np.max(log_density) for i, t in enumerate(np.arange(0, max_density, max_density / 200)): nbr = str(i) nbr = nbr.zfill(3) mask = log_density >= t vol_filename = os.path.join(volume_folder, "vol" + nbr + "_t" + str(t) + ".nii.gz") nib.Nifti1Image(mask.astype("int32"), affine, seed_mask_img.header).to_filename(vol_filename) shutil.make_archive(output_vumc_zip_file_path[:-4], 'zip', volume_folder) ################### # Upload the data # ################### context.set_progress(message='Uploading results...') #context.upload_file(fa_file_path, 'fa.nii.gz') # context.upload_file(fod_file_path, 'fod.nii.gz') # context.upload_file(streamlines_file_path, 'streamlines.trk') if postprocessing in ["EPFL", "ALL"]: context.upload_file(output_epfl_zip_file_path, 'TrackyMcTrackface_' + dataset +'_EPFL.zip') if postprocessing in ["VUMC", "ALL"]: context.upload_file(output_vumc_zip_file_path, 'TrackyMcTrackface_' + dataset +'_VUMC.zip')
def particle_tracking(self): self.sphere = get_sphere("repulsion724") if self.mod_type == "det": maxcrossing = 1 print("Obtaining peaks from model...") self.mod_peaks = peaks_from_model( self.mod, self.data, self.sphere, relative_peak_threshold=0.5, min_separation_angle=25, mask=self.wm_in_dwi_data, npeaks=5, normalize_peaks=True, ) qa_tensor.create_qa_figure(self.mod_peaks.peak_dirs, self.mod_peaks.peak_values, self.qa_tensor_out, self.mod_func) self.streamline_generator = ParticleFilteringTracking( self.mod_peaks, self.tiss_classifier, self.seeds, self.stream_affine, max_cross=maxcrossing, step_size=0.5, maxlen=1000, pft_back_tracking_dist=2, pft_front_tracking_dist=1, particle_count=15, return_all=True, ) elif self.mod_type == "prob": maxcrossing = 2 print("Preparing probabilistic tracking...") print("Fitting model to data...") self.mod_fit = self.mod.fit(self.data, self.wm_in_dwi_data) print("Building direction-getter...") self.mod_peaks = peaks_from_model( self.mod, self.data, self.sphere, relative_peak_threshold=0.5, min_separation_angle=25, mask=self.wm_in_dwi_data, npeaks=5, normalize_peaks=True, ) qa_tensor.create_qa_figure(self.mod_peaks.peak_dirs, self.mod_peaks.peak_values, self.qa_tensor_out, self.mod_func) try: print( "Proceeding using spherical harmonic coefficient from model estimation..." ) self.pdg = ProbabilisticDirectionGetter.from_shcoeff( self.mod_fit.shm_coeff, max_angle=60.0, sphere=self.sphere) except: print("Proceeding using FOD PMF from model estimation...") self.fod = self.mod_fit.odf(self.sphere) self.pmf = self.fod.clip(min=0) self.pdg = ProbabilisticDirectionGetter.from_pmf( self.pmf, max_angle=60.0, sphere=self.sphere) self.streamline_generator = ParticleFilteringTracking( self.pdg, self.tiss_classifier, self.seeds, self.stream_affine, max_cross=maxcrossing, step_size=0.5, maxlen=1000, pft_back_tracking_dist=2, pft_front_tracking_dist=1, particle_count=15, return_all=True, ) print("Reconstructing tractogram streamlines...") self.streamlines = Streamlines(self.streamline_generator) return self.streamlines
def test_particle_filtering_tractography(): """This tests that the ParticleFilteringTracking produces more streamlines connecting the gray matter than LocalTracking. """ sphere = get_sphere('repulsion100') step_size = 0.2 # Simple tissue masks simple_wm = np.array([[0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0]]) simple_wm = np.dstack([ np.zeros(simple_wm.shape), simple_wm, simple_wm, simple_wm, np.zeros(simple_wm.shape) ]) simple_gm = np.array([[1, 1, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0], [0, 1, 0, 0, 1, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0]]) simple_gm = np.dstack([ np.zeros(simple_gm.shape), simple_gm, simple_gm, simple_gm, np.zeros(simple_gm.shape) ]) simple_csf = np.ones(simple_wm.shape) - simple_wm - simple_gm sc = ActStoppingCriterion.from_pve(simple_wm, simple_gm, simple_csf) seeds = seeds_from_mask(simple_wm, np.eye(4), density=2) # Random pmf in every voxel shape_img = list(simple_wm.shape) shape_img.extend([sphere.vertices.shape[0]]) np.random.seed(0) # Random number generator initialization pmf = np.random.random(shape_img) # Test that PFT recover equal or more streamlines than localTracking dg = ProbabilisticDirectionGetter.from_pmf(pmf, 60, sphere) local_streamlines_generator = LocalTracking(dg, sc, seeds, np.eye(4), step_size, max_cross=1, return_all=False) local_streamlines = Streamlines(local_streamlines_generator) pft_streamlines_generator = ParticleFilteringTracking( dg, sc, seeds, np.eye(4), step_size, max_cross=1, return_all=False, pft_back_tracking_dist=1, pft_front_tracking_dist=0.5) pft_streamlines = Streamlines(pft_streamlines_generator) npt.assert_(np.array([len(pft_streamlines) > 0])) npt.assert_(np.array([len(pft_streamlines) >= len(local_streamlines)])) # Test that all points are equally spaced for l in [1, 2, 5, 10, 100]: pft_streamlines = ParticleFilteringTracking(dg, sc, seeds, np.eye(4), step_size, max_cross=1, return_all=True, maxlen=l) for s in pft_streamlines: for i in range(len(s) - 1): npt.assert_almost_equal(np.linalg.norm(s[i] - s[i + 1]), step_size) # Test that all points are within the image volume seeds = seeds_from_mask(np.ones(simple_wm.shape), np.eye(4), density=1) pft_streamlines_generator = ParticleFilteringTracking(dg, sc, seeds, np.eye(4), step_size, max_cross=1, return_all=True) pft_streamlines = Streamlines(pft_streamlines_generator) for s in pft_streamlines: npt.assert_(np.all((s + 0.5).astype(int) >= 0)) npt.assert_(np.all((s + 0.5).astype(int) < simple_wm.shape)) # Test that the number of streamline return with return_all=True equal the # number of seeds places npt.assert_(np.array([len(pft_streamlines) == len(seeds)])) # Test non WM seed position seeds = [[0, 5, 4], [0, 0, 1], [50, 50, 50]] pft_streamlines_generator = ParticleFilteringTracking(dg, sc, seeds, np.eye(4), step_size, max_cross=1, return_all=True) pft_streamlines = Streamlines(pft_streamlines_generator) npt.assert_equal(len(pft_streamlines[0]), 3) # INVALIDPOINT npt.assert_equal(len(pft_streamlines[1]), 3) # ENDPOINT npt.assert_equal(len(pft_streamlines[2]), 1) # OUTSIDEIMAGE # Test with wrong StoppingCriterion type sc_bin = BinaryStoppingCriterion(simple_wm) npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, sc_bin, seeds, np.eye(4), step_size)) # Test with invalid back/front tracking distances npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, sc, seeds, np.eye(4), step_size, pft_back_tracking_dist=0, pft_front_tracking_dist=0)) npt.assert_raises( ValueError, lambda: ParticleFilteringTracking( dg, sc, seeds, np.eye(4), step_size, pft_back_tracking_dist=-1)) npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, sc, seeds, np.eye(4), step_size, pft_back_tracking_dist=0, pft_front_tracking_dist=-2)) # Test with invalid affine shape npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, sc, seeds, np.eye(3), step_size)) # Test with invalid maxlen npt.assert_raises( ValueError, lambda: ParticleFilteringTracking( dg, sc, seeds, np.eye(4), step_size, maxlen=0)) npt.assert_raises( ValueError, lambda: ParticleFilteringTracking( dg, sc, seeds, np.eye(4), step_size, maxlen=-1)) # Test with invalid particle count npt.assert_raises( ValueError, lambda: ParticleFilteringTracking( dg, sc, seeds, np.eye(4), step_size, particle_count=0)) npt.assert_raises( ValueError, lambda: ParticleFilteringTracking( dg, sc, seeds, np.eye(4), step_size, particle_count=-1)) # Test reproducibility tracking1 = Streamlines( ParticleFilteringTracking(dg, sc, seeds, np.eye(4), step_size, random_seed=0))._data tracking2 = Streamlines( ParticleFilteringTracking(dg, sc, seeds, np.eye(4), step_size, random_seed=0))._data npt.assert_equal(tracking1, tracking2)
def test_particle_filtering_tractography(): """This tests that the ParticleFilteringTracking produces more streamlines connecting the gray matter than LocalTracking. """ sphere = get_sphere('repulsion100') step_size = 0.2 # Simple tissue masks simple_wm = np.array([[0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0]]) simple_wm = np.dstack([np.zeros(simple_wm.shape), simple_wm, simple_wm, simple_wm, np.zeros(simple_wm.shape)]) simple_gm = np.array([[1, 1, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0], [0, 1, 0, 0, 1, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0]]) simple_gm = np.dstack([np.zeros(simple_gm.shape), simple_gm, simple_gm, simple_gm, np.zeros(simple_gm.shape)]) simple_csf = np.ones(simple_wm.shape) - simple_wm - simple_gm tc = ActTissueClassifier.from_pve(simple_wm, simple_gm, simple_csf) seeds = seeds_from_mask(simple_wm, density=2) # Random pmf in every voxel shape_img = list(simple_wm.shape) shape_img.extend([sphere.vertices.shape[0]]) np.random.seed(0) # Random number generator initialization pmf = np.random.random(shape_img) # Test that PFT recover equal or more streamlines than localTracking dg = ProbabilisticDirectionGetter.from_pmf(pmf, 60, sphere) local_streamlines_generator = LocalTracking(dg, tc, seeds, np.eye(4), step_size, max_cross=1, return_all=False) local_streamlines = Streamlines(local_streamlines_generator) pft_streamlines_generator = ParticleFilteringTracking( dg, tc, seeds, np.eye(4), step_size, max_cross=1, return_all=False, pft_back_tracking_dist=1, pft_front_tracking_dist=0.5) pft_streamlines = Streamlines(pft_streamlines_generator) npt.assert_(np.array([len(pft_streamlines) > 0])) npt.assert_(np.array([len(pft_streamlines) >= len(local_streamlines)])) # Test that all points are equally spaced for l in [1, 2, 5, 10, 100]: pft_streamlines = ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, max_cross=1, return_all=True, maxlen=l) for s in pft_streamlines: for i in range(len(s) - 1): npt.assert_almost_equal(np.linalg.norm(s[i] - s[i + 1]), step_size) # Test that all points are within the image volume seeds = seeds_from_mask(np.ones(simple_wm.shape), density=1) pft_streamlines_generator = ParticleFilteringTracking( dg, tc, seeds, np.eye(4), step_size, max_cross=1, return_all=True) pft_streamlines = Streamlines(pft_streamlines_generator) for s in pft_streamlines: npt.assert_(np.all((s + 0.5).astype(int) >= 0)) npt.assert_(np.all((s + 0.5).astype(int) < simple_wm.shape)) # Test that the number of streamline return with return_all=True equal the # number of seeds places npt.assert_(np.array([len(pft_streamlines) == len(seeds)])) # Test non WM seed position seeds = [[0, 5, 4], [0, 0, 1], [50, 50, 50]] pft_streamlines_generator = ParticleFilteringTracking( dg, tc, seeds, np.eye(4), step_size, max_cross=1, return_all=True) pft_streamlines = Streamlines(pft_streamlines_generator) npt.assert_equal(len(pft_streamlines[0]), 3) # INVALIDPOINT npt.assert_equal(len(pft_streamlines[1]), 3) # ENDPOINT npt.assert_equal(len(pft_streamlines[2]), 1) # OUTSIDEIMAGE # Test with wrong tissueclassifier type tc_bin = BinaryTissueClassifier(simple_wm) npt.assert_raises(ValueError, lambda: ParticleFilteringTracking(dg, tc_bin, seeds, np.eye(4), step_size)) # Test with invalid back/front tracking distances npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, pft_back_tracking_dist=0, pft_front_tracking_dist=0)) npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, pft_back_tracking_dist=-1)) npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, pft_back_tracking_dist=0, pft_front_tracking_dist=-2)) # Test with invalid affine shape npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, tc, seeds, np.eye(3), step_size)) # Test with invalid maxlen npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, maxlen=0)) npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, maxlen=-1)) # Test with invalid particle count npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, particle_count=0)) npt.assert_raises( ValueError, lambda: ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, particle_count=-1)) # Test reproducibility tracking_1 = Streamlines(ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, random_seed=0)).data tracking_2 = Streamlines(ParticleFilteringTracking(dg, tc, seeds, np.eye(4), step_size, random_seed=0)).data npt.assert_equal(tracking_1, tracking_2)
def test_stop_conditions(): """This tests that the Local Tracker behaves as expected for the following tissue types. """ # TissueTypes.TRACKPOINT = 1 # TissueTypes.ENDPOINT = 2 # TissueTypes.INVALIDPOINT = 0 tissue = np.array([[2, 1, 1, 2, 1], [2, 2, 1, 1, 2], [1, 1, 1, 1, 1], [1, 1, 1, 2, 2], [0, 1, 1, 1, 2], [0, 1, 1, 0, 2], [1, 0, 1, 1, 1]]) tissue = tissue[None] sphere = HemiSphere.from_sphere(unit_octahedron) pmf_lookup = np.array([[0., 0., 0., ], [0., 0., 1.]]) pmf = pmf_lookup[(tissue > 0).astype("int")] # Create a seeds along x = np.array([0., 0, 0, 0, 0, 0, 0]) y = np.array([0., 1, 2, 3, 4, 5, 6]) z = np.array([1., 1, 1, 0, 1, 1, 1]) seeds = np.column_stack([x, y, z]) # Set up tracking endpoint_mask = tissue == TissueTypes.ENDPOINT invalidpoint_mask = tissue == TissueTypes.INVALIDPOINT tc = ActTissueClassifier(endpoint_mask, invalidpoint_mask) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 60, sphere) # valid streamlines only streamlines_generator = LocalTracking(direction_getter=dg, tissue_classifier=tc, seeds=seeds, affine=np.eye(4), step_size=1., return_all=False) streamlines_not_all = iter(streamlines_generator) # all streamlines streamlines_all_generator = LocalTracking(direction_getter=dg, tissue_classifier=tc, seeds=seeds, affine=np.eye(4), step_size=1., return_all=True) streamlines_all = iter(streamlines_all_generator) # Check that the first streamline stops at 0 and 3 (ENDPOINT) y = 0 sl = next(streamlines_not_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 3]) npt.assert_equal(len(sl), 4) sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 3]) npt.assert_equal(len(sl), 4) # Check that the first streamline stops at 0 and 4 (ENDPOINT) y = 1 sl = next(streamlines_not_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) # This streamline should be the same as above. This row does not have # ENDPOINTs, but the streamline should stop at the edge and not include # OUTSIDEIMAGE points. y = 2 sl = next(streamlines_not_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) # If we seed on the edge, the first (or last) point in the streamline # should be the seed. y = 3 sl = next(streamlines_not_all) npt.assert_equal(sl[0], seeds[y]) sl = next(streamlines_all) npt.assert_equal(sl[0], seeds[y]) # The last 3 seeds should not produce streamlines, # INVALIDPOINT streamlines are rejected (return_all=False). npt.assert_equal(len(list(streamlines_not_all)), 0) # The last 3 seeds should produce invalid streamlines, # INVALIDPOINT streamlines are kept (return_all=True). # The streamline stops at 0 (INVALIDPOINT) and 4 (ENDPOINT) y = 4 sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) # The streamline stops at 0 (INVALIDPOINT) and 4 (INVALIDPOINT) y = 5 sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 3]) npt.assert_equal(len(sl), 4) # The last streamline should contain only one point, the seed point, # because no valid inital direction was returned. y = 6 sl = next(streamlines_all) npt.assert_equal(sl[0], seeds[y]) npt.assert_equal(sl[-1], seeds[y]) npt.assert_equal(len(sl), 1)
def test_probabilistic_odf_weighted_tracker(): """This tests that the Probabalistic Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.6, .4, 0.]]) simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.])] * 30 mask = (simple_image > 0).astype(float) tc = ThresholdTissueClassifier(mask, .5) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.], [2., 5., 0.]]), np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]])] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) path = [False, False] for sl in streamlines: if allclose(sl, expected[0]): path[0] = True elif allclose(sl, expected[1]): path[1] = True else: raise AssertionError() npt.assert_(all(path)) # The first path is not possible if 90 degree turns are excluded dg = ProbabilisticDirectionGetter.from_pmf(pmf, 80, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1])) # The first path is not possible if pmf_threshold > 0.67 # 0.4/0.6 < 2/3, multiplying the pmf should not change the ratio dg = ProbabilisticDirectionGetter.from_pmf(10*pmf, 90, sphere, pmf_threshold=0.67) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1])) # Test non WM seed position seeds = [[0, 0, 0], [5, 5, 5]] streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 0.2, max_cross=1, return_all=True) streamlines = Streamlines(streamlines) npt.assert_(len(streamlines[0]) == 3) # INVALIDPOINT npt.assert_(len(streamlines[1]) == 1) # OUTSIDEIMAGE # Test that all points are within the image volume seeds = seeds_from_mask(np.ones(mask.shape), density=2) streamline_generator = LocalTracking(dg, tc, seeds, np.eye(4), 0.5, return_all=True) streamlines = Streamlines(streamline_generator) for s in streamlines: npt.assert_(np.all((s + 0.5).astype(int) >= 0)) npt.assert_(np.all((s + 0.5).astype(int) < mask.shape)) # Test that the number of streamline return with return_all=True equal the # number of seeds places npt.assert_(np.array([len(streamlines) == len(seeds)])) # Test reproducibility tracking_1 = Streamlines(LocalTracking(dg, tc, seeds, np.eye(4), 0.5, random_seed=0)).data tracking_2 = Streamlines(LocalTracking(dg, tc, seeds, np.eye(4), 0.5, random_seed=0)).data npt.assert_equal(tracking_1, tracking_2)
def test_ProbabilisticOdfWeightedTracker(): """This tests that the Probabalistic Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.5, .5, 0.]]) simple_image = np.array([[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.])] * 30 mask = (simple_image > 0).astype(float) tc = ThresholdTissueClassifier(mask, .5) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, sphere) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) expected = [np.array([[ 0., 1., 0.], [ 1., 1., 0.], [ 2., 1., 0.], [ 2., 2., 0.], [ 2., 3., 0.], [ 2., 4., 0.], [ 2., 5., 0.]]), np.array([[ 0., 1., 0.], [ 1., 1., 0.], [ 2., 1., 0.], [ 3., 1., 0.], [ 4., 1., 0.]]) ] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) path = [False, False] for sl in streamlines: dir = ( -sphere.vertices[0] ).copy() if allclose(sl, expected[0]): path[0] = True elif allclose(sl, expected[1]): path[1] = True else: raise AssertionError() npt.assert_(all(path)) # The first path is not possible if 90 degree turns are excluded dg = ProbabilisticDirectionGetter.from_pmf(pmf, 80, sphere) streamlines = LocalTracking(dg, tc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1]))
def tracking(image, bvecs, bvals, wm, seeds, fibers, prune_length=3, rseed=42, plot=False, proba=False, verbose=False): # Pipelines transcribed from: # https://dipy.org/documentation/1.1.1./examples_built/tracking_introduction_eudx/#example-tracking-introduction-eudx # https://dipy.org/documentation/1.1.1./examples_built/tracking_probabilistic/ # Load Images dwi_loaded = nib.load(image) dwi_data = dwi_loaded.get_fdata() wm_loaded = nib.load(wm) wm_data = wm_loaded.get_fdata() seeds_loaded = nib.load(seeds) seeds_data = seeds_loaded.get_fdata() seeds = utils.seeds_from_mask(seeds_data, dwi_loaded.affine, density=2) # Load B-values & B-vectors # NB. Use aligned b-vecs if providing eddy-aligned data bvals, bvecs = read_bvals_bvecs(bvals, bvecs) gtab = gradient_table(bvals, bvecs) csa_model = CsaOdfModel(gtab, sh_order=6) # Set stopping criterion gfa = csa_model.fit(dwi_data, mask=wm_data).gfa stop_criterion = ThresholdStoppingCriterion(gfa, .25) if proba: # Establish ODF model response, ratio = auto_response(gtab, dwi_data, roi_radius=10, fa_thr=0.7) csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order=6) csd_fit = csd_model.fit(dwi_data, mask=wm_data) # Create Probabilisitic direction getter fod = csd_fit.odf(default_sphere) pmf = fod.clip(min=0) prob_dg = ProbabilisticDirectionGetter.from_pmf(pmf, max_angle=30., sphere=default_sphere) # Use the probabilisitic direction getter as the dg dg = prob_dg else: # Establish ODF model csa_peaks = peaks_from_model(csa_model, dwi_data, default_sphere, relative_peak_threshold=0.8, min_separation_angle=45, mask=wm_data) # Use the CSA peaks as the dg dg = csa_peaks # Create generator and perform tracing s_generator = LocalTracking(dg, stop_criterion, seeds, dwi_loaded.affine, 0.5, random_seed=rseed) streamlines = Streamlines(s_generator) # Prune streamlines streamlines = ArraySequence( [strline for strline in streamlines if len(strline) > prune_length]) sft = StatefulTractogram(streamlines, dwi_loaded, Space.RASMM) # Save streamlines save_trk(sft, fibers + ".trk") # Visualize fibers if plot and has_fury: from dipy.viz import window, actor, colormap as cmap # Create the 3D display. r = window.Renderer() r.add(actor.line(streamlines, cmap.line_colors(streamlines))) window.record(r, out_path=fibers + '.png', size=(800, 800))
def test_probabilistic_odf_weighted_tracker(): """This tests that the Probabalistic Direction Getter plays nice LocalTracking and produces reasonable streamlines in a simple example. """ sphere = HemiSphere.from_sphere(unit_octahedron) # A simple image with three possible configurations, a vertical tract, # a horizontal tract and a crossing pmf_lookup = np.array([[0., 0., 1.], [1., 0., 0.], [0., 1., 0.], [.6, .4, 0.]]) simple_image = np.array([ [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 3, 2, 2, 2, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ]) simple_image = simple_image[..., None] pmf = pmf_lookup[simple_image] seeds = [np.array([1., 1., 0.])] * 30 mask = (simple_image > 0).astype(float) sc = ThresholdStoppingCriterion(mask, .5) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, sc, seeds, np.eye(4), 1.) expected = [ np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [2., 2., 0.], [2., 3., 0.], [2., 4., 0.]]), np.array([[0., 1., 0.], [1., 1., 0.], [2., 1., 0.], [3., 1., 0.], [4., 1., 0.]]) ] def allclose(x, y): return x.shape == y.shape and np.allclose(x, y) path = [False, False] for sl in streamlines: if allclose(sl, expected[0]): path[0] = True elif allclose(sl, expected[1]): path[1] = True else: raise AssertionError() npt.assert_(all(path)) # The first path is not possible if 90 degree turns are excluded dg = ProbabilisticDirectionGetter.from_pmf(pmf, 80, sphere, pmf_threshold=0.1) streamlines = LocalTracking(dg, sc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1])) # The first path is not possible if pmf_threshold > 0.67 # 0.4/0.6 < 2/3, multiplying the pmf should not change the ratio dg = ProbabilisticDirectionGetter.from_pmf(10 * pmf, 90, sphere, pmf_threshold=0.67) streamlines = LocalTracking(dg, sc, seeds, np.eye(4), 1.) for sl in streamlines: npt.assert_(np.allclose(sl, expected[1])) # Test non WM seed position seeds = [[0, 0, 0], [5, 5, 5]] streamlines = LocalTracking(dg, sc, seeds, np.eye(4), 0.2, max_cross=1, return_all=True) streamlines = Streamlines(streamlines) npt.assert_(len(streamlines[0]) == 1) # INVALIDPOINT npt.assert_(len(streamlines[1]) == 1) # OUTSIDEIMAGE # Test that all points are within the image volume seeds = seeds_from_mask(np.ones(mask.shape), np.eye(4), density=2) streamline_generator = LocalTracking(dg, sc, seeds, np.eye(4), 0.5, return_all=True) streamlines = Streamlines(streamline_generator) for s in streamlines: npt.assert_(np.all((s + 0.5).astype(int) >= 0)) npt.assert_(np.all((s + 0.5).astype(int) < mask.shape)) # Test that the number of streamline return with return_all=True equal the # number of seeds places npt.assert_(np.array([len(streamlines) == len(seeds)])) # Test reproducibility tracking_1 = Streamlines( LocalTracking(dg, sc, seeds, np.eye(4), 0.5, random_seed=0))._data tracking_2 = Streamlines( LocalTracking(dg, sc, seeds, np.eye(4), 0.5, random_seed=0))._data npt.assert_equal(tracking_1, tracking_2)
def test_stop_conditions(): """This tests that the Local Tracker behaves as expected for the following tissue types. """ # StreamlineStatus.TRACKPOINT = 1 # StreamlineStatus.ENDPOINT = 2 # StreamlineStatus.INVALIDPOINT = 0 tissue = np.array([[2, 1, 1, 2, 1], [2, 2, 1, 1, 2], [1, 1, 1, 1, 1], [1, 1, 1, 2, 2], [0, 1, 1, 1, 2], [0, 1, 1, 0, 2], [1, 0, 1, 1, 1], [2, 1, 2, 0, 0]]) tissue = tissue[None] sphere = HemiSphere.from_sphere(unit_octahedron) pmf_lookup = np.array([[ 0., 0., 0., ], [0., 0., 1.]]) pmf = pmf_lookup[(tissue > 0).astype("int")] # Create a seeds along x = np.array([0., 0, 0, 0, 0, 0, 0, 0]) y = np.array([0., 1, 2, 3, 4, 5, 6, 7]) z = np.array([1., 1, 1, 0, 1, 1, 1, 1]) seeds = np.column_stack([x, y, z]) # Set up tracking endpoint_mask = tissue == StreamlineStatus.ENDPOINT invalidpoint_mask = tissue == StreamlineStatus.INVALIDPOINT sc = ActStoppingCriterion(endpoint_mask, invalidpoint_mask) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 60, sphere) # valid streamlines only streamlines_generator = LocalTracking(direction_getter=dg, stopping_criterion=sc, seeds=seeds, affine=np.eye(4), step_size=1., return_all=False) streamlines_not_all = iter(streamlines_generator) # all streamlines streamlines_all_generator = LocalTracking(direction_getter=dg, stopping_criterion=sc, seeds=seeds, affine=np.eye(4), step_size=1., return_all=True) streamlines_all = iter(streamlines_all_generator) # Check that the first streamline stops at 1 and 2 (ENDPOINT) y = 0 sl = next(streamlines_not_all) npt.assert_equal(sl[0], [0, y, 1]) npt.assert_equal(sl[-1], [0, y, 2]) npt.assert_equal(len(sl), 2) sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 1]) npt.assert_equal(sl[-1], [0, y, 2]) npt.assert_equal(len(sl), 2) # Check that the next streamline stops at 1 and 3 (ENDPOINT) y = 1 sl = next(streamlines_not_all) npt.assert_equal(sl[0], [0, y, 1]) npt.assert_equal(sl[-1], [0, y, 3]) npt.assert_equal(len(sl), 3) sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 1]) npt.assert_equal(sl[-1], [0, y, 3]) npt.assert_equal(len(sl), 3) # This streamline should be the same as above. This row does not have # ENDPOINTs, but the streamline should stop at the edge and not include # OUTSIDEIMAGE points. y = 2 sl = next(streamlines_not_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 0]) npt.assert_equal(sl[-1], [0, y, 4]) npt.assert_equal(len(sl), 5) # If we seed on the edge, the first (or last) point in the streamline # should be the seed. y = 3 sl = next(streamlines_not_all) npt.assert_equal(sl[0], seeds[y]) sl = next(streamlines_all) npt.assert_equal(sl[0], seeds[y]) # The last 3 seeds should not produce streamlines, # INVALIDPOINT streamlines are rejected (return_all=False). npt.assert_equal(len(list(streamlines_not_all)), 0) # The last 3 seeds should produce invalid streamlines, # INVALIDPOINT streamlines are kept (return_all=True). # The streamline stops at 1 (INVALIDPOINT) and 3 (ENDPOINT) y = 4 sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 1]) npt.assert_equal(sl[-1], [0, y, 3]) npt.assert_equal(len(sl), 3) # The streamline stops at 0 (INVALIDPOINT) and 2 (INVALIDPOINT) y = 5 sl = next(streamlines_all) npt.assert_equal(sl[0], [0, y, 1]) npt.assert_equal(sl[-1], [0, y, 2]) npt.assert_equal(len(sl), 2) # The streamline should contain only one point, the seed point, # because no valid inital direction was returned. y = 6 sl = next(streamlines_all) npt.assert_equal(sl[0], seeds[y]) npt.assert_equal(sl[-1], seeds[y]) npt.assert_equal(len(sl), 1) # The streamline should contain only one point, the seed point, # because no valid neighboring voxel (ENDPOINT) y = 7 sl = next(streamlines_all) npt.assert_equal(sl[0], seeds[y]) npt.assert_equal(sl[-1], seeds[y]) npt.assert_equal(len(sl), 1)
def test_ProbabilisticDirectionGetter(): # Test the constructors and errors of the ProbabilisticDirectionGetter class SillyModel(SphHarmModel): sh_order = 4 def fit(self, data, mask=None): coeff = np.zeros(data.shape[:-1] + (15,)) return SphHarmFit(self, coeff, mask=None) model = SillyModel(gtab=None) data = np.zeros((3, 3, 3, 7)) # Test if the tracking works on different dtype of the same data. for dtype in [np.float32, np.float64]: fit = model.fit(data.astype(dtype)) # Sample point and direction point = np.zeros(3) dir = unit_octahedron.vertices[0].copy() # make a dg from a fit with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=descoteaux07_legacy_msg, category=PendingDeprecationWarning) dg = ProbabilisticDirectionGetter.from_shcoeff( fit.shm_coeff, 90, unit_octahedron) state = dg.get_direction(point, dir) npt.assert_equal(state, 1) # Make a dg from a pmf N = unit_octahedron.theta.shape[0] pmf = np.zeros((3, 3, 3, N)) dg = ProbabilisticDirectionGetter.from_pmf(pmf, 90, unit_octahedron) state = dg.get_direction(point, dir) npt.assert_equal(state, 1) # pmf shape must match sphere bad_pmf = pmf[..., 1:] npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_pmf, bad_pmf, 90, unit_octahedron) # pmf must have 4 dimensions bad_pmf = pmf[0, ...] npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_pmf, bad_pmf, 90, unit_octahedron) # pmf cannot have negative values pmf[0, 0, 0, 0] = -1 npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_pmf, pmf, 90, unit_octahedron) # Check basis_type keyword with warnings.catch_warnings(): warnings.filterwarnings( "ignore", message=tournier07_legacy_msg, category=PendingDeprecationWarning) dg = ProbabilisticDirectionGetter.from_shcoeff( fit.shm_coeff, 90, unit_octahedron, basis_type="tournier07") npt.assert_raises(ValueError, ProbabilisticDirectionGetter.from_shcoeff, fit.shm_coeff, 90, unit_octahedron, basis_type="not a basis")
represent the FOD using a discrete sphere. This discrete FOD can be used by the ``ProbabilisticDirectionGetter`` as a PMF for sampling tracking directions. We need to clip the FOD to use it as a PMF because the latter cannot have negative values. Ideally, the FOD should be strictly positive, but because of noise and/or model failures sometimes it can have negative values. """ from dipy.direction import ProbabilisticDirectionGetter from dipy.data import small_sphere from dipy.io.stateful_tractogram import Space, StatefulTractogram from dipy.io.streamline import save_trk fod = csd_fit.odf(small_sphere) pmf = fod.clip(min=0) prob_dg = ProbabilisticDirectionGetter.from_pmf(pmf, max_angle=30., sphere=small_sphere) streamline_generator = LocalTracking(prob_dg, stopping_criterion, seeds, affine, step_size=.5) streamlines = Streamlines(streamline_generator) sft = StatefulTractogram(streamlines, hardi_img, Space.RASMM) save_trk(sft, "tractogram_probabilistic_dg_pmf.trk") if has_fury: scene = window.Scene() scene.add(actor.line(streamlines, colormap.line_colors(streamlines))) window.record(scene, out_path='tractogram_probabilistic_dg_pmf.png',
def run_tractography(fdwi, fbval, fbvec, fwmparc, mod_func, mod_type, seed_density=20): """ mod_func : 'str' 'csd' or 'csa' mod_type : 'str' 'det' or 'prob' seed_density : int, default=20 Seeding density for tractography """ # Getting default params sphere = get_sphere("repulsion724") stream_affine = np.eye(4) # Loading data print("Loading Data...") dwi, gtab, wm_mask = load_data(fdwi, fbval, fbvec, fwmparc) # Make tissue classifier tiss_classifier = BinaryStoppingCriterion(wm_mask) if mod_func == "csd": mod = csd_mod_est(gtab, dwi, wm_mask) elif mod_func == "csa": mod = odf_mod_est(gtab) # Build seed list seeds = utils.random_seeds_from_mask( wm_mask, affine=stream_affine, seeds_count=int(seed_density), seed_count_per_voxel=True, ) # Make streamlines if mod_type == "det": print("Obtaining peaks from model...") direction_getter = peaks_from_model( mod, dwi, sphere, relative_peak_threshold=0.5, min_separation_angle=25, mask=wm_mask, npeaks=5, normalize_peaks=True, ) elif mod_type == "prob": print("Preparing probabilistic tracking...") print("Fitting model to data...") mod_fit = mod.fit(dwi, wm_mask) print("Building direction-getter...") try: print( "Proceeding using spherical harmonic coefficient from model estimation..." ) direction_getter = ProbabilisticDirectionGetter.from_shcoeff( mod_fit.shm_coeff, max_angle=60.0, sphere=sphere) except: print("Proceeding using FOD PMF from model estimation...") fod = mod_fit.odf(sphere) pmf = fod.clip(min=0) direction_getter = ProbabilisticDirectionGetter.from_pmf( pmf, max_angle=60.0, sphere=sphere) print("Running Local Tracking") streamline_generator = LocalTracking( direction_getter, tiss_classifier, seeds, stream_affine, step_size=0.5, return_all=True, ) print("Reconstructing tractogram streamlines...") streamlines = Streamlines(streamline_generator) tracks = Streamlines([track for track in streamlines if len(track) > 60]) return tracks