Exemple #1
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
Exemple #2
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
Exemple #3
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
    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
    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
Exemple #6
0
    def model_params(self):
        """
        Fit the parameters of the kernel model
        """

        # The file already exists:
        if os.path.isfile(self.params_file):
            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 params for 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]

            # 1 parameter for each basis function + 1 for the intercept:
            out_flat = np.empty(
                (self._flat_signal.shape[0], self.quad_points + 1))
            for vox in xrange(out_flat.shape[0]):
                this_fit = self._km.fit(self._flat_relative_signal[vox])
                beta = this_fit.beta
                intercept = this_fit.intercept
                # Fit the model, get the params:
                out_flat[vox] = np.hstack([intercept, beta])

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

            out_params = ozu.nans(self.signal.shape[:3] +
                                  (self.quad_points + 1, ))
            out_params[self.mask] = out_flat
            if self.params_file != 'temp':
                # Save the params for future use:
                params_ni = ni.Nifti1Image(out_params, self.affine)
                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
Exemple #7
0
    def voxel2fiber(self):
        """
        The first list 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 voxel, for each fiber, which
        nodes are in that voxel? 
        """
        # Preallocate for speed:

        # Make a voxels by fibers grid. If the fiber is in the voxel, the value
        # there will be 1, otherwise 0:
        v2f = np.zeros((len(self.fg_idx_unique.T), len(self.FG.fibers)))

        # This is a grid of size (fibers, maximal length of a fiber), so that
        # we can capture put in the voxel number in each fiber/node combination:
        v2fn = ozu.nans((len(self.FG.fibers),
                         np.max([f.coords.shape[-1] for f in self.FG])))

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

        # In each fiber:
        for f_idx, f in enumerate(self.FG.fibers):
            # In each voxel present in there:
            for vv in f.coords.astype(int).T:
                # What serial number is this voxel in the unique fiber indices:
                voxel_id = np.where((vv[0] == self.fg_idx_unique[0]) *
                                    (vv[1] == self.fg_idx_unique[1]) *
                                    (vv[2] == self.fg_idx_unique[2]))[0]
                # Add that combination to the grid:
                v2f[voxel_id, f_idx] += 1
                # All the nodes going through this voxel get its number:
                v2fn[f_idx][np.where(
                    (f.coords.astype(int)[0] == vv[0]) *
                    (f.coords.astype(int)[1] == vv[1]) *
                    (f.coords.astype(int)[2] == vv[2]))] = voxel_id

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

        return v2f, v2fn
Exemple #8
0
    def calibrate(self):
        """"
        This is the function to perform the calibration optimization on. When
        this is done, self.AD and self.RD will be set and parameter estimation
        can proceed as in the super-class

        """

        out = np.empty(
            (self.calibration_signal.shape[0], len(self.start_params)))

        if self.verbose:
            print('Calibrating for AD/RD')
            prog_bar = ozu.ProgressBar(self.calibration_signal.shape[0])
            this_class = str(self.__class__).split("'")[-2].split('.')[-1]
            f_name = this_class + '.' + inspect.stack()[0][3]

        for vox in xrange(self.calibration_signal.shape[0]):
            # Perform the fitting itself:
            #out[vox], ier = leastsqbound(self._err_func,
            #                             self.start_params,
            #                             bounds = bounds,
            #                             args=(self.calibration_signal[vox]),
            #                             **optim_kwds)

            out[vox], ier = opt.leastsq(self._err_func,
                                        self.start_params,
                                        args=(self.calibration_signal[vox]))

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

        # Set the object's AD/RD according to the calibration:
        self.ad = np.median(out[:, -2])
        self.rd = np.median(out[:, -1])
        # The isotropic component diffusivity is set to be the same as the
        # axial diffusivity in the fiber component:
        self.iso_diffusivity = self.ad

        return out
Exemple #9
0
    def fiber_signal(self):
        """
        The relative signal predicted along each fiber. 
        """

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

        sig = []
        for f_idx, f in enumerate(self.FG):
            sig.append(
                f.predicted_signal(self.bvecs[:, self.b_idx],
                                   self.bvals[self.b_idx],
                                   self.axial_diffusivity,
                                   self.radial_diffusivity))

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

        return sig
    def fit(self):
        """
        Predict the signal attenuation from the fit of the
        MultiCanonicalTensorModel 
        """

        if self.verbose:
            print("Predicting signal from 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]

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

        for vox in xrange(out_flat.shape[0]):
            # If there's a nan in there, just ignore this voxel and set it to
            # all nans:
            if ~np.any(np.isnan(flat_params[vox, 1])):
                b_w = flat_params[vox, 1:1 + self.n_canonicals]
                i_w = flat_params[vox, -1]
                # This gets saved as a float, but we can safely assume it's
                # going to be an integer:
                rot_idx = self.rot_idx[int(flat_params[vox, 0])]

                out_flat[vox] = (
                    np.dot(b_w, np.array([self.rotations[i]
                                          for i in rot_idx])) +
                    self.regressors[0][0] * i_w) * self._flat_S0[vox]
            else:
                out_flat[vox] = np.nan  # This gets broadcast to the right
                # length on assigment?
            if self.verbose:
                prog_bar.animate(vox, f_name=f_name)

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

        return out
Exemple #11
0
    def matrix(self):
        """
        The matrix of fiber-contributions to the DWI signal.
        """
        # Assign some local variables, for shorthand:
        vox_coords = self.fg_idx_unique.T
        n_vox = self.fg_idx_unique.shape[-1]
        n_bvecs = self.b_idx.shape[0]
        v2f, v2fn = self.voxel2fiber

        # 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)

        # And this will hold weights to soak up the isotropic component in each
        # voxel:
        i_matrix_sig = np.zeros(n_vox * n_bvecs)
        i_matrix_row = np.zeros(n_vox * n_bvecs)
        i_matrix_col = np.zeros(n_vox * n_bvecs)

        keep_ct1 = 0
        keep_ct2 = 0

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

        # In each voxel:
        for v_idx, vox in enumerate(vox_coords):
            # 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:
                pred_sig = np.zeros(n_bvecs)
                for n_idx in np.where(v2fn[f_idx] == v_idx)[0]:
                    relative_signal = self.fiber_signal[f_idx][n_idx]
                    if self.mode == 'relative_signal':
                        # Predict the signal and demean it, so that the isotropic
                        # part can carry that:
                        pred_sig += (relative_signal - np.mean(
                            self.relative_signal[vox[0], vox[1], vox[2]]))
                    elif self.mode == 'signal_attenuation':
                        pred_sig += ((1 - relative_signal) - np.mean(
                            1 - self.relative_signal[vox[0], vox[1], vox[2]]))

            # 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_ct1:keep_ct1+n_bvecs] =\
                np.arange(n_bvecs) + v_idx * n_bvecs
            f_matrix_col[keep_ct1:keep_ct1 +
                         n_bvecs] = np.ones(n_bvecs) * f_idx
            f_matrix_sig[keep_ct1:keep_ct1 + n_bvecs] = pred_sig
            keep_ct1 += n_bvecs

            # Put in the isotropic part in the other matrix:
            i_matrix_row[keep_ct2:keep_ct2+n_bvecs]=\
                np.arange(v_idx*n_bvecs, (v_idx + 1)*n_bvecs)
            i_matrix_col[keep_ct2:keep_ct2 +
                         n_bvecs] = v_idx * np.ones(n_bvecs)
            i_matrix_sig[keep_ct2:keep_ct2 + n_bvecs] = 1
            keep_ct2 += n_bvecs
            if self.verbose:
                prog_bar.animate(v_idx, f_name=f_name)

        # Allocate the sparse matrices, using the more memory-efficient 'csr'
        # format:
        fiber_matrix = sparse.coo_matrix(
            (f_matrix_sig, [f_matrix_row, f_matrix_col])).tocsr()
        iso_matrix = sparse.coo_matrix(
            (i_matrix_sig, [i_matrix_row, i_matrix_col])).tocsr()

        if self.verbose:
            print("Generated model matrices")

        return (fiber_matrix, iso_matrix)
    def model_params(self):
        """
        The model parameters.

        Similar to the CanonicalTensorModel, if a fit has ocurred, the data is
        cached on disk as a nifti file 

        If a fit hasn't occured yet, calling this will trigger a model fit and
        derive the parameters.

        In that case, the steps are as follows:

        1. Perform OLS fitting on all voxels in the mask, with each of the
           $\vec{b}$ combinations, choosing only sets for which all weights are
           non-negative. 

        2. Find the PDD combination that most readily explains the data (highest
           correlation coefficient between the data and the predicted signal)
           That will be the combination used to derive the fit for that voxel.

        """
        # The file already exists:
        if os.path.isfile(self.params_file):
            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:
            # Looks like we might need to do some fitting...

            # 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

            # Weight for each canonical tensor, plus a place for the index into
            # rot_idx and one more slot for the isotropic weight (at the end)
            params = np.empty(
                (self._flat_signal.shape[0], self.n_canonicals + 2))

            if self.verbose:
                print("Fitting 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]

            # Find the best OLS solution in each voxel:
            for vox in xrange(self._flat_signal.shape[0]):
                # We do this in each voxel (instead of all at once, which is
                # possible...) to not blow up the memory:
                vox_fits = np.empty((len(self.rot_idx), len(self.b_idx)))

                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':
                        vox_fits[idx] = this_relative * self._flat_S0[vox]
                    elif self.mode == 'signal_attenuation':
                        vox_fits[idx] = (1 -
                                         this_relative) * self._flat_S0[vox]

                # Find the predicted signal that best matches the original
                # signal attenuation. That will choose the direction for the
                # tensor we use:
                corrs = ozu.coeff_of_determination(self._flat_signal[vox],
                                                   vox_fits)

                idx = np.where(corrs == np.nanmax(corrs))[0]

                # Sometimes there is no good solution:
                if len(idx):
                    # In case more than one fits the bill, just choose the
                    # first one:
                    if len(idx) > 1:
                        idx = idx[0]

                    params[vox, :] = np.hstack([
                        idx,
                        np.array([x for x in b_w[idx, :, vox]]).squeeze(),
                        i_w[idx, vox]
                    ])
                else:
                    # In which case we set it to all nans:
                    params[vox, :] = np.hstack(
                        [np.nan, self.n_canonicals * (np.nan, ), np.nan])

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

            # Save the params for future use:
            out_params = ozu.nans(self.signal.shape[:3] + (params.shape[-1], ))
            out_params[self.mask] = np.array(params).squeeze()
            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
    def model_params(self):
        """
        The model parameters.

        Similar to the TensorModel, if a fit has ocurred, the data is cached on
        disk as a nifti file 

        If a fit hasn't occured yet, calling this will trigger a model fit and
        derive the parameters.

        In that case, the steps are as follows:

        1. Perform OLS fitting on all voxels in the mask, with each of the
           $\vec{b}$. Choose only the non-negative weights. 

        2. Find the PDD that most readily explains the data (highest
           correlation coefficient between the data and the predicted signal)
           and use that one to derive the fit for that voxel

        """

        # The file already exists:
        if os.path.isfile(self.params_file):
            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:
            # Looks like we might need to do some fitting...
            # Get the bvec weights and the isotropic weights
            b_w = self.ols[:, 0, :].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

            params = np.empty((self._flat_signal.shape[0], 3))
            if self.verbose:
                print("Fitting CanonicalTensorModel:")
                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]
            # Find the best OLS solution in each voxel:
            for vox in xrange(self._flat_signal.shape[0]):
                # We do this in each voxel (instead of all at once, which is
                # possible...) to not blow up the memory:
                vox_fits = np.empty(self.rotations.shape)
                for rot_i, rot in enumerate(self.rotations):
                    if self.mode == 'log':
                        this_sig = (
                            np.exp(b_w[rot_i, vox] * rot +
                                   self.regressors[0][0] * i_w[rot_i, vox]) *
                            self._flat_S0[vox])
                    else:
                        this_relative = (
                            b_w[rot_i, vox] * rot +
                            self.regressors[0][0] * i_w[rot_i, vox])
                        if self.mode == 'signal_attenuation':
                            this_relative = 1 - this_relative

                        this_sig = this_relative * self._flat_S0[vox]

                    vox_fits[rot_i] = this_sig

                # Find the predicted signal that best matches the original
                # relative signal. That will choose the direction for the
                # tensor we use:
                corrs = ozu.coeff_of_determination(self._flat_signal[vox],
                                                   vox_fits)
                idx = np.where(corrs == np.nanmax(corrs))[0]

                # Sometimes there is no good solution (maybe we need to fit
                # just an isotropic to all of these?):
                if len(idx):
                    # In case more than one fits the bill, just choose the
                    # first one:
                    if len(idx) > 1:
                        idx = idx[0]

                    params[vox, :] = np.array(
                        [idx, b_w[idx, vox], i_w[idx, vox]]).squeeze()
                else:
                    params[vox, :] = np.array([np.nan, np.nan, np.nan])

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

            # Save the params for future use:
            out_params = ozu.nans(self.signal.shape[:3] + (3, ))
            out_params[self.mask] = np.array(params).squeeze()
            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
Exemple #14
0
def isotropic_params(data,
                     bvals,
                     bvecs,
                     mask,
                     func,
                     factor=1000,
                     initial="preset",
                     bounds="preset",
                     params_file='temp',
                     signal="relative_signal"):
    """
    Finds the parameters of the given function to the given data
    that minimizes the sum squared errors.

    Parameters
    ----------
    data: 4 dimensional array
        Diffusion MRI data
    bvals: 1 dimensional array
        All b values
    mask: 3 dimensional array
        Brain mask of the data
    func: str or callable
        String indicating the mean model function to perform kfold
        cross-validation on.
    initial: tuple
        Initial values for the parameters.
    factor: int
        Integer indicating the scaling factor for the b values
    bounds: list
        List containing tuples indicating the bounds for each parameter in
        the mean model function.

    Returns
    -------
    param_out: 2 dimensional array
        Parameters that minimize the residuals
    fit_out: 2 dimensional array
        Model fitted means
    ss_err: 2 dimensional array
        Sum squared error between the model fitted means and the actual means
    """
    if isinstance(func, str):
        # Grab the function handle for the desired mean model
        func = globals()[func]

    # Get the initial values for the desired mean model
    if (bounds == "preset") | (initial == "preset"):
        all_params = initial_params(data,
                                    bvecs,
                                    bvals,
                                    func,
                                    mask=mask,
                                    params_file=params_file)
    if bounds == "preset":
        bounds = all_params[0]
    if initial == "preset":
        func_initial = all_params[1]
    else:
        this_initial = initial

    # Separate b values and grab their indices
    bval_list, b_inds, unique_b, rounded_bvals = ozu.separate_bvals(bvals)
    all_b_idx, b0_inds = _diffusion_inds(bvals, b_inds, rounded_bvals)

    # Divide the b values by a scaling factor first.
    b = bvals[all_b_idx] / factor
    flat_data = data[np.where(mask)]

    # Get the number of inputs to the mean diffusivity function
    param_num = len(inspect.getargspec(func)[0])

    # Pre-allocate the outputs:
    param_out = np.zeros((int(np.sum(mask)), param_num - 1))
    cod = ozu.nans(np.sum(mask))
    fit_out = ozu.nans(cod.shape + (len(all_b_idx), ))

    prog_bar = ozu.ProgressBar(flat_data.shape[0])

    for vox in np.arange(np.sum(mask)).astype(int):
        prog_bar.animate(vox)
        s0 = np.mean(flat_data[vox, b0_inds], -1)

        if initial == "preset":
            this_initial = func_initial[vox]

        input_signal = flat_data[vox, all_b_idx] / s0
        if signal == "log":
            input_signal = np.log(input_signal)

        if bounds == None:
            params, _ = opt.leastsq(err_func,
                                    this_initial,
                                    args=(b, input_signal, func))
        else:
            lsq_b_out = lsq.leastsqbound(err_func,
                                         this_initial,
                                         args=(b, input_signal, func),
                                         bounds=bounds)
            params = lsq_b_out[0]

        param_out[vox] = np.squeeze(params)
        fit_out[vox] = func(b, *params)
        cod[vox] = ozu.coeff_of_determination(input_signal, fit_out[vox])

    return param_out, fit_out, cod