Example #1
0
def place_predict(files):
    data_path = "/biac4/wandell/data/klchan13/100307/Diffusion/data"

    # Get file object
    data_file = nib.load(os.path.join(data_path, "data.nii.gz"))
    wm_data_file = nib.load(os.path.join(data_path,"wm_mask_registered.nii.gz"))

    # Get data and indices
    data = data_file.get_data()
    wm_data = wm_data_file.get_data()
    wm_idx = np.where(wm_data==1)

    # b values
    bvals = np.loadtxt(os.path.join(data_path, "bvals"))
    bval_list, b_inds, unique_b, rounded_bvals = snr.separate_bvals(bvals/1000)
    all_b_idx = np.squeeze(np.where(rounded_bvals != 0))

    all_predict_brain = ozu.nans((wm_data_file.shape + bvals.shape))
    bvals_predict_brain = ozu.nans((wm_data_file.shape + bvals.shape))
    
    # Keep track of files in case there are any missing files
    i_track = np.ones(1830)
    for f_idx in np.arange(len(files)):
        this_file = files[f_idx]
        
        predict_data = nib.load(this_file).get_data()
        if this_file[0:11] == "all_predict":
            i = int(this_file.split(".")[0][11:])
            print "Placing all_predict %4.2f of 1830"%(i+1)
            low = i*70
            high = np.min([(i+1) * 70, int(np.sum(wm_data))])
            all_predict_brain[wm_idx[0][low:high], wm_idx[1][low:high], wm_idx[2][low:high]] = predict_data
        elif this_file[0:13] == "bvals_predict":
            i = int(this_file.split(".")[0][13:])
            print "Placing bvals_predict %4.2f of 1830"%(i+1)
            low = i*70
            high = np.min([(i+1) * 70, int(np.sum(wm_data))])
            bvals_predict_brain[wm_idx[0][low:high], wm_idx[1][low:high], wm_idx[2][low:high]] = predict_data
        i_track[i] = 0
        
    actual = data[wm_idx, :][:, all_b_idx]
    missing_files = np.where(i_track)
    rmse_b = np.sqrt(np.mean((actual - all_predict_brain[wm_idx])**2,-1))
    rmse_mb = p.sqrt(np.mean((actual - bvals_predict_brain[wm_idx])**2,-1))

    # Save the rmse and predict data
    aff = data_file.get_affine()
    nib.Nifti1Image(all_predict_brain, aff).to_filename("all_predict_brain.nii.gz")
    nib.Nifti1Image(bvals_predict_brain, aff).to_filename("bvals_predict_brain.nii.gz")

    rmse_aff = np.eye(4)
    nib.Nifti1Image(rmse_b_flat, rmse_aff).to_filename("rmse_b_flat.nii.gz")
    nib.Nifti1Image(rmse_mb_flat, rmse_aff).to_filename("rmse_mb_flat.nii.gz")
    
    return missing_files, all_predict_brain, bvals_predict_brain, rmse_b_flat, rmse_mb_flat
Example #2
0
def pdd_reliability(model1, model2):
    """

    Compute the angle between the first PDD in two models.

    Parameters
    ----------
    model1, model2: two objects from a class inherited from BaseModel.
       Must implement an auto_attr class method 'principal_diffusion_direction',
       which returns arrays of 3-vectors representing a direction in three space
       which is the principal diffusion direction in each voxel. Some models
       will have more than one principal diffusion direction in each voxel. In
       that case, the first direction in each voxel will be used to represent
       that voxel. 
    
    """
    vol_shape = model1.shape[:3]
    pdd1 = model1.principal_diffusion_direction[model1.mask]
    pdd2 = model2.principal_diffusion_direction[model2.mask]

    # Some models create not only the first PDD, but subsequent directions as
    # well, so If we have a lot of PDD, we take only the first one: 
    if len(pdd1.shape) == 3:
        pdd1 = pdd1[:, 0]
    if len(pdd2.shape) == 3:
        pdd2 = pdd2[:, 0]

    out_flat = np.empty(pdd1.shape[0])    
    for vox in xrange(pdd1.shape[0]):
        this_ang = np.rad2deg(ozu.vector_angle(pdd1[vox], pdd2[vox]))
        out_flat[vox] = np.min([this_ang, 180-this_ang])

    out = ozu.nans(vol_shape)
    out[model1.mask] = out_flat
    return out
    def fit_angle(self):
        """
        The angle between the tensors that were fitted
        """
        out_flat = np.empty(self._flat_signal.shape[0])
        flat_params = self.model_params[self.mask]
        for vox in xrange(out_flat.shape[0]):
            if ~np.isnan(flat_params[vox][0]):
                idx = [i for i in self.rot_idx[int(flat_params[vox][0])]]
                # Sort them according to their weight and take the two
                # weightiest ones:
                w = flat_params[vox, 1:1 + self.n_canonicals]
                idx = np.array(idx)[np.argsort(w)]
                ang = np.rad2deg(
                    ozu.vector_angle(self.rot_vecs.T[idx[-1]],
                                     self.rot_vecs.T[idx[-2]]))

                ang = np.min([ang, 180 - ang])

                out_flat[vox] = ang

            else:
                out_flat[vox] = np.nan

        out = ozu.nans(self.signal.shape[:3])
        out[self.mask] = out_flat

        return out
    def fit_angle(self):
        """
        The angle between the tensors that were fitted
        """
        out_flat = np.empty(self._flat_signal.shape[0])
        flat_params = self.model_params[self.mask]
        for vox in xrange(out_flat.shape[0]):
            if ~np.isnan(flat_params[vox][0]):
                idx = [i for i in self.rot_idx[int(flat_params[vox][0])]]
                # Sort them according to their weight and take the two
                # weightiest ones:
                w = flat_params[vox,1:1+self.n_canonicals]
                idx = np.array(idx)[np.argsort(w)]
                ang = np.rad2deg(ozu.vector_angle(
                    self.rot_vecs.T[idx[-1]],
                    self.rot_vecs.T[idx[-2]]))

                ang = np.min([ang, 180-ang])
                
                out_flat[vox] = ang
                
            else:
                out_flat[vox] = np.nan

        
        out = ozu.nans(self.signal.shape[:3])
        out[self.mask] = out_flat

        return out
Example #5
0
    def model_diffusion(self, vertices=None, mode='ADC'):
        """
        Calculate the ADC/diffusion distance on a novel set of vertices

        Parameters
        ----------
        vertices : an n by 3 array

        """
        # If none are provided, use the measurement points:
        if vertices is None:
            self.bvecs[:, self.b_idx]

        # Start by generating the values of the rotations we use in these
        # coordinates on the sphere 
        rotations = self._calc_rotations(vertices, mode=mode)
        
        if self.verbose:
            print("Predicting signal from CanonicalTensorModel")
            
        out_flat = np.empty((self._flat_signal.shape[0], vertices.shape[-1]))
        flat_params = self.model_params[self.mask]
        for vox in xrange(out_flat.shape[0]):
            if ~np.isnan(flat_params[vox, 1]):
                this_dist = flat_params[vox,1] * rotations[flat_params[vox,0]]
                out_flat[vox]= this_dist
            else:
                out_flat[vox] = np.nan
                
        out = ozu.nans(self.signal.shape[:3] + (vertices.shape[-1], ))
        out[self.mask] = out_flat

        return out
Example #6
0
    def fit(self):
        """
        Predict the signal from the fit of the CanonicalTensorModel
        """
        if self.verbose:
            print("Predicting signal from CanonicalTensorModel")
            
        out_flat = np.empty(self._flat_signal.shape)
        flat_params = self.model_params[self.mask]
        for vox in xrange(out_flat.shape[0]):
            if ~np.isnan(flat_params[vox, 1]):
                if self.mode == 'log':
                    this_relative = np.exp(flat_params[vox,1] *
                                self.rotations[flat_params[vox,0]] +
                                self.regressors[0][0] * flat_params[vox,2]) 
                else: 
                    this_relative = (flat_params[vox,1] *
                                self.rotations[flat_params[vox,0]] +
                                self.regressors[0][0] * flat_params[vox,2]) 
        
                    if self.mode == 'signal_attenuation':
                        this_relative = 1 - this_relative

                out_flat[vox]= this_relative * self._flat_S0[vox]
            else:
                out_flat[vox] = np.nan
                
        out = ozu.nans(self.signal.shape)
        out[self.mask] = out_flat

        return out
Example #7
0
    def crossing_index(self):
        """
        Calculate an index of crossing in each voxel. This index is an analogue
        of FA, in that it is a normalized standard deviation between the values
        of the magnitudes of the peaks, which is then normalized by the
        standard deviation of the case in which there is only 1 peak with the
        value '1'.
        """
        # Flatten and sort (small => large)
        flat_peaks = self.odf_peaks[self.mask]
        cross_flat = np.empty(flat_peaks.shape[0])
        for vox in xrange(cross_flat.shape[0]):
            # Normalize all the peaks by the 2-norm of the vector:
            peaks_norm = flat_peaks[vox] / np.sqrt(np.dot(flat_peaks[vox], flat_peaks[vox]))
            non_zero_idx = np.where(peaks_norm > 0)[0]

            # Deal with some corner cases - if there is no peak, we define this
            # to be 0:
            if len(non_zero_idx) == 0:
                cross_flat[vox] = 0
            # If there's only one peak, we define it to be 1:
            elif len(non_zero_idx) == 1:
                cross_flat[vox] = 1
            # Otherwise, we need to do some math:
            else:
                std_peaks = np.std(peaks_norm[non_zero_idx])
                std_norm = np.std(np.hstack([1, np.zeros(len(non_zero_idx) - 1)]))
                cross_flat[vox] = std_peaks / std_norm

        cross = ozu.nans(self.data.shape[:3])
        cross[self.mask] = cross_flat
        return cross
Example #8
0
    def _correlator(self, correlator, r_idx=0, square=True):
        """
        Helper function that uses a callable "func" to apply between two 1-d
        arrays. These 1-d arrays can have different outputs and the one we
        always want is the one which is r_idx into the output tuple 
        """

        val = np.empty(self._flat_signal.shape[0])

        for ii in xrange(len(val)):
            if r_idx>=0:
                val[ii] = correlator(self._flat_signal[ii],
                                     self._flat_fit[ii])[r_idx] 
            else:
                val[ii] = correlator(self._flat_signal[ii],
                                     self._flat_fit[ii]) 
        if square:
            if has_numexpr:
                r_squared = numexpr.evaluate('val**2')
            else:
                r_squared = val**2
        else:
            r_squared = val
        
        # Re-package it into a volume:
        out = ozu.nans(self.shape[:3])
        out[self.mask] = r_squared

        out[out<-1]=-1.0
        out[out>1]=1.0

        return out 
Example #9
0
    def odf(self):
        """
        The orientation distribution function estimated from the SparseKernel
        model  
        """
        _verts = self.odf_verts[0] # These are the vertices on which we estimate
                                   # the ODF 
        if self.verbose:
            prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split('.')[-1]
            f_name = this_class + '.' + inspect.stack()[0][3]

        out_flat = np.zeros((self._flat_signal.shape[0], _verts.shape[0]))
        flat_params = self.model_params[self.mask]

        # We are going to use cached computations in the fit object: 
        _fit_obj = None # Initially we don't have a cached fit object
        for vox in xrange(out_flat.shape[0]):
            this_fit = self.kernel_model.SparseKernelFit(
                                                flat_params[vox][1:],
                                                flat_params[vox][0],
                                                model=self._km)
            out_flat[vox] = this_fit.odf(cache=_fit_obj, vertices=_verts)
            _fit_obj = this_fit # From now on, we will use this cached object
            _verts = None # And we need to ignore the vertices, so that the
                          # cached fit object can use the cached computation. 
            
            if self.verbose:
                prog_bar.animate(vox, f_name=f_name)

        out = ozu.nans(self.signal.shape[:3] + (out_flat.shape[-1],))
        out[self.mask] = out_flat
        return out
Example #10
0
    def fit(self):
        """
        This is the signal estimated from the odf.
        """
        if self.verbose:
            print("Predicting signal from SphericalHarmonicsModel")
            prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split('.')[-1]
            f_name = this_class + '.' + inspect.stack()[0][3]

        # Reshape the odf to be one voxel per row:
        flat_odf = self.odf[self.mask]
        pred_sig = np.empty(flat_odf.shape)

        for vox in xrange(pred_sig.shape[0]):
            # Predict based on the convolution:
            this_pred_sig = self.response_function.convolve_odf(
                flat_odf[vox], self._flat_S0[vox])

            # We might have a scaling and an offset in addition, so let's fit
            # those in each voxel based on the signal:
            a, b = np.polyfit(this_pred_sig, self._flat_signal[vox], 1)
            pred_sig[vox] = a * this_pred_sig + b

            if self.verbose:
                prog_bar.animate(vox, f_name=f_name)

        # Pack it back into a volume shaped thing:
        out = ozu.nans(self.signal.shape)
        out[self.mask] = pred_sig
        return out
Example #11
0
    def crossing_index(self):
        """
        Calculate an index of crossing in each voxel. This index is an analogue
        of FA, in that it is a normalized standard deviation between the values
        of the magnitudes of the peaks, which is then normalized by the
        standard deviation of the case in which there is only 1 peak with the
        value '1'.
        """
        # Flatten and sort (small => large)
        flat_peaks = self.odf_peaks[self.mask]
        cross_flat = np.empty(flat_peaks.shape[0])
        for vox in xrange(cross_flat.shape[0]):
            # Normalize all the peaks by the 2-norm of the vector:
            peaks_norm = flat_peaks[vox] / np.sqrt(
                np.dot(flat_peaks[vox], flat_peaks[vox]))
            non_zero_idx = np.where(peaks_norm > 0)[0]

            # Deal with some corner cases - if there is no peak, we define this
            # to be 0:
            if len(non_zero_idx) == 0:
                cross_flat[vox] = 0
            # If there's only one peak, we define it to be 1:
            elif len(non_zero_idx) == 1:
                cross_flat[vox] = 1
            # Otherwise, we need to do some math:
            else:
                std_peaks = (np.std(peaks_norm[non_zero_idx]))
                std_norm = np.std(
                    np.hstack([1, np.zeros(len(non_zero_idx) - 1)]))
                cross_flat[vox] = std_peaks / std_norm

        cross = ozu.nans(self.data.shape[:3])
        cross[self.mask] = cross_flat
        return cross
Example #12
0
    def model_params(self):
        """
        The diffusion tensor parameters estimated from the data, using dipy.
        If this calculation has already occurred, just load the data from a
        nifti file, which has shape x by y by z by 12, where the last dimension
        is the model params:

        evecs (9) + evals (3)
        
        """
        out = ozu.nans((self.data.shape[:3] +  (12,)))
        
        flat_params = np.empty((self._flat_S0.shape[0], 12))
        
        # The file already exists: 
        if os.path.isfile(self.params_file):
            if self.verbose:
                print("Loading TensorModel params from: %s" %self.params_file)
            out[self.mask] = ni.load(self.params_file).get_data()[self.mask]
        else:
            if self.verbose:
                print("Fitting TensorModel params using dipy")
            tensor_model = dti.TensorModel(self.gtab,
                                  fit_method=self.fit_method)
            for vox, vox_data in enumerate(self.data[self.mask]):
                flat_params[vox] = tensor_model.fit(vox_data).model_params

            out[self.mask] = flat_params
            # Save the params for future use: 
            params_ni = ni.Nifti1Image(out, self.affine)
            # If we asked it to be temporary, no need to save anywhere: 
            if self.params_file != 'temp':
                params_ni.to_filename(self.params_file)
        # And return the params for current use:
        return out
Example #13
0
 def principal_diffusion_direction(self):
     """
     Gives you not only the principal, but also the 2nd, 3rd, etc
     """
     out_flat = ozu.nans(self._flat_signal.shape + (3,))
     # flat_peaks = self.odf_peaks[self.mask]
     flat_peaks = self.model_params[self.mask]
     for vox in xrange(out_flat.shape[0]):
         coeff_idx = np.where(flat_peaks[vox]>0)[0]
         for i, idx in enumerate(coeff_idx):
             out_flat[vox, i] = self.bvecs[:,self.b_idx].T[idx]
     
     out = ozu.nans(self.signal.shape + (3,))
     out[self.mask] = out_flat
         
     return out
Example #14
0
    def predict(self, sphere, bvals=None):
        """
        Predict the values of the signal on a novel sphere (not neccesarily
        measured points) in every voxel

        Parameters
        ----------
        sphere : 3 x n array
        
        """
        if self.verbose:
            print("Predicting signal from TensorModel")
        if bvals is None: 
            # Assume there's only one b value and you know where to find it: 
            bvals = self.bvals[self.b_idx][0] * np.ones(sphere.shape[-1])
        else:
            # If you gave them as input, you need to scale them:
            bvals = bvals/float(self.scaling_factor)
            
        pred_adc_flat = self.predict_adc(sphere)[self.mask]
        predict_flat = np.empty(pred_adc_flat.shape)

        out = ozu.nans(self.signal.shape[:3] + (sphere.shape[-1], ))
        for ii in xrange(len(predict_flat)):
            predict_flat[ii] = ozt.stejskal_tanner(self._flat_S0[ii],
                                                   bvals,
                                                   pred_adc_flat[ii])

        out[self.mask] = predict_flat
        return out
Example #15
0
    def fit(self):
        """
        Predict the signal from the fit of the CanonicalTensorModel
        """
        if self.verbose:
            print("Predicting signal from CanonicalTensorModel")

        out_flat = np.empty(self._flat_signal.shape)
        flat_params = self.model_params[self.mask]
        for vox in xrange(out_flat.shape[0]):
            if ~np.isnan(flat_params[vox, 1]):
                if self.mode == 'log':
                    this_relative = np.exp(
                        flat_params[vox, 1] *
                        self.rotations[flat_params[vox, 0]] +
                        self.regressors[0][0] * flat_params[vox, 2])
                else:
                    this_relative = (
                        flat_params[vox, 1] *
                        self.rotations[flat_params[vox, 0]] +
                        self.regressors[0][0] * flat_params[vox, 2])

                    if self.mode == 'signal_attenuation':
                        this_relative = 1 - this_relative

                out_flat[vox] = this_relative * self._flat_S0[vox]
            else:
                out_flat[vox] = np.nan

        out = ozu.nans(self.signal.shape)
        out[self.mask] = out_flat

        return out
Example #16
0
    def fit(self):
        """
        Predict the signal based on the kernel model fit
        """
        if self.verbose:
            print("Predicting signal from SparseKernelModel")
            prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split('.')[-1]
            f_name = this_class + '.' + inspect.stack()[0][3]

        out_flat = np.zeros(self._flat_signal.shape)
        flat_params = self.model_params[self.mask]

        # We will use a cached fit object generated in the first iteration
        _fit_obj = None
        # And the vertices are the b vectors:
        _verts = self.bvecs[:, self.b_idx].T
        for vox in xrange(out_flat.shape[0]):
            this_fit = self.kernel_model.SparseKernelFit(flat_params[vox][1:],
                                                         flat_params[vox][0],
                                                         model=self._km)

            this_relative = this_fit.predict(cache=_fit_obj, vertices=_verts)
            _fit_obj = this_fit  # From now on, we will use this cached object
            _verts = None  # And set the verts input to None, so that it is
            # ignored in future iterations

            out_flat[vox] = this_relative * self._flat_S0[vox]

            if self.verbose:
                prog_bar.animate(vox, f_name=f_name)

        out = ozu.nans(self.signal.shape)
        out[self.mask] = out_flat
        return out
Example #17
0
    def fit_angle(self):
        """
        The angle between the two primary peaks in the ODF
        
        """
        out_flat = np.zeros(self._flat_signal.shape[0])
        flat_odf = self.odf[self.mask]
        for vox in xrange(out_flat.shape[0]):
            if np.any(np.isnan(flat_odf[vox])):
                out_flat[vox] = np.nan
            else:
                p, i = recspeed.local_maxima(flat_odf[vox], self.odf_verts[1])
                mask = p > 0.5 * np.max(p)
                p = p[mask]
                i = i[mask]

                if len(p) < 2:
                    out_flat[vox] = np.nan
                else:
                    out_flat[vox] = np.rad2deg(ozu.vector_angle(
                                        self.odf_verts[0][i[0]],
                                        self.odf_verts[0][i[1]]))

        out = ozu.nans(self.signal.shape[:3])
        out[self.mask] = out_flat
        return out
Example #18
0
    def model_diffusion(self, vertices=None, mode='ADC'):
        """
        Calculate the ADC/diffusion distance on a novel set of vertices

        Parameters
        ----------
        vertices : an n by 3 array

        """
        # If none are provided, use the measurement points:
        if vertices is None:
            self.bvecs[:, self.b_idx]

        # Start by generating the values of the rotations we use in these
        # coordinates on the sphere
        rotations = self._calc_rotations(vertices, mode=mode)

        if self.verbose:
            print("Predicting signal from CanonicalTensorModel")

        out_flat = np.empty((self._flat_signal.shape[0], vertices.shape[-1]))
        flat_params = self.model_params[self.mask]
        for vox in xrange(out_flat.shape[0]):
            if ~np.isnan(flat_params[vox, 1]):
                this_dist = flat_params[vox, 1] * rotations[flat_params[vox,
                                                                        0]]
                out_flat[vox] = this_dist
            else:
                out_flat[vox] = np.nan

        out = ozu.nans(self.signal.shape[:3] + (vertices.shape[-1], ))
        out[self.mask] = out_flat

        return out
Example #19
0
    def fit(self):
        """
        Predict the data from the fit of the SparseDeconvolutionModel
        """
        if self.verbose:
            msg = "Predicting signal from SparseDeconvolutionModel"
            msg += " with %s"%self.solver
            print(msg)
        
        iso_regressor, tensor_regressor, fit_to = self.regressors
        out_flat = np.empty(self._flat_signal.shape)
        
        for vox in xrange(self._n_vox):
            this_params = self._flat_params[vox]
            this_params[np.isnan(this_params)] = 0.0             
            if self.mode == 'log':
                this_relative=np.exp(np.dot(self.design_matrix, this_params)+
                                     np.mean(fit_to.T[vox]))
            else:     
                this_relative = (np.dot(self.design_matrix, this_params) + 
                                 np.mean(fit_to.T[vox]))
            if (self.mode == 'relative_signal' or self.mode=='normalize' or
                self.mode=='log'):
                this_pred_sig = this_relative * self._flat_S0[vox]
            elif self.mode == 'signal_attenuation':
                this_pred_sig =  (1 - this_relative) * self._flat_S0[vox]

            # Fit scale and offset:
            #a,b = np.polyfit(this_pred_sig, self._flat_signal[vox], 1)
            # out_flat[vox] = a*this_pred_sig + b
            out_flat[vox] = this_pred_sig 
        out = ozu.nans(self.signal.shape)
        out[self.mask] = out_flat

        return out
Example #20
0
    def fit(self):
        """
        This is the signal estimated from the odf.
        """
        if self.verbose:
            print("Predicting signal from SphericalHarmonicsModel")
            prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split(".")[-1]
            f_name = this_class + "." + inspect.stack()[0][3]

        # Reshape the odf to be one voxel per row:
        flat_odf = self.odf[self.mask]
        pred_sig = np.empty(flat_odf.shape)

        for vox in xrange(pred_sig.shape[0]):
            # Predict based on the convolution:
            this_pred_sig = self.response_function.convolve_odf(flat_odf[vox], self._flat_S0[vox])

            # We might have a scaling and an offset in addition, so let's fit
            # those in each voxel based on the signal:
            a, b = np.polyfit(this_pred_sig, self._flat_signal[vox], 1)
            pred_sig[vox] = a * this_pred_sig + b

            if self.verbose:
                prog_bar.animate(vox, f_name=f_name)

        # Pack it back into a volume shaped thing:
        out = ozu.nans(self.signal.shape)
        out[self.mask] = pred_sig
        return out
Example #21
0
    def fit_angle(self):
        """
        The angle between the two primary peaks in the ODF
        
        """
        out_flat = np.zeros(self._flat_signal.shape[0])
        flat_odf = self.odf[self.mask]
        for vox in xrange(out_flat.shape[0]):
            if np.any(np.isnan(flat_odf[vox])):
                out_flat[vox] = np.nan
            else:
                p, i = recspeed.local_maxima(flat_odf[vox], self.odf_verts[1])
                mask = p > 0.5 * np.max(p)
                p = p[mask]
                i = i[mask]

                if len(p) < 2:
                    out_flat[vox] = np.nan
                else:
                    out_flat[vox] = np.rad2deg(
                        ozu.vector_angle(self.odf_verts[0][i[0]],
                                         self.odf_verts[0][i[1]]))

        out = ozu.nans(self.signal.shape[:3])
        out[self.mask] = out_flat
        return out
Example #22
0
def relative_mae(model1, model2):
    """
    Given two model objects, compare the model fits to signal-to-signal
    reliability in the mean absolute error sense
    """
    # Assume that the last dimension is the signal dimension, so the dimension
    # across which the mae will be calculated:
    out = ozu.nans(model1.shape[:-1])

    sig1 = model1.signal[model1.mask]
    sig2 = model2.signal[model2.mask]
    fit1 = model1.fit[model1.mask]
    fit2 = model2.fit[model2.mask]

    signal_mae = ozu.mae(sig1, sig2)
    fit1_mae = ozu.mae(fit1, sig2)
    fit2_mae = ozu.mae(fit2, sig1)

    # Average in each element:
    fit_mae = (fit1_mae + fit2_mae) / 2.

    rel_mae = fit_mae / signal_mae

    out[model1.mask] = rel_mae

    return out
Example #23
0
    def predict(self, sphere, bvals=None):
        """
        Predict the values of the signal on a novel sphere (not neccesarily
        measured points) in every voxel

        Parameters
        ----------
        sphere : 3 x n array
        
        """
        if self.verbose:
            print("Predicting signal from TensorModel")
        if bvals is None:
            # Assume there's only one b value and you know where to find it:
            bvals = self.bvals[self.b_idx][0] * np.ones(sphere.shape[-1])
        else:
            # If you gave them as input, you need to scale them:
            bvals = bvals / float(self.scaling_factor)

        pred_adc_flat = self.predict_adc(sphere)[self.mask]
        predict_flat = np.empty(pred_adc_flat.shape)

        out = ozu.nans(self.signal.shape[:3] + (sphere.shape[-1], ))
        for ii in xrange(len(predict_flat)):
            predict_flat[ii] = ozt.stejskal_tanner(self._flat_S0[ii], bvals,
                                                   pred_adc_flat[ii])

        out[self.mask] = predict_flat
        return out
Example #24
0
def pdd_reliability(model1, model2):
    """

    Compute the angle between the first PDD in two models.

    Parameters
    ----------
    model1, model2: two objects from a class inherited from BaseModel.
       Must implement an auto_attr class method 'principal_diffusion_direction',
       which returns arrays of 3-vectors representing a direction in three space
       which is the principal diffusion direction in each voxel. Some models
       will have more than one principal diffusion direction in each voxel. In
       that case, the first direction in each voxel will be used to represent
       that voxel. 
    
    """
    vol_shape = model1.shape[:3]
    pdd1 = model1.principal_diffusion_direction[model1.mask]
    pdd2 = model2.principal_diffusion_direction[model2.mask]

    # Some models create not only the first PDD, but subsequent directions as
    # well, so If we have a lot of PDD, we take only the first one:
    if len(pdd1.shape) == 3:
        pdd1 = pdd1[:, 0]
    if len(pdd2.shape) == 3:
        pdd2 = pdd2[:, 0]

    out_flat = np.empty(pdd1.shape[0])
    for vox in xrange(pdd1.shape[0]):
        this_ang = np.rad2deg(ozu.vector_angle(pdd1[vox], pdd2[vox]))
        out_flat[vox] = np.min([this_ang, 180 - this_ang])

    out = ozu.nans(vol_shape)
    out[model1.mask] = out_flat
    return out
Example #25
0
    def odf(self):
        """
        The orientation distribution function estimated from the SparseKernel
        model  
        """
        _verts = self.odf_verts[
            0]  # These are the vertices on which we estimate
        # the ODF
        if self.verbose:
            prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split('.')[-1]
            f_name = this_class + '.' + inspect.stack()[0][3]

        out_flat = np.zeros((self._flat_signal.shape[0], _verts.shape[0]))
        flat_params = self.model_params[self.mask]

        # We are going to use cached computations in the fit object:
        _fit_obj = None  # Initially we don't have a cached fit object
        for vox in xrange(out_flat.shape[0]):
            this_fit = self.kernel_model.SparseKernelFit(flat_params[vox][1:],
                                                         flat_params[vox][0],
                                                         model=self._km)
            out_flat[vox] = this_fit.odf(cache=_fit_obj, vertices=_verts)
            _fit_obj = this_fit  # From now on, we will use this cached object
            _verts = None  # And we need to ignore the vertices, so that the
            # cached fit object can use the cached computation.

            if self.verbose:
                prog_bar.animate(vox, f_name=f_name)

        out = ozu.nans(self.signal.shape[:3] + (out_flat.shape[-1], ))
        out[self.mask] = out_flat
        return out
Example #26
0
def overfitting_index(model1, model2):
    """
    How badly is the model overfitting? This can be assessed by comparing the
    RMSE of the model compared to the fit data (or learning set), relative to
    the RMSE of the model on another data set (or testing set)
    """
    sig1 = model1.signal[model1.mask]
    sig2 = model2.signal[model2.mask]
    fit1 = model1.fit[model1.mask]
    fit2 = model2.fit[model2.mask]

    rmse_train1 = ozu.rmse(fit1, sig1)
    rmse_train2 = ozu.rmse(fit2, sig2)

    rmse_test1 = ozu.rmse(fit1, sig2)
    rmse_test2 = ozu.rmse(fit2, sig1)

    fit_rmse = (rmse_train1 + rmse_train2) / 2.
    predict_rmse = (rmse_test1 + rmse_test2) / 2.

    # The measure is a contrast index of the error on the training data vs. the
    # error on the testing data:
    overfit = (fit_rmse - predict_rmse) / (fit_rmse + predict_rmse)

    out = ozu.nans(model1.shape[:-1])
    out[model1.mask] = overfit

    return out
Example #27
0
    def model_params(self):
        """
        The diffusion tensor parameters estimated from the data, using dipy.
        If this calculation has already occurred, just load the data from a
        nifti file, which has shape x by y by z by 12, where the last dimension
        is the model params:

        evecs (9) + evals (3)
        
        """
        out = ozu.nans((self.data.shape[:3] + (12, )))

        flat_params = np.empty((self._flat_S0.shape[0], 12))

        # The file already exists:
        if os.path.isfile(self.params_file):
            if self.verbose:
                print("Loading TensorModel params from: %s" % self.params_file)
            out[self.mask] = ni.load(self.params_file).get_data()[self.mask]
        else:
            if self.verbose:
                print("Fitting TensorModel params using dipy")
            tensor_model = dti.TensorModel(self.gtab,
                                           fit_method=self.fit_method)
            for vox, vox_data in enumerate(self.data[self.mask]):
                flat_params[vox] = tensor_model.fit(vox_data).model_params

            out[self.mask] = flat_params
            # Save the params for future use:
            params_ni = ni.Nifti1Image(out, self.affine)
            # If we asked it to be temporary, no need to save anywhere:
            if self.params_file != 'temp':
                params_ni.to_filename(self.params_file)
        # And return the params for current use:
        return out
Example #28
0
    def _correlator(self, correlator, r_idx=0, square=True):
        """
        Helper function that uses a callable "func" to apply between two 1-d
        arrays. These 1-d arrays can have different outputs and the one we
        always want is the one which is r_idx into the output tuple 
        """

        val = np.empty(self._flat_signal.shape[0])

        for ii in xrange(len(val)):
            if r_idx >= 0:
                val[ii] = correlator(self._flat_signal[ii],
                                     self._flat_fit[ii])[r_idx]
            else:
                val[ii] = correlator(self._flat_signal[ii], self._flat_fit[ii])
        if square:
            if has_numexpr:
                r_squared = numexpr.evaluate('val**2')
            else:
                r_squared = val**2
        else:
            r_squared = val

        # Re-package it into a volume:
        out = ozu.nans(self.shape[:3])
        out[self.mask] = r_squared

        out[out < -1] = -1.0
        out[out > 1] = 1.0

        return out
Example #29
0
def cross_predict(model1, model2):
    """
    Given two model objects, fit to the measurements conducted in one, and then
    predict the measurements in the other model's rotational coordinate frame
    (b vectors). Calculate relative RMSE on that prediction, relative to the
    noise in the measurement due to the rotation. Average across both
    directions of this operation.

    """

    out = ozu.nans(model1.shape[:-1])

    sig1 = model1.signal[model1.mask]
    sig2 = model2.signal[model2.mask]
    # Cross predict, using the parameters from one model to predict the
    # measurements in the other model's b vectors:
    predict1 = model1.predict(model2.bvecs[:, model2.b_idx])[model1.mask]
    predict2 = model2.predict(model1.bvecs[:, model1.b_idx])[model2.mask]

    signal_rmse = ozu.rmse(sig1, sig2)
    predict1_rmse = ozu.rmse(predict1, sig2)
    predict2_rmse = ozu.rmse(predict2, sig1)

    # Average in each element:
    predict_rmse = (predict1_rmse + predict2_rmse) / 2.
    rel_rmse = predict_rmse / signal_rmse

    out[model1.mask] = rel_rmse

    return out
Example #30
0
def relative_mae(model1, model2):
    """
    Given two model objects, compare the model fits to signal-to-signal
    reliability in the mean absolute error sense
    """
    # Assume that the last dimension is the signal dimension, so the dimension
    # across which the mae will be calculated: 
    out = ozu.nans(model1.shape[:-1])
    
    sig1 = model1.signal[model1.mask]
    sig2 = model2.signal[model2.mask]
    fit1 = model1.fit[model1.mask]
    fit2 = model2.fit[model2.mask]

    signal_mae = ozu.mae(sig1, sig2)
    fit1_mae = ozu.mae(fit1, sig2)
    fit2_mae = ozu.mae(fit2, sig1)

    # Average in each element:
    fit_mae = (fit1_mae + fit2_mae) / 2.

    rel_mae = fit_mae/signal_mae

    out[model1.mask] = rel_mae

    return out
Example #31
0
def cross_predict(model1, model2):
    """
    Given two model objects, fit to the measurements conducted in one, and then
    predict the measurements in the other model's rotational coordinate frame
    (b vectors). Calculate relative RMSE on that prediction, relative to the
    noise in the measurement due to the rotation. Average across both
    directions of this operation.

    """

    out = ozu.nans(model1.shape[:-1])
    
    sig1 = model1.signal[model1.mask]
    sig2 = model2.signal[model2.mask]
    # Cross predict, using the parameters from one model to predict the
    # measurements in the other model's b vectors:
    predict1 = model1.predict(model2.bvecs[:, model2.b_idx])[model1.mask]
    predict2 = model2.predict(model1.bvecs[:, model1.b_idx])[model2.mask]

    signal_rmse = ozu.rmse(sig1, sig2)
    predict1_rmse = ozu.rmse(predict1, sig2)
    predict2_rmse = ozu.rmse(predict2, sig1)

    # Average in each element:
    predict_rmse = (predict1_rmse + predict2_rmse) / 2.
    rel_rmse = predict_rmse/signal_rmse

    out[model1.mask] = rel_rmse

    return out
Example #32
0
def overfitting_index(model1, model2):
    """
    How badly is the model overfitting? This can be assessed by comparing the
    RMSE of the model compared to the fit data (or learning set), relative to
    the RMSE of the model on another data set (or testing set)
    """
    sig1 = model1.signal[model1.mask]
    sig2 = model2.signal[model2.mask]
    fit1 = model1.fit[model1.mask]
    fit2 = model2.fit[model2.mask]

    rmse_train1 = ozu.rmse(fit1, sig1)
    rmse_train2 = ozu.rmse(fit2, sig2)

    rmse_test1 = ozu.rmse(fit1, sig2)
    rmse_test2 = ozu.rmse(fit2, sig1)

    fit_rmse = (rmse_train1 + rmse_train2) / 2.
    predict_rmse = (rmse_test1 + rmse_test2) /2. 

    # The measure is a contrast index of the error on the training data vs. the
    # error on the testing data:
    overfit = (fit_rmse - predict_rmse) / (fit_rmse + predict_rmse) 
    
    out = ozu.nans(model1.shape[:-1])    
    out[model1.mask] = overfit

    return out
Example #33
0
    def signal_reliability(self,
                           DWI2,
                           correlator=stats.pearsonr,
                           r_idx=0,
                           square=True):
        """
        Calculate the r-squared of the correlator function provided, in each
        voxel (across directions, not including b0) between this class instance
        and another class instance, provided as input.

        Parameters
        ----------
        DWI2: Another DWI class instance, with data that should look the same,
            if there wasn't any noise in the measurement

        correlator: callable. This is a function that calculates a measure of
             correlation (e.g. stats.pearsonr, or stats.linregress)

        r_idx: int,
            points to the location of r within the tuple returned by
            the correlator callable if r_idx is negative, that means that the
            return value is not a tuple and should be treated as the value
            itself.

        square: bool,
            If square is True, that means that the value returned from
            the correlator should be squared before returning it, otherwise,
            the value itself is returned.
            
        """
                
        val = np.empty(self._flat_signal.shape[0])

        for ii in xrange(len(val)):
            if r_idx>=0:
                val[ii] = correlator(self._flat_signal[ii],
                                     DWI2._flat_signal[ii])[r_idx] 
            else:
                val[ii] = correlator(self._flat_signal[ii],
                                     DWI2._flat_signal[ii]) 

        if square:
            if has_numexpr:
                r_squared = numexpr.evaluate('val**2')
            else:
                r_squared = val**2
        else:
            r_squared = val
        
        # Re-package it into a volume:
        out = ozu.nans(self.shape[:3])
        out[self.mask] = r_squared

        out[out<-1]=-1.0
        out[out>1]=1.0

        return out 
Example #34
0
    def signal_reliability(self,
                           DWI2,
                           correlator=stats.pearsonr,
                           r_idx=0,
                           square=True):
        """
        Calculate the r-squared of the correlator function provided, in each
        voxel (across directions, not including b0) between this class instance
        and another class instance, provided as input.

        Parameters
        ----------
        DWI2: Another DWI class instance, with data that should look the same,
            if there wasn't any noise in the measurement

        correlator: callable. This is a function that calculates a measure of
             correlation (e.g. stats.pearsonr, or stats.linregress)

        r_idx: int,
            points to the location of r within the tuple returned by
            the correlator callable if r_idx is negative, that means that the
            return value is not a tuple and should be treated as the value
            itself.

        square: bool,
            If square is True, that means that the value returned from
            the correlator should be squared before returning it, otherwise,
            the value itself is returned.
            
        """

        val = np.empty(self._flat_signal.shape[0])

        for ii in xrange(len(val)):
            if r_idx >= 0:
                val[ii] = correlator(self._flat_signal[ii],
                                     DWI2._flat_signal[ii])[r_idx]
            else:
                val[ii] = correlator(self._flat_signal[ii],
                                     DWI2._flat_signal[ii])

        if square:
            if has_numexpr:
                r_squared = numexpr.evaluate('val**2')
            else:
                r_squared = val**2
        else:
            r_squared = val

        # Re-package it into a volume:
        out = ozu.nans(self.shape[:3])
        out[self.mask] = r_squared

        out[out < -1] = -1.0
        out[out > 1] = 1.0

        return out
Example #35
0
    def model_adc(self):
        """
        
        """
        fit_rel_sig = self.fit[self.mask]/self._flat_S0.reshape(self._n_vox,1)
        log_rel_sig = np.log(fit_rel_sig)

        out_flat = log_rel_sig/(-self.bvals[self.b_idx][0])
        out = ozu.nans(self.signal.shape)
        out[self.mask] = out_flat
        return out
Example #36
0
 def RMSE(self):
     """
     We need to overload this to make the shapes to broadcast into make
     sense. XXX Need to consider whether it makes sense to take out our
     overloaded signal and relative_signal above, so we might not need this
     either... 
     """
     out = ozu.nans(self.signal.shape[:3])
     flat_fit = self.fit[self.mask][:,:self.fit.shape[-1]-1]
     flat_rmse = ozu.rmse(self._flat_signal, flat_fit)                
     out[self.mask] = flat_rmse
     return out
Example #37
0
    def tensors(self):
        out = ozu.nans(self.evecs.shape)
        evals = self.evals[self.mask]
        evecs = self.evecs[self.mask]
        D_flat = np.empty(evecs.shape)
        for ii in xrange(len(D_flat)):
            Q = evecs[ii]
            L = evals[ii]
            D_flat[ii] = np.dot(Q * L, Q.T)

        out[self.mask] = D_flat
        return out
Example #38
0
 def RMSE(self):
     """
     We need to overload this to make the shapes to broadcast into make
     sense. XXX Need to consider whether it makes sense to take out our
     overloaded signal and relative_signal above, so we might not need this
     either... 
     """
     out = ozu.nans(self.signal.shape[:3])
     flat_fit = self.fit[self.mask][:, :self.fit.shape[-1] - 1]
     flat_rmse = ozu.rmse(self._flat_signal, flat_fit)
     out[self.mask] = flat_rmse
     return out
Example #39
0
    def tensors(self):
        out = ozu.nans(self.evecs.shape)
        evals = self.evals[self.mask]
        evecs = self.evecs[self.mask]
        D_flat = np.empty(evecs.shape)
        for ii in xrange(len(D_flat)):
            Q = evecs[ii]
            L = evals[ii]
            D_flat[ii] = np.dot(Q*L, Q.T)

        out[self.mask] = D_flat
        return out
    def predict_all(self):
        """
        Calculate the predicted signal for all the possible OLS solutions
        """
        # Get the bvec weights (we don't know how many...) and the
        # isotropic weights (which are always last):
        b_w = self.ols[:, :-1, :].copy().squeeze()
        i_w = self.ols[:, -1, :].copy().squeeze()

        # nan out the places where weights are negative:
        #b_w[b_w<0] = np.nan
        #i_w[i_w<0] = np.nan

        # A predicted signal for each voxel, for each rot_idx, for each
        # direction:
        flat_out = np.empty((self._flat_signal.shape[0], len(self.rot_idx),
                             self._flat_signal.shape[-1]))

        if self.verbose:
            print("Predicting all signals for MultiCanonicalTensorModel:")
            prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split('.')[-1]
            f_name = this_class + '.' + inspect.stack()[0][3]

        for vox in xrange(flat_out.shape[0]):
            for idx, rot_idx in enumerate(self.rot_idx):
                # The constant regressor gets added in first:
                this_relative = i_w[idx, vox] * self.regressors[0][0]
                # And we add the different canonicals on top of that:
                this_relative += (
                    np.dot(
                        b_w[idx, :, vox],
                        # The tensor regressors are different in cases where we
                        # are fitting to relative/attenuation signal, so grab that
                        # from the regressors attr:
                        np.array([self.regressors[1][x] for x in rot_idx])))

                if self.mode == 'relative_signal' or self.mode == 'normalize':
                    flat_out[vox, idx] = this_relative * self._flat_S0[vox]
                elif self.mode == 'signal_attenuation':
                    flat_out[vox,
                             idx] = (1 - this_relative) * self._flat_S0[vox]

            if self.verbose:
                prog_bar.animate(vox, f_name=f_name)

        out = ozu.nans(self.signal.shape[:3] + (len(self.rot_idx), ) +
                       (self.signal.shape[-1], ))
        out[self.mask] = flat_out

        return out
Example #41
0
    def fit(self):
        if self.verbose:
            print("Predicting signal from TensorModel")
        adc_flat = self.model_adc[self.mask]
        fit_flat = np.empty(adc_flat.shape)
        out = ozu.nans(self.signal.shape)

        for ii in xrange(len(fit_flat)):
            fit_flat[ii] = ozt.stejskal_tanner(self._flat_S0[ii],
                                               self.bvals[self.b_idx],
                                               adc_flat[ii])

        out[self.mask] = fit_flat
        return out
    def predict_all(self):
        """
        Calculate the predicted signal for all the possible OLS solutions
        """
        # Get the bvec weights (we don't know how many...) and the
        # isotropic weights (which are always last): 
        b_w = self.ols[:,:-1,:].copy().squeeze()
        i_w = self.ols[:,-1,:].copy().squeeze()
        
        # nan out the places where weights are negative: 
        #b_w[b_w<0] = np.nan
        #i_w[i_w<0] = np.nan

        # A predicted signal for each voxel, for each rot_idx, for each
        # direction: 
        flat_out = np.empty((self._flat_signal.shape[0],
                           len(self.rot_idx),
                           self._flat_signal.shape[-1]))

        if self.verbose:
            print("Predicting all signals for MultiCanonicalTensorModel:")
            prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split('.')[-1]
            f_name = this_class + '.' + inspect.stack()[0][3]

        for vox in xrange(flat_out.shape[0]):
            for idx, rot_idx in enumerate(self.rot_idx):
                # The constant regressor gets added in first:
                this_relative = i_w[idx,vox] * self.regressors[0][0]
                # And we add the different canonicals on top of that:
                this_relative += (np.dot(b_w[idx,:,vox],
                # The tensor regressors are different in cases where we
                # are fitting to relative/attenuation signal, so grab that
                # from the regressors attr:
                np.array([self.regressors[1][x] for x in rot_idx])))

                if self.mode == 'relative_signal' or self.mode=='normalize':
                    flat_out[vox, idx] = this_relative * self._flat_S0[vox]
                elif self.mode == 'signal_attenuation':
                    flat_out[vox, idx] = (1-this_relative)*self._flat_S0[vox]

            if self.verbose: 
                prog_bar.animate(vox, f_name=f_name)

        out = ozu.nans(self.signal.shape[:3] + 
                       (len(self.rot_idx),) + 
                       (self.signal.shape[-1],))
        out[self.mask] = flat_out

        return out
Example #43
0
    def model_params(self):
        """

        Use sklearn to fit the parameters:

        """
        # The file already exists: 
        if os.path.isfile(self.params_file) and not self.force_recompute:
            if self.verbose:
                print("Loading params from file: %s"%self.params_file)
            # Get the cached values and be done with it:
            return ni.load(self.params_file).get_data()

        else:

            if self.verbose:
                print("Fitting SparseDeconvolutionModel:")
                prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
                this_class = str(self.__class__).split("'")[-2].split('.')[-1]
                f_name = this_class + '.' + inspect.stack()[0][3]

            iso_regressor, tensor_regressor, fit_to = self.regressors

            if self._n_vox==1:
                # We have to be a bit (too) clever here, so that the indexing
                # below works out:
                fit_to = np.array([fit_to]).T

            # One weight for each rotation
            params = np.empty((self._n_vox, self.rotations.shape[0]))
                
            for vox in xrange(self._n_vox):
                # Call out to the core fitting routine: 
                params[vox] = self._fit_it(fit_to.T[vox], self.design_matrix)
                if self.verbose:
                    prog_bar.animate(vox, f_name=f_name)
                    
            out_params = ozu.nans((self.signal.shape[:3] + 
                                        (self.design_matrix.shape[-1],)))
            
            out_params[self.mask] = params
            # Save the params to a file: 
            params_ni = ni.Nifti1Image(out_params, self.affine)
            if self.params_file != 'temp':
                if self.verbose:
                    print("Saving params to file: %s"%self.params_file)
                params_ni.to_filename(self.params_file)

            # And return the params for current use:
            return out_params
Example #44
0
    def odf_peak_angles(self):
        """
        Calculate the angle between the two largest peaks in the odf peak
        distribution
        """
        out_flat = ozu.nans(self._flat_signal.shape[0])
        flat_odf_peaks = self.odf_peaks[self.mask]
        for vox in xrange(out_flat.shape[0]):
            if ~np.isnan(flat_odf_peaks[vox][0]):
                idx1 = np.argsort(flat_odf_peaks[vox])[-1]
                idx2 = np.argsort(flat_odf_peaks[vox])[-2]
                if idx1 != idx2:
                    ang = np.rad2deg(ozu.vector_angle(
                        self.bvecs[:,self.b_idx].T[idx1],
                        self.bvecs[:,self.b_idx].T[idx2]))

                    ang = np.min([ang, 180-ang])
                
                    out_flat[vox] = ang
                        
        out = ozu.nans(self.signal.shape[:3])
        out[self.mask] = out_flat
        return out
Example #45
0
    def model_diffusion_distance(self):
        """

        The diffusion distance implied by the model parameters
        """
        tensors_flat = self.tensors[self.mask]
        dist_flat = np.empty(self._flat_signal.shape)        
        for vox in xrange(len(dist_flat)):
            dist_flat[vox]=ozt.diffusion_distance(self.bvecs[:, self.b_idx],
                                                  tensors_flat[vox])
        out = ozu.nans(self.signal.shape)
        out[self.mask] = dist_flat

        return out
Example #46
0
    def fit(self):
        if self.verbose:
            print("Predicting signal from TensorModel")
        adc_flat = self.model_adc[self.mask]
        fit_flat = np.empty(adc_flat.shape)
        out = ozu.nans(self.signal.shape)

        for ii in xrange(len(fit_flat)):
            fit_flat[ii] = ozt.stejskal_tanner(self._flat_S0[ii],
                                               self.bvals[self.b_idx],
                                               adc_flat[ii])

        out[self.mask] = fit_flat
        return out
Example #47
0
    def model_diffusion_distance(self):
        """

        The diffusion distance implied by the model parameters
        """
        tensors_flat = self.tensors[self.mask]
        dist_flat = np.empty(self._flat_signal.shape)
        for vox in xrange(len(dist_flat)):
            dist_flat[vox] = ozt.diffusion_distance(self.bvecs[:, self.b_idx],
                                                    tensors_flat[vox])
        out = ozu.nans(self.signal.shape)
        out[self.mask] = dist_flat

        return out
Example #48
0
    def model_params(self):
        """
        Find the model parameters using least-squares optimization.
        """
        if self.model_form == 'constrained':
            n_params = 3
        elif self.model_form=='flexible' or self.model_form == 'ball_and_stick':
            n_params = 4 
        else:
            e_s = "%s is not a recognized model form"% self.model_form
            raise ValueError(e_s)

        params = np.empty((self.fit_signal.shape[0],n_params))

        if self.verbose:
            print('Fitting CanonicalTensorModelOpt:')
            prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split('.')[-1]
            f_name = this_class + '.' + inspect.stack()[0][3]

        # Initialize the starting conditions for the first voxel
        if self.model_form == 'constrained':
            this_params = 0, 0, np.mean(self.fit_signal[0])
        elif (self.model_form=='flexible' or
              self.model_form=='ball_and_stick'):
            this_params = (0, 0, np.mean(self.fit_signal[0]),
                           np.mean(self.fit_signal[0]))

        for vox in xrange(self.fit_signal.shape[0]):
            # From the second voxel and onwards, we use the end point of the
            # last voxel as the starting point for this voxel:
            start_params = this_params

            # Do the least-squares fitting (setting tolerance to a rather
            # lenient value?):
            this_params, status = opt.leastsq(self._err_func,
                                              start_params,
                                    args=(self.fit_signal[vox]),
                                              ftol=10e-5
                                              )
            params[vox] = this_params

            if self.verbose: 
                prog_bar.animate(vox, f_name=f_name)

        out_params = ozu.nans(self.signal.shape[:3] + (n_params,))
        out_params[self.mask] = np.array(params).squeeze()

        return out_params
Example #49
0
    def residuals(self):
        """
        The prediction-subtracted residual in each voxel
        """
        out = ozu.nans(self.signal.shape)
        sig = self._flat_signal
        fit = self._flat_fit
        
        if has_numexpr:
            out[self.mask] = numexpr.evaluate('sig - fit')

        else:
            out[self.mask] = sig - fit
            
        return out
Example #50
0
    def residuals(self):
        """
        The prediction-subtracted residual in each voxel
        """
        out = ozu.nans(self.signal.shape)
        sig = self._flat_signal
        fit = self._flat_fit

        if has_numexpr:
            out[self.mask] = numexpr.evaluate('sig - fit')

        else:
            out[self.mask] = sig - fit

        return out
Example #51
0
    def adc(self):
        """
        This is the empirically defined ADC:

        .. math::
        
            ADC = -log \frac{S}{b S0}

        """
        out = ozu.nans(self.signal.shape)

        out[self.mask] = ((-1 / self.bvals[self.b_idx][0]) *
                          np.log(self._flat_relative_signal))

        return out
Example #52
0
    def adc(self):
        """
        This is the empirically defined ADC:

        .. math::
        
            ADC = -log \frac{S}{b S0}

        """
        out = ozu.nans(self.signal.shape)
        
        out[self.mask] = ((-1/self.bvals[self.b_idx][0]) *
                        np.log(self._flat_relative_signal))

        return out
Example #53
0
    def model_params(self):
        """
        Find the model parameters using least-squares optimization.
        """
        if self.model_form == 'constrained':
            n_params = 3
        elif self.model_form == 'flexible' or self.model_form == 'ball_and_stick':
            n_params = 4
        else:
            e_s = "%s is not a recognized model form" % self.model_form
            raise ValueError(e_s)

        params = np.empty((self.fit_signal.shape[0], n_params))

        if self.verbose:
            print('Fitting CanonicalTensorModelOpt:')
            prog_bar = ozu.ProgressBar(self._flat_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split('.')[-1]
            f_name = this_class + '.' + inspect.stack()[0][3]

        # Initialize the starting conditions for the first voxel
        if self.model_form == 'constrained':
            this_params = 0, 0, np.mean(self.fit_signal[0])
        elif (self.model_form == 'flexible'
              or self.model_form == 'ball_and_stick'):
            this_params = (0, 0, np.mean(self.fit_signal[0]),
                           np.mean(self.fit_signal[0]))

        for vox in xrange(self.fit_signal.shape[0]):
            # From the second voxel and onwards, we use the end point of the
            # last voxel as the starting point for this voxel:
            start_params = this_params

            # Do the least-squares fitting (setting tolerance to a rather
            # lenient value?):
            this_params, status = opt.leastsq(self._err_func,
                                              start_params,
                                              args=(self.fit_signal[vox]),
                                              ftol=10e-5)
            params[vox] = this_params

            if self.verbose:
                prog_bar.animate(vox, f_name=f_name)

        out_params = ozu.nans(self.signal.shape[:3] + (n_params, ))
        out_params[self.mask] = np.array(params).squeeze()

        return out_params
Example #54
0
def rmse(model1, model2):
    """
    Calculate the voxel-wise RMSE between one model signal and the other model
    signal. 
    """
    out = ozu.nans(model1.shape[:-1])
    
    sig1 = model1.signal[model1.mask]
    sig2 = model2.signal[model2.mask]
    out_flat = np.empty(sig1.shape[0])
    
    for vox in xrange(sig1.shape[0]):
        out_flat[vox] = ozu.rmse(sig1[vox], sig2[vox])

    out[model1.mask] = out_flat
    return out
Example #55
0
    def RMSE(self):
        """
        The square-root of the mean of the squared residuals
        """

        # Preallocate the output:

        out = ozu.nans(self.data.shape[:3])
        res = self.residuals[self.mask]

        if has_numexpr:
            out[self.mask] = np.sqrt(np.mean(numexpr.evaluate('res ** 2'), -1))
        else:
            out[self.mask] = np.sqrt(np.mean(np.power(res, 2), -1))

        return out
Example #56
0
def rmse(model1, model2):
    """
    Calculate the voxel-wise RMSE between one model signal and the other model
    signal. 
    """
    out = ozu.nans(model1.shape[:-1])

    sig1 = model1.signal[model1.mask]
    sig2 = model2.signal[model2.mask]
    out_flat = np.empty(sig1.shape[0])

    for vox in xrange(sig1.shape[0]):
        out_flat[vox] = ozu.rmse(sig1[vox], sig2[vox])

    out[model1.mask] = out_flat
    return out
Example #57
0
    def predict_adc(self, sphere):
        """

        The ADC predicted on a sphere (containing points other than the bvecs)
        
        """
        out = ozu.nans(self.signal.shape[:3] + (sphere.shape[-1], ))
        tensors_flat = self.tensors[self.mask].reshape((-1, 3, 3))
        pred_adc_flat = np.empty((np.sum(self.mask), sphere.shape[-1]))

        for ii in xrange(len(pred_adc_flat)):
            pred_adc_flat[ii] = ozt.apparent_diffusion_coef(
                sphere, tensors_flat[ii])

        out[self.mask] = pred_adc_flat

        return out
Example #58
0
    def principal_diffusion_direction(self):
        """
        The direction in which the best fit canonical tensor is pointing
        (in x,y,z coordinates)
        """
        flat_params = self.model_params[self.mask]
        out_flat = np.empty(flat_params.shape)

        for vox in xrange(flat_params.shape[0]):
            if not np.isnan(flat_params[vox, 0]):
                out_flat[vox] = self.rot_vecs.T[flat_params[vox, 0]]
            else:
                out_flat[vox] = [np.nan, np.nan, np.nan]

        out = ozu.nans(self.model_params.shape)
        out[self.mask] = out_flat
        return out
Example #59
0
def relative_rmse(model1, model2):
    """
    Given two model objects, compare the model fits to signal-to-signal
    reliability in the root mean square error sense.

    Parameters
    ----------
    model1, model2: two objects from a class inherited from BaseModel

    Returns
    -------
    relative_RMSE: A measure of goodness of fit, relative to measurement
    reliability. The measure is larger than 1 when the model is worse than
    signal-to-signal reliability and smaller than 1 when the model is better. 

    Notes
    -----
    More specificially, we calculate the rmse from the fit in model1 to the
    signal in model2 and then vice-versa. We average between the two
    results. Then, we calculate the rmse from the signal in model1 to the
    signal in model2. We normalize the average model-to-signal rmse to the
    signal-to-signal rmse as a measure of goodness of fit of the model. 

    """
    # Assume that the last dimension is the signal dimension, so the dimension
    # across which the rmse will be calculated:
    out = ozu.nans(model1.shape[:-1])

    sig1 = model1.signal[model1.mask]
    sig2 = model2.signal[model2.mask]
    fit1 = model1.fit[model1.mask]
    fit2 = model2.fit[model2.mask]

    signal_rmse = ozu.rmse(sig1, sig2)
    fit1_rmse = ozu.rmse(fit1, sig2)
    fit2_rmse = ozu.rmse(fit2, sig1)

    # Average in each element:
    fit_rmse = (fit1_rmse + fit2_rmse) / 2.

    rel_rmse = fit_rmse / signal_rmse

    out[model1.mask] = rel_rmse

    return out
Example #60
0
def fit_reliability(model1, model2):
    """
    Compute the vector angle between the model-predicted signal in each voxel
    as a measure of model reliability. 
    """
    vol_shape = model1.shape[:3]

    fit1 = model1.fit[model1.mask]
    fit2 = model2.fit[model1.mask]

    out_flat = np.empty(fit1.shape[0])

    for vox in xrange(out_flat.shape[0]):
        out_flat[vox] = np.corrcoef(fit1[vox], fit2[vox])[0, 1]

    out = ozu.nans(vol_shape)
    out[model1.mask] = out_flat
    return out