|
2 | 2 | from shutil import copyfile
|
3 | 3 |
|
4 | 4 |
|
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, |
6 | 6 | subjects_dir, working_dir, out_dir):
|
7 |
| - """This is for HBN diffusion data |
8 | 7 |
|
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 | +
|
10 | 223 | """
|
11 | 224 |
|
12 | 225 | import nipype.interfaces.freesurfer as fs
|
|
0 commit comments