def _run_interface(self, runtime): tracks, header = trk.read(self.inputs.in_file) if not isdefined(self.inputs.data_dims): data_dims = header['dim'] else: data_dims = self.inputs.data_dims if not isdefined(self.inputs.voxel_dims): voxel_size = header['voxel_size'] else: voxel_size = self.inputs.voxel_dims affine = header['vox_to_ras'] streams = ((ii[0]) for ii in tracks) data = density_map(streams, data_dims, voxel_size) if data.max() < 2**15: data = data.astype('int16') img = nb.Nifti1Image(data,affine) out_file = op.abspath(self.inputs.out_filename) nb.save(img, out_file) iflogger.info('Track density map saved as {i}'.format(i=out_file)) iflogger.info('Data Dimensions {d}'.format(d=data_dims)) iflogger.info('Voxel Dimensions {v}'.format(v=voxel_size)) return runtime
def _run_interface(self, runtime): tracks, header = trk.read(self.inputs.in_file) if not isdefined(self.inputs.data_dims): data_dims = header['dim'] else: data_dims = self.inputs.data_dims if not isdefined(self.inputs.voxel_dims): voxel_size = header['voxel_size'] else: voxel_size = self.inputs.voxel_dims affine = header['vox_to_ras'] streams = ((ii[0]) for ii in tracks) data = density_map(streams, data_dims, voxel_size) if data.max() < 2**15: data = data.astype('int16') img = nb.Nifti1Image(data, affine) out_file = op.abspath(self.inputs.out_filename) nb.save(img, out_file) iflogger.info('Track density map saved as {i}'.format(i=out_file)) iflogger.info('Data Dimensions {d}'.format(d=data_dims)) iflogger.info('Voxel Dimensions {v}'.format(v=voxel_size)) return runtime
def _run_interface(self, runtime): # Loading the ROI file import nibabel as nib import numpy as np from dipy.tracking import utils img = nib.load(self.inputs.ROI_file) data = img.get_data() affine = img.get_affine() # Getting the FA file img = nib.load(self.inputs.FA_file) FA_data = img.get_data() FA_affine = img.get_affine() # Loading the streamlines from nibabel import trackvis streams, hdr = trackvis.read(self.inputs.trackfile,points_space='rasmm') streamlines = [s[0] for s in streams] streamlines_affine = trackvis.aff_from_hdr(hdr,atleast_v2=True) # Checking for negative values from dipy.tracking._utils import _mapping_to_voxel, _to_voxel_coordinates endpoints = [sl[0::len(sl)-1] for sl in streamlines] lin_T, offset = _mapping_to_voxel(affine, (1.,1.,1.)) inds = np.dot(endpoints, lin_T) inds += offset negative_values = np.where(inds <0)[0] for negative_value in sorted(negative_values, reverse=True): del streamlines[negative_value] # Constructing the streamlines matrix matrix,mapping = utils.connectivity_matrix(streamlines=streamlines,label_volume=data,affine=streamlines_affine,symmetric=True,return_mapping=True,mapping_as_streamlines=True) matrix[matrix < 10] = 0 # Constructing the FA matrix dimensions = matrix.shape FA_matrix = np.empty(shape=dimensions) for i in range(0,dimensions[0]): for j in range(0,dimensions[1]): if matrix[i,j]: dm = utils.density_map(mapping[i,j], FA_data.shape, affine=streamlines_affine) FA_matrix[i,j] = np.mean(FA_data[dm>0]) else: FA_matrix[i,j] = 0 FA_matrix[np.tril_indices(n=len(FA_matrix))] = 0 FA_matrix = FA_matrix.T + FA_matrix - np.diagonal(FA_matrix) from nipype.utils.filemanip import split_filename _, base, _ = split_filename(self.inputs.trackfile) np.savetxt(base + '_FA_matrix.txt',FA_matrix,delimiter='\t') return runtime
def test_density_map(): # One streamline diagonal in volume streamlines = [np.array([np.arange(10)] * 3).T] shape = (10, 10, 10) x = np.arange(10) expected = np.zeros(shape) expected[x, x, x] = 1. dm = density_map(streamlines, np.eye(4), shape) npt.assert_array_equal(dm, expected) # add streamline, make voxel_size smaller. Each streamline should only be # counted once, even if multiple points lie in a voxel streamlines.append(np.ones((5, 3))) shape = (5, 5, 5) x = np.arange(5) expected = np.zeros(shape) expected[x, x, x] = 1. expected[0, 0, 0] += 1 affine = np.eye(4) * 2 affine[:3, 3] = 0.05 dm = density_map(streamlines, affine, shape) npt.assert_array_equal(dm, expected) # should work with a generator dm = density_map(iter(streamlines), affine, shape) npt.assert_array_equal(dm, expected) # Test passing affine affine = np.diag([2, 2, 2, 1.]) affine[:3, 3] = 1. dm = density_map(streamlines, affine, shape) npt.assert_array_equal(dm, expected) # Shift the image by 2 voxels, ie 4mm affine[:3, 3] -= 4. expected_old = expected new_shape = [i + 2 for i in shape] expected = np.zeros(new_shape) expected[2:, 2:, 2:] = expected_old dm = density_map(streamlines, affine, new_shape) npt.assert_array_equal(dm, expected)
def test_density_map(): # One streamline diagonal in volume streamlines = [np.array([np.arange(10)]*3).T] shape = (10, 10, 10) x = np.arange(10) expected = np.zeros(shape) expected[x, x, x] = 1. dm = density_map(streamlines, vol_dims=shape, voxel_size=(1, 1, 1)) assert_array_equal(dm, expected) # add streamline, make voxel_size smaller. Each streamline should only be # counted once, even if multiple points lie in a voxel streamlines.append(np.ones((5, 3))) shape = (5, 5, 5) x = np.arange(5) expected = np.zeros(shape) expected[x, x, x] = 1. expected[0, 0, 0] += 1 dm = density_map(streamlines, vol_dims=shape, voxel_size=(2, 2, 2)) assert_array_equal(dm, expected) # should work with a generator dm = density_map(iter(streamlines), vol_dims=shape, voxel_size=(2, 2, 2)) assert_array_equal(dm, expected) # Test passing affine affine = np.diag([2, 2, 2, 1.]) affine[:3, 3] = 1. dm = density_map(streamlines, shape, affine=affine) assert_array_equal(dm, expected) # Shift the image by 2 voxels, ie 4mm affine[:3, 3] -= 4. expected_old = expected new_shape = [i + 2 for i in shape] expected = np.zeros(new_shape) expected[2:, 2:, 2:] = expected_old dm = density_map(streamlines, new_shape, affine=affine) assert_array_equal(dm, expected)
def create_tract_mask(trk_file_path, mask_output_path, ref_img_path, hole_closing=0, blob_th=10): """Adapted from https://github.com/MIC-DKFZ/TractSeg/issues/39#issuecomment-496181262 Creates binary mask from streamlines in .trk file. Args: trk_file_path: Path for the .trk file mask_output_path: Path to save the binary mask. ref_img_path: Path to the reference image to get affine and shape hole_closing: Integer for closing the holes. (Default value = 0) blob_th: Threshold for removing small blobs. (Default value = 10) Returns: None """ ref_img = nib.load(ref_img_path) ref_affine = ref_img.affine ref_shape = ref_img.shape streamlines = nib.streamlines.load(trk_file_path).streamlines # Upsample Streamlines (very important, especially when using DensityMap Threshold. Without upsampling eroded # results) print("Upsampling...") print("Nr of points before upsampling " + str(get_number_of_points(streamlines))) max_seq_len = abs(ref_affine[0, 0] / 4) print("max_seq_len: {}".format(max_seq_len)) streamlines = list(utils_trk.subsegment(streamlines, max_seq_len)) print("Nr of points after upsampling " + str(get_number_of_points(streamlines))) # Remember: Does not count if a fibers has no node inside of a voxel -> upsampling helps, but not perfect # Counts the number of unique streamlines that pass through each voxel -> oversampling does not distort result dm = utils_trk.density_map(streamlines, ref_shape, affine=ref_affine) # Create Binary Map dm_binary = dm > 1 # Using higher Threshold problematic, because often very sparse dm_binary_c = dm_binary # Filter Blobs dm_binary_c = remove_small_blobs(dm_binary_c, threshold=blob_th) # Closing of Holes (not ideal because tends to remove valid holes, e.g. in MCP) if hole_closing > 0: size = hole_closing dm_binary_c = ndimage.binary_closing(dm_binary_c, structure=np.ones((size, size, size))).astype(dm_binary.dtype) # Save Binary Mask dm_binary_img = nib.Nifti1Image(dm_binary_c.astype("uint8"), ref_affine) nib.save(dm_binary_img, mask_output_path)
def test_density_map(): #One streamline diagonal in volume streamlines = [np.array([np.arange(10)]*3).T] shape = (10, 10, 10) x = np.arange(10) expected = np.zeros(shape) expected[x, x, x] = 1. dm = density_map(streamlines, vol_dims=shape, voxel_size=(1, 1, 1)) assert_array_equal(dm, expected) #add streamline, make voxel_size smaller. Each streamline should only be #counted once, even if multiple points lie in a voxel streamlines.append(np.ones((5, 3))) shape = (5, 5, 5) x = np.arange(5) expected = np.zeros(shape) expected[x, x, x] = 1. expected[0, 0, 0] += 1 dm = density_map(streamlines, vol_dims=shape, voxel_size=(2, 2, 2)) assert_array_equal(dm, expected) #should work with a generator streamlines = iter(streamlines) dm = density_map(streamlines, vol_dims=shape, voxel_size=(2, 2, 2)) assert_array_equal(dm, expected)
def test_density_map(): #One streamline diagonal in volume streamlines = [np.array([np.arange(10)] * 3).T] shape = (10, 10, 10) x = np.arange(10) expected = np.zeros(shape) expected[x, x, x] = 1. dm = density_map(streamlines, vol_dims=shape, voxel_size=(1, 1, 1)) assert_array_equal(dm, expected) #add streamline, make voxel_size smaller. Each streamline should only be #counted once, even if multiple points lie in a voxel streamlines.append(np.ones((5, 3))) shape = (5, 5, 5) x = np.arange(5) expected = np.zeros(shape) expected[x, x, x] = 1. expected[0, 0, 0] += 1 dm = density_map(streamlines, vol_dims=shape, voxel_size=(2, 2, 2)) assert_array_equal(dm, expected) #should work with a generator streamlines = iter(streamlines) dm = density_map(streamlines, vol_dims=shape, voxel_size=(2, 2, 2)) assert_array_equal(dm, expected)
def gen_dens(tracts, ref_vol, out_dens): print('reading sta streamlines') streams, hdr = trackvis.read(tracts) streamlines = [s[0] for s in streams] print('reading reference volume') niihdr = nib.load(ref_vol) nii = niihdr.get_data() print('generating density map from tracts') dm = utils.density_map(streamlines, affine=niihdr.affine, vol_dims=nii.shape) dm_img = nib.Nifti1Image(dm.astype("uint16"), niihdr.affine) print('saving tracts map') dm_img.to_filename(out_dens)
def fib_density_map(volume, fiber, output): ''' fiber density map :param volume: structure image T1w :param fiber: tck file :param output: density map file :return: ''' shape = volume.shape affine = volume.affine streamstck = fiber.streamlines image_volume = ditu.density_map(streamstck, vol_dims=shape, voxel_size=0.625, affine=affine) dm_img = nib.Nifti1Image(image_volume.astype("int16"), affine) dm_img.to_filename(output)
def _run_interface(self, runtime): from numpy import min_scalar_type from dipy.tracking.utils import density_map import nibabel.trackvis as nbt tracks, header = nbt.read(self.inputs.in_file) streams = ((ii[0]) for ii in tracks) if isdefined(self.inputs.reference): refnii = nb.load(self.inputs.reference) affine = refnii.affine data_dims = refnii.shape[:3] kwargs = dict(affine=affine) else: IFLOGGER.warning( "voxel_dims and data_dims are deprecated as of dipy " "0.7.1. Please use reference input instead") if not isdefined(self.inputs.data_dims): data_dims = header["dim"] else: data_dims = self.inputs.data_dims if not isdefined(self.inputs.voxel_dims): voxel_size = header["voxel_size"] else: voxel_size = self.inputs.voxel_dims affine = header["vox_to_ras"] kwargs = dict(voxel_size=voxel_size) data = density_map(streams, data_dims, **kwargs) data = data.astype(min_scalar_type(data.max())) img = nb.Nifti1Image(data, affine) out_file = op.abspath(self.inputs.out_filename) nb.save(img, out_file) IFLOGGER.info( "Track density map saved as %s, size=%s, dimensions=%s", out_file, img.shape, img.header.get_zooms(), ) return runtime
def tract_to_binary(file_in: str, file_out: str, ref_img_path: str): HOLE_CLOSING = 0 # choose from "trk" or "trk_legacy" # Use "trk_legacy" for zenodo dataset v1.1.0 and below # Use "trk" for zenodo dataset v1.2.0 tracking_format = "trk" ref_img = nib.load(ref_img_path) ref_affine = ref_img.get_affine() ref_shape = ref_img.get_data().shape streams, hdr = trackvis.read(file_in) streamlines = [s[0] for s in streams] # list of 2d ndarrays if tracking_format == "trk_legacy": streams, hdr = trackvis.read(file_in) streamlines = [s[0] for s in streams] else: sl_file = nib.streamlines.load(file_in) streamlines = sl_file.streamlines # Upsample Streamlines (very important, especially when using DensityMap Threshold. Without upsampling eroded results) max_seq_len = abs(ref_affine[0, 0] / 4) streamlines = list(utils_trk.subsegment(streamlines, max_seq_len)) # Remember: Does not count if a fibers has no node inside of a voxel -> upsampling helps, but not perfect # Counts the number of unique streamlines that pass through each voxel -> oversampling does not distort result dm = utils_trk.density_map(streamlines, ref_affine, ref_shape) # Create Binary Map dm_binary = dm > 0 # Using higher Threshold problematic, because tends to remove valid parts (sparse fibers) dm_binary_c = dm_binary # Filter Blobs (might remove valid parts) -> do not use # dm_binary_c = remove_small_blobs(dm_binary_c, threshold=10) # Closing of Holes (not ideal because tends to remove valid holes, e.g. in MCP) -> do not use # size = 1 # dm_binary_c = ndimage.binary_closing(dm_binary_c, structure=np.ones((size, size, size))).astype(dm_binary.dtype) # Save Binary Mask dm_binary_img = nib.Nifti1Image(dm_binary_c.astype("uint8"), ref_affine) nib.save(dm_binary_img, file_out)
def make_sub_cnxn_visitation_map(sub,dpy_file,parc_file,warp_file,ref_file, cnxn_inds_file,cnxn_name,vismap_fstr): overwrite=True import os,h5py,numpy as np,nibabel as nib from dipy.io import Dpy from dipy.tracking.utils import connectivity_matrix,length,density_map from nipype.interfaces.fsl import ApplyWarp F = h5py.File(cnxn_inds_file, 'r') if cnxn_name in F.keys(): stream_idxs = F[cnxn_name].value F.close() D = Dpy(dpy_file, 'r') streams = D.read_tracksi(stream_idxs) D.close() parc_img = nib.load(parc_file) affine = np.eye(4) outfile_nat = os.path.abspath('vismap_nat_%s.nii.gz' %cnxn_name) outfile_std = os.path.abspath(vismap_fstr %cnxn_name) dm = density_map(streams,parc_img.shape,affine=affine) dm_img = nib.Nifti1Image(dm.astype("int16"), parc_img.affine) dm_img.to_filename(outfile_nat) print 'warping cnxn image to standard space' aw = ApplyWarp(in_file=outfile_nat, out_file=outfile_std, ref_file =ref_file, field_file =warp_file) aw.run() else: outfile_std = 'cnxn not found' F.close() return outfile_std
def density_map(tractogram, n_sls=None, to_vox=False, normalize=False): """ Create a streamline density map. based on: https://dipy.org/documentation/1.1.1./examples_built/streamline_formats/ Parameters ---------- tractogram : StatefulTractogram Stateful tractogram whose streamlines are used to make the density map. n_sls : int or None, optional n_sls to randomly select to make the density map. If None, all streamlines are used. Default: None to_vox : bool, optional Whether to put the stateful tractogram in VOX space before making the density map. Default: False normalize : bool, optional Whether to normalize maximum values to 1. Default: False Returns ------- Nifti1Image containing the density map. """ if to_vox: tractogram.to_vox() sls = tractogram.streamlines if n_sls is not None: sls = select_random_set_of_streamlines(sls, n_sls) affine, vol_dims, voxel_sizes, voxel_order = get_reference_info(tractogram) tractogram_density = dtu.density_map(sls, np.eye(4), vol_dims) if normalize: tractogram_density = tractogram_density / tractogram_density.max() nifti_header = create_nifti_header(affine, vol_dims, voxel_sizes) density_map_img = nib.Nifti1Image(tractogram_density, affine, nifti_header) return density_map_img
def computeStats(subjectID,reference,streamlines,classification,outdir): avg_length = [] avg_curv = [] stream_count = [] mean_x = [] mean_y = [] mean_z = [] num_vox = [] tract_index_labels = np.trim_zeros(np.unique(classification['index'].tolist())) qb = QuickBundles(np.inf) for i in tract_index_labels: indices = [ t for t in range(len(classification['index'].tolist())) if classification['index'].tolist()[int(t)] == i ] avg_length.append(np.mean(length(streamlines[indices]))) clusters = qb.cluster(streamlines[indices]) avg_curv.append(mean_curvature(clusters.centroids[0])) orientation = mean_orientation(clusters.centroids[0]) mean_x.append(orientation[0]) mean_y.append(orientation[1]) mean_z.append(orientation[2]) stream_count.append(len(indices)) denmap = density_map(streamlines[indices],reference.affine,reference.shape) num_vox.append(len(denmap[denmap>0])) df = pd.DataFrame([],dtype=object) df['subjectID'] = [ subjectID for f in range(len(classification['names'].tolist())) ] df['structureID'] = [ f for f in classification['names'].tolist() ] df['nodeID'] = [ 1 for f in range(len(df['structureID'])) ] df['streamline_count'] = stream_count df['average_length'] = avg_length df['average_curvature'] = avg_curv df['voxel_count'] = num_vox df['centroid_x'] = mean_x df['centroid_y'] = mean_y df['centroid_z'] = mean_z df.to_csv('%s/output_FiberStats.csv' %outdir,index=False)
def _run_interface(self, runtime): from numpy import min_scalar_type tracks, header = nbt.read(self.inputs.in_file) streams = ((ii[0]) for ii in tracks) if isdefined(self.inputs.reference): refnii = nb.load(self.inputs.reference) affine = refnii.get_affine() data_dims = refnii.get_shape()[:3] kwargs = dict(affine=affine) else: iflogger.warn(('voxel_dims and data_dims are deprecated' 'as of dipy 0.7.1. Please use reference ' 'input instead')) if not isdefined(self.inputs.data_dims): data_dims = header['dim'] else: data_dims = self.inputs.data_dims if not isdefined(self.inputs.voxel_dims): voxel_size = header['voxel_size'] else: voxel_size = self.inputs.voxel_dims affine = header['vox_to_ras'] kwargs = dict(voxel_size=voxel_size) data = density_map(streams, data_dims, **kwargs) data = data.astype(min_scalar_type(data.max())) img = nb.Nifti1Image(data, affine) out_file = op.abspath(self.inputs.out_filename) nb.save(img, out_file) iflogger.info( ('Track density map saved as {i}, size={d}, ' 'dimensions={v}').format( i=out_file, d=img.get_shape(), v=img.get_header().get_zooms())) return runtime
def _run_interface(self, runtime): from numpy import min_scalar_type from dipy.tracking.utils import density_map tracks, header = nbt.read(self.inputs.in_file) streams = ((ii[0]) for ii in tracks) if isdefined(self.inputs.reference): refnii = nb.load(self.inputs.reference) affine = refnii.affine data_dims = refnii.shape[:3] kwargs = dict(affine=affine) else: IFLOGGER.warn( 'voxel_dims and data_dims are deprecated as of dipy 0.7.1. Please use reference ' 'input instead') if not isdefined(self.inputs.data_dims): data_dims = header['dim'] else: data_dims = self.inputs.data_dims if not isdefined(self.inputs.voxel_dims): voxel_size = header['voxel_size'] else: voxel_size = self.inputs.voxel_dims affine = header['vox_to_ras'] kwargs = dict(voxel_size=voxel_size) data = density_map(streams, data_dims, **kwargs) data = data.astype(min_scalar_type(data.max())) img = nb.Nifti1Image(data, affine) out_file = op.abspath(self.inputs.out_filename) nb.save(img, out_file) IFLOGGER.info( 'Track density map saved as %s, size=%s, dimensions=%s', out_file, img.shape, img.header.get_zooms()) return runtime
def fib_density_map(volume, fiber): """ fiber to volume density map Parameters ---------- volume : volume data fiber : streamlines data Return ------ streamline density in each voxel """ shape = volume.shape affine = volume.affine streamlines = fiber voxel_size = nib.affine.voxel_size(affine) image_volume = ditu.density_map(streamlines, vol_dims=shape, voxel_size=voxel_size, affine=affine) return image_volume
def _run_interface(self, runtime): from numpy import min_scalar_type tracks, header = nbt.read(self.inputs.in_file) streams = ((ii[0]) for ii in tracks) if isdefined(self.inputs.reference): refnii = nb.load(self.inputs.reference) affine = refnii.get_affine() data_dims = refnii.get_shape()[:3] kwargs = dict(affine=affine) else: iflogger.warn(('voxel_dims and data_dims are deprecated' 'as of dipy 0.7.1. Please use reference ' 'input instead')) if not isdefined(self.inputs.data_dims): data_dims = header['dim'] else: data_dims = self.inputs.data_dims if not isdefined(self.inputs.voxel_dims): voxel_size = header['voxel_size'] else: voxel_size = self.inputs.voxel_dims affine = header['vox_to_ras'] kwargs = dict(voxel_size=voxel_size) data = density_map(streams, data_dims, **kwargs) data = data.astype(min_scalar_type(data.max())) img = nb.Nifti1Image(data, affine) out_file = op.abspath(self.inputs.out_filename) nb.save(img, out_file) iflogger.info( ('Track density map saved as {i}, size={d}, ' 'dimensions={v}').format(i=out_file, d=img.get_shape(), v=img.get_header().get_zooms())) return runtime
def filter_streamlines(dwi_file, dir_path, gtab, streamlines, life_run, min_length, conn_model, target_samples, node_size, curv_thr_list, step_list): from dipy.tracking import utils from pynets.dmri.track import save_streams, run_LIFE_all dwi_img = nib.load(dwi_file) data = dwi_img.get_fdata() # Flatten streamlines list, and apply min length filter print('Filtering streamlines...') streamlines = nib.streamlines.array_sequence.ArraySequence([s for s in streamlines if len(s) > float(min_length)]) # Fit LiFE model if life_run is True: print('Fitting LiFE...') # Fit Linear Fascicle Evaluation (LiFE) [streamlines, rmse] = run_LIFE_all(data, gtab, streamlines) mean_rmse = np.mean(rmse) print("%s%s" % ('Mean RMSE: ', mean_rmse)) if mean_rmse > 5: print('WARNING: LiFE revealed high model error. Check streamlines output and review tracking parameters used.') # Create density map dm = utils.density_map(streamlines, dwi_img.shape, affine=np.eye(4)) # Save density map dm_img = nib.Nifti1Image(dm.astype('int16'), dwi_img.affine) dm_img.to_filename("%s%s%s%s%s%s%s%s%s%s%s%s" % (dir_path, '/density_map_', conn_model, '_', target_samples, '_', node_size, 'mm_curv', str(curv_thr_list).replace(', ', '_'), '_step', str(step_list).replace(', ', '_'), '.nii.gz')) # Save streamlines to trk streams = "%s%s%s%s%s%s%s%s%s%s%s%s" % (dir_path, '/streamlines_', conn_model, '_', target_samples, '_', node_size, 'mm_curv', str(curv_thr_list).replace(', ', '_'), '_step', str(step_list).replace(', ', '_'), '.trk') streams = save_streams(dwi_img, streamlines, streams) return streams, dir_path
def direct_streamline_norm(streams, fa_path, ap_path, dir_path, track_type, conn_model, subnet, node_radius, dens_thresh, ID, roi, min_span_tree, disp_filt, parc, prune, atlas, labels_im_file, parcellation, labels, coords, norm, binary, atlas_t1w, basedir_path, curv_thr_list, step_list, traversal, min_length, t1w_brain, run_dsn=False): """ A Function to perform normalization of streamlines tracked in native diffusion space to an MNI-space template. Parameters ---------- streams : str File path to save streamline array sequence in .trk format. fa_path : str File path to FA Nifti1Image. ap_path : str File path to the anisotropic power Nifti1Image. dir_path : str Path to directory containing subject derivative data for a given pynets run. track_type : str Tracking algorithm used (e.g. 'local' or 'particle'). conn_model : str Connectivity reconstruction method (e.g. 'csa', 'tensor', 'csd'). subnet : str Resting-state subnet based on Yeo-7 and Yeo-17 naming (e.g. 'Default') used to filter nodes in the study of brain subgraphs. node_radius : int Spherical centroid node size in the case that coordinate-based centroids are used as ROI's for tracking. dens_thresh : bool Indicates whether a target graph density is to be used as the basis for thresholding. ID : str A subject id or other unique identifier. roi : str File path to binarized/boolean region-of-interest Nifti1Image file. min_span_tree : bool Indicates whether local thresholding from the Minimum Spanning Tree should be used. disp_filt : bool Indicates whether local thresholding using a disparity filter and 'backbone subnet' should be used. parc : bool Indicates whether to use parcels instead of coordinates as ROI nodes. prune : bool Indicates whether to prune final graph of disconnected nodes/isolates. atlas : str Name of atlas parcellation used. labels_im_file : str File path to atlas parcellation Nifti1Image aligned to dwi space. parcellation : str File path to atlas parcellation Nifti1Image in MNI template space. labels : list List of string labels corresponding to graph nodes. coords : list List of (x, y, z) tuples corresponding to a coordinate atlas used or which represent the center-of-mass of each parcellation node. norm : int Indicates method of normalizing resulting graph. binary : bool Indicates whether to binarize resulting graph edges to form an unweighted graph. atlas_t1w : str File path to atlas parcellation Nifti1Image in T1w-conformed space. basedir_path : str Path to directory to output direct-streamline normalized temp files and outputs. curv_thr_list : list List of integer curvature thresholds used to perform ensemble tracking. step_list : list List of float step-sizes used to perform ensemble tracking. traversal : str The statistical approach to tracking. Options are: det (deterministic), closest (clos), boot (bootstrapped), and prob (probabilistic). min_length : int Minimum fiber length threshold in mm to restrict tracking. t1w_brain : str File path to the T1w Nifti1Image. Returns ------- streams_warp : str File path to normalized streamline array sequence in .trk format. dir_path : str Path to directory containing subject derivative data for a given pynets run. track_type : str Tracking algorithm used (e.g. 'local' or 'particle'). conn_model : str Connectivity reconstruction method (e.g. 'csa', 'tensor', 'csd'). subnet : str Resting-state subnet based on Yeo-7 and Yeo-17 naming (e.g. 'Default') used to filter nodes in the study of brain subgraphs. node_radius : int Spherical centroid node size in the case that coordinate-based centroids are used as ROI's for tracking. dens_thresh : bool Indicates whether a target graph density is to be used as the basis for thresholding. ID : str A subject id or other unique identifier. roi : str File path to binarized/boolean region-of-interest Nifti1Image file. min_span_tree : bool Indicates whether local thresholding from the Minimum Spanning Tree should be used. disp_filt : bool Indicates whether local thresholding using a disparity filter and 'backbone subnet' should be used. parc : bool Indicates whether to use parcels instead of coordinates as ROI nodes. prune : bool Indicates whether to prune final graph of disconnected nodes/isolates. atlas : str Name of atlas parcellation used. parcellation : str File path to atlas parcellation Nifti1Image in MNI template space. labels : list List of string labels corresponding to graph nodes. coords : list List of (x, y, z) tuples corresponding to a coordinate atlas used or which represent the center-of-mass of each parcellation node. norm : int Indicates method of normalizing resulting graph. binary : bool Indicates whether to binarize resulting graph edges to form an unweighted graph. atlas_for_streams : str File path to atlas parcellation Nifti1Image in the same morphological space as the streamlines. traversal : str The statistical approach to tracking. Options are: det (deterministic), closest (clos), boot (bootstrapped), and prob (probabilistic). warped_fa : str File path to MNI-space warped FA Nifti1Image. min_length : int Minimum fiber length threshold in mm to restrict tracking. References ---------- .. [1] Greene, C., Cieslak, M., & Grafton, S. T. (2017). Effect of different spatial normalization approaches on tractography and structural brain subnets. subnet Neuroscience, 1-19. """ import gc from dipy.tracking.streamline import transform_streamlines from pynets.registration import utils as regutils from pynets.plotting.brain import show_template_bundles import os.path as op from dipy.io.streamline import load_tractogram from dipy.tracking._utils import _mapping_to_voxel from dipy.tracking.utils import density_map from dipy.io.stateful_tractogram import Space, StatefulTractogram, Origin from dipy.io.streamline import save_tractogram if run_dsn is True: dsn_dir = f"{basedir_path}/dmri_reg/DSN" if not op.isdir(dsn_dir): os.mkdir(dsn_dir) namer_dir = f"{dir_path}/tractography" if not op.isdir(namer_dir): os.mkdir(namer_dir) atlas_img = nib.load(labels_im_file) # Run SyN and normalize streamlines fa_img = nib.load(fa_path) atlas_t1w_img = nib.load(atlas_t1w) t1w_brain_img = nib.load(t1w_brain) brain_mask = np.asarray(t1w_brain_img.dataobj).astype("bool") streams_t1w = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ( namer_dir, "/streamlines_t1w_", "%s" % (subnet + "_" if subnet is not None else ""), "%s" % (op.basename(roi).split(".")[0] + "_" if roi is not None else ""), conn_model, "%s" % ("%s%s" % ("_" + str(node_radius), "mm_") if ((node_radius != "parc") and (node_radius is not None)) else "_"), "curv", str(curv_thr_list).replace(", ", "_"), "step", str(step_list).replace(", ", "_"), "tracktype-", track_type, "_traversal-", traversal, "_minlength-", min_length, ".trk", ) density_t1w = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ( namer_dir, "/density_map_t1w_", "%s" % (subnet + "_" if subnet is not None else ""), "%s" % (op.basename(roi).split(".")[0] + "_" if roi is not None else ""), conn_model, "%s" % ("%s%s" % ("_" + str(node_radius), "mm_") if ((node_radius != "parc") and (node_radius is not None)) else "_"), "curv", str(curv_thr_list).replace(", ", "_"), "step", str(step_list).replace(", ", "_"), "tracktype-", track_type, "_traversal-", traversal, "_minlength-", min_length, ".nii.gz", ) streams_warp_png = '/tmp/dsn.png' # SyN FA->Template [mapping, affine_map, warped_fa] = regutils.wm_syn(t1w_brain, ap_path, dsn_dir) tractogram = load_tractogram( streams, fa_img, to_origin=Origin.NIFTI, to_space=Space.VOXMM, bbox_valid_check=False, ) fa_img.uncache() streamlines = tractogram.streamlines warped_fa_img = nib.load(warped_fa) warped_fa_affine = warped_fa_img.affine warped_fa_shape = warped_fa_img.shape streams_in_curr_grid = transform_streamlines(streamlines, affine_map.affine_inv) streams_final_filt = regutils.warp_streamlines(t1w_brain_img.affine, fa_img.affine, mapping, warped_fa_img, streams_in_curr_grid, brain_mask) # Remove streamlines with negative voxel indices lin_T, offset = _mapping_to_voxel(np.eye(4)) streams_final_filt_final = [] for sl in streams_final_filt: inds = np.dot(sl, lin_T) inds += offset if not inds.min().round(decimals=6) < 0: streams_final_filt_final.append(sl) # Save streamlines stf = StatefulTractogram( streams_final_filt_final, reference=t1w_brain_img, space=Space.VOXMM, origin=Origin.NIFTI, ) stf.remove_invalid_streamlines() streams_final_filt_final = stf.streamlines save_tractogram(stf, streams_t1w, bbox_valid_check=True) warped_fa_img.uncache() # DSN QC plotting show_template_bundles(streams_final_filt_final, atlas_t1w, streams_warp_png) nib.save( nib.Nifti1Image( density_map(streams_final_filt_final, affine=np.eye(4), vol_dims=warped_fa_shape), warped_fa_affine, ), density_t1w, ) del ( tractogram, streamlines, stf, streams_final_filt_final, streams_final_filt, streams_in_curr_grid, brain_mask, ) gc.collect() assert len(coords) == len(labels) atlas_for_streams = atlas_t1w else: print( "Skipping Direct Streamline Normalization (DSN). Will proceed to " "define fiber connectivity in native diffusion space...") streams_t1w = streams warped_fa = fa_path atlas_for_streams = labels_im_file return (streams_t1w, dir_path, track_type, conn_model, subnet, node_radius, dens_thresh, ID, roi, min_span_tree, disp_filt, parc, prune, atlas, parcellation, labels, coords, norm, binary, atlas_for_streams, traversal, warped_fa, min_length)
However, the interpretation of streamline counts can be tricky. The relationship between the underlying biology and the streamline counts will depend on several factors, including how the tracking was done, and the correct way to interpret these kinds of connectivity matrices is still an open question in the diffusion imaging literature. The next function we'll demonstrate is ``density_map``. This function allows one to represent the spatial distribution of a track by counting the density of streamlines in each voxel. For example, let's take the track connecting the left and right superior frontal gyrus. """ lr_superiorfrontal_track = grouping[11, 54] shape = labels.shape dm = utils.density_map(lr_superiorfrontal_track, shape, affine=affine) """ Let's save this density map and the streamlines so that they can be visualized together. In order to save the streamlines in a ".trk" file we'll need to move them to "trackvis space", or the representation of streamlines specified by the trackvis Track File format. To do that, we will use tools available in `nibabel <http://nipy.org/nibabel>`_) """ import nibabel as nib from dipy.io.stateful_tractogram import Space, StatefulTractogram from dipy.io.streamline import save_trk # Save density map
However, the interpretation of streamline counts can be tricky. The relationship between the underlying biology and the streamline counts will depend on several factors, including how the tracking was done, and the correct way to interpret these kinds of connectivity matrices is still an open question in the diffusion imaging literature. The next function we'll demonstrate is ``density_map``. This function allows one to represent the spatial distribution of a track by counting the density of streamlines in each voxel. For example, let's take the track connecting the left and right superior frontal gyrus. """ lr_superiorfrontal_track = grouping[11, 54] shape = labels.shape dm = utils.density_map(lr_superiorfrontal_track, shape, affine=affine) """ Let's save this density map and the streamlines so that they can be visualized together. In order to save the streamlines in a ".trk" file we'll need to move them to "trackvis space", or the representation of streamlines specified by the trackvis Track File format. To do that, we will use tools available in `nibabel <http://nipy.org/nibabel>`_) """ import nibabel as nib from dipy.io.streamline import save_trk # Save density map dm_img = nib.Nifti1Image(dm.astype("int16"), hardi_img.affine) dm_img.to_filename("lr-superiorfrontal-dm.nii.gz")
However, the interpretation of streamline counts can be tricky. The relationship between the underlying biology and the streamline counts will depend on several factors, including how the tracking was done, and the correct way to interpret these kinds of connectivity matrices is still an open question in the diffusion imaging literature. The next function we'll demonstrate is ``density_map``. This function allows one to represent the spatial distribution of a track by counting the density of streamlines in each voxel. For example, let's take the track connecting the left and right superior frontal gyrus. """ lr_superiorfrontal_track = grouping[11, 54] shape = labels.shape dm = utils.density_map(lr_superiorfrontal_track, shape, affine=affine) """ Let's save this density map and the streamlines so that they can be visualized together. In order to save the streamlines in a ".trk" file we'll need to move them to "trackvis space", or the representation of streamlines specified by the trackvis Track File format. To do that, we will use tools available in `nibabel <http://nipy.org/nibabel>`_) """ import nibabel as nib from dipy.io.streamline import save_trk # Save density map dm_img = nib.Nifti1Image(dm.astype("int16"), hardi_img.affine)
import pandas as pd import dipy.tracking.utils as ut import random #you'll need to be in the right directory for this to work import WMA_pyFuncs # esablish path to tractogram pathToTestTractogram = '/media/dan/storage/data/exploreTractography/test_1M_1.tck' testTractogram = nib.streamlines.load(pathToTestTractogram) # establish path to reference T1 pathToTestT1 = '/media/dan/storage/data/exploreTractography/T1_in_b0_unifized_GM.nii.gz' testT1 = nib.load(pathToTestT1) # get a mask of the whole brain tract tractMask = ut.density_map(testTractogram.streamlines, testT1.affine, testT1.shape) #extract these as coordinates imgSpaceTractVoxels = np.array(np.where(tractMask)).T subjectSpaceTractCoords = nib.affines.apply_affine(testT1.affine, imgSpaceTractVoxels) # create a dataframe for the output outputDataframe = pd.DataFrame(columns=[ 'dipyTime', 'modifiedTime', 'dipyCount', 'modifiedCount', 'testRadius', 'operation' ]) #arbitrarily set the number of rois per test. #Increasing this beyond 2 isn't that helpful because its highly unlikely that #any given streamline passes though 3 randomly placed spheres roiNumToTest = 2
def fiber_density_map(tracks, template, outdir, basename=None, fiber_ends_only=False, overlay=False, overlay_alpha=None, ext=".png", axes="RAS"): """ Create a density map of fibers or fiber-ends as a Nifti, along with a snap file if requested. Parameters ---------- tracks: str Path to the streamlines. It has to be loadable by nibabel. template: str Path to Nifti defining the space of output density map (e.g. nodif). outdir: str Path to directory where to output. basename: str, default None Filename without extension of output files. By default None, means "fiber_ends_density" if 'fiber_ends_only' is True else "fiber_density". fiber_ends_only: bool, default False If true, create a density map of fiber ends. overlay: bool, default False If True, fibers are overlayed on the template in the PDF snapshot. Otherwise the fiber density is plotted alone. overlay_alpha: float, default None fix the overlay alpha value (0-1). ext: str, default '.png' Snapshot extension, used to specify the output format. axes: str, default 'RAS' orientation of the original axes X, Y, and Z specified with the following convention: L=Left, R=Right, A=Anterion, P=Posterior, I=Inferior, S=Superior. Return ------ fiber_density_map: str Path to the output Nifti density map. fiber_density_snap: str or None Path to the output PDF file, if requested (create_pdf = True). """ # Import here to avoid dependency on dipy & pydcmio for the whole package from dipy.tracking.utils import density_map from pydcmio.plotting.slicer import mosaic # Create the default output file name if basename is None: basename = "fiber_ends_density" if fiber_ends_only else "fiber_density" # Load tracks tracks = nibabel.streamlines.load(tracks) # Load template image: needed to get affine matrix and shape template_im = nibabel.load(template) # If user has requested density of fiber ends: keep only the end points # of the fibers as a 2d-array if fiber_ends_only: streamlines = [arr[[0, -1]] for arr in tracks.streamlines] else: streamlines = tracks.streamlines # Compute fiber count in each voxel density_map_arr = density_map(streamlines=streamlines, vol_dims=template_im.get_shape(), affine=tracks.affine) # Create and write Nifti fiber_density_map = os.path.join(outdir, basename + ".nii.gz") density_map_nii = nibabel.Nifti1Image(density_map_arr.astype("int"), template_im.get_affine()) density_map_nii.to_filename(fiber_density_map) # Swap axes if axes != "RAS": reorient_image(in_file=fiber_density_map, axes=axes, prefix="", output_directory=None, is_direct=False) # If requested use fibers as overlay on the template kwargs = dict(outdir=outdir, title="Density Map", basename=basename, ext=ext, overlay_alpha=overlay_alpha) if overlay: fiber_density_snap = mosaic(impath=template, overlay=fiber_density_map, **kwargs) else: fiber_density_snap = mosaic(impath=fiber_density_map, **kwargs) return fiber_density_map, fiber_density_snap
def create_density_map( fa_img, dir_path, streamlines, conn_model, node_radius, curv_thr_list, step_list, subnet, roi, traversal, min_length, namer_dir, ): """ Create a density map of the list of streamlines. Parameters ---------- fa_img : Nifti1Image Dwi data stored as a Nifti1image object. dir_path : str Path to directory containing subject derivative data for a given pynets run. streamlines : ArraySequence DiPy list/array-like object of streamline points from tractography. conn_model : str Connectivity reconstruction method (e.g. 'csa', 'tensor', 'csd'). node_radius : int Spherical centroid node size in the case that coordinate-based centroids are used as ROI's for tracking. curv_thr_list : list List of integer curvature thresholds used to perform ensemble tracking. step_list : list List of float step-sizes used to perform ensemble tracking. subnet : str Resting-state subnet based on Yeo-7 and Yeo-17 naming (e.g. 'Default') used to filter nodes in the study of brain subgraphs. roi : str File path to binarized/boolean region-of-interest Nifti1Image file. traversal : str The statistical approach to tracking. Options are: det (deterministic), closest (clos), boot (bootstrapped), and prob (probabilistic). min_length : int Minimum fiber length threshold in mm to restrict tracking. Returns ------- streams : str File path to saved streamline array sequence in DTK-compatible trackvis (.trk) format. dir_path : str Path to directory containing subject derivative data for a given pynets run. dm_path : str File path to fiber density map Nifti1Image. """ import os.path as op from dipy.tracking import utils from dipy.tracking._utils import _mapping_to_voxel # Remove streamlines with negative voxel indices lin_T, offset = _mapping_to_voxel(np.eye(4)) streams_filt = [] for sl in streamlines: inds = np.dot(sl, lin_T) inds += offset if not inds.min().round(decimals=6) < 0: streams_filt.append(sl) # Create density map dm = utils.density_map(streams_filt, affine=np.eye(4), vol_dims=fa_img.shape) # Save density map dm_img = nib.Nifti1Image(dm.astype("float32"), fa_img.affine) dm_path = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ( namer_dir, "/density_map_", "%s" % (subnet + "_" if subnet is not None else ""), "%s" % (op.basename(roi).split(".")[0] + "_" if roi is not None else ""), conn_model, "_", "%s" % ("%s%s" % (node_radius, "mm_") if ((node_radius != "parc") and (node_radius is not None)) else "parc_"), "curv-", str(curv_thr_list).replace(", ", "_"), "_step-", str(step_list).replace(", ", "_"), "_traversal-", traversal, "_minlength-", min_length, ".nii.gz", ) dm_img.to_filename(dm_path) del streamlines, streams_filt dm_img.uncache() return dir_path, dm_path
However, the interpretation of streamline counts can be tricky. The relationship between the underlying biology and the streamline counts will depend on several factors, including how the tracking was done, and the correct way to interpret these kinds of connectivity matrices is still an open question in the diffusion imaging literature. The next function we'll demonstrate is ``density_map``. This function allows one to represent the spatial distribution of a track by counting the density of streamlines in each voxel. For example, let's take the track connecting the left and right superior frontal gyrus. """ lr_superiorfrontal_track = grouping[11, 54] shape = labels.shape dm = utils.density_map(lr_superiorfrontal_track, shape, affine=affine) """ Let's save this density map and the streamlines so that they can be visualized together. In order to save the streamlines in a ".trk" file we'll need to move them to "trackvis space", or the representation of streamlines specified by the trackvis Track File format. To do that, we will use tools available in [nibabel](http://nipy.org/nibabel) """ import nibabel as nib # Save density map dm_img = nib.Nifti1Image(dm.astype("int16"), hardi_img.get_affine()) dm_img.to_filename("lr-superiorfrontal-dm.nii.gz")
def save_results(table, area_pairs, medium, subject_name): with open('saved/{}_track_result'.format(subject_name), 'wb') as f: pickle.dump(table, f) streamlines = table['streamlines'] sft = table['sft'] sub_affine = table['affine'] data = table['data'] labels = table["labels"] affine = np.eye(4) # extract streamlines only pass through ROIs cc_slice = (labels == medium[0]) # ROIs if len(medium) > 1: for m in medium: cc_slice = cc_slice | (labels == m) cc_streamlines = utils.target(streamlines, affine, cc_slice) cc_streamlines = Streamlines(cc_streamlines) other_streamlines = utils.target(streamlines, affine, cc_slice, include=False) other_streamlines = Streamlines(other_streamlines) assert len(other_streamlines) + len(cc_streamlines) == len(streamlines) M, grouping = utils.connectivity_matrix(streamlines, affine, labels, return_mapping=True, mapping_as_streamlines=True) M[:3, :] = 0 M[:, :3] = 0 for pair in area_pairs: track = grouping[pair[0], pair[1]] shape = labels.shape dm = utils.density_map(track, affine, shape) import nibabel as nib # Save density map dm_img = nib.Nifti1Image(dm.astype("int16"), sub_affine) dm_img.to_filename("saved/{}_{}.nii.gz".format(subject_name, pair)) # save_trk(sft, "tractogram_probabilistic_dg.trk") # # visualzie # # Enables/disables interactive visualization # interactive = False # if has_fury: # # Prepare the display objects. # color = colormap.line_colors(streamlines) # streamlines_actor = actor.line(streamlines, color) # # Create the 3D display. # r = window.Scene() # r.add(streamlines_actor) # # Save still images for this static example. Or for interactivity use # window.record(r, out_path='fiber_tracking_Caudate_l_r_result.png', size=(800, 800)) # if interactive: # window.show(r)
def label_streamlines_density(streamlines, labels, affine, f_name, img, label_img): """ .. figure:: connectivity.png :align: center **Connectivity of Corpus Callosum** .. include:: ../links_names.inc """ shape = labels.shape dm = utils.density_map(streamlines, shape, affine=affine) sum = 0 count = 0 for i in dm: for j in i: for k in j: if (k != 0): sum = sum + k count += 1 density = sum * 1.0 / count print density """ To do that, we will use tools available in [nibabel](http://nipy.org/nibabel) """ # Save density map dm_img = nib.Nifti1Image(dm.astype("int16"), img.get_affine()) dm_img.to_filename(f_name + "-dm.nii.gz") # Make a trackvis header so we can save streamlines voxel_size = label_img.get_header().get_zooms() trackvis_header = nib.trackvis.empty_header() trackvis_header['voxel_size'] = voxel_size trackvis_header['dim'] = shape trackvis_header['voxel_order'] = "RAS" # Move streamlines to "trackvis space" trackvis_point_space = utils.affine_for_trackvis(voxel_size) lr_sf_trk = utils.move_streamlines(streamlines, trackvis_point_space, input_space=affine) lr_sf_trk = list(lr_sf_trk) """ # Save streamlines for_save = [(sl, None, None) for sl in lr_sf_trk] nib.trackvis.write(f_name+"_label1.trk", for_save, trackvis_header) """ """ import tractconverter as tc density_file = f_name+"_label1.trk" input_format=tc.detect_format(density_file) input=input_format(density_file) output=tc.FORMATS['vtk'].create(density_file+".vtk",input.hdr) tc.convert(input,output) """ """ Let's take a moment here to consider the representation of streamlines used in dipy. Streamlines are a path though the 3d space of an image represented by a set of points. For these points to have a meaningful interpretation, these points must be given in a known coordinate system. The ``affine`` attribute of the ``streamline_generator`` object specifies the coordinate system of the points with respect to the voxel indices of the input data. ``trackvis_point_space`` specifies the trackvis coordinate system with respect to the same indices. The ``move_streamlines`` function returns a new set of streamlines from an existing set of streamlines in the target space. The target space and the input space must be specified as affine transformations with respect to the same reference [#]_. If no input space is given, the input space will be the same as the current representation of the streamlines, in other words the input space is assumed to be ``np.eye(4)``, the 4-by-4 identity matrix. All of the functions above that allow streamlines to interact with volumes take an affine argument. This argument allows these functions to work with streamlines regardless of their coordinate system. For example even though we moved our streamlines to "trackvis space", we can still compute the density map as long as we specify the right coordinate system. """ dm_trackvis = utils.density_map(lr_sf_trk, shape, affine=trackvis_point_space) assert np.all(dm == dm_trackvis) return dm, density """ This means that streamlines can interact with any image volume, for example a high resolution structural image, as long as one can register that image to the diffusion images and calculate the coordinate system with respect to that image. """ """
lpt_sft.to_corner() rpt_sft.to_corner() cc_streamlines_vox = select_random_set_of_streamlines(cc_sft.streamlines, 1000) laf_streamlines_vox = select_random_set_of_streamlines(laf_sft.streamlines, 1000) raf_streamlines_vox = select_random_set_of_streamlines(raf_sft.streamlines, 1000) lpt_streamlines_vox = select_random_set_of_streamlines(lpt_sft.streamlines, 1000) rpt_streamlines_vox = select_random_set_of_streamlines(rpt_sft.streamlines, 1000) # Same dimensions for every stateful tractogram, can be re-use affine, dimensions, voxel_sizes, voxel_order = cc_sft.space_attributes cc_density = density_map(cc_streamlines_vox, np.eye(4), dimensions) laf_density = density_map(laf_streamlines_vox, np.eye(4), dimensions) raf_density = density_map(raf_streamlines_vox, np.eye(4), dimensions) lpt_density = density_map(lpt_streamlines_vox, np.eye(4), dimensions) rpt_density = density_map(rpt_streamlines_vox, np.eye(4), dimensions) """ Replacing streamlines is possible, but if the state was modified between operations such as this one is not recommended: -> cc_sft.streamlines = cc_streamlines_vox It is recommended to re-create a new StatefulTractogram object and explicitly specify in which space the streamlines are. Be careful to follow the order of operations. If the tractogram was from a Trk file with metadata, this will be lost. If you wish to keep metadata while manipulating the number or the order
def label_streamlines_density(streamlines,labels,affine,f_name,img,label_img): """ .. figure:: connectivity.png :align: center **Connectivity of Corpus Callosum** .. include:: ../links_names.inc """ shape = labels.shape dm = utils.density_map(streamlines, shape, affine=affine) sum=0 ;count=0 for i in dm: for j in i: for k in j: if (k != 0): sum=sum+k count += 1 density = sum*1.0/count print density """ To do that, we will use tools available in [nibabel](http://nipy.org/nibabel) """ # Save density map dm_img = nib.Nifti1Image(dm.astype("int16"), img.get_affine()) dm_img.to_filename(f_name+"-dm.nii.gz") # Make a trackvis header so we can save streamlines voxel_size = label_img.get_header().get_zooms() trackvis_header = nib.trackvis.empty_header() trackvis_header['voxel_size'] = voxel_size trackvis_header['dim'] = shape trackvis_header['voxel_order'] = "RAS" # Move streamlines to "trackvis space" trackvis_point_space = utils.affine_for_trackvis(voxel_size) lr_sf_trk = utils.move_streamlines(streamlines, trackvis_point_space, input_space=affine) lr_sf_trk = list(lr_sf_trk) """ # Save streamlines for_save = [(sl, None, None) for sl in lr_sf_trk] nib.trackvis.write(f_name+"_label1.trk", for_save, trackvis_header) """ """ import tractconverter as tc density_file = f_name+"_label1.trk" input_format=tc.detect_format(density_file) input=input_format(density_file) output=tc.FORMATS['vtk'].create(density_file+".vtk",input.hdr) tc.convert(input,output) """ """ Let's take a moment here to consider the representation of streamlines used in dipy. Streamlines are a path though the 3d space of an image represented by a set of points. For these points to have a meaningful interpretation, these points must be given in a known coordinate system. The ``affine`` attribute of the ``streamline_generator`` object specifies the coordinate system of the points with respect to the voxel indices of the input data. ``trackvis_point_space`` specifies the trackvis coordinate system with respect to the same indices. The ``move_streamlines`` function returns a new set of streamlines from an existing set of streamlines in the target space. The target space and the input space must be specified as affine transformations with respect to the same reference [#]_. If no input space is given, the input space will be the same as the current representation of the streamlines, in other words the input space is assumed to be ``np.eye(4)``, the 4-by-4 identity matrix. All of the functions above that allow streamlines to interact with volumes take an affine argument. This argument allows these functions to work with streamlines regardless of their coordinate system. For example even though we moved our streamlines to "trackvis space", we can still compute the density map as long as we specify the right coordinate system. """ dm_trackvis = utils.density_map(lr_sf_trk, shape, affine=trackvis_point_space) assert np.all(dm == dm_trackvis) return dm,density """ This means that streamlines can interact with any image volume, for example a high resolution structural image, as long as one can register that image to the diffusion images and calculate the coordinate system with respect to that image. """ """
def direct_streamline_norm( streams, fa_path, ap_path, dir_path, track_type, target_samples, conn_model, network, node_size, dens_thresh, ID, roi, min_span_tree, disp_filt, parc, prune, atlas, labels_im_file, uatlas, labels, coords, norm, binary, atlas_mni, basedir_path, curv_thr_list, step_list, directget, min_length, error_margin, t1_aligned_mni ): """ A Function to perform normalization of streamlines tracked in native diffusion space to an MNI-space template. Parameters ---------- streams : str File path to save streamline array sequence in .trk format. fa_path : str File path to FA Nifti1Image. ap_path : str File path to the anisotropic power Nifti1Image. dir_path : str Path to directory containing subject derivative data for a given pynets run. track_type : str Tracking algorithm used (e.g. 'local' or 'particle'). target_samples : int Total number of streamline samples specified to generate streams. conn_model : str Connectivity reconstruction method (e.g. 'csa', 'tensor', 'csd'). network : str Resting-state network based on Yeo-7 and Yeo-17 naming (e.g. 'Default') used to filter nodes in the study of brain subgraphs. node_size : int Spherical centroid node size in the case that coordinate-based centroids are used as ROI's for tracking. dens_thresh : bool Indicates whether a target graph density is to be used as the basis for thresholding. ID : str A subject id or other unique identifier. roi : str File path to binarized/boolean region-of-interest Nifti1Image file. min_span_tree : bool Indicates whether local thresholding from the Minimum Spanning Tree should be used. disp_filt : bool Indicates whether local thresholding using a disparity filter and 'backbone network' should be used. parc : bool Indicates whether to use parcels instead of coordinates as ROI nodes. prune : bool Indicates whether to prune final graph of disconnected nodes/isolates. atlas : str Name of atlas parcellation used. labels_im_file : str File path to atlas parcellation Nifti1Image aligned to dwi space. uatlas : str File path to atlas parcellation Nifti1Image in MNI template space. labels : list List of string labels corresponding to graph nodes. coords : list List of (x, y, z) tuples corresponding to a coordinate atlas used or which represent the center-of-mass of each parcellation node. norm : int Indicates method of normalizing resulting graph. binary : bool Indicates whether to binarize resulting graph edges to form an unweighted graph. atlas_mni : str File path to atlas parcellation Nifti1Image in T1w-warped MNI space. basedir_path : str Path to directory to output direct-streamline normalized temp files and outputs. curv_thr_list : list List of integer curvature thresholds used to perform ensemble tracking. step_list : list List of float step-sizes used to perform ensemble tracking. directget : str The statistical approach to tracking. Options are: det (deterministic), closest (clos), boot (bootstrapped), and prob (probabilistic). min_length : int Minimum fiber length threshold in mm to restrict tracking. t1_aligned_mni : str File path to the T1w Nifti1Image in template MNI space. Returns ------- streams_warp : str File path to normalized streamline array sequence in .trk format. dir_path : str Path to directory containing subject derivative data for a given pynets run. track_type : str Tracking algorithm used (e.g. 'local' or 'particle'). target_samples : int Total number of streamline samples specified to generate streams. conn_model : str Connectivity reconstruction method (e.g. 'csa', 'tensor', 'csd'). network : str Resting-state network based on Yeo-7 and Yeo-17 naming (e.g. 'Default') used to filter nodes in the study of brain subgraphs. node_size : int Spherical centroid node size in the case that coordinate-based centroids are used as ROI's for tracking. dens_thresh : bool Indicates whether a target graph density is to be used as the basis for thresholding. ID : str A subject id or other unique identifier. roi : str File path to binarized/boolean region-of-interest Nifti1Image file. min_span_tree : bool Indicates whether local thresholding from the Minimum Spanning Tree should be used. disp_filt : bool Indicates whether local thresholding using a disparity filter and 'backbone network' should be used. parc : bool Indicates whether to use parcels instead of coordinates as ROI nodes. prune : bool Indicates whether to prune final graph of disconnected nodes/isolates. atlas : str Name of atlas parcellation used. uatlas : str File path to atlas parcellation Nifti1Image in MNI template space. labels : list List of string labels corresponding to graph nodes. coords : list List of (x, y, z) tuples corresponding to a coordinate atlas used or which represent the center-of-mass of each parcellation node. norm : int Indicates method of normalizing resulting graph. binary : bool Indicates whether to binarize resulting graph edges to form an unweighted graph. atlas_mni : str File path to atlas parcellation Nifti1Image in T1w-warped MNI space. directget : str The statistical approach to tracking. Options are: det (deterministic), closest (clos), boot (bootstrapped), and prob (probabilistic). warped_fa : str File path to MNI-space warped FA Nifti1Image. min_length : int Minimum fiber length threshold in mm to restrict tracking. References ---------- .. [1] Greene, C., Cieslak, M., & Grafton, S. T. (2017). Effect of different spatial normalization approaches on tractography and structural brain networks. Network Neuroscience, 1-19. """ import sys import gc from dipy.tracking.streamline import transform_streamlines from pynets.registration import reg_utils as regutils # from pynets.plotting import plot_gen import pkg_resources import yaml import os.path as op from pynets.registration.reg_utils import vdc from nilearn.image import resample_to_img from dipy.io.streamline import load_tractogram from dipy.tracking import utils from dipy.tracking._utils import _mapping_to_voxel from dipy.io.stateful_tractogram import Space, StatefulTractogram, Origin from dipy.io.streamline import save_tractogram # from pynets.core.utils import missing_elements with open( pkg_resources.resource_filename("pynets", "runconfig.yaml"), "r" ) as stream: try: hardcoded_params = yaml.load(stream) run_dsn = hardcoded_params['tracking']["DSN"][0] except FileNotFoundError as e: import sys print(e, "Failed to parse runconfig.yaml") exit(1) stream.close() if run_dsn is True: dsn_dir = f"{basedir_path}/dmri_reg/DSN" if not op.isdir(dsn_dir): os.mkdir(dsn_dir) namer_dir = f"{dir_path}/tractography" if not op.isdir(namer_dir): os.mkdir(namer_dir) atlas_img = nib.load(labels_im_file) # Run SyN and normalize streamlines fa_img = nib.load(fa_path) vox_size = fa_img.header.get_zooms()[0] template_path = pkg_resources.resource_filename( "pynets", f"templates/FA_{int(vox_size)}mm.nii.gz" ) if sys.platform.startswith('win') is False: try: template_img = nib.load(template_path) except indexed_gzip.ZranError as e: print(e, f"\nCannot load FA template. Do you have git-lfs " f"installed?") sys.exit(1) else: try: template_img = nib.load(template_path) except ImportError as e: print(e, f"\nCannot load FA template. Do you have git-lfs " f"installed?") sys.exit(1) uatlas_mni_img = nib.load(atlas_mni) t1_aligned_mni_img = nib.load(t1_aligned_mni) brain_mask = np.asarray(t1_aligned_mni_img.dataobj).astype("bool") streams_mni = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ( namer_dir, "/streamlines_mni_", "%s" % (network + "_" if network is not None else ""), "%s" % (op.basename(roi).split(".")[0] + "_" if roi is not None else ""), conn_model, "_", target_samples, "%s" % ( "%s%s" % ("_" + str(node_size), "mm_") if ((node_size != "parc") and (node_size is not None)) else "_" ), "curv", str(curv_thr_list).replace(", ", "_"), "step", str(step_list).replace(", ", "_"), "tracktype-", track_type, "_directget-", directget, "_minlength-", min_length, "_tol-", error_margin, ".trk", ) density_mni = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ( namer_dir, "/density_map_mni_", "%s" % (network + "_" if network is not None else ""), "%s" % (op.basename(roi).split(".")[0] + "_" if roi is not None else ""), conn_model, "_", target_samples, "%s" % ( "%s%s" % ("_" + str(node_size), "mm_") if ((node_size != "parc") and (node_size is not None)) else "_" ), "curv", str(curv_thr_list).replace(", ", "_"), "step", str(step_list).replace(", ", "_"), "tracktype-", track_type, "_directget-", directget, "_minlength-", min_length, "_tol-", error_margin, ".nii.gz", ) # streams_warp_png = '/tmp/dsn.png' # SyN FA->Template [mapping, affine_map, warped_fa] = regutils.wm_syn( template_path, fa_path, t1_aligned_mni, ap_path, dsn_dir ) tractogram = load_tractogram( streams, fa_img, to_origin=Origin.NIFTI, to_space=Space.VOXMM, bbox_valid_check=False, ) fa_img.uncache() streamlines = tractogram.streamlines warped_fa_img = nib.load(warped_fa) warped_fa_affine = warped_fa_img.affine warped_fa_shape = warped_fa_img.shape streams_in_curr_grid = transform_streamlines( streamlines, warped_fa_affine) # Create isocenter mapping where we anchor the origin transformation # affine to the corner of the FOV by scaling x, y, z offsets according # to a multiplicative van der Corput sequence with a base value equal # to the voxel resolution [x_mul, y_mul, z_mul] = [vdc(i, vox_size) for i in range(1, 4)] ref_grid_aff = vox_size * np.eye(4) ref_grid_aff[3][3] = 1 streams_final_filt = [] i = 0 # Test for various types of voxel-grid configurations combs = [(-x_mul, -y_mul, -z_mul), (-x_mul, -y_mul, z_mul), (-x_mul, y_mul, -z_mul), (x_mul, -y_mul, -z_mul), (x_mul, y_mul, z_mul)] while len(streams_final_filt)/len(streams_in_curr_grid) < 0.90: print(f"Warping streamlines to MNI space. Attempt {i}...") print(len(streams_final_filt)/len(streams_in_curr_grid)) adjusted_affine = affine_map.affine.copy() if i > len(combs) - 1: raise ValueError('DSN failed. Header orientation ' 'information may be corrupted. ' 'Is your dataset oblique?') adjusted_affine[0][3] = adjusted_affine[0][3] * combs[i][0] adjusted_affine[1][3] = adjusted_affine[1][3] * combs[i][1] adjusted_affine[2][3] = adjusted_affine[2][3] * combs[i][2] streams_final_filt = regutils.warp_streamlines(adjusted_affine, ref_grid_aff, mapping, warped_fa_img, streams_in_curr_grid, brain_mask) i += 1 # Remove streamlines with negative voxel indices lin_T, offset = _mapping_to_voxel(np.eye(4)) streams_final_filt_final = [] for sl in streams_final_filt: inds = np.dot(sl, lin_T) inds += offset if not inds.min().round(decimals=6) < 0: streams_final_filt_final.append(sl) # Save streamlines stf = StatefulTractogram( streams_final_filt_final, reference=uatlas_mni_img, space=Space.VOXMM, origin=Origin.NIFTI, ) stf.remove_invalid_streamlines() streams_final_filt_final = stf.streamlines save_tractogram(stf, streams_mni, bbox_valid_check=True) warped_fa_img.uncache() # DSN QC plotting # plot_gen.show_template_bundles(streams_final_filt_final, atlas_mni, # streams_warp_png) plot_gen.show_template_bundles(streamlines, # fa_path, streams_warp_png) # Create and save MNI density map nib.save( nib.Nifti1Image( utils.density_map( streams_final_filt_final, affine=np.eye(4), vol_dims=warped_fa_shape), warped_fa_affine, ), density_mni, ) # Map parcellation from native space back to MNI-space and create an # 'uncertainty-union' parcellation with original mni-space uatlas warped_uatlas = affine_map.transform_inverse( mapping.transform( np.asarray(atlas_img.dataobj).astype("int"), interpolation="nearestneighbour", ), interp="nearest", ) atlas_img.uncache() warped_uatlas_img_res_data = np.asarray( resample_to_img( nib.Nifti1Image(warped_uatlas, affine=warped_fa_affine), uatlas_mni_img, interpolation="nearest", clip=False, ).dataobj ) uatlas_mni_data = np.asarray(uatlas_mni_img.dataobj) uatlas_mni_img.uncache() overlap_mask = np.invert( warped_uatlas_img_res_data.astype("bool") * uatlas_mni_data.astype("bool")) os.makedirs(f"{dir_path}/parcellations", exist_ok=True) atlas_mni = f"{dir_path}/parcellations/" \ f"{op.basename(uatlas).split('.nii')[0]}_liberal.nii.gz" nib.save( nib.Nifti1Image( warped_uatlas_img_res_data * overlap_mask.astype("int") + uatlas_mni_data * overlap_mask.astype("int") + np.invert(overlap_mask).astype("int") * warped_uatlas_img_res_data.astype("int"), affine=uatlas_mni_img.affine, ), atlas_mni, ) del ( tractogram, streamlines, warped_uatlas_img_res_data, uatlas_mni_data, overlap_mask, stf, streams_final_filt_final, streams_final_filt, streams_in_curr_grid, brain_mask, ) gc.collect() assert len(coords) == len(labels) else: print( "Skipping Direct Streamline Normalization (DSN). Will proceed to " "define fiber connectivity in native diffusion space...") streams_mni = streams warped_fa = fa_path atlas_mni = labels_im_file return ( streams_mni, dir_path, track_type, target_samples, conn_model, network, node_size, dens_thresh, ID, roi, min_span_tree, disp_filt, parc, prune, atlas, uatlas, labels, coords, norm, binary, atlas_mni, directget, warped_fa, min_length, error_margin )
However, the interpretation of streamline counts can be tricky. The relationship between the underlying biology and the streamline counts will depend on several factors, including how the tracking was done, and the correct way to interpret these kinds of connectivity matrices is still an open question in the diffusion imaging literature. The next function we'll demonstrate is ``density_map``. This function allows one to represent the spatial distribution of a track by counting the density of streamlines in each voxel. For example, let's take the track connecting the left and right superior frontal gyrus. """ lr_superiorfrontal_track = grouping[11, 54] shape = labels.shape dm = utils.density_map(lr_superiorfrontal_track, shape, affine=affine) """ Let's save this density map and the streamlines so that they can be visualized together. In order to save the streamlines in a ".trk" file we'll need to move them to "trackvis space", or the representation of streamlines specified by the trackvis Track File format. To do that, we will use tools available in [nibabel](http://nipy.org/nibabel) """ import nibabel as nib # Save density map dm_img = nib.Nifti1Image(dm.astype("int16"), hardi_img.affine) dm_img.to_filename("lr-superiorfrontal-dm.nii.gz")
streamlines = [s[0] for s in streams] # list of 2d ndarrays if tracking_format == "trk_legacy": streams, hdr = trackvis.read(file_in) streamlines = [s[0] for s in streams] else: sl_file = nib.streamlines.load(file_in) streamlines = sl_file.streamlines #Upsample Streamlines (very important, especially when using DensityMap Threshold. Without upsampling eroded results) max_seq_len = abs(ref_affine[0, 0] / 4) streamlines = list(utils_trk.subsegment(streamlines, max_seq_len)) # Remember: Does not count if a fibers has no node inside of a voxel -> upsampling helps, but not perfect # Counts the number of unique streamlines that pass through each voxel -> oversampling does not distort result dm = utils_trk.density_map(streamlines, ref_shape, affine=ref_affine) # Create Binary Map dm_binary = dm > 0 # Using higher Threshold problematic, because tends to remove valid parts (sparse fibers) dm_binary_c = dm_binary #Filter Blobs (might remove valid parts) -> do not use #dm_binary_c = remove_small_blobs(dm_binary_c, threshold=10) #Closing of Holes (not ideal because tends to remove valid holes, e.g. in MCP) -> do not use # size = 1 # dm_binary_c = ndimage.binary_closing(dm_binary_c, structure=np.ones((size, size, size))).astype(dm_binary.dtype) #Save Binary Mask dm_binary_img = nib.Nifti1Image(dm_binary_c.astype("uint8"), ref_affine) nib.save(dm_binary_img, file_out)
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 create_density_map(dwi_img, dir_path, streamlines, conn_model, target_samples, node_size, curv_thr_list, step_list, network, roi, directget, max_length): """ Create a density map of the list of streamlines. Parameters ---------- dwi_img : Nifti1Image Dwi data stored as a Nifti1image object. dir_path : str Path to directory containing subject derivative data for a given pynets run. streamlines : ArraySequence DiPy list/array-like object of streamline points from tractography. conn_model : str Connectivity reconstruction method (e.g. 'csa', 'tensor', 'csd'). target_samples : int Total number of streamline samples specified to generate streams. node_size : int Spherical centroid node size in the case that coordinate-based centroids are used as ROI's for tracking. curv_thr_list : list List of integer curvature thresholds used to perform ensemble tracking. step_list : list List of float step-sizes used to perform ensemble tracking. network : str Resting-state network based on Yeo-7 and Yeo-17 naming (e.g. 'Default') used to filter nodes in the study of brain subgraphs. roi : str File path to binarized/boolean region-of-interest Nifti1Image file. directget : str The statistical approach to tracking. Options are: det (deterministic), closest (clos), boot (bootstrapped), and prob (probabilistic). max_length : int Maximum fiber length threshold in mm to restrict tracking. Returns ------- streams : str File path to saved streamline array sequence in DTK-compatible trackvis (.trk) format. dir_path : str Path to directory containing subject derivative data for a given pynets run. dm_path : str File path to fiber density map Nifti1Image. """ import os import os.path as op from dipy.tracking import utils from dipy.io.stateful_tractogram import Space, StatefulTractogram, Origin from dipy.io.streamline import save_tractogram # Create density map dm = utils.density_map(streamlines, affine=np.eye(4), vol_dims=dwi_img.shape) # Save density map dm_img = nib.Nifti1Image(dm.astype('int'), dwi_img.affine) namer_dir = '{}/tractography'.format(dir_path) if not os.path.isdir(namer_dir): os.mkdir(namer_dir) dm_path = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ( namer_dir, '/density_map_', '%s' % (network + '_' if network is not None else ''), '%s' % (op.basename(roi).split('.')[0] + '_' if roi is not None else ''), conn_model, '_', target_samples, '_', '%s' % ("%s%s" % (node_size, 'mm_') if ((node_size != 'parc') and (node_size is not None)) else 'parc_'), 'curv-', str(curv_thr_list).replace( ', ', '_'), '_step-', str(step_list).replace( ', ', '_'), '_dg-', directget, '_ml-', max_length, '.nii.gz') dm_img.to_filename(dm_path) # Save streamlines to trk streams = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ( namer_dir, '/streamlines_', '%s' % (network + '_' if network is not None else ''), '%s' % (op.basename(roi).split('.')[0] + '_' if roi is not None else ''), conn_model, '_', target_samples, '_', '%s' % ("%s%s" % (node_size, 'mm_') if ((node_size != 'parc') and (node_size is not None)) else 'parc_'), 'curv-', str(curv_thr_list).replace( ', ', '_'), '_step-', str(step_list).replace( ', ', '_'), '_dg-', directget, '_ml-', max_length, '.trk') save_tractogram(StatefulTractogram(streamlines, reference=dwi_img, space=Space.RASMM, origin=Origin.TRACKVIS), streams, bbox_valid_check=False) del streamlines dm_img.uncache() return streams, dir_path, dm_path
def create_density_map( dwi_img, dir_path, streamlines, conn_model, target_samples, node_size, curv_thr_list, step_list, network, roi, directget, min_length, namer_dir, ): """ Create a density map of the list of streamlines. Parameters ---------- dwi_img : Nifti1Image Dwi data stored as a Nifti1image object. dir_path : str Path to directory containing subject derivative data for a given pynets run. streamlines : ArraySequence DiPy list/array-like object of streamline points from tractography. conn_model : str Connectivity reconstruction method (e.g. 'csa', 'tensor', 'csd'). target_samples : int Total number of streamline samples specified to generate streams. node_size : int Spherical centroid node size in the case that coordinate-based centroids are used as ROI's for tracking. curv_thr_list : list List of integer curvature thresholds used to perform ensemble tracking. step_list : list List of float step-sizes used to perform ensemble tracking. network : str Resting-state network based on Yeo-7 and Yeo-17 naming (e.g. 'Default') used to filter nodes in the study of brain subgraphs. roi : str File path to binarized/boolean region-of-interest Nifti1Image file. directget : str The statistical approach to tracking. Options are: det (deterministic), closest (clos), boot (bootstrapped), and prob (probabilistic). min_length : int Minimum fiber length threshold in mm to restrict tracking. Returns ------- streams : str File path to saved streamline array sequence in DTK-compatible trackvis (.trk) format. dir_path : str Path to directory containing subject derivative data for a given pynets run. dm_path : str File path to fiber density map Nifti1Image. """ import os.path as op from dipy.tracking import utils # Create density map dm = utils.density_map(streamlines, affine=np.eye(4), vol_dims=dwi_img.shape) # Save density map dm_img = nib.Nifti1Image(dm.astype("float32"), dwi_img.affine) dm_path = "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" % ( namer_dir, "/density_map_", "%s" % (network + "_" if network is not None else ""), "%s" % (op.basename(roi).split(".")[0] + "_" if roi is not None else ""), conn_model, "_", target_samples, "_", "%s" % ("%s%s" % (node_size, "mm_") if ((node_size != "parc") and (node_size is not None)) else "parc_"), "curv-", str(curv_thr_list).replace(", ", "_"), "_step-", str(step_list).replace(", ", "_"), "_directget-", directget, "_minlength-", min_length, ".nii.gz", ) dm_img.to_filename(dm_path) del streamlines dm_img.uncache() return dir_path, dm_path
def _run_interface(self, runtime): # Loading the ROI file from dipy.tracking import utils import nibabel as nib import numpy as np import os img = nib.load(self.inputs.ROI_file) data = img.get_data() affine = img.get_affine() # Getting ROI volumes if they haven't been generated if not os.path.isfile('/imaging/jb07/CALM/DWI/FA_connectome/Atlas_volumes.csv'): import nibabel as nib import numpy as np import os import pandas as pd import subprocess atlas_file = ROI_file img = nib.load(atlas_file) data = img.get_data() affine = img.get_affine() volumes = pd.DataFrame() atlas_labels = np.unique(data) for atlas_label in atlas_labels: data = nib.load(atlas_file).get_data() data[data != atlas_label] = 0 data[data == atlas_label] = 1 nib.save(nib.Nifti1Image(data, affine), 'temp.nii.gz') volumes.set_value(atlas_label, 'volume', subprocess.check_output(os.environ['FSLDIR'] + '/bin/fslstats temp.nii.gz -V', shell=True).split(' ')[0]) os.remove('temp.nii.gz') volumes.to_csv('/imaging/jb07/CALM/DWI/FA_connectome/Atlas_volumes.csv') ROI_volumes = pd.read_csv('/home/jb07/CALM/DWI/FA_connectome/Atlas_volumes.csv') # Getting the FA file img = nib.load(self.inputs.FA_file) FA_data = img.get_data() FA_affine = img.get_affine() # Loading the streamlines from nibabel import trackvis streams, hdr = trackvis.read(self.inputs.trackfile,points_space='rasmm') streamlines = [s[0] for s in streams] streamlines_affine = trackvis.aff_from_hdr(hdr,atleast_v2=True) # Checking for negative values from dipy.tracking._utils import _mapping_to_voxel, _to_voxel_coordinates endpoints = [sl[0::len(sl)-1] for sl in streamlines] lin_T, offset = _mapping_to_voxel(affine, (1.,1.,1.)) inds = np.dot(endpoints, lin_T) inds += offset negative_values = np.where(inds <0)[0] for negative_value in sorted(negative_values, reverse=True): del streamlines[negative_value] # Constructing the streamlines matrix matrix,mapping = utils.connectivity_matrix(streamlines=streamlines,label_volume=data,affine=streamlines_affine,symmetric=True,return_mapping=True,mapping_as_streamlines=True) matrix[matrix < 10] = 0 # Constructing the FA matrix dimensions = matrix.shape FA_matrix = np.empty(shape=dimensions) density_matrix = np.empty(shape=dimensions) density_corrected_matrix = np.empty(shape=dimensions) for i in range(0,dimensions[0]): for j in range(0,dimensions[1]): if matrix[i,j]: dm = utils.density_map(mapping[i,j], FA_data.shape, affine=streamlines_affine) FA_matrix[i,j] = np.mean(FA_data[dm>0]) if np.sum(dm > 0) > 0: density_matrix[i,j] = np.sum(dm[dm > 0]) density_corrected_matrix[i,j] = np.sum(dm[dm > 0])/np.sum([ROI_volumes.iloc[i].values.astype('int'), ROI_volumes.iloc[j].values.astype('int')]) else: density_matrix[i,j] = 0 density_corrected_matrix[i,j] = 0 else: FA_matrix[i,j] = 0 density_matrix[i,j] = 0 density_corrected_matrix[i,j] = 0 FA_matrix[np.tril_indices(n=len(FA_matrix))] = 0 FA_matrix = FA_matrix.T + FA_matrix - np.diagonal(FA_matrix) density_matrix[np.tril_indices(n=len(density_matrix))] = 0 density_matrix = density_matrix.T + density_matrix - np.diagonal(density_matrix) density_corrected_matrix[np.tril_indices(n=len(density_corrected_matrix))] = 0 density_corrected_matrix = density_corrected_matrix.T + density_corrected_matrix - np.diagonal(density_corrected_matrix) from nipype.utils.filemanip import split_filename _, base, _ = split_filename(self.inputs.trackfile) np.savetxt(base + '_FA_matrix.txt',FA_matrix,delimiter='\t') np.savetxt(base + '_density_matrix.txt',density_matrix,delimiter='\t') np.savetxt(base + '_volume_corrected_density_matrix.txt',density_corrected_matrix,delimiter='\t')
from dipy.tracking.vox2track import track_counts from dipy.tracking.utils import density_map import nibabel as nib from nibabel.trackvis import write, empty_header grid = np.mgrid[1.1:1.8:3j,1.1:1.8:3j,.5:5] grid = np.rollaxis(grid, 0, 4) streamlines = [] for ii in grid: for jj in ii: streamlines.append(jj) #Treat these streamlines as if they are in trackvis format and generate counts counts_trackvis = density_map(streamlines, (4,4,5), (1,1,1)) #Treat these streamlines as if they are in nifti format and generate counts counts_nifti = track_counts(streamlines, (4,4,5), (1,1,1), return_elements=False) print("saving trk files and track_count volumes") aff = np.eye(4) aff[0, 0] = -1 img = nib.Nifti1Image(counts_trackvis.astype('int16'), aff) nib.save(img, 'counts_trackvis.nii.gz') img = nib.Nifti1Image(counts_nifti.astype('int16'), aff) nib.save(img, 'counts_nifti.nii.gz') hdr = empty_header() hdr['voxel_size'] = (1,1,1)