Skip to content

Commit 1b8d632

Browse files
committed
expose bmag parameter and check number of b0s consistent after rounding
1 parent 25652d6 commit 1b8d632

File tree

4 files changed

+34
-13
lines changed

4 files changed

+34
-13
lines changed

dmriprep/interfaces/reports.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
\t\t<summary>Summary</summary>
3636
\t\t<ul class="elem-desc">
3737
\t\t\t<li>Phase-encoding (PE) direction: {pe_direction}</li>
38-
\t\t\t<li>Distinct shells: {shells_dist}</li>
38+
\t\t\t<li>Shell distribution: {shell_dist}</li>
3939
\t\t</ul>
4040
\t\t</details>
4141
"""
@@ -133,18 +133,22 @@ class DiffusionSummaryInputSpec(BaseInterfaceInputSpec):
133133
pe_direction = traits.Enum(
134134
None, "i", "i-", "j", "j-", "k", "k-", desc="Phase-encoding direction detected"
135135
)
136-
shells_dist = traits.Dict(mandatory=True, desc="Number of distinct shells")
136+
shell_dist = traits.Dict(mandatory=True, desc="Number of distinct shells")
137137

138138

139139
class DiffusionSummary(SummaryInterface):
140140
input_spec = DiffusionSummaryInputSpec
141141

142142
def _generate_segment(self):
143143
pe_direction = self.inputs.pe_direction
144-
shells_dist = self.inputs.shells_dist
144+
shell_dist = self.inputs.shell_dist
145+
shell_dist_text = ", ".join(
146+
f"{gtab.count_shells[key]} directions at b={key} s/mm\u00B2"
147+
for key in shell_dist
148+
)
145149

146150
return DIFFUSION_TEMPLATE.format(
147-
pe_direction=pe_direction, shells_dist=shells_dist
151+
pe_direction=pe_direction, shell_dist=shell_dist_text
148152
)
149153

150154

dmriprep/interfaces/vectors.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class _CheckGradientTableInputSpec(BaseInterfaceInputSpec):
2020
in_rasb = File(exists=True, xor=["in_bval", "in_bvec"])
2121
b0_threshold = traits.Float(B0_THRESHOLD, usedefault=True)
2222
bvec_norm_epsilon = traits.Float(BVEC_NORM_EPSILON, usedefault=True)
23+
b_mag = traits.Int(None, usedefault=True)
2324
b_scale = traits.Bool(True, usedefault=True)
2425

2526

@@ -30,7 +31,7 @@ class _CheckGradientTableOutputSpec(TraitedSpec):
3031
full_sphere = traits.Bool()
3132
pole = traits.Tuple(traits.Float, traits.Float, traits.Float)
3233
num_shells = traits.Int
33-
shells_dist = traits.Dict
34+
shell_dist = traits.Dict
3435
b0_ixs = traits.List(traits.Int)
3536

3637

@@ -51,7 +52,7 @@ class CheckGradientTable(SimpleInterface):
5152
True
5253
>>> check.outputs.num_shells
5354
3
54-
>>> check.outputs.shells_dist
55+
>>> check.outputs.shell_dist
5556
{0.0: 12, 1200.0: 32, 2500.0: 61}
5657
5758
>>> check = CheckGradientTable(
@@ -64,7 +65,7 @@ class CheckGradientTable(SimpleInterface):
6465
True
6566
>>> check.outputs.num_shells
6667
3
67-
>>> check.outputs.shells_dist
68+
>>> check.outputs.shell_dist
6869
{0: 12, 1200: 32, 2500: 61}
6970
>>> newrasb = np.loadtxt(check.outputs.out_rasb, skiprows=1)
7071
>>> oldrasb = np.loadtxt(str(data_dir / 'dwi.tsv'), skiprows=1)
@@ -84,6 +85,7 @@ def _run_interface(self, runtime):
8485
bvecs=_undefined(self.inputs, "in_bvec"),
8586
bvals=_undefined(self.inputs, "in_bval"),
8687
rasb_file=rasb_file,
88+
b_mag=self.inputs.b_mag,
8789
b_scale=self.inputs.b_scale,
8890
bvec_norm_epsilon=self.inputs.bvec_norm_epsilon,
8991
b0_threshold=self.inputs.b0_threshold,
@@ -92,7 +94,7 @@ def _run_interface(self, runtime):
9294
self._results["pole"] = tuple(pole)
9395
self._results["full_sphere"] = np.all(pole == 0.0)
9496
self._results["num_shells"] = len(table.count_shells)
95-
self._results["shells_dist"] = table.count_shells
97+
self._results["shell_dist"] = table.count_shells
9698
self._results["b0_ixs"] = np.where(table.b0mask)[0].tolist()
9799

98100
cwd = Path(runtime.cwd).absolute()

dmriprep/utils/vectors.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class DiffusionGradientTable:
1717
__slots__ = [
1818
"_affine",
1919
"_b0_thres",
20+
"_b_mag",
2021
"_b_scale",
2122
"_bvals",
2223
"_bvec_norm_epsilon",
@@ -30,6 +31,7 @@ class DiffusionGradientTable:
3031
def __init__(
3132
self,
3233
b0_threshold=B0_THRESHOLD,
34+
b_mag=None,
3335
b_scale=True,
3436
bvals=None,
3537
bvec_norm_epsilon=BVEC_NORM_EPSILON,
@@ -46,12 +48,14 @@ def __init__(
4648
----------
4749
b0_threshold : :obj:`float`
4850
The upper threshold to consider a low-b shell as :math:`b=0`.
51+
b_mag : :obj:`int`
52+
The order of magnitude to round the b-values.
4953
b_scale : :obj:`bool`
5054
Automatically scale the *b*-values with the norm of the corresponding
5155
*b*-vectors before the latter are normalized.
5256
bvals : str or os.pathlike or numpy.ndarray
5357
File path of the b-values.
54-
b_vec_norm_epsilon : :obj:`float`
58+
bvec_norm_epsilon : :obj:`float`
5559
The minimum difference in the norm of two *b*-vectors to consider them different.
5660
bvecs : str or os.pathlike or numpy.ndarray
5761
File path of the b-vectors.
@@ -93,6 +97,7 @@ def __init__(
9397
"""
9498
self._affine = None
9599
self._b0_thres = b0_threshold
100+
self._b_mag = b_mag
96101
self._b_scale = b_scale
97102
self._bvals = None
98103
self._bvec_norm_epsilon = bvec_norm_epsilon
@@ -195,6 +200,7 @@ def normalize(self):
195200
self.bvals,
196201
b0_threshold=self._b0_thres,
197202
bvec_norm_epsilon=self._bvec_norm_epsilon,
203+
b_mag=self._b_mag,
198204
b_scale=self._b_scale,
199205
raise_error=self._raise_inconsistent,
200206
)
@@ -290,6 +296,7 @@ def normalize_gradients(
290296
bvals,
291297
b0_threshold=B0_THRESHOLD,
292298
bvec_norm_epsilon=BVEC_NORM_EPSILON,
299+
b_mag=None,
293300
b_scale=True,
294301
raise_error=False,
295302
):
@@ -361,17 +368,25 @@ def normalize_gradients(
361368
raise ValueError(msg)
362369
config.loggers.cli.warning(msg)
363370

364-
# Rescale b-vals if requested
371+
# Rescale bvals if requested
365372
if b_scale:
366373
bvals[~b0s] *= np.linalg.norm(bvecs[~b0s], axis=1) ** 2
367374

368375
# Ensure b0s have (0, 0, 0) vectors
369376
bvecs[b0s, :3] = np.zeros(3)
370377

371378
# Round bvals
372-
bvals = round_bvals(bvals)
379+
bvals = round_bvals(bvals, bmag=b_mag)
373380

374-
# Rescale b-vecs, skipping b0's, on the appropriate axis to unit-norm length.
381+
# Ensure rounding bvals doesn't change the number of b0s
382+
rounded_b0s = bvals == 0
383+
if not np.all(b0s == rounded_b0s):
384+
msg = f"Inconsistent b0s before ({b0s.sum()}) and after rounding ({rounded_b0s.sum()})."
385+
if raise_error:
386+
raise ValueError(msg)
387+
config.loggers.cli.warning(msg)
388+
389+
# Rescale bvecs, skipping b0's, on the appropriate axis to unit-norm length.
375390
bvecs[~b0s] /= np.linalg.norm(bvecs[~b0s], axis=1)[..., np.newaxis]
376391
return bvecs, bvals.astype("uint16")
377392

dmriprep/workflows/dwi/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def init_dwi_preproc_wf(dwi_file, has_fieldmap=False):
161161
("in_bvec", "in_bvec"),
162162
("in_bval", "in_bval")]),
163163
(inputnode, dwi_reference_wf, [("dwi_file", "inputnode.dwi_file")]),
164-
(gradient_table, summary, [("shells_dist", "shells_dist")]),
164+
(gradient_table, summary, [("shell_dist", "shell_dist")]),
165165
(gradient_table, dwi_reference_wf, [("b0_ixs", "inputnode.b0_ixs")]),
166166
(gradient_table, outputnode, [("out_rasb", "gradients_rasb")]),
167167
])

0 commit comments

Comments
 (0)