def voxel2streamline(streamline, affine, unique_idx=None): """ Maps voxels to streamlines and streamlines to voxels, for setting up the LiFE equations matrix Parameters ---------- streamline : list A collection of streamlines, each n by 3, with n being the number of nodes in the fiber. affine : array_like (4, 4) The mapping from voxel coordinates to streamline points. The voxel_to_rasmm matrix, typically from a NIFTI file. unique_idx : array (optional). The unique indices in the streamlines Returns ------- v2f, v2fn : tuple of dicts The first dict in the tuple answers the question: Given a voxel (from the unique indices in this model), which fibers pass through it? The second answers the question: Given a streamline, for each voxel that this streamline passes through, which nodes of that streamline are in that voxel? """ transformed_streamline = transform_streamlines(streamline, affine) if unique_idx is None: all_coords = np.concatenate(transformed_streamline) unique_idx = unique_rows(np.round(all_coords)) return _voxel2streamline(transformed_streamline, unique_idx.astype(np.intp))
def test_unique_rows(): """ Testing the function unique_coords """ arr = np.array([[1, 2, 3], [1, 2, 3], [2, 3, 4], [3, 4, 5]]) arr_w_unique = np.array([[1, 2, 3], [2, 3, 4], [3, 4, 5]]) assert_array_equal(unique_rows(arr), arr_w_unique) # Should preserve order: arr = np.array([[2, 3, 4], [1, 2, 3], [1, 2, 3], [3, 4, 5]]) arr_w_unique = np.array([[2, 3, 4], [1, 2, 3], [3, 4, 5]]) assert_array_equal(unique_rows(arr), arr_w_unique) # Should work even with longer arrays: arr = np.array([[2, 3, 4], [1, 2, 3], [1, 2, 3], [3, 4, 5], [6, 7, 8], [0, 1, 0], [1, 0, 1]]) arr_w_unique = np.array([[2, 3, 4], [1, 2, 3], [3, 4, 5], [6, 7, 8], [0, 1, 0], [1, 0, 1]]) assert_array_equal(unique_rows(arr), arr_w_unique)
def test_unique_rows(): """ Testing the function unique_coords """ arr = np.array([[1, 2, 3], [1, 2, 3], [2, 3, 4], [3, 4, 5]]) arr_w_unique = np.array([[1, 2, 3], [2, 3, 4], [3, 4, 5]]) npt.assert_array_equal(unique_rows(arr), arr_w_unique) # Should preserve order: arr = np.array([[2, 3, 4], [1, 2, 3], [1, 2, 3], [3, 4, 5]]) arr_w_unique = np.array([[2, 3, 4], [1, 2, 3], [3, 4, 5]]) npt.assert_array_equal(unique_rows(arr), arr_w_unique) # Should work even with longer arrays: arr = np.array([[2, 3, 4], [1, 2, 3], [1, 2, 3], [3, 4, 5], [6, 7, 8], [0, 1, 0], [1, 0, 1]]) arr_w_unique = np.array([[2, 3, 4], [1, 2, 3], [3, 4, 5], [6, 7, 8], [0, 1, 0], [1, 0, 1]]) npt.assert_array_equal(unique_rows(arr), arr_w_unique)
def voxel2streamline(streamline, transformed=False, affine=None, unique_idx=None): """ Maps voxels to streamlines and streamlines to voxels, for setting up the LiFE equations matrix Parameters ---------- streamline : list A collection of streamlines, each n by 3, with n being the number of nodes in the fiber. affine : 4 by 4 array (optional) Defines the spatial transformation from streamline to data. Default: np.eye(4) transformed : bool (optional) Whether the streamlines have been already transformed (in which case they don't need to be transformed in here). unique_idx : array (optional). The unique indices in the streamlines Returns ------- v2f, v2fn : tuple of dicts The first dict in the tuple answers the question: Given a voxel (from the unique indices in this model), which fibers pass through it? The second answers the question: Given a streamline, for each voxel that this streamline passes through, which nodes of that streamline are in that voxel? """ if transformed: transformed_streamline = streamline else: if affine is None: affine = np.eye(4) transformed_streamline = transform_streamlines(streamline, affine) if unique_idx is None: all_coords = np.concatenate(transformed_streamline) unique_idx = unique_rows(all_coords.astype(int)) else: unique_idx = unique_idx return _voxel2streamline(transformed_streamline, unique_idx)
def voxel2streamline(streamline, transformed=False, affine=None, unique_idx=None): """ Maps voxels to streamlines and streamlines to voxels, for setting up the LiFE equations matrix Parameters ---------- streamline : list A collection of streamlines, each n by 3, with n being the number of nodes in the fiber. affine : 4 by 4 array (optional) Defines the spatial transformation from streamline to data. Default: np.eye(4) transformed : bool (optional) Whether the streamlines have been already transformed (in which case they don't need to be transformed in here). unique_idx : array (optional). The unique indices in the streamlines Returns ------- v2f, v2fn : tuple of arrays The first array in the tuple answers the question: Given a voxel (from the unique indices in this model), which fibers pass through it? Shape: (n_voxels, n_fibers). The second answers the question: Given a voxel, for each fiber, which nodes are in that voxel? Shape: (n_voxels, max(n_nodes per fiber)). """ if transformed: transformed_streamline = streamline else: if affine is None: affine = np.eye(4) transformed_streamline = transform_streamlines(streamline, affine) if unique_idx is None: all_coords = np.concatenate(transformed_streamline) unique_idx = unique_rows(all_coords.astype(int)) else: unique_idx = unique_idx return _voxel2streamline(transformed_streamline, unique_idx)
def reduct(streamlines, data): new_streamlines = [] for i in range(len(streamlines)): line = streamlines[i] line = np.round(line).astype(np.intp) line = unique_rows(line) flag = 0 if line.shape[0] > 1: for j in range(line.shape[0]): #print(line[j, :]) if data[line[j, 0], line[j, 1], line[j, 2]].any() == 0: flag = 1 break if flag == 0: new_streamlines.append(line) return new_streamlines
def setup(self, streamline, affine, evals=[0.001, 0, 0], sphere=None): """ Set up the necessary components for the LiFE model: the matrix of fiber-contributions to the DWI signal, and the coordinates of voxels for which the equations will be solved Parameters ---------- streamline : list Streamlines, each is an array of shape (n, 3) affine : array_like (4, 4) The mapping from voxel coordinates to streamline points. The voxel_to_rasmm matrix, typically from a NIFTI file. evals : list (3 items, optional) The eigenvalues of the canonical tensor used as a response function. Default:[0.001, 0, 0]. sphere: `dipy.core.Sphere` instance. Whether to approximate (and cache) the signal on a discrete sphere. This may confer a significant speed-up in setting up the problem, but is not as accurate. If `False`, we use the exact gradients along the streamlines to calculate the matrix, instead of an approximation. Defaults to use the 724-vertex symmetric sphere from :mod:`dipy.data` """ if sphere is not False: SignalMaker = LifeSignalMaker(self.gtab, evals=evals, sphere=sphere) streamline = transform_streamlines(streamline, affine) # Assign some local variables, for shorthand: all_coords = np.concatenate(streamline) vox_coords = unique_rows(np.round(all_coords).astype(np.intp)) del all_coords # We only consider the diffusion-weighted signals: n_bvecs = self.gtab.bvals[~self.gtab.b0s_mask].shape[0] v2f, v2fn = voxel2streamline(streamline, np.eye(4), unique_idx=vox_coords) # How many fibers in each voxel (this will determine how many # components are in the matrix): n_unique_f = len(np.hstack(list(v2f.values()))) # Preallocate these, which will be used to generate the sparse # matrix: f_matrix_sig = np.zeros(n_unique_f * n_bvecs, dtype=float) f_matrix_row = np.zeros(n_unique_f * n_bvecs, dtype=np.intp) f_matrix_col = np.zeros(n_unique_f * n_bvecs, dtype=np.intp) fiber_signal = [] for s_idx, s in enumerate(streamline): if sphere is not False: fiber_signal.append(SignalMaker.streamline_signal(s)) else: fiber_signal.append(streamline_signal(s, self.gtab, evals)) del streamline if sphere is not False: del SignalMaker keep_ct = 0 range_bvecs = np.arange(n_bvecs).astype(int) # In each voxel: for v_idx in range(vox_coords.shape[0]): mat_row_idx = (range_bvecs + v_idx * n_bvecs).astype(np.intp) # For each fiber in that voxel: for f_idx in v2f[v_idx]: # For each fiber-voxel combination, store the row/column # indices in the pre-allocated linear arrays f_matrix_row[keep_ct:keep_ct+n_bvecs] = mat_row_idx f_matrix_col[keep_ct:keep_ct+n_bvecs] = f_idx vox_fiber_sig = np.zeros(n_bvecs) for node_idx in v2fn[f_idx][v_idx]: # Sum the signal from each node of the fiber in that voxel: vox_fiber_sig += fiber_signal[f_idx][node_idx] # And add the summed thing into the corresponding rows: f_matrix_sig[keep_ct:keep_ct+n_bvecs] += vox_fiber_sig keep_ct = keep_ct + n_bvecs del v2f, v2fn # Allocate the sparse matrix, using the more memory-efficient 'csr' # format: life_matrix = sps.csr_matrix((f_matrix_sig, [f_matrix_row, f_matrix_col])) return life_matrix, vox_coords
def setup(self, streamline, affine, evals=[0.001, 0, 0], sphere=None, processes=1, verbose=False): """ Set up the necessary components for the LiFE model: the matrix of fiber-contributions to the DWI signal, and the coordinates of voxels for which the equations will be solved Parameters ---------- streamline : list Streamlines, each is an array of shape (n, 3) affine : array_like (4, 4) The mapping from voxel coordinates to streamline points. The voxel_to_rasmm matrix, typically from a NIFTI file. evals : list (3 items, optional) The eigenvalues of the canonical tensor used as a response function. Default:[0.001, 0, 0]. sphere: `dipy.core.Sphere` instance. Whether to approximate (and cache) the signal on a discrete sphere. This may confer a significant speed-up in setting up the problem, but is not as accurate. If `False`, we use the exact gradients along the streamlines to calculate the matrix, instead of an approximation. Defaults to use the 724-vertex symmetric sphere from :mod:`dipy.data` """ if sphere is not False: SignalMaker = LifeSignalMaker(self.gtab, evals=evals, sphere=sphere) streamline = transform_streamlines(streamline, affine) #picklepath1 = '/Users/alex/jacques/fiber_signal_parallel.p' #fiber_signal=pickle.load(open(picklepath1, "rb")) #picklepath2 = '/Users/alex/jacques/fiber_signal_orig.p' #fiber_signal_orig=pickle.load(open(picklepath2, "rb")) #original location of the vox steps, moved them for faster streamline processing debug fiber_signal = [] fiber_signal_orig = [] fiber_signal_list = [] skiplist = [] #the stuff that got moved around for faster processing # Assign some local variables, for shorthand: #if save_fibsignal: # pickle.dump(fiber_signal, open(picklepath1, "wb")) all_coords = np.concatenate(streamline) vox_coords = unique_rows(np.round(all_coords).astype(np.intp)) del all_coords # We only consider the diffusion-weighted signals: n_bvecs = self.gtab.bvals[~self.gtab.b0s_mask].shape[0] v2f, v2fn = voxel2streamline(streamline, np.eye(4), unique_idx=vox_coords) # How many fibers in each voxel (this will determine how many # components are in the matrix): n_unique_f = len(np.hstack(v2f.values())) """ save_fibsignal=True picklepath1 = '/Users/alex/jacques/fiber_signal_parallel_rev.p' picklepath2 = '/Users/alex/jacques/fiber_signal_parallel.p' try: fiber_signal = pickle.load(open(picklepath1, "rb")) save_fibsignal=False print("getting Fiber signal from" + picklepath1) fiber_signal_orig = pickle.load(open(picklepath2, "rb")) except FileNotFoundError: """ print("computing the fiber signal values") duration1 = time() if processes > 1: pool = mp.Pool(processes) fiber_signal = pool.starmap_async( fiber_treatment, [(s, idx, self.gtab, evals, SignalMaker, sphere) for idx, s in enumerate(streamline)]).get() #for idx,fiber in enumerate(fiber_signal_list): pool.close() else: for s_idx, s in enumerate(streamline): streamshape = np.shape(np.asarray(s)) if sphere is not False: fiber_signal.append(SignalMaker.streamline_signal(s)) else: fiber_signal.append(streamline_signal(s, self.gtab, evals)) #print("Took care of "+ str(s_idx) + " out of " + str(len(streamline)) + " streamlines") if verbose: print("Obtaining fiber signal process done in " + str(time() - duration1) + "s") if sphere is not False: del SignalMaker # Preallocate these, which will be used to generate the sparse # matrix: f_matrix_sig = np.zeros(n_unique_f * n_bvecs, dtype=np.float) f_matrix_row = np.zeros(n_unique_f * n_bvecs, dtype=np.intp) f_matrix_col = np.zeros(n_unique_f * n_bvecs, dtype=np.intp) #end of moved block JS del streamline keep_ct = 0 range_bvecs = np.arange(n_bvecs).astype(int) duration2 = time() # In each voxel: for v_idx in range(vox_coords.shape[0]): mat_row_idx = (range_bvecs + v_idx * n_bvecs).astype(np.intp) # For each fiber in that voxel: for f_idx in v2f[v_idx]: # For each fiber-voxel combination, store the row/column # indices in the pre-allocated linear arrays f_matrix_row[keep_ct:keep_ct + n_bvecs] = mat_row_idx f_matrix_col[keep_ct:keep_ct + n_bvecs] = f_idx vox_fiber_sig = np.zeros(n_bvecs) for node_idx in v2fn[f_idx][v_idx]: # Sum the signal from each node of the fiber in that voxel: try: vox_fiber_sig += fiber_signal[f_idx][node_idx] except IndexError: print("hi") raise IndexError # And add the summed thing into the corresponding rows: f_matrix_sig[keep_ct:keep_ct + n_bvecs] += vox_fiber_sig keep_ct = keep_ct + n_bvecs del v2f, v2fn # Allocate the sparse matrix, using the more memory-efficient 'csr' # format: if verbose: print("Fiber vos matrix calculated in " + str(time() - duration2) + "s") duration3 = time() life_matrix = sps.csr_matrix( (f_matrix_sig, [f_matrix_row, f_matrix_col])) if verbose: print("Life matrix caluclated in " + str(time() - duration3) + "s") return life_matrix, vox_coords
def setup(self, streamline, affine, evals=[0.001, 0, 0], sphere=None): """ Set up the necessary components for the LiFE model: the matrix of fiber-contributions to the DWI signal, and the coordinates of voxels for which the equations will be solved Parameters ---------- streamline : list Streamlines, each is an array of shape (n, 3) affine : 4 by 4 array Mapping from the streamline coordinates to the data evals : list (3 items, optional) The eigenvalues of the canonical tensor used as a response function. Default:[0.001, 0, 0]. sphere: `dipy.core.Sphere` instance. Whether to approximate (and cache) the signal on a discrete sphere. This may confer a significant speed-up in setting up the problem, but is not as accurate. If `False`, we use the exact gradients along the streamlines to calculate the matrix, instead of an approximation. Defaults to use the 724-vertex symmetric sphere from :mod:`dipy.data` """ if sphere is not False: SignalMaker = LifeSignalMaker(self.gtab, evals=evals, sphere=sphere) if affine is None: affine = np.eye(4) streamline = transform_streamlines(streamline, affine) # Assign some local variables, for shorthand: all_coords = np.concatenate(streamline) vox_coords = unique_rows(np.round(all_coords).astype(np.intp)) del all_coords # We only consider the diffusion-weighted signals: n_bvecs = self.gtab.bvals[~self.gtab.b0s_mask].shape[0] v2f, v2fn = voxel2streamline(streamline, transformed=True, affine=affine, unique_idx=vox_coords) # How many fibers in each voxel (this will determine how many # components are in the matrix): n_unique_f = len(np.hstack(v2f.values())) # Preallocate these, which will be used to generate the sparse # matrix: f_matrix_sig = np.zeros(n_unique_f * n_bvecs, dtype=np.float) f_matrix_row = np.zeros(n_unique_f * n_bvecs, dtype=np.intp) f_matrix_col = np.zeros(n_unique_f * n_bvecs, dtype=np.intp) fiber_signal = [] for s_idx, s in enumerate(streamline): if sphere is not False: fiber_signal.append(SignalMaker.streamline_signal(s)) else: fiber_signal.append(streamline_signal(s, self.gtab, evals)) del streamline if sphere is not False: del SignalMaker keep_ct = 0 range_bvecs = np.arange(n_bvecs).astype(int) # In each voxel: for v_idx in range(vox_coords.shape[0]): mat_row_idx = (range_bvecs + v_idx * n_bvecs).astype(np.intp) # For each fiber in that voxel: for f_idx in v2f[v_idx]: # For each fiber-voxel combination, store the row/column # indices in the pre-allocated linear arrays f_matrix_row[keep_ct:keep_ct+n_bvecs] = mat_row_idx f_matrix_col[keep_ct:keep_ct+n_bvecs] = f_idx vox_fiber_sig = np.zeros(n_bvecs) for node_idx in v2fn[f_idx][v_idx]: # Sum the signal from each node of the fiber in that voxel: vox_fiber_sig += fiber_signal[f_idx][node_idx] # And add the summed thing into the corresponding rows: f_matrix_sig[keep_ct:keep_ct+n_bvecs] += vox_fiber_sig keep_ct = keep_ct + n_bvecs del v2f, v2fn # Allocate the sparse matrix, using the more memory-efficient 'csr' # format: life_matrix = sps.csr_matrix((f_matrix_sig, [f_matrix_row, f_matrix_col])) return life_matrix, vox_coords
def setup(self, streamline, affine, evals=[0.001, 0, 0], sphere=None): """ Set up the necessary components for the LiFE model: the matrix of fiber-contributions to the DWI signal, and the coordinates of voxels for which the equations will be solved Parameters ---------- streamline : list Streamlines, each is an array of shape (n, 3) affine : 4 by 4 array Mapping from the streamline coordinates to the data evals : list (3 items, optional) The eigenvalues of the canonical tensor used as a response function sphere: `dipy.core.Sphere` instance. Whether to approximate (and cache) the signal on a discrete sphere. This may confer a significant speed-up in setting up the problem, but is not as accurate. If `False`, we use the exact gradients along the streamlines to calculate the matrix, instead of an approximation. Defaults to use the 724-vertex symmetric sphere from :mod:`dipy.data` """ if sphere is not False: SignalMaker = LifeSignalMaker(self.gtab, evals=evals, sphere=sphere) if affine is None: affine = np.eye(4) streamline = transform_streamlines(streamline, affine) # Assign some local variables, for shorthand: all_coords = np.concatenate(streamline) vox_coords = unique_rows(all_coords.astype(int)) n_vox = vox_coords.shape[0] # We only consider the diffusion-weighted signals: n_bvecs = self.gtab.bvals[~self.gtab.b0s_mask].shape[0] v2f, v2fn = voxel2streamline(streamline, transformed=True, affine=affine, unique_idx=vox_coords) # How many fibers in each voxel (this will determine how many # components are in the fiber part of the matrix): n_unique_f = np.sum(v2f) # Preallocate these, which will be used to generate the two sparse # matrices: # This one will hold the fiber-predicted signal f_matrix_sig = np.zeros(n_unique_f * n_bvecs) f_matrix_row = np.zeros(n_unique_f * n_bvecs) f_matrix_col = np.zeros(n_unique_f * n_bvecs) keep_ct = 0 if sphere is not False: fiber_signal = [SignalMaker.streamline_signal(s) for s in streamline] else: fiber_signal = [streamline_signal(s, self.gtab, evals) for s in streamline] # In each voxel: for v_idx, vox in enumerate(vox_coords): # dbg: # print(100*float(v_idx)/n_vox) # For each fiber: for f_idx in np.where(v2f[v_idx])[0]: # Sum the signal from each node of the fiber in that voxel: vox_fiber_sig = np.zeros(n_bvecs) for node_idx in np.where(v2fn[f_idx] == v_idx)[0]: this_signal = fiber_signal[f_idx][node_idx] vox_fiber_sig += (this_signal - np.mean(this_signal)) # For each fiber-voxel combination, we now store the row/column # indices and the signal in the pre-allocated linear arrays f_matrix_row[keep_ct:keep_ct+n_bvecs] =\ np.arange(n_bvecs) + v_idx * n_bvecs f_matrix_col[keep_ct:keep_ct+n_bvecs] =\ np.ones(n_bvecs) * f_idx f_matrix_sig[keep_ct:keep_ct+n_bvecs] = vox_fiber_sig keep_ct += n_bvecs # Allocate the sparse matrix, using the more memory-efficient 'csr' # format (converted from the coo format, which we rely on for the # initial allocation): life_matrix = sps.coo_matrix((f_matrix_sig, [f_matrix_row, f_matrix_col])).tocsr() return life_matrix, vox_coords