def test_subsample(): """ Test subsampling """ # Sub-sampling 100 out of a random collection of 150 unit-vectors: bvecs = np.array([ozu.unit_vector(x) for x in np.random.randn(3,150)]) # The following runs through most of the module w/o verifying correctness: sub_sample = ozb.subsample(bvecs, 100) # optionally, you can provide elec_points as input. Here we test this with # the same points sub_sample = ozb.subsample(bvecs, 100, elec_points=ozu.get_camino_pts(100).T)
def test_subsample(): """ Test subsampling """ # Sub-sampling 100 out of a random collection of 150 unit-vectors: bvecs = np.array([ozu.unit_vector(x) for x in np.random.randn(3, 150)]) # The following runs through most of the module w/o verifying correctness: sub_sample = ozb.subsample(bvecs, 100) # optionally, you can provide elec_points as input. Here we test this with # the same points sub_sample = ozb.subsample(bvecs, 100, elec_points=ozu.get_camino_pts(100).T)
def __init__(self, data, bvecs, bvals, affine=None, mask=None, scaling_factor=SCALE_FACTOR, sub_sample=None, verbose=True, b0_tol=0.005): """ Initialize a DWI object Parameters ---------- data: str or array The diffusion weighted mr data provided either as a full path to a nifti file containing the data, or as a 4-d array. bvecs: str or array The unit vectors describing the directions of data acquisition. Either an 3 by n array, or a full path to a text file containing the 3 by n data. bvals: str or array The values of b weighting in the data acquisition. Either a 1 by n array, or a full path to a text file containing the values. affine: optional, 4 by 4 array The affine provided in the file can be overridden by explicitely setting this input variable. If this is left as None, one of two things will happen. If the 'data' input was a file-name, the affine will be read from that file. Otherwise, a warning will be issued and affine will default to np.eye(4). mask: optional, 3-d array When provided, used as a boolean mask into the data for access. sub_sample: int or array of ints. If we want to sub-sample the DWI data on the sphere (in the bvecs), we can do one of two things: 1. If sub_sample is an integer, that number of random bvecs will be chosen from the data. 2. If an array of indices is provided, these will serve as indices into the last dimension of the data and only that part of the data will be used verbose: boolean, optional. Whether or not to print out various messages as you go along. Default: True """ self.verbose = verbose self.b0_tol = b0_tol self.scaling_factor = scaling_factor # All inputs are handled essentially the same. Inputs can be either # strings, in which case file reads are required, or arrays, in which # case no file reads are needed and we assign these arrays into the # attributes: for name, val in zip(['data', 'bvecs', 'bvals'], [data, bvecs, bvals]): if val is None: exec("self.%s = None" % name) elif isinstance(val, str): exec("self.%s_file = '%s'" % (name, val)) elif isinstance(val, np.ndarray): # This time we need to give it the name-space: exec("self.%s = val" % name, dict(self=self, val=val)) else: e_s = "%s seems to be neither an array, " % name e_s += "nor a file-name\n" e_s += "The value provided was: %s" % val raise ValueError(e_s) # You might have to scale the bvalues by some factor, so that the units # come out correctly in the adc calculation: if self.bvals is not None: self.bvals = self.bvals.copy() / scaling_factor # You can provide your own affine, if you want and that bypasses the # class method provided below as an auto-attr: if affine is not None: self.affine = np.matrix(affine) # If a mask is provided, we will use it to access the data if mask is not None: # If it's a string, assume it's the full-path to a nifti file with # a binary mask: if isinstance(mask, str): mask = ni.load(mask).get_data() else: self.mask = np.array(mask, dtype=bool) pre_mask = np.array(mask, dtype=bool) ravel_mask = np.ravel(pre_mask) # Eliminate the voxels where self.S0 == 0. ravel_mask[np.where(ravel_mask)[0][np.where( self.S0[pre_mask] == 0)]] = False self.mask = np.reshape(ravel_mask, pre_mask.shape) else: # If only one voxel was provided, we will assume that all the data # in the voxel should be taken if len(self.shape) == 1: self.mask = slice(0, None, None) # Otherwise, we make a 3D mask, with all the voxels in the volume: else: # Spatial mask (take only the spatial dimensions): self.mask = np.ones(self.shape[:3], dtype=bool) if sub_sample is not None: if np.iterable(sub_sample): idx = sub_sample else: idx = boot.subsample(self.bvecs[:, self.b_idx], sub_sample)[1] self.b_idx = self.b_idx[idx] # At this point, signal will be taken according to these # sub-sampled indices: self.data = np.concatenate( [self.signal, self.data[:, :, :, self.b0_idx]], -1) self.b0_idx = np.arange(len(self.b0_idx)) self.bvecs = np.concatenate( [np.zeros( (3, len(self.b0_idx))), self.bvecs[:, self.b_idx]], -1) self.bvals = np.concatenate( [np.zeros(len(self.b0_idx)), self.bvals[self.b_idx]]) self.b_idx = np.arange(len(self.b0_idx), len(self.b0_idx) + len(idx))
def __init__(self, data, bvecs, bvals, affine=None, mask=None, scaling_factor=SCALE_FACTOR, sub_sample=None, verbose=True ): """ Initialize a DWI object Parameters ---------- data: str or array The diffusion weighted mr data provided either as a full path to a nifti file containing the data, or as a 4-d array. bvecs: str or array The unit vectors describing the directions of data acquisition. Either an 3 by n array, or a full path to a text file containing the 3 by n data. bvals: str or array The values of b weighting in the data acquisition. Either a 1 by n array, or a full path to a text file containing the values. affine: optional, 4 by 4 array The affine provided in the file can be overridden by explicitely setting this input variable. If this is left as None, one of two things will happen. If the 'data' input was a file-name, the affine will be read from that file. Otherwise, a warning will be issued and affine will default to np.eye(4). mask: optional, 3-d array When provided, used as a boolean mask into the data for access. sub_sample: int or array of ints. If we want to sub-sample the DWI data on the sphere (in the bvecs), we can do one of two things: 1. If sub_sample is an integer, that number of random bvecs will be chosen from the data. 2. If an array of indices is provided, these will serve as indices into the last dimension of the data and only that part of the data will be used verbose: boolean, optional. Whether or not to print out various messages as you go along. Default: True """ self.verbose=verbose # All inputs are handled essentially the same. Inputs can be either # strings, in which case file reads are required, or arrays, in which # case no file reads are needed and we assign these arrays into the # attributes: for name, val in zip(['data', 'bvecs', 'bvals'], [data, bvecs, bvals]): if val is None: exec("self.%s = None"%name) elif isinstance(val, str): exec("self.%s_file = '%s'"% (name, val)) elif isinstance(val, np.ndarray): # This time we need to give it the name-space: exec("self.%s = val"% name, dict(self=self, val=val)) else: e_s = "%s seems to be neither an array, "% name e_s += "nor a file-name\n" e_s += "The value provided was: %s" % val raise ValueError(e_s) # You might have to scale the bvalues by some factor, so that the units # come out correctly in the adc calculation: if self.bvals is not None: self.bvals = self.bvals.copy() / scaling_factor # You can provide your own affine, if you want and that bypasses the # class method provided below as an auto-attr: if affine is not None: self.affine = np.matrix(affine) # If a mask is provided, we will use it to access the data if mask is not None: # If it's a string, assume it's the full-path to a nifti file with # a binary mask: if isinstance(mask, str): mask = ni.load(mask).get_data() self.mask = np.array(mask, dtype=bool) else: # If only one voxel was provided, we will assume that all the data # in the voxel should be taken if len(self.shape)==1: self.mask = slice(0,None,None) # Otherwise, we make a 3D mask, with all the voxels in the volume: else: # Spatial mask (take only the spatial dimensions): self.mask = np.ones(self.shape[:3], dtype=bool) if sub_sample is not None: if np.iterable(sub_sample): idx = sub_sample else: idx = boot.subsample(self.bvecs[:,self.b_idx].T, sub_sample)[1] self.b_idx = self.b_idx[idx] # At this point, signal will be taken according to these # sub-sampled indices: self.data = np.concatenate([self.signal, self.data[:,:,:,self.b0_idx]],-1) self.b0_idx = np.arange(len(self.b0_idx)) self.bvecs = np.concatenate([np.zeros((3,len(self.b0_idx))), self.bvecs[:, self.b_idx]],-1) self.bvals = np.concatenate([np.zeros(len(self.b0_idx)), self.bvals[self.b_idx]]) self.b_idx = np.arange(len(self.b0_idx), len(self.b0_idx) + len(idx))