def subsample(bvecs, n_dirs, elec_points=None): """ Generate a sub-sample of size n of directions from the provided bvecs Parameters ---------- bvecs: int array (n by 3), a set of cartesian coordinates for a set of bvecs n_dirs: int, how many bvecs to sub-sample from this set. elec_points: optional, a set of points read from the camino points, using Jones (2003) algorithm for electro-static repulsion Returns ------- [x,y,z]: The coordinates of the sub-sample bvec_idx: The indices into the original bvecs that would give this sub-sample Notes ----- Directions are chosen from the camino-generated electro-static repulsion points in the directory camino_pts. """ if elec_points is None: # We need a n by 3 here: xyz = ozu.get_camino_pts(n_dirs).T else: xyz = elec_points.copy() # Rotate all the points to align with the seed, the bvec relative to which # all the rest are chosen (lots going on in this one line): new_points = np.array(bvecs * ozu.calculate_rotation( bvecs[np.ceil(np.random.rand() * xyz.shape[0]).astype(int)], xyz[0])) sample_bvecs = np.zeros((3, n_dirs)) bvec_idx = [] for vec in xrange(n_dirs): this = new_points[vec] delta = np.zeros(bvecs.shape[0]) for j in xrange(bvecs.shape[0]): delta[j] = ozu.vector_angle(this, bvecs[j]) this_idx = np.where(delta==np.min(delta)) bvec_idx.append(this_idx) sample_bvecs[:, vec] = bvecs[this_idx] return sample_bvecs, np.array(bvec_idx).squeeze()
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 test_fodf_emd(): """ Test EMD on fODFs """ bvecs = ozu.get_camino_pts(150) fodf1 = np.zeros(bvecs.shape[-1]) fodf2 = np.zeros(bvecs.shape[-1]) ii = np.random.randint(0, fodf1.shape[0]) jj = np.random.randint(0, fodf2.shape[0]) fodf1[ii] = 1 fodf2[jj] = 1 emd1 = pn.fODF_EMD(fodf1, fodf2, bvecs1=bvecs, bvecs2=bvecs) angles = np.arccos(np.dot(bvecs.T, bvecs)) angles[np.isnan(angles)] = 0 angles = np.min(np.array([angles, np.pi - angles]), 0) angles = angles.ravel() emd2 = pn.fODF_EMD(fodf1, fodf2, bvecs1=bvecs, dist=angles) npt.assert_equal(emd1, emd2)
def subsample(bvecs, n_dirs, elec_points=None): """ Generate a sub-sample of size n of directions from the provided bvecs Parameters ---------- bvecs: int array (n by 3), a set of cartesian coordinates for a set of bvecs n_dirs: int, how many bvecs to sub-sample from this set. elec_points: optional, a set of points read from the camino points, using Jones (2003) algorithm for electro-static repulsion Returns ------- [x,y,z]: The coordinates of the sub-sample bvec_idx: The indices into the original bvecs that would give this sub-sample Notes ----- Directions are chosen from the camino-generated electro-static repulsion points in the directory camino_pts. """ if elec_points is None: # We need a n by 3 here: xyz = ozu.get_camino_pts(n_dirs).T else: xyz = elec_points.copy() # Rotate all the points to align with the seed, the bvec relative to which # all the rest are chosen (lots going on in this one line): rot_to_first = ozu.calculate_rotation( bvecs[:, np.ceil(np.random.randint(xyz.shape[0]))], xyz[0]) new_points = np.dot(rot_to_first, bvecs).T sample_bvecs = np.zeros((3, n_dirs)) bvec_idx = [] potential_indices = np.arange(bvecs.shape[-1]) for vec in xrange(n_dirs): this = new_points[vec] delta = np.zeros(potential_indices.shape) for j in range(delta.shape[0]): delta[j] = ozu.vector_angle(this, bvecs[:, j]) this_idx = np.where(delta==np.min(delta)) bvec_idx.append(potential_indices[this_idx]) sample_bvecs[:, vec] = np.squeeze(bvecs[:, this_idx]) # Remove bvecs that you've used, so that you don't have them more than # once: bvecs = np.hstack([bvecs[:, :this_idx[0]],bvecs[:, this_idx[0]+1:]]) potential_indices = np.hstack([potential_indices[:this_idx[0]], potential_indices[this_idx[0]+1:]]) return sample_bvecs, np.array(bvec_idx).squeeze()
def __init__(self, data, bvecs, bvals, params_file=None, axial_diffusivity=AD, radial_diffusivity=RD, affine=None, mask=None, scaling_factor=SCALE_FACTOR, sub_sample=None, over_sample=None, mode='relative_signal', iso_diffusivity=None, verbose=True): """ Initialize a CanonicalTensorModel class instance. Parameters ---------- params_file: str, optional full path to the name of the file in which to save the model parameters, once a model is fit. over_sample: optional, int. Sometimes you might want to probe the sphere at a higher resolution than that provided by the measurement. You can do that using two possible sources of information. The first is the camino points, which ship together with osmosis and are used for the boot-strapping (see osmosis.boot). These are used for integers smaller than 150, for 246 and for 755. The other sources of information are the symmetric spheres provided as part of dipy. These are used if 362 or 642 are provided. Note that these might be problematic, because they contain polar opposite points, so use with caution. mode: string, optional This can take one of several values, determining the form of the regressors and the form of the signal to fit to. 'relative_signal': The fit is to $\frac{S}{S_0}$ and the regressors are the relative signal predicted for a canonical tensor and the relative signal predicted for a isotropic diffusivity compartment: .. math:: \frac{S}{S_0} = \beta_1 e^{-bD} + \beta_2 e^{-b\vec{b}Q\vec{b}^t} 'signal_attenuation': The fit is to $1-\frac{S}{S_0}$ and the regressors are the signal attenuation for the canonical tensor and the signal attenuation due to isotropic diffusion: .. math:: 1-\frac{S}{S_0} = \beta_1 (1-e^{-bD}) + \beta_2 (1-e^{-b\vec{b} Q \vec{b}^t}) 'normalize': in this case, we fit to $\frac{S}{S_0}$, but our regressor set is normalized to maximum of 1 in each columns. This affects the values of the weights, and also the goodness-of-fit (because of the relative scaling of the regressors in the OLS). The equation in this case is: .. math:: \frac{S}{S_0} = \beta_1 + \beta_2 \frac{e^{-b\vec{b} Q \vec{b}^t}}{max(e^{-b\vec{b} Q \vec{b}^t})} 'log': in this case, we fit to $log(\frac{S}{S_0})$ and our regressors are the exponents: .. math:: log(\frac{S}{S_0}) = \beta_1 -bD + \beta_1 -b\vec{b}Q\vec{b}^t iso_diffusivity: optional, float What the diffusivity of the isotropic component should be set to. This is irrelevant for the 'normalize' mode. Defaults to be equal to the axial_diffusivity """ # Initialize the super-class: BaseModel.__init__(self, data, bvecs, bvals, affine=affine, mask=mask, scaling_factor=scaling_factor, sub_sample=sub_sample, params_file=params_file, verbose=verbose) self.ad = axial_diffusivity self.rd = radial_diffusivity if iso_diffusivity is None: iso_diffusivity = axial_diffusivity self.iso_diffusivity = iso_diffusivity if over_sample is not None: # Symmetric spheres from dipy: if over_sample in[362, 642]: # We want to get these vertices: verts = dpd.get_sphere('symmetric%s'%over_sample).vertices # Their convention is transposed relative to ours: self.rot_vecs = verts.T elif (isinstance(over_sample, int) and (over_sample<=150 or over_sample in [246,755])): self.rot_vecs = ozu.get_camino_pts(over_sample) elif over_sample== 'quad132': pp = os.path.split(oz.__file__)[0]+'/data/SparseKernelModel/' self.rot_vecs = np.loadtxt(pp + 'qsph1-16-132DP.dat')[:,:-1].T else: e_s = "You asked to sample the sphere in %s"%over_sample e_s += " different directions. Can only do that for n<=150" e_s += " or n in [246, 362, 642, 755]" raise ValueError(e_s) else: self.rot_vecs = self.bvecs[:,self.b_idx] if mode not in ['relative_signal', 'signal_attenuation', 'normalize', 'log']: raise ValueError("Not a recognized mode of CanonicalTensorModel") self.mode = mode self.iso_diffusivity = iso_diffusivity
def subsample(bvecs, n_dirs, elec_points=None): """ Generate a sub-sample of size n of directions from the provided bvecs Parameters ---------- bvecs: int array (n by 3), a set of cartesian coordinates for a set of bvecs n_dirs: int, how many bvecs to sub-sample from this set. elec_points: optional, a set of points read from the camino points, using Jones (2003) algorithm for electro-static repulsion Returns ------- [x,y,z]: The coordinates of the sub-sample bvec_idx: The indices into the original bvecs that would give this sub-sample Notes ----- Directions are chosen from the camino-generated electro-static repulsion points in the directory camino_pts. """ if elec_points is None: # We need a n by 3 here: xyz = ozu.get_camino_pts(n_dirs).T else: xyz = elec_points.copy() # Rotate all the points to align with the seed, the bvec relative to which # all the rest are chosen (lots going on in this one line): rot_to_first = ozu.calculate_rotation( bvecs[:, np.ceil(np.random.randint(xyz.shape[0]))], xyz[0]) new_points = np.dot(rot_to_first, bvecs).T sample_bvecs = np.zeros((3, n_dirs)) bvec_idx = [] potential_indices = np.arange(bvecs.shape[-1]) for vec in xrange(n_dirs): this = new_points[vec] delta = np.zeros(potential_indices.shape) for j in range(delta.shape[0]): delta[j] = ozu.vector_angle(this, bvecs[:, j]) this_idx = np.where(delta == np.min(delta)) bvec_idx.append(potential_indices[this_idx]) sample_bvecs[:, vec] = np.squeeze(bvecs[:, this_idx]) # Remove bvecs that you've used, so that you don't have them more than # once: bvecs = np.hstack([bvecs[:, :this_idx[0]], bvecs[:, this_idx[0] + 1:]]) potential_indices = np.hstack([ potential_indices[:this_idx[0]], potential_indices[this_idx[0] + 1:] ]) return sample_bvecs, np.array(bvec_idx).squeeze()
def __init__(self, data, bvecs, bvals, params_file=None, axial_diffusivity=AD, radial_diffusivity=RD, affine=None, mask=None, scaling_factor=SCALE_FACTOR, sub_sample=None, over_sample=None, mode='relative_signal', iso_diffusivity=None, verbose=True): """ Initialize a CanonicalTensorModel class instance. Parameters ---------- params_file: str, optional full path to the name of the file in which to save the model parameters, once a model is fit. over_sample: optional, int. Sometimes you might want to probe the sphere at a higher resolution than that provided by the measurement. You can do that using two possible sources of information. The first is the camino points, which ship together with osmosis and are used for the boot-strapping (see osmosis.boot). These are used for integers smaller than 150, for 246 and for 755. The other sources of information are the symmetric spheres provided as part of dipy. These are used if 362 or 642 are provided. Note that these might be problematic, because they contain polar opposite points, so use with caution. mode: string, optional This can take one of several values, determining the form of the regressors and the form of the signal to fit to. 'relative_signal': The fit is to $\frac{S}{S_0}$ and the regressors are the relative signal predicted for a canonical tensor and the relative signal predicted for a isotropic diffusivity compartment: .. math:: \frac{S}{S_0} = \beta_1 e^{-bD} + \beta_2 e^{-b\vec{b}Q\vec{b}^t} 'signal_attenuation': The fit is to $1-\frac{S}{S_0}$ and the regressors are the signal attenuation for the canonical tensor and the signal attenuation due to isotropic diffusion: .. math:: 1-\frac{S}{S_0} = \beta_1 (1-e^{-bD}) + \beta_2 (1-e^{-b\vec{b} Q \vec{b}^t}) 'normalize': in this case, we fit to $\frac{S}{S_0}$, but our regressor set is normalized to maximum of 1 in each columns. This affects the values of the weights, and also the goodness-of-fit (because of the relative scaling of the regressors in the OLS). The equation in this case is: .. math:: \frac{S}{S_0} = \beta_1 + \beta_2 \frac{e^{-b\vec{b} Q \vec{b}^t}}{max(e^{-b\vec{b} Q \vec{b}^t})} 'log': in this case, we fit to $log(\frac{S}{S_0})$ and our regressors are the exponents: .. math:: log(\frac{S}{S_0}) = \beta_1 -bD + \beta_1 -b\vec{b}Q\vec{b}^t iso_diffusivity: optional, float What the diffusivity of the isotropic component should be set to. This is irrelevant for the 'normalize' mode. Defaults to be equal to the axial_diffusivity """ # Initialize the super-class: BaseModel.__init__(self, data, bvecs, bvals, affine=affine, mask=mask, scaling_factor=scaling_factor, sub_sample=sub_sample, params_file=params_file, verbose=verbose) self.ad = axial_diffusivity self.rd = radial_diffusivity if iso_diffusivity is None: iso_diffusivity = axial_diffusivity self.iso_diffusivity = iso_diffusivity if over_sample is not None: # Symmetric spheres from dipy: if over_sample in [362, 642]: # We want to get these vertices: verts = dpd.get_sphere('symmetric%s' % over_sample).vertices # Their convention is transposed relative to ours: self.rot_vecs = verts.T elif (isinstance(over_sample, int) and (over_sample <= 150 or over_sample in [246, 755])): self.rot_vecs = ozu.get_camino_pts(over_sample) elif over_sample == 'quad132': pp = os.path.split(oz.__file__)[0] + '/data/SparseKernelModel/' self.rot_vecs = np.loadtxt(pp + 'qsph1-16-132DP.dat')[:, :-1].T else: e_s = "You asked to sample the sphere in %s" % over_sample e_s += " different directions. Can only do that for n<=150" e_s += " or n in [246, 362, 642, 755]" raise ValueError(e_s) else: self.rot_vecs = self.bvecs[:, self.b_idx] if mode not in [ 'relative_signal', 'signal_attenuation', 'normalize', 'log' ]: raise ValueError("Not a recognized mode of CanonicalTensorModel") self.mode = mode self.iso_diffusivity = iso_diffusivity