Skip to content
This repository was archived by the owner on Dec 27, 2022. It is now read-only.

Commit dfc3882

Browse files
committed
Adds another workflow for situations where there is only one PE direction.
1 parent 1873538 commit dfc3882

File tree

1 file changed

+216
-3
lines changed

1 file changed

+216
-3
lines changed

dmriprep/run.py

Lines changed: 216 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,224 @@
22
from shutil import copyfile
33

44

5-
def run_dmriprep(dwi_file, dwi_file_AP, dwi_file_PA, bvec_file, bval_file,
5+
def run_dmriprep(dwi_file, bvec_file, bval_file,
66
subjects_dir, working_dir, out_dir):
7-
"""This is for HBN diffusion data
87

9-
Assuming phase-encode direction is AP/PA for topup
8+
"""
9+
Runs dmriprep for acquisitions with just one PE direction.
10+
11+
"""
12+
wf = create_dmri_preprocessing(name='dmriprep',
13+
use_fieldmap=False,
14+
fieldmap_registration=False)
15+
wf.inputs.inputnode.ref_num = 0
16+
wf.inputs.inputnode.in_file = dwi_file
17+
wf.inputs.inputnode.in_bvec = bvec_file
18+
19+
dwi_fname = op.split(dwi_file)[1].split(".nii.gz")[0]
20+
bids_sub_name = dwi_fname.split("_")[0]
21+
assert bids_sub_name.startswith("sub-")
22+
23+
# inputnode = wf.get_node("inputnode")
24+
outputspec = wf.get_node("outputnode")
25+
26+
# QC: FLIRT translation and rotation parameters
27+
flirt = wf.get_node("motion_correct.coregistration")
28+
# flirt.inputs.save_mats = True
29+
30+
get_tensor = pe.Node(DTI(), name="dipy_tensor")
31+
wf.connect(outputspec, "dmri_corrected", get_tensor, "in_file")
32+
# wf.connect(inputspec2,"bvals", get_tensor, "in_bval")
33+
get_tensor.inputs.in_bval = bval_file
34+
wf.connect(outputspec, "bvec_rotated", get_tensor, "in_bvec")
35+
36+
scale_tensor = pe.Node(name='scale_tensor', interface=fsl.BinaryMaths())
37+
scale_tensor.inputs.operation = 'mul'
38+
scale_tensor.inputs.operand_value = 1000
39+
wf.connect(get_tensor, 'out_file', scale_tensor, 'in_file')
40+
41+
fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi")
42+
wf.connect(outputspec, "dmri_corrected", fslroi, "in_file")
43+
44+
bbreg = pe.Node(fs.BBRegister(contrast_type="t2", init="fsl",
45+
out_fsl_file=True, subjects_dir=subjects_dir,
46+
epi_mask=True), name="bbreg")
47+
# wf.connect(inputspec2,"fsid", bbreg,"subject_id")
48+
bbreg.inputs.subject_id = 'freesurfer' # bids_sub_name
49+
wf.connect(fslroi, "roi_file", bbreg, "source_file")
50+
51+
voltransform = pe.Node(fs.ApplyVolTransform(inverse=True),
52+
iterfield=['source_file', 'reg_file'],
53+
name='transform')
54+
voltransform.inputs.subjects_dir = subjects_dir
55+
56+
vt2 = voltransform.clone("transform_aparcaseg")
57+
vt2.inputs.interp = "nearest"
58+
59+
def binarize_aparc(aparc_aseg):
60+
img = nib.load(aparc_aseg)
61+
data, aff = img.get_data(), img.affine
62+
outfile = fname_presuffix(
63+
aparc_aseg, suffix="bin.nii.gz",
64+
newpath=op.abspath("."), use_ext=False
65+
)
66+
nib.Nifti1Image((data > 0).astype(float), aff).to_filename(outfile)
67+
return outfile
68+
69+
# wf.connect(inputspec2, "mask_nii", voltransform, "target_file")
70+
create_mask = pe.Node(niu.Function(input_names=["aparc_aseg"],
71+
output_names=["outfile"],
72+
function=binarize_aparc),
73+
name="bin_aparc")
74+
75+
def get_aparc_aseg(subjects_dir, sub):
76+
return op.join(subjects_dir, sub, "mri", "aparc+aseg.mgz")
77+
78+
create_mask.inputs.aparc_aseg = get_aparc_aseg(subjects_dir, 'freesurfer')
79+
wf.connect(create_mask, "outfile", voltransform, "target_file")
80+
81+
wf.connect(fslroi, "roi_file", voltransform, "source_file")
82+
wf.connect(bbreg, "out_reg_file", voltransform, "reg_file")
83+
84+
vt2.inputs.target_file = get_aparc_aseg(subjects_dir, 'freesurfer')
85+
# wf.connect(inputspec2, "aparc_aseg", vt2, "target_file")
86+
wf.connect(fslroi, "roi_file", vt2, "source_file")
87+
wf.connect(bbreg, "out_reg_file", vt2, "reg_file")
88+
89+
# AK (2017): THIS NODE MIGHT NOT BE NECESSARY
90+
# AK (2018) doesn't know why she said that above..
91+
threshold2 = pe.Node(fs.Binarize(min=0.5, out_type='nii.gz', dilate=1),
92+
iterfield=['in_file'],
93+
name='threshold2')
94+
wf.connect(voltransform, "transformed_file", threshold2, "in_file")
95+
96+
# wf.connect(getmotion, "motion_params", datasink, "dti.@motparams")
97+
98+
def get_flirt_motion_parameters(flirt_out_mats):
99+
def get_params(A):
100+
"""This is a copy of spm's spm_imatrix where
101+
we already know the rotations and translations matrix,
102+
shears and zooms (as outputs from fsl FLIRT/avscale)
103+
Let A = the 4x4 rotation and translation matrix
104+
R = [ c5*c6, c5*s6, s5]
105+
[-s4*s5*c6-c4*s6, -s4*s5*s6+c4*c6, s4*c5]
106+
[-c4*s5*c6+s4*s6, -c4*s5*s6-s4*c6, c4*c5]
107+
"""
108+
def rang(b):
109+
a = min(max(b, -1), 1)
110+
return a
111+
Ry = np.arcsin(A[0, 2])
112+
# Rx = np.arcsin(A[1, 2] / np.cos(Ry))
113+
# Rz = np.arccos(A[0, 1] / np.sin(Ry))
114+
115+
if (abs(Ry)-np.pi/2)**2 < 1e-9:
116+
Rx = 0
117+
Rz = np.arctan2(-rang(A[1, 0]), rang(-A[2, 0]/A[0, 2]))
118+
else:
119+
c = np.cos(Ry)
120+
Rx = np.arctan2(rang(A[1, 2]/c), rang(A[2, 2]/c))
121+
Rz = np.arctan2(rang(A[0, 1]/c), rang(A[0, 0]/c))
122+
123+
rotations = [Rx, Ry, Rz]
124+
translations = [A[0, 3], A[1, 3], A[2, 3]]
125+
126+
return rotations, translations
127+
128+
motion_params = open(op.abspath('motion_parameters.par'), 'w')
129+
for mat in flirt_out_mats:
130+
res = AvScale(mat_file=mat).run()
131+
A = np.asarray(res.outputs.rotation_translation_matrix)
132+
rotations, translations = get_params(A)
133+
for i in rotations+translations:
134+
motion_params.write('%f ' % i)
135+
motion_params.write('\n')
136+
motion_params.close()
137+
motion_params = op.abspath('motion_parameters.par')
138+
return motion_params
139+
140+
getmotion = pe.Node(
141+
niu.Function(input_names=["flirt_out_mats"],
142+
output_names=["motion_params"],
143+
function=get_flirt_motion_parameters),
144+
name="get_motion_parameters",
145+
iterfield="flirt_out_mats"
146+
)
147+
148+
wf.connect(flirt, "out_matrix_file", getmotion, "flirt_out_mats")
149+
150+
art = pe.Node(interface=ArtifactDetect(), name="art")
151+
art.inputs.use_differences = [True, True]
152+
art.inputs.save_plot = False
153+
art.inputs.use_norm = True
154+
art.inputs.norm_threshold = 3
155+
art.inputs.zintensity_threshold = 9
156+
art.inputs.mask_type = 'spm_global'
157+
art.inputs.parameter_source = 'FSL'
158+
159+
wf.connect(getmotion, "motion_params", art, "realignment_parameters")
160+
wf.connect(outputspec, "dmri_corrected", art, "realigned_files")
161+
162+
datasink = pe.Node(nio.DataSink(), name="sinker")
163+
datasink.inputs.base_directory = out_dir
164+
datasink.inputs.substitutions = [
165+
("vol0000_flirt_merged.nii.gz", dwi_fname + '.nii.gz'),
166+
("stats.vol0000_flirt_merged.txt", dwi_fname + ".art.json"),
167+
("motion_parameters.par", dwi_fname + ".motion.txt"),
168+
("_rotated.bvec", ".bvec"),
169+
("aparc+aseg_warped_out", dwi_fname.replace("_dwi", "_aparc+aseg")),
170+
("art.vol0000_flirt_merged_outliers.txt", dwi_fname + ".outliers.txt"),
171+
("vol0000_flirt_merged", dwi_fname),
172+
("_roi_bbreg_freesurfer", "_register"),
173+
("aparc+asegbin_warped_thresh", dwi_fname.replace("_dwi", "_mask")),
174+
("derivatives/dmriprep", "derivatives/{}/dmriprep".format(bids_sub_name))
175+
]
176+
177+
wf.connect(art, "statistic_files", datasink, "dmriprep.art.@artstat")
178+
wf.connect(art, "outlier_files", datasink, "dmriprep.art.@artoutlier")
179+
wf.connect(outputspec, "dmri_corrected", datasink, "dmriprep.dwi.@corrected")
180+
wf.connect(outputspec, "bvec_rotated", datasink, "dmriprep.dwi.@rotated")
181+
wf.connect(getmotion, "motion_params", datasink, "dmriprep.art.@motion")
182+
183+
wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@tensor")
184+
wf.connect(get_tensor, "fa_file", datasink, "dmriprep.dti.@fa")
185+
wf.connect(get_tensor, "md_file", datasink, "dmriprep.dti.@md")
186+
wf.connect(get_tensor, "ad_file", datasink, "dmriprep.dti.@ad")
187+
wf.connect(get_tensor, "rd_file", datasink, "dmriprep.dti.@rd")
188+
wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@scaled_tensor")
189+
190+
wf.connect(bbreg, "min_cost_file", datasink, "dmriprep.reg.@mincost")
191+
wf.connect(bbreg, "out_fsl_file", datasink, "dmriprep.reg.@fslfile")
192+
wf.connect(bbreg, "out_reg_file", datasink, "dmriprep.reg.@reg")
193+
wf.connect(threshold2, "binary_file", datasink, "dmriprep.anat.@mask")
194+
# wf.connect(vt2, "transformed_file", datasink, "dwi.@aparc_aseg")
195+
196+
convert = pe.Node(fs.MRIConvert(out_type="niigz"), name="convert2nii")
197+
wf.connect(vt2, "transformed_file", convert, "in_file")
198+
wf.connect(convert, "out_file", datasink, "dmriprep.anat.@aparc_aseg")
199+
200+
wf.base_dir = working_dir
201+
wf.run()
202+
203+
copyfile(bval_file, op.join(
204+
out_dir, bids_sub_name, "dmriprep", "dwi",
205+
op.split(bval_file)[1]
206+
))
207+
208+
dmri_corrected = glob(op.join(out_dir, '*/dmriprep/dwi', '*.nii.gz'))[0]
209+
bvec_rotated = glob(op.join(out_dir, '*/dmriprep/dwi', '*.bvec'))[0]
210+
bval_file = glob(op.join(out_dir, '*/dmriprep/dwi', '*.bval'))[0]
211+
art_file = glob(op.join(out_dir, '*/dmriprep/art', '*.art.json'))[0]
212+
motion_file = glob(op.join(out_dir, '*/dmriprep/art', '*.motion.txt'))[0]
213+
outlier_file = glob(op.join(out_dir, '*/dmriprep/art', '*.outliers.txt'))[0]
214+
return dmri_corrected, bvec_rotated, art_file, motion_file, outlier_file
215+
216+
217+
def run_dmriprep_pe(dwi_file, dwi_file_AP, dwi_file_PA, bvec_file, bval_file,
218+
subjects_dir, working_dir, out_dir):
219+
"""
220+
This assumes that there are scans with phase-encode directions AP/PA for
221+
topup.
222+
10223
"""
11224

12225
import nipype.interfaces.freesurfer as fs

0 commit comments

Comments
 (0)