Skip to content

Commit 69e3bd5

Browse files
josephmjeoesteban
authored andcommitted
initial b0 registration adapted from niworkflows
1 parent d93d6c8 commit 69e3bd5

File tree

2 files changed

+83
-11
lines changed

2 files changed

+83
-11
lines changed

dmriprep/interfaces/registration.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Registration tools interfaces."""
2+
3+
import os
4+
import nibabel as nb
5+
import numpy as np
6+
from nipype.interfaces.base import (
7+
traits, TraitedSpec, BaseInterfaceInputSpec, File, SimpleInterface)
8+
from nipype.interfaces import fsl, afni
9+
10+
11+
class EstimateReferenceImageInputSpec(BaseInterfaceInputSpec):
12+
in_file = File(exists=True, mandatory=True, desc="b0 file")
13+
pre_mask = File(exists=True, mandatory=True, desc="rough mask of skull-stripped b0")
14+
mc_method = traits.Enum(
15+
"AFNI", "FSL", usedefault=True,
16+
desc="Which software to use to perform motion correction")
17+
18+
19+
class EstimateReferenceImageOutputSpec(TraitedSpec):
20+
ref_image = File(exists=True, desc="3D reference image")
21+
22+
23+
class EstimateReferenceImage(SimpleInterface):
24+
"""
25+
Given an 4D EPI file estimate an optimal reference image that could be later
26+
used for motion estimation and coregistration purposes. If detected uses
27+
T1 saturated volumes (non-steady state) otherwise a median of
28+
of a subset of motion corrected volumes is used.
29+
"""
30+
input_spec = EstimateReferenceImageInputSpec
31+
output_spec = EstimateReferenceImageOutputSpec
32+
33+
def _run_interface(self, runtime):
34+
ref_name = self.inputs.in_file
35+
pre_mask_name = self.inputs.pre_mask
36+
37+
ref_nii = nb.load(ref_name)
38+
pre_mask_nii = nb.load(pre_mask_name)
39+
40+
out_ref_fname = os.path.join(runtime.cwd, "ref_dwi.nii.gz")
41+
42+
# If reference is only 1 volume, return it directly
43+
if len(ref_nii.shape) == 3:
44+
ref_nii.header.extensions.clear()
45+
ref_nii.to_filename(out_ref_fname)
46+
self._results['ref_image'] = out_ref_fname
47+
return runtime
48+
else:
49+
ref_nii.header.extensions.clear()
50+
51+
if self.inputs.mc_method == "AFNI":
52+
res = afni.Volreg(in_file=ref_name, args='-Fourier -twopass',
53+
zpad=4, outputtype='NIFTI_GZ').run()
54+
elif self.inputs.mc_method == "FSL":
55+
res = fsl.MCFLIRT(in_file=ref_name,
56+
ref_vol=0, interpolation='sinc').run()
57+
mc_ref_nii = nb.load(res.outputs.out_file)
58+
59+
pre_mask_data = pre_mask_nii.get_fdata()
60+
61+
image_data = mc_ref_nii.get_fdata()
62+
63+
normalized_image_data = 1000 * image_data / image_data[pre_mask_data, ...].mean(axis=-1)
64+
65+
median_image_data = np.median(normalized_image_data, axis=-1)
66+
67+
nb.Nifti1Image(median_image_data, ref_nii.affine,
68+
ref_nii.header).to_filename(out_ref_fname)
69+
70+
self._results["ref_image"] = out_ref_fname
71+
72+
return runtime

dmriprep/workflows/dwi/util.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2-
# vi: set ft=python sts=4 ts=4 sw=4 et:
3-
41
"""
52
Utility workflows
63
^^^^^^^^^^^^^^^^^
@@ -19,7 +16,8 @@
1916
from niworkflows.interfaces.masks import SimpleShowMaskRPT
2017
from niworkflows.interfaces.utils import CopyXForm
2118

22-
from ...interfaces.images import MeanB0
19+
from ...interfaces.images import ExtractB0
20+
from ...interfaces.registration import EstimateReferenceImage
2321

2422
DEFAULT_MEMORY_MIN_GB = 0.01
2523

@@ -57,7 +55,7 @@ def init_dwi_reference_wf(omp_nthreads, dwi_file=None,
5755
dwi_file
5856
Validated dwi NIfTI file
5957
raw_ref_image
60-
Reference image to which dwi series is motion corrected
58+
Reference image
6159
ref_image
6260
Contrast-enhanced reference image
6361
ref_image_brain
@@ -92,19 +90,21 @@ def init_dwi_reference_wf(omp_nthreads, dwi_file=None,
9290

9391
validate = pe.Node(ValidateImage(), name='validate', mem_gb=DEFAULT_MEMORY_MIN_GB)
9492

95-
gen_ref = pe.Node(MeanB0(), name="gen_ref")
93+
extract_b0 = pe.Node(ExtractB0(), name="extract_b0")
9694

9795
pre_mask = pe.Node(afni.Automask(outputtype="NIFTI_GZ"), name="pre_mask")
9896

97+
gen_ref = pe.Node(EstimateReferenceImage(), name='gen_ref')
98+
9999
enhance_and_skullstrip_dwi_wf = init_enhance_and_skullstrip_dwi_wf(
100100
omp_nthreads=omp_nthreads)
101101

102102
workflow.connect([
103103
(inputnode, validate, [('dwi_file', 'in_file')]),
104-
(validate, gen_ref, [('out_file', 'in_file')]),
105-
(inputnode, gen_ref, [('bvec_file', 'in_bvec'),
106-
('bval_file', 'in_bval')]),
107-
(gen_ref, pre_mask, [('out_file', 'in_file')]),
104+
(validate, extract_b0, [('out_file', 'in_file')]),
105+
(extract_b0, pre_mask, [('out_file', 'in_file')]),
106+
(extract_b0, gen_ref, [('out_file', 'in_file')]),
107+
(pre_mask, gen_ref, [('out_file', 'pre_mask')]),
108108
(gen_ref, enhance_and_skullstrip_dwi_wf, [('out_file', 'inputnode.in_file')]),
109109
(pre_mask, enhance_and_skullstrip_dwi_wf, [('out_file', 'inputnode.pre_mask')]),
110110
(validate, outputnode, [('out_file', 'dwi_file'),
@@ -132,7 +132,7 @@ def init_enhance_and_skullstrip_dwi_wf(
132132
name='enhance_and_skullstrip_dwi_wf',
133133
omp_nthreads=1):
134134
"""
135-
This workflow takes in a dwi reference iamge and sharpens the histogram
135+
This workflow takes in a dwi reference image and sharpens the histogram
136136
with the application of the N4 algorithm for removing the
137137
:abbr:`INU (intensity non-uniformity)` bias field and calculates a signal
138138
mask.

0 commit comments

Comments
 (0)