Developer documentation
Version 3.0.3-105-gd3941f44
msmt_csd.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __dwi_sdeconv_msmt_csd_h__
18#define __dwi_sdeconv_msmt_csd_h__
19
20#include "header.h"
21#include "types.h"
22#include "dwi/gradient.h"
23#include "dwi/shells.h"
25#include "math/math.h"
26#include "math/SH.h"
27#include "math/ZSH.h"
28
30
31#define DEFAULT_MSMTCSD_LMAX 8
32#define DEFAULT_MSMTCSD_NORM_LAMBDA 1.0e-10
33#define DEFAULT_MSMTCSD_NEG_LAMBDA 1.0e-10
34
35namespace MR
36{
37 namespace DWI
38 {
39 namespace SDeconv
40 {
41
42 extern const App::OptionGroup MSMT_CSD_options;
43
45 public:
46
47 class Shared { MEMALIGN(Shared)
48 public:
49 Shared (const Header& dwi_header) :
50 grad (DWI::get_DW_scheme (dwi_header)),
51 shells (grad),
53 solution_min_norm_regularisation (DEFAULT_MSMTCSD_NORM_LAMBDA),
54 constraint_min_norm_regularisation (DEFAULT_MSMTCSD_NEG_LAMBDA) { shells.select_shells(false,false,false); }
55
56
57 void parse_cmdline_options()
58 {
59 using namespace App;
60 auto opt = get_options ("lmax");
61 if (opt.size())
62 lmax = parse_ints<uint32_t> (opt[0][0]);
63 opt = get_options ("directions");
64 if (opt.size())
65 HR_dirs = load_matrix (opt[0][0]);
66 opt = get_options ("norm_lambda");
67 if (opt.size())
68 solution_min_norm_regularisation = opt[0][0];
69 opt = get_options ("neg_lambda");
70 if (opt.size())
71 constraint_min_norm_regularisation = opt[0][0];
72 }
73
74
75
76 void set_responses (const vector<std::string>& files)
77 {
78 lmax_response.clear();
79 for (const auto& s : files) {
80 Eigen::MatrixXd r;
81 try {
82 r = load_matrix (s);
83 } catch (Exception& e) {
84 throw Exception (e, "File \"" + s + "\" is not a valid response function file");
85 }
86 responses.push_back (std::move (r));
87 }
88 prepare_responses();
89 response_files = files;
90 }
91
92 void set_responses (const vector<Eigen::MatrixXd>& matrices)
93 {
94 responses = matrices;
95 prepare_responses();
96 }
97
98
99
100 void init()
101 {
102 if (lmax.empty()) {
103 lmax = lmax_response;
104 for (size_t t = 0; t != num_tissues(); ++t) {
105 lmax[t] = std::min (uint32_t(DEFAULT_MSMTCSD_LMAX), lmax[t]);
106 }
107 } else {
108 if (lmax.size() != num_tissues())
109 throw Exception ("Number of lmaxes specified (" + str(lmax.size()) + ") does not match number of tissues (" + str(num_tissues()) + ")");
110 for (const auto i : lmax) {
111 if (i % 2)
112 throw Exception ("Each value of lmax must be a non-negative even integer");
113 }
114 }
115
116 for (size_t t = 0; t != num_tissues(); ++t) {
117 if (size_t(responses[t].rows()) != num_shells())
118 throw Exception ("number of rows in response functions must match number of b-value shells; "
119 "number of shells is " + str(num_shells()) + ", but file \"" + response_files[t] + "\" contains " + str(responses[t].rows()) + " rows");
120 // Pad response functions out to the requested lmax for this tissue
121 responses[t].conservativeResizeLike (Eigen::MatrixXd::Zero (num_shells(), Math::ZSH::NforL (lmax[t])));
122 }
123
125 // Set up the constrained least squares problem //
127
128 size_t nparams = 0;
129 uint32_t maxlmax = 0;
130 for (size_t i = 0; i < num_tissues(); i++) {
131 nparams += Math::SH::NforL (lmax[i]);
132 maxlmax = std::max (maxlmax, lmax[i]);
133 }
134
135 INFO ("initialising multi-tissue CSD for " + str(num_tissues()) + " tissue types, with " + str (nparams) + " parameters");
136
137 Eigen::MatrixXd C = Eigen::MatrixXd::Zero (grad.rows(), nparams);
138
139 vector<size_t> dwilist;
140 for (size_t i = 0; i != size_t(grad.rows()); i++)
141 dwilist.push_back(i);
142
143 Eigen::MatrixXd directions = DWI::gen_direction_matrix (grad, dwilist);
144 Eigen::MatrixXd SHT = Math::SH::init_transform (directions, maxlmax);
145 for (ssize_t i = 0; i < SHT.rows(); i++)
146 for (ssize_t j = 0; j < SHT.cols(); j++)
147 if (std::isnan (SHT(i,j)))
148 SHT(i,j) = 0.0;
149
150 // TODO: is this just computing the Associated Legrendre polynomials...?
151 Eigen::MatrixXd delta(1,2); delta << 0, 0;
152 Eigen::MatrixXd DSH__ = Math::SH::init_transform (delta, maxlmax);
153 Eigen::VectorXd DSH_ = DSH__.row(0);
154 Eigen::VectorXd DSH (maxlmax/2+1);
155 size_t j = 0;
156 for (ssize_t i = 0; i < DSH_.size(); i++)
157 if (DSH_[i] != 0.0) {
158 DSH[j] = DSH_[i];
159 j++;
160 }
161
162 size_t pbegin = 0;
163 for (size_t tissue_idx = 0; tissue_idx < num_tissues(); ++tissue_idx) {
164 const size_t tissue_lmax = lmax[tissue_idx];
165 const size_t tissue_n = Math::SH::NforL (tissue_lmax);
166 const size_t tissue_nmzero = tissue_lmax/2+1;
167
168 for (size_t shell_idx = 0; shell_idx < num_shells(); ++shell_idx) {
169 Eigen::VectorXd response_ = responses[tissue_idx].row (shell_idx);
170 response_ = (response_.array() / DSH.head (tissue_nmzero).array()).matrix();
171 Eigen::VectorXd fconv (tissue_n);
172 int li = 0; int mi = 0;
173 for (int l = 0; l <= int(tissue_lmax); l+=2) {
174 for (int m = -l; m <= l; m++) {
175 fconv[mi] = response_[li];
176 mi++;
177 }
178 li++;
179 }
180 vector<size_t> vols = shells[shell_idx].get_volumes();
181 for (size_t idx = 0; idx < vols.size(); idx++) {
182 Eigen::VectorXd SHT_(SHT.row (vols[idx]).head (tissue_n));
183 SHT_ = (SHT_.array()*fconv.array()).matrix();
184 C.row (vols[idx]).segment (pbegin, tissue_n) = SHT_;
185 }
186 }
187 pbegin += tissue_n;
188 }
189
190 vector<size_t> m (num_tissues());
191 vector<size_t> n (num_tissues());
192 size_t M = 0;
193 size_t N = 0;
194
195 Eigen::MatrixXd HR_SHT = Math::SH::init_transform (HR_dirs, maxlmax);
196
197 for (size_t i = 0; i != num_tissues(); i++) {
198 if (lmax[i] > 0)
199 m[i] = HR_dirs.rows();
200 else
201 m[i] = 1;
202 M += m[i];
203 n[i] = Math::SH::NforL (lmax[i]);
204 N += n[i];
205 }
206
207 Eigen::MatrixXd A (Eigen::MatrixXd::Zero (M, N));
208 size_t b_m = 0; size_t b_n = 0;
209 for (size_t i = 0; i != num_tissues(); i++) {
210 A.block (b_m,b_n,m[i],n[i]) = HR_SHT.block (0,0,m[i],n[i]);
211 b_m += m[i];
212 b_n += n[i];
213 }
214 problem = Math::ICLS::Problem<double> (C, A, Eigen::VectorXd(), 0,
215 solution_min_norm_regularisation, constraint_min_norm_regularisation);
216
217 INFO ("Multi-shell, multi-tissue CSD initialised successfully");
218 }
219
220
221 size_t num_shells() const { return shells.count(); }
222 size_t num_tissues() const { return responses.size(); }
223
224
225 const Eigen::MatrixXd grad;
226 DWI::Shells shells;
227 Eigen::MatrixXd HR_dirs;
228 vector<uint32_t> lmax, lmax_response;
229 vector<Eigen::MatrixXd> responses;
230 vector<std::string> response_files;
232 double solution_min_norm_regularisation, constraint_min_norm_regularisation;
233
234
235 private:
236
237 void prepare_responses() {
238 for (size_t t = 0; t != num_tissues(); ++t) {
239 Eigen::MatrixXd& r (responses[t]);
240 size_t n = 0;
241 for (size_t row = 0; row < size_t(r.rows()); row++) {
242 for (size_t col = 0; col < size_t(r.cols()); col++) {
243 if (r(row,col))
244 n = std::max (n, col+1);
245 }
246 }
247 // Clip off any empty columns, i.e. degrees containing zero coefficients for all shells
248 r.conservativeResize (r.rows(), n);
249 // Store the lmax for each tissue based on their response functions;
250 // if the user doesn't manually specify lmax, these will determine the
251 // lmax of each tissue ODF output, with a further default lmax=8
252 // restriction at that stage
253 lmax_response.push_back (Math::ZSH::LforN (r.cols()));
254 }
255 }
256
257 };
258
259
260
261
262 MSMT_CSD (const Shared& shared_data) :
263 niter (0),
264 shared (shared_data),
265 solver (shared.problem) { }
266
267 void operator() (const Eigen::VectorXd& data, Eigen::VectorXd& output) {
268 niter = solver (output, data);
269 }
270
271 size_t niter;
272 const Shared& shared;
273
274 private:
276
277
278 };
279
280
281
282 }
283 }
284}
285
286#endif
287
288
#define INFO(msg)
Definition: exception.h:74
constexpr double e
Definition: math.h:39
VectorType1 & delta(VectorType1 &delta_vec, const VectorType2 &unit_dir, int lmax)
Definition: SH.h:279
size_t NforL(int lmax)
the number of (even-degree) coefficients for the given value of lmax
Definition: SH.h:46
Eigen::Matrix< typename MatrixType::Scalar, Eigen::Dynamic, Eigen::Dynamic > init_transform(const MatrixType &dirs, const int lmax)
form the SH->amplitudes matrix
Definition: SH.h:82
size_t NforL(int lmax)
the number of (even-degree) coefficients for the given value of lmax
Definition: ZSH.h:42
size_t LforN(int N)
returns the largest lmax given N parameters
Definition: ZSH.h:54
#define DEFAULT_MSMTCSD_LMAX
Definition: msmt_csd.h:31
#define DEFAULT_MSMTCSD_NORM_LAMBDA
Definition: msmt_csd.h:32
#define DEFAULT_MSMTCSD_NEG_LAMBDA
Definition: msmt_csd.h:33
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()
Definition: predefined.h:51
const App::OptionGroup MSMT_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...
Definition: gradient.h:75
Definition: base.h:24
Eigen::Matrix< ValueType, Eigen::Dynamic, Eigen::Dynamic > load_matrix(const std::string &filename)
read matrix data into an Eigen::Matrix filename
Definition: math.h:196
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
#define MEMALIGN(...)
Definition: types.h:185