Skip to content

Commit 8805d0d

Browse files
authored
Merge pull request #3044 from MRtrix3/dwi2adc_interface
dwi2adc: Change IVIM interface
2 parents 0948693 + 5e27a75 commit 8805d0d

File tree

6 files changed

+155
-87
lines changed

6 files changed

+155
-87
lines changed

cpp/cmd/dwi2adc.cpp

Lines changed: 118 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
using namespace MR;
2626
using namespace App;
2727

28+
using value_type = float;
29+
30+
constexpr value_type ivim_cutoff_default = 120.0F;
31+
2832
// clang-format off
2933
void usage ()
3034
{
@@ -33,9 +37,9 @@ void usage ()
3337
SYNOPSIS = "Calculate ADC and/or IVIM parameters.";
3438

3539
DESCRIPTION
36-
+ "By default, the command will estimate the Apparent Diffusion Coefficient (ADC) "
37-
"using the isotropic mono-exponential model: S(b) = S(0) * exp(-D * b). "
38-
"The output consists of 2 volumes, respectively S(0) and D."
40+
+ "The command estimates the Apparent Diffusion Coefficient (ADC) "
41+
"using the isotropic mono-exponential model: S(b) = S(0) * exp(-ADC * b). "
42+
"The value of S(0) can be optionally exported using command-line option -szero."
3943

4044
+ "When using the -ivim option, the command will additionally estimate the "
4145
"Intra-Voxel Incoherent Motion (IVIM) parameters f and D', i.e., the perfusion "
@@ -45,126 +49,164 @@ void usage ()
4549
"the DWI data with b > cutoff, and the other parameters are estimated subsequently. "
4650
"The output consists of 4 volumes, respectively S(0), D, f, and D'."
4751

48-
+ "Note that this command ignores the gradient orientation entirely. This approach is "
49-
"therefore only suited for mean DWI (trace-weighted) images or for DWI data collected "
50-
"with isotropically-distributed gradient directions.";
52+
+ "Note that this command ignores the gradient orientation entirely. "
53+
"If a conventional DWI series is provided as input, "
54+
"all volumes will contribute equally toward the model fit "
55+
"irrespective of direction of diffusion sensitisation; "
56+
"DWI data should therefore ideally consist of isotropically-distributed gradient directions. "
57+
"The approach can alternatively be applied to mean DWI (trace-weighted) images.";
5158

5259
ARGUMENTS
53-
+ Argument ("input", "the input image.").type_image_in ()
54-
+ Argument ("output", "the output image.").type_image_out ();
60+
+ Argument ("input", "the input image").type_image_in()
61+
+ Argument ("output", "the output ADC image").type_image_out();
5562

5663
OPTIONS
57-
+ Option ("ivim", "also estimate IVIM parameters in 2-stage fit.")
64+
+ Option("szero",
65+
"export image of S(0); "
66+
"that is, the model-estimated signal intensity in the absence of diffusion weighting")
67+
+ Argument("image").type_image_out()
5868

59-
+ Option ("cutoff", "minimum b-value for ADC estimation in IVIM fit (default = 120 s/mm^2).")
60-
+ Argument ("bval").type_integer (0, 1000)
69+
+ Option ("ivim", "also estimate IVIM parameters in 2-stage fit, "
70+
"yielding two images encoding signal fraction and diffusivity respectively of perfusion1 component")
71+
+ Argument("fraction").type_image_out()
72+
+ Argument("diffusivity").type_image_out()
73+
74+
+ Option ("cutoff", "minimum b-value for ADC estimation in IVIM fit "
75+
"(default = " + str(ivim_cutoff_default) + " s/mm^2).")
76+
+ Argument ("bval").type_float (0.0, 1000.0)
6177

6278
+ DWI::GradImportOptions();
6379

6480
REFERENCES
6581
+ "Le Bihan, D.; Breton, E.; Lallemand, D.; Aubin, M.L.; Vignaud, J.; Laval-Jeantet, M. "
6682
"Separation of diffusion and perfusion in intravoxel incoherent motion MR imaging. "
67-
"Radiology, 1988, 168, 497505."
83+
"Radiology, 1988, 168, 497-505."
6884

69-
+ "Jalnefjord, O.; Andersson, M.; Montelius; M.; Starck, G.; Elf, A.; Johanson, V.; Svensson, J.; Ljungberg, M. "
85+
+ "If using -ivim option: "
86+
"Jalnefjord, O.; Andersson, M.; Montelius; M.; Starck, G.; Elf, A.; Johanson, V.; Svensson, J.; Ljungberg, M. "
7087
"Comparison of methods for estimation of the intravoxel incoherent motion (IVIM) "
7188
"diffusion coefficient (D) and perfusion fraction (f). "
72-
"Magn Reson Mater Phy, 2018, 31, 715723.";
89+
"Magn Reson Mater Phy, 2018, 31, 715-723.";
7390
}
7491
// clang-format on
7592

76-
using value_type = float;
77-
7893
class DWI2ADC {
7994
public:
80-
DWI2ADC(const Eigen::VectorXd &bvals, size_t dwi_axis, bool ivim, int cutoff)
81-
: bvals(bvals), dwi(bvals.size()), adc(2), dwi_axis(dwi_axis), ivim(ivim), cutoff(cutoff) {
82-
Eigen::MatrixXd b(bvals.size(), 2);
95+
DWI2ADC(const Header &header, const Eigen::VectorXd &bvals, const size_t dwi_axis)
96+
: H(header),
97+
bvals(bvals),
98+
dwi(bvals.size()),
99+
b(bvals.size(), 2),
100+
logszero_and_adc(2),
101+
dwi_axis(dwi_axis),
102+
ivim_cutoff(std::numeric_limits<value_type>::signaling_NaN()) {
83103
for (ssize_t i = 0; i < b.rows(); ++i) {
84104
b(i, 0) = 1.0;
85105
b(i, 1) = -bvals(i);
86106
}
87107
binv = Math::pinv(b);
88-
if (ivim) {
89-
// select volumes with b-value > cutoff
90-
for (ssize_t j = 0; j < bvals.size(); j++) {
91-
if (bvals[j] > cutoff)
92-
idx.push_back(j);
93-
}
94-
const Eigen::MatrixXd bsub = b(idx, Eigen::all);
95-
bsubinv = Math::pinv(bsub);
108+
}
109+
110+
void set_bzero_path(std::string_view path) { szero_image = Image<value_type>::create(path, H); }
111+
112+
void initialise_ivim(std::string_view ivimfrac_path, std::string_view ivimdiff_path, const value_type cutoff) {
113+
ivimfrac_image = Image<value_type>::create(ivimfrac_path, H);
114+
ivimdiff_image = Image<value_type>::create(ivimdiff_path, H);
115+
ivim_cutoff = cutoff;
116+
// select volumes with b-value > cutoff
117+
for (ssize_t j = 0; j < bvals.size(); j++) {
118+
if (bvals[j] > cutoff)
119+
ivim_suprathresh_idx.push_back(j);
96120
}
121+
const Eigen::MatrixXd bsub = b(ivim_suprathresh_idx, Eigen::all);
122+
bsubinv = Math::pinv(bsub);
97123
}
98124

99-
template <class DWIType, class ADCType> void operator()(DWIType &dwi_image, ADCType &adc_image) {
125+
void operator()(Image<value_type> &dwi_image, Image<value_type> &adc_image) {
100126
for (auto l = Loop(dwi_axis)(dwi_image); l; ++l) {
101-
value_type val = dwi_image.value();
127+
const value_type val = dwi_image.value();
102128
dwi[dwi_image.index(dwi_axis)] = val > 1.0e-12 ? std::log(val) : 1.0e-12;
103129
}
104130

105-
if (ivim) {
106-
dwisub = dwi(idx);
107-
adc = bsubinv * dwisub;
131+
if (std::isnan(ivim_cutoff)) {
132+
logszero_and_adc = binv * dwi;
108133
} else {
109-
adc = binv * dwi;
134+
dwisub = dwi(ivim_suprathresh_idx);
135+
logszero_and_adc = bsubinv * dwisub;
136+
}
137+
138+
adc_image.value() = static_cast<value_type>(logszero_and_adc[1]);
139+
140+
if (std::isnan(ivim_cutoff)) {
141+
if (szero_image.valid()) {
142+
assign_pos_of(adc_image).to(szero_image);
143+
szero_image.value() = static_cast<value_type>(std::exp(logszero_and_adc[0]));
144+
}
145+
return;
110146
}
111147

112-
adc_image.index(3) = 0;
113-
adc_image.value() = std::exp(adc[0]);
114-
adc_image.index(3) = 1;
115-
adc_image.value() = adc[1];
116-
117-
if (ivim) {
118-
const double A = std::exp(adc[0]);
119-
const double D = adc[1];
120-
Eigen::VectorXd logS = adc[0] - D * bvals.array();
121-
Eigen::VectorXd logdiff = (dwi.array() > logS.array()).select(dwi, logS);
122-
logdiff.array() += Eigen::log(1 - Eigen::exp(-(dwi - logS).array().abs()));
123-
adc = binv * logdiff;
124-
const double C = std::exp(adc[0]);
125-
const double Dstar = adc[1];
126-
const double S0 = A + C;
127-
const double f = C / S0;
128-
adc_image.index(3) = 0;
129-
adc_image.value() = S0;
130-
adc_image.index(3) = 2;
131-
adc_image.value() = f;
132-
adc_image.index(3) = 3;
133-
adc_image.value() = Dstar;
148+
const double A = std::exp(logszero_and_adc[0]);
149+
const double D = logszero_and_adc[1];
150+
const Eigen::VectorXd logS = logszero_and_adc[0] - D * bvals.array();
151+
Eigen::VectorXd logdiff = (dwi.array() > logS.array()).select(dwi, logS);
152+
logdiff.array() += Eigen::log(1 - Eigen::exp(-(dwi - logS).array().abs()));
153+
logszero_and_adc = binv * logdiff;
154+
const double C = std::exp(logszero_and_adc[0]);
155+
const double Dstar = logszero_and_adc[1];
156+
const double S0 = A + C;
157+
const double f = C / S0;
158+
if (szero_image.valid()) {
159+
assign_pos_of(adc_image).to(szero_image);
160+
szero_image.value() = static_cast<value_type>(S0);
134161
}
162+
assign_pos_of(adc_image).to(ivimfrac_image, ivimdiff_image);
163+
ivimfrac_image.value() = static_cast<value_type>(f);
164+
ivimdiff_image.value() = static_cast<value_type>(Dstar);
135165
}
136166

137167
private:
138-
Eigen::VectorXd bvals, dwi, dwisub, adc;
139-
Eigen::MatrixXd binv, bsubinv;
140-
std::vector<size_t> idx;
168+
const Header H;
169+
Eigen::VectorXd bvals, dwi, dwisub, logszero_and_adc;
170+
Eigen::MatrixXd b, binv;
141171
const size_t dwi_axis;
142-
const bool ivim;
143-
const int cutoff;
172+
173+
// Optional export
174+
Image<value_type> szero_image;
175+
176+
// Members relating to IVIM operation
177+
std::vector<size_t> ivim_suprathresh_idx;
178+
Eigen::MatrixXd bsubinv;
179+
Image<value_type> ivimfrac_image;
180+
Image<value_type> ivimdiff_image;
181+
value_type ivim_cutoff;
144182
};
145183

146184
void run() {
147-
auto header_in = Header::open(argument[0]);
148-
auto grad = DWI::get_DW_scheme(header_in);
149-
185+
auto H_in = Header::open(argument[0]);
186+
auto grad = DWI::get_DW_scheme(H_in);
150187
size_t dwi_axis = 3;
151-
while (header_in.size(dwi_axis) < 2)
188+
while (H_in.size(dwi_axis) < 2)
152189
++dwi_axis;
153190
INFO("assuming DW images are stored along axis " + str(dwi_axis));
154191

155-
auto opt = get_options("ivim");
156-
bool ivim = opt.size();
157-
int bmin = get_option_value("cutoff", 120);
192+
Header H_out(H_in);
193+
H_out.datatype() = DataType::Float32;
194+
H_out.datatype().set_byte_order_native();
195+
H_out.ndim() = 3;
196+
DWI::stash_DW_scheme(H_out, grad);
197+
Metadata::PhaseEncoding::clear_scheme(H_out.keyval());
198+
199+
DWI2ADC functor(H_out, grad.col(3), dwi_axis);
158200

159-
Header header_out(header_in);
160-
header_out.datatype() = DataType::Float32;
161-
DWI::stash_DW_scheme(header_out, grad);
162-
Metadata::PhaseEncoding::clear_scheme(header_out.keyval());
163-
header_out.ndim() = 4;
164-
header_out.size(3) = ivim ? 4 : 2;
201+
auto opt = get_options("szero");
202+
if (!opt.empty())
203+
functor.set_bzero_path(opt[0][0]);
204+
opt = get_options("ivim");
205+
if (!opt.empty())
206+
functor.initialise_ivim(opt[0][0], opt[0][1], get_option_value("cutoff", ivim_cutoff_default));
165207

166-
auto dwi = header_in.get_image<value_type>();
167-
auto adc = Image<value_type>::create(argument[1], header_out);
208+
auto dwi = H_in.get_image<value_type>();
209+
auto adc = Image<value_type>::create(argument[1], H_out);
168210

169-
ThreadedLoop("computing ADC values", dwi, 0, 3).run(DWI2ADC(grad.col(3), dwi_axis, ivim, bmin), dwi, adc);
211+
ThreadedLoop("computing ADC values", H_out).run(functor, dwi, adc);
170212
}

docs/reference/commands/dwi2adc.rst

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,24 @@ Usage
1515

1616
dwi2adc [ options ] input output
1717

18-
- *input*: the input image.
19-
- *output*: the output image.
18+
- *input*: the input image
19+
- *output*: the output ADC image
2020

2121
Description
2222
-----------
2323

24-
By default, the command will estimate the Apparent Diffusion Coefficient (ADC) using the isotropic mono-exponential model: S(b) = S(0) * exp(-D * b). The output consists of 2 volumes, respectively S(0) and D.
24+
The command estimates the Apparent Diffusion Coefficient (ADC) using the isotropic mono-exponential model: S(b) = S(0) * exp(-ADC * b). The value of S(0) can be optionally exported using command-line option -szero.
2525

2626
When using the -ivim option, the command will additionally estimate the Intra-Voxel Incoherent Motion (IVIM) parameters f and D', i.e., the perfusion fraction and the pseudo-diffusion coefficient. IVIM assumes a bi-exponential model: S(b) = S(0) * ((1-f) * exp(-D * b) + f * exp(-D' * b)). This command adopts a 2-stage fitting strategy, in which the ADC is first estimated based on the DWI data with b > cutoff, and the other parameters are estimated subsequently. The output consists of 4 volumes, respectively S(0), D, f, and D'.
2727

28-
Note that this command ignores the gradient orientation entirely. This approach is therefore only suited for mean DWI (trace-weighted) images or for DWI data collected with isotropically-distributed gradient directions.
28+
Note that this command ignores the gradient orientation entirely. If a conventional DWI series is provided as input, all volumes will contribute equally toward the model fit irrespective of direction of diffusion sensitisation; DWI data should therefore ideally consist of isotropically-distributed gradient directions. The approach can alternatively be applied to mean DWI (trace-weighted) images.
2929

3030
Options
3131
-------
3232

33-
- **-ivim** also estimate IVIM parameters in 2-stage fit.
33+
- **-szero image** export image of S(0); that is, the model-estimated signal intensity in the absence of diffusion weighting
34+
35+
- **-ivim fraction diffusivity** also estimate IVIM parameters in 2-stage fit, yielding two images encoding signal fraction and diffusivity respectively of perfusion1 component
3436

3537
- **-cutoff bval** minimum b-value for ADC estimation in IVIM fit (default = 120 s/mm^2).
3638

@@ -63,9 +65,9 @@ Standard options
6365
References
6466
^^^^^^^^^^
6567

66-
Le Bihan, D.; Breton, E.; Lallemand, D.; Aubin, M.L.; Vignaud, J.; Laval-Jeantet, M. Separation of diffusion and perfusion in intravoxel incoherent motion MR imaging. Radiology, 1988, 168, 497505.
68+
Le Bihan, D.; Breton, E.; Lallemand, D.; Aubin, M.L.; Vignaud, J.; Laval-Jeantet, M. Separation of diffusion and perfusion in intravoxel incoherent motion MR imaging. Radiology, 1988, 168, 497-505.
6769

68-
Jalnefjord, O.; Andersson, M.; Montelius; M.; Starck, G.; Elf, A.; Johanson, V.; Svensson, J.; Ljungberg, M. Comparison of methods for estimation of the intravoxel incoherent motion (IVIM) diffusion coefficient (D) and perfusion fraction (f). Magn Reson Mater Phy, 2018, 31, 715723.
70+
If using -ivim option: Jalnefjord, O.; Andersson, M.; Montelius; M.; Starck, G.; Elf, A.; Johanson, V.; Svensson, J.; Ljungberg, M. Comparison of methods for estimation of the intravoxel incoherent motion (IVIM) diffusion coefficient (D) and perfusion fraction (f). Magn Reson Mater Phy, 2018, 31, 715-723.
6971

7072
Tournier, J.-D.; Smith, R. E.; Raffelt, D.; Tabbara, R.; Dhollander, T.; Pietsch, M.; Christiaens, D.; Jeurissen, B.; Yeh, C.-H. & Connelly, A. MRtrix3: A fast, flexible and open software framework for medical image processing and visualisation. NeuroImage, 2019, 202, 116137
7173

testing/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ include(ExternalProject)
1616
ExternalProject_Add(BinariesTestData
1717
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/binaries_data
1818
GIT_REPOSITORY ${mrtrix_binaries_data_url}
19-
GIT_TAG 5350c7c38539ea7180a509c33c2701804c6f19d2
19+
GIT_TAG 3d30c7fff58f9ca0bae5ccfe5565f35aa76b6dc6
2020
GIT_PROGRESS TRUE
2121
CONFIGURE_COMMAND ""
2222
BUILD_COMMAND ""

testing/binaries/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ add_bash_binary_test(dirrotate/default)
7474
add_bash_binary_test(dirrotate/onerotation)
7575

7676
add_bash_binary_test(dwi2adc/default)
77+
add_bash_binary_test(dwi2adc/ivim)
7778

7879
add_bash_binary_test(dwi2fod/csd_default)
7980
add_bash_binary_test(dwi2fod/csd_lmax)
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
#!/bin/bash
22
# Basic test of default operation of dwi2adc command
3-
# COmpares output to that generated from a prior software version
4-
dwi2adc dwi2adc/in.mif - | \
5-
testing_diff_image - dwi2adc/out.mif -frac 1e-5
3+
# Compares output to that generated from a prior software version
4+
#
5+
# Note that in the pre-generated reference output test data,
6+
# dwi2adc created a single output image,
7+
# where the first volume was S(0) and the second volume was ADC.
8+
# The updated interface involves writing a compulsory ADC image,
9+
# and an optional S(0) volume written to a different image
10+
# using explicit command-line option -szero.
11+
12+
dwi2adc dwi2adc/in.mif tmp_adc.mif -szero tmp_szero.mif -force
13+
testing_diff_image tmp_adc.mif dwi2adc/adc.mif -frac 1e-5
14+
testing_diff_image tmp_szero.mif dwi2adc/szero.mif -frac 1e-5
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
# Test of operation of dwi2adc command
3+
# when the -ivim option is used
4+
# Compares output to that generated from a prior software version
5+
6+
dwi2adc dwi2adc/IVIM_in.mif tmp_adc.mif \
7+
-szero tmp_szero.mif \
8+
-ivim tmp_ivimfrac.mif tmp_ivimd.mif \
9+
-force
10+
11+
testing_diff_image tmp_adc.mif dwi2adc/IVIM_adc.mif -frac 1e-5
12+
testing_diff_image tmp_szero.mif dwi2adc/IVIM_szero.mif -frac 1e-5
13+
testing_diff_image tmp_ivimfrac.mif dwi2adc/IVIM_fraction.mif -frac 1e-3
14+
testing_diff_image tmp_ivimd.mif dwi2adc/IVIM_D.mif -frac 1e-3

0 commit comments

Comments
 (0)