17#ifndef __dwi_sdeconv_csd_h__
18#define __dwi_sdeconv_csd_h__
28#define NORM_LAMBDA_MULTIPLIER 0.0002
30#define DEFAULT_CSD_LMAX 8
31#define DEFAULT_CSD_NEG_LAMBDA 1.0
32#define DEFAULT_CSD_NORM_LAMBDA 1.0
33#define DEFAULT_CSD_THRESHOLD 0.0
34#define DEFAULT_CSD_NITER 50
51 Shared (
const Header& dwi_header) :
70 void parse_cmdline_options()
75 auto list = parse_ints<uint32_t> (opt[0][0]);
77 throw Exception (
"CSD algorithm expects a single lmax to be specified");
78 lmax_cmdline = list.front();
88 neg_lambda = opt[0][0];
91 norm_lambda = opt[0][0];
94 threshold = opt[0][0];
101 void set_response (
const std::string& path)
103 INFO (
"loading response function from file \"" + path +
"\"");
107 template <
class Derived>
108 void set_response (
const Eigen::MatrixBase<Derived>& in)
112 INFO (
"setting response function using even SH coefficients: " +
str (response.transpose()));
118 using namespace Math::SH;
121 throw Exception (
"data contain too few directions even for lmax = 2");
123 if (lmax_response <= 0)
124 throw Exception (
"response function does not contain anisotropic terms");
126 lmax = ( lmax_cmdline ? lmax_cmdline : std::min (lmax_response, uint32_t(
DEFAULT_CSD_LMAX)) );
128 if (lmax <= 0 || lmax % 2)
129 throw Exception (
"lmax must be a positive even integer");
131 assert (response.size());
132 lmax_response = std::min (lmax_response, std::min (lmax_data, lmax));
133 INFO (
"calculating even spherical harmonic components up to order " +
str (lmax_response) +
" for initialisation");
135 if (!init_filter.size())
136 init_filter = Eigen::VectorXd::Ones(3);
137 init_filter.conservativeResizeLike (Eigen::VectorXd::Zero (
Math::ZSH::NforL (lmax_response)));
141 RH.conservativeResizeLike (Eigen::VectorXd::Zero (
Math::ZSH::NforL (lmax)));
145 rconv.resize (fconv.cols(), fconv.rows());
146 fconv.diagonal().array() += 1.0e-2;
150 ssize_t l = 0, nl = 1;
151 for (ssize_t row = 0; row < rconv.rows(); ++row) {
156 rconv.row (row).array() *= init_filter[l] / RH[l];
161 INFO (
"calculating even spherical harmonic components up to order " +
str (lmax) +
" for output");
165 for (ssize_t col = 0; col < fconv.cols(); ++col) {
170 fconv.col (col).array() *= RH[l];
176 HR_trans.array() *= constraint_multiplier;
179 threshold *= constraint_multiplier;
182 assert (fconv.cols() <= HR_trans.cols());
183 M.resize (DW_dirs.rows(), HR_trans.cols());
184 M.leftCols (fconv.cols()) = fconv;
185 M.rightCols (M.cols() - fconv.cols()).setZero();
186 Mt_M.resize (M.cols(), M.cols());
187 Mt_M.triangularView<Eigen::Lower>() = M.transpose() * M;
193 Mt_M.diagonal().array() += norm_lambda;
196 INFO (
"constrained spherical deconvolution initialised successfully");
199 size_t nSH ()
const {
200 return HR_trans.cols();
203 Eigen::MatrixXd grad;
204 Eigen::VectorXd response, init_filter;
205 Eigen::MatrixXd DW_dirs, HR_dirs;
206 Eigen::MatrixXd rconv, HR_trans, M, Mt_M;
209 uint32_t lmax_response, lmax_data, lmax_cmdline, lmax;
220 CSD (
const Shared& shared_data) :
221 shared (shared_data),
222 work (shared.Mt_M.rows(), shared.Mt_M.cols()),
223 HR_T (shared.HR_trans.rows(), shared.HR_trans.cols()),
224 F (shared.HR_trans.cols()),
225 init_F (shared.rconv.rows()),
226 HR_amps (shared.HR_trans.rows()),
227 Mt_b (shared.HR_trans.cols()),
229 old_neg (shared.HR_trans.rows()) { }
231 CSD (
const CSD&) =
default;
235 template <
class VectorType>
236 void set (
const VectorType& DW_signals) {
237 F.head (shared.rconv.rows()) = shared.rconv * DW_signals;
238 F.tail (
F.size()-shared.rconv.rows()).setZero();
241 Mt_b = shared.M.transpose() * DW_signals;
247 for (ssize_t n = 0; n <
HR_amps.size(); n++)
248 if (
HR_amps[n] < shared.threshold)
254 work.triangularView<Eigen::Lower>() = shared.Mt_M.triangularView<Eigen::Lower>();
257 for (
size_t i = 0; i <
neg.size(); i++)
258 HR_T.row (i) = shared.HR_trans.row (
neg[i]);
259 auto HR_T_view =
HR_T.topRows (
neg.size());
260 work.triangularView<Eigen::Lower>() += HR_T_view.transpose() * HR_T_view;
263 F.noalias() =
llt.compute (
work.triangularView<Eigen::Lower>()).solve (
Mt_b);
270 const Eigen::VectorXd& FOD ()
const {
return F; }
273 const Shared& shared;
278 Eigen::LLT<Eigen::MatrixXd>
llt;
a class to hold a named list of Option's
Eigen::LLT< Eigen::MatrixXd > llt
const vector< size_t > & get_volumes() const
const Shell & largest() const
Shells & select_shells(const bool force_singleshell, const bool force_with_bzero, const bool force_without_bzero)
#define DEFAULT_CSD_NITER
#define DEFAULT_CSD_THRESHOLD
#define NORM_LAMBDA_MULTIPLIER
#define DEFAULT_CSD_NEG_LAMBDA
#define DEFAULT_CSD_NORM_LAMBDA
Eigen::Matrix< typename MatrixType::Scalar, Eigen::Dynamic, Eigen::Dynamic > pinv(const MatrixType &M)
return Moore-Penrose pseudo-inverse of M
size_t LforN(int N)
returns the largest lmax given N parameters
size_t NforL(int lmax)
the number of (even-degree) coefficients for the given value of lmax
Eigen::Matrix< typename MatrixType::Scalar, Eigen::Dynamic, Eigen::Dynamic > init_transform(const MatrixType &dirs, const int lmax)
form the SH->amplitudes matrix
VectorType1 & ZSH2RH(VectorType1 &rh, const VectorType2 &zsh)
size_t NforL(int lmax)
the number of (even-degree) coefficients for the given value of lmax
size_t LforN(int N)
returns the largest lmax given N parameters
const vector< ParsedOption > get_options(const std::string &name)
return all command-line options matching name
void init(int argc, const char *const *argv)
initialise MRtrix and parse command-line arguments
Eigen::MatrixXd electrostatic_repulsion_300()
const App::OptionGroup CSD_options
Eigen::MatrixXd get_DW_scheme(const Header &header, BValueScalingBehaviour bvalue_scaling=BValueScalingBehaviour::Auto)
get the fully-interpreted DW gradient encoding matrix
Eigen::MatrixXd gen_direction_matrix(const MatrixType &grad, const IndexVectorType &dwi)
convert the DW encoding matrix in grad into a azimuth/elevation direction set, using only the DWI vol...
Eigen::Matrix< ValueType, Eigen::Dynamic, Eigen::Dynamic > load_matrix(const std::string &filename)
read matrix data into an Eigen::Matrix filename
double default_type
the default type used throughout MRtrix
std::string str(const T &value, int precision=0)
Eigen::Matrix< ValueType, Eigen::Dynamic, 1 > load_vector(const std::string &filename)
read the vector data from filename