Developer documentation
Version 3.0.3-105-gd3941f44
gradient.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_gradient_h__
18#define __dwi_gradient_h__
19
20#include "app.h"
21#include "header.h"
22#include "dwi/shells.h"
23#include "file/config.h"
24#include "file/path.h"
26#include "math/sphere.h"
27#include "math/SH.h"
28
29
30namespace MR
31{
32 namespace App { class OptionGroup; }
33
34 namespace DWI
35 {
36
39
41 extern const char* const bvalue_scaling_description;
42
43
44
46
49 template <class MatrixType>
50 inline void check_DW_scheme (const Header& header, const MatrixType& grad)
51 {
52 if (!grad.rows())
53 throw Exception ("no valid diffusion gradient table found");
54 if (grad.cols() < 4)
55 throw Exception ("unexpected diffusion gradient table matrix dimensions");
56
57 if (header.ndim() >= 4) {
58 if (header.size (3) != (int) grad.rows())
59 throw Exception ("number of studies in base image (" + str(header.size(3))
60 + ") does not match number of rows in diffusion gradient table (" + str(grad.rows()) + ")");
61 }
62 else if (grad.rows() != 1)
63 throw Exception ("For images with less than four dimensions, gradient table can have one row only");
64 }
65
66
67
68
69
70
74 template <class MatrixType, class IndexVectorType>
75 inline Eigen::MatrixXd gen_direction_matrix (
76 const MatrixType& grad,
77 const IndexVectorType& dwi)
78 {
79 Eigen::MatrixXd dirs (dwi.size(),2);
80 for (size_t i = 0; i < dwi.size(); i++) {
81 dirs (i,0) = std::atan2 (grad (dwi[i],1), grad (dwi[i],0));
82 auto z = grad (dwi[i],2) / grad.row (dwi[i]).template head<3>().norm();
83 if (z >= 1.0)
84 dirs(i,1) = 0.0;
85 else if (z <= -1.0)
86 dirs (i,1) = Math::pi;
87 else
88 dirs (i,1) = std::acos (z);
89 }
90 return dirs;
91 }
92
93
94
95
96
97 template <class MatrixType>
98 default_type condition_number_for_lmax (const MatrixType& dirs, int lmax)
99 {
100 Eigen::MatrixXd g;
101 if (dirs.cols() == 2) // spherical coordinates:
102 g = dirs;
103 else // Cartesian to spherical:
104 g = Math::Sphere::cartesian2spherical (dirs).leftCols(2);
105
107 }
108
109
110
112
117 Eigen::MatrixXd load_bvecs_bvals (const Header& header, const std::string& bvecs_path, const std::string& bvals_path);
118
119
120
122
128 void save_bvecs_bvals (const Header&, const std::string&, const std::string&);
129
130
131
132 namespace
133 {
134 template <class MatrixType>
135 std::string scheme2str (const MatrixType& G)
136 {
137 std::string dw_scheme;
138 for (ssize_t row = 0; row < G.rows(); ++row) {
139 std::string line = str(G(row,0), 10);
140 for (ssize_t col = 1; col < G.cols(); ++col)
141 line += "," + str(G(row,col), 10);
142 add_line (dw_scheme, line);
143 }
144 return dw_scheme;
145 }
146 }
147
148
149
151
154 template <class MatrixType>
155 void set_DW_scheme (Header& header, const MatrixType& G)
156 {
157 if (!G.rows()) {
158 auto it = header.keyval().find ("dw_scheme");
159 if (it != header.keyval().end())
160 header.keyval().erase (it);
161 return;
162 }
163 try {
164 check_DW_scheme (header, G);
165 header.keyval()["dw_scheme"] = scheme2str (G);
166 } catch (Exception&) {
167 WARN ("attempt to add non-matching DW scheme to header - ignored");
168 }
169 }
170
171
172
174
181 Eigen::MatrixXd parse_DW_scheme (const Header& header);
182
183
184
186
193 Eigen::MatrixXd get_raw_DW_scheme (const Header& header);
194
195
196
199
200
201
203
211 template <class MatrixType>
212 void stash_DW_scheme (Header& header, const MatrixType& grad)
213 {
214 clear_DW_scheme (header);
215 if (grad.rows())
216 header.keyval()["prior_dw_scheme"] = scheme2str (grad);
217 }
218
219
220
221
222
225
227
240 Eigen::MatrixXd get_DW_scheme (const Header& header, BValueScalingBehaviour bvalue_scaling = BValueScalingBehaviour::Auto);
241
242
243
244
246
248 void export_grad_commandline (const Header& header);
249
250
252
263 template <class MatrixType>
264 Eigen::MatrixXd compute_SH2amp_mapping (
265 const MatrixType& directions,
266 bool lmax_from_command_line = true,
267 int default_lmax = 8)
268 {
269 int lmax = -1;
270 int lmax_from_ndir = Math::SH::LforN (directions.rows());
271 bool lmax_set_from_commandline = false;
272 if (lmax_from_command_line) {
273 auto opt = App::get_options ("lmax");
274 if (opt.size()) {
275 lmax_set_from_commandline = true;
276 lmax = to<int> (opt[0][0]);
277 if (lmax % 2)
278 throw Exception ("lmax must be an even number");
279 if (lmax < 0)
280 throw Exception ("lmax must be a non-negative number");
281 if (lmax > lmax_from_ndir) {
282 WARN ("not enough directions for lmax = " + str(lmax) + " - dropping down to " + str(lmax_from_ndir));
283 lmax = lmax_from_ndir;
284 }
285 }
286 }
287
288 if (lmax < 0) {
289 lmax = lmax_from_ndir;
290 if (lmax > default_lmax)
291 lmax = default_lmax;
292 }
293
294 INFO ("computing SH transform using lmax = " + str (lmax));
295
296 int lmax_prev = lmax;
297 Eigen::MatrixXd mapping;
298 do {
299 mapping = Math::SH::init_transform (directions, lmax);
300 const default_type cond = Math::condition_number (mapping);
301 if (cond < 10.0)
302 break;
303 WARN ("directions are poorly distributed for lmax = " + str(lmax) + " (condition number = " + str (cond) + ")");
304 if (cond < 100.0 || lmax_set_from_commandline)
305 break;
306 lmax -= 2;
307 } while (lmax >= 0);
308
309 if (lmax_prev != lmax)
310 WARN ("reducing lmax to " + str(lmax) + " to improve conditioning");
311
312 return mapping;
313 }
314
315
316
317
318
320
324 inline size_t lmax_for_directions (const Eigen::MatrixXd& directions,
325 const bool lmax_from_command_line = true,
326 const int default_lmax = 8)
327 {
328 const auto mapping = compute_SH2amp_mapping (directions, lmax_from_command_line, default_lmax);
329 return Math::SH::LforN (mapping.cols());
330 }
331
332
333 }
334}
335
336#endif
337
a class to hold a named list of Option's
A class to specify a command-line option.
#define WARN(msg)
Definition: exception.h:73
#define INFO(msg)
Definition: exception.h:74
constexpr double pi
Definition: math.h:40
size_t LforN(int N)
returns the largest lmax given N parameters
Definition: SH.h:70
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
const vector< ParsedOption > get_options(const std::string &name)
return all command-line options matching name
void stash_DW_scheme(Header &header, const MatrixType &grad)
'stash' the DW gradient table
Definition: gradient.h:212
Eigen::MatrixXd compute_SH2amp_mapping(const MatrixType &directions, bool lmax_from_command_line=true, int default_lmax=8)
get the matrix mapping SH coefficients to amplitudes
Definition: gradient.h:264
Eigen::MatrixXd get_raw_DW_scheme(const Header &header)
get the DW scheme as found in the headers or supplied at the command-line
App::Option bvalue_scaling_option
void save_bvecs_bvals(const Header &, const std::string &, const std::string &)
export gradient table in FSL format (bvecs/bvals)
void set_DW_scheme(Header &header, const MatrixType &G)
store the DW gradient encoding matrix in a header
Definition: gradient.h:155
size_t lmax_for_directions(const Eigen::MatrixXd &directions, const bool lmax_from_command_line=true, const int default_lmax=8)
get the maximum spherical harmonic order given a set of directions
Definition: gradient.h:324
void export_grad_commandline(const Header &header)
process GradExportOptions command-line options
App::OptionGroup GradImportOptions()
Eigen::MatrixXd parse_DW_scheme(const Header &header)
parse the DW gradient encoding matrix from a header
BValueScalingBehaviour
Definition: gradient.h:223
Eigen::MatrixXd load_bvecs_bvals(const Header &header, const std::string &bvecs_path, const std::string &bvals_path)
load and rectify FSL-style bvecs/bvals DW encoding files
BValueScalingBehaviour get_cmdline_bvalue_scaling_behaviour()
const char *const bvalue_scaling_description
default_type condition_number_for_lmax(const MatrixType &dirs, int lmax)
Definition: gradient.h:98
Eigen::MatrixXd get_DW_scheme(const Header &header, BValueScalingBehaviour bvalue_scaling=BValueScalingBehaviour::Auto)
get the fully-interpreted DW gradient encoding matrix
App::OptionGroup GradExportOptions()
void clear_DW_scheme(Header &)
clear any DW gradient encoding scheme from the header
void check_DW_scheme(const Header &header, const MatrixType &grad)
check that the DW scheme matches the DWI data in header
Definition: gradient.h:50
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
std::enable_if< VectorType1::IsVectorAtCompileTime, void >::type cartesian2spherical(const VectorType1 &xyz, VectorType2 &&az_el_r)
convert Cartesian coordinates to spherical coordinates
Definition: sphere.h:83
default_type condition_number(const M &data)
Definition: base.h:24
double default_type
the default type used throughout MRtrix
Definition: types.h:228
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
std::string & add_line(std::string &original, const std::string &new_line)
add a line to a string, taking care of inserting a newline if needed
Definition: mrtrix.h:81
InputImageType dwi