|
5 | 5 | import nipype.pipeline.engine as pe
|
6 | 6 | import nipype.interfaces.utility as niu
|
7 | 7 | import nipype.interfaces.fsl as fsl
|
| 8 | +import nipype.interfaces.freesurfer as fs |
8 | 9 | import nipype.interfaces.ants as ants
|
9 | 10 | import os
|
10 | 11 |
|
11 | 12 | from .utils import *
|
12 | 13 |
|
| 14 | + |
| 15 | +def all_fmb_pipeline(name='hmc_sdc_ecc'): |
| 16 | + """ |
| 17 | + Builds a pipeline including three artifact corrections: head-motion correction (HMC), |
| 18 | + susceptibility-derived distortion correction (SDC), and Eddy currents-derived distortion |
| 19 | + correction (ECC). |
| 20 | + """ |
| 21 | + inputnode = pe.Node(niu.IdentityInterface(fields=['in_file', 'in_bvec', 'in_bval', |
| 22 | + 'bmap_pha', 'bmap_mag']), name='inputnode') |
| 23 | + |
| 24 | + outputnode = pe.Node(niu.IdentityInterface(fields=['out_file', 'out_mask', 'out_bvec']), |
| 25 | + name='outputnode') |
| 26 | + |
| 27 | + |
| 28 | + avg_b0_0 = pe.Node(niu.Function(input_names=['in_dwi', 'in_bval'], |
| 29 | + output_names=['out_file'], function=b0_average), name='b0_avg_pre') |
| 30 | + avg_b0_1 = pe.Node(niu.Function(input_names=['in_dwi', 'in_bval'], |
| 31 | + output_names=['out_file'], function=b0_average), name='b0_avg_post') |
| 32 | + bet_dwi0 = pe.Node(fsl.BET(frac=0.3, mask=True, robust=True), name='bet_dwi_pre') |
| 33 | + bet_dwi1 = pe.Node(fsl.BET(frac=0.3, mask=True, robust=True), name='bet_dwi_post') |
| 34 | + |
| 35 | + hmc = hmc_pipeline() |
| 36 | + sdc = sdc_fmb() |
| 37 | + ecc = ecc_pipeline() |
| 38 | + |
| 39 | + regrid = pe.Node(fs.MRIConvert(vox_size=(2.0, 2.0, 2.0), out_orientation='RAS'), |
| 40 | + name='Reslice') |
| 41 | + |
| 42 | + wf = pe.Workflow('dMRI_Artifacts') |
| 43 | + wf.connect([ |
| 44 | + (inputnode, hmc, [('in_file', 'inputnode.in_file'), |
| 45 | + ('in_bvec', 'inputnode.in_bvec')]) |
| 46 | + ,(inputnode, avg_b0_0, [('in_file', 'in_dwi'), |
| 47 | + ('in_bval', 'in_bval')]) |
| 48 | + ,(avg_b0_0, bet_dwi0, [('out_file','in_file')]) |
| 49 | + ,(bet_dwi0, hmc, [('mask_file', 'inputnode.in_mask')]) |
| 50 | + |
| 51 | + ,(hmc, sdc, [('outputnode.out_file', 'inputnode.in_file')]) |
| 52 | + ,(bet_dwi0, sdc, [('mask_file', 'inputnode.in_mask')]) |
| 53 | + ,(inputnode, sdc, [('in_bval', 'inputnode.in_bval'), |
| 54 | + ('bmap_pha', 'inputnode.bmap_pha'), |
| 55 | + ('bmap_mag', 'inputnode.bmap_mag')]) |
| 56 | + ,(inputnode, ecc, [('in_bval', 'inputnode.in_bval')]) |
| 57 | + ,(bet_dwi0, ecc, [('mask_file', 'inputnode.in_mask')]) |
| 58 | + ,(sdc, ecc, [('outputnode.out_file', 'inputnode.in_file')]) |
| 59 | + ,(hmc, outputnode, [('outputnode.out_bvec', 'out_bvec')]) |
| 60 | + ,(ecc, regrid, [('outputnode.out_file', 'in_file')]) |
| 61 | + ,(regrid, outputnode, [('out_file', 'out_file')]) |
| 62 | + ,(regrid, avg_b0_1, [('out_file', 'in_dwi')]) |
| 63 | + ,(inputnode, avg_b0_1, [('in_bval', 'in_bval')]) |
| 64 | + ,(avg_b0_1, bet_dwi1, [('out_file','in_file')]) |
| 65 | + ,(bet_dwi1, outputnode, [('mask_file', 'out_mask')]) |
| 66 | + ]) |
| 67 | + return wf |
| 68 | + |
| 69 | + |
| 70 | +def all_peb_pipeline(name='hmc_sdc_ecc', |
| 71 | + epi_params=dict(echospacing=0.77e-3, |
| 72 | + acc_factor=3, |
| 73 | + enc_dir='y-'), |
| 74 | + altepi_params=dict(echospacing=0.77e-3, |
| 75 | + acc_factor=3, |
| 76 | + enc_dir='y')): |
| 77 | + """ |
| 78 | + Builds a pipeline including three artifact corrections: head-motion correction (HMC), |
| 79 | + susceptibility-derived distortion correction (SDC), and Eddy currents-derived distortion |
| 80 | + correction (ECC). |
| 81 | + """ |
| 82 | + inputnode = pe.Node(niu.IdentityInterface(fields=['in_file', 'in_bvec', 'in_bval', |
| 83 | + 'alt_file']), name='inputnode') |
| 84 | + |
| 85 | + outputnode = pe.Node(niu.IdentityInterface(fields=['out_file', 'out_mask', 'out_bvec']), |
| 86 | + name='outputnode') |
| 87 | + |
| 88 | + avg_b0_0 = pe.Node(niu.Function(input_names=['in_dwi', 'in_bval'], |
| 89 | + output_names=['out_file'], function=b0_average), name='b0_avg_pre') |
| 90 | + avg_b0_1 = pe.Node(niu.Function(input_names=['in_dwi', 'in_bval'], |
| 91 | + output_names=['out_file'], function=b0_average), name='b0_avg_post') |
| 92 | + bet_dwi0 = pe.Node(fsl.BET(frac=0.3, mask=True, robust=True), name='bet_dwi_pre') |
| 93 | + bet_dwi1 = pe.Node(fsl.BET(frac=0.3, mask=True, robust=True), name='bet_dwi_post') |
| 94 | + |
| 95 | + hmc = hmc_pipeline() |
| 96 | + sdc = sdc_peb(epi_params=epi_params, altepi_params=altepi_params) |
| 97 | + ecc = ecc_pipeline() |
| 98 | + |
| 99 | + regrid = pe.Node(fs.MRIConvert(vox_size=(2.0, 2.0, 2.0), out_orientation='RAS'), |
| 100 | + name='Reslice') |
| 101 | + |
| 102 | + wf = pe.Workflow('dMRI_Artifacts') |
| 103 | + wf.connect([ |
| 104 | + (inputnode, hmc, [('in_file', 'inputnode.in_file'), |
| 105 | + ('in_bvec', 'inputnode.in_bvec')]) |
| 106 | + ,(inputnode, avg_b0_0, [('in_file', 'in_dwi'), |
| 107 | + ('in_bval', 'in_bval')]) |
| 108 | + ,(avg_b0_0, bet_dwi0, [('out_file','in_file')]) |
| 109 | + ,(bet_dwi0, hmc, [('mask_file', 'inputnode.in_mask')]) |
| 110 | + ,(hmc, sdc, [('outputnode.out_file', 'inputnode.in_file')]) |
| 111 | + ,(bet_dwi0, sdc, [('mask_file', 'inputnode.in_mask')]) |
| 112 | + ,(inputnode, sdc, [('in_bval', 'inputnode.in_bval'), |
| 113 | + ('alt_file', 'inputnode.alt_file')]) |
| 114 | + ,(inputnode, ecc, [('in_bval', 'inputnode.in_bval')]) |
| 115 | + ,(bet_dwi0, ecc, [('mask_file', 'inputnode.in_mask')]) |
| 116 | + ,(sdc, ecc, [('outputnode.out_file', 'inputnode.in_file')]) |
| 117 | + ,(hmc, outputnode, [('outputnode.out_bvec', 'out_bvec')]) |
| 118 | + ,(ecc, regrid, [('outputnode.out_file', 'in_file')]) |
| 119 | + ,(regrid, outputnode, [('out_file', 'out_file')]) |
| 120 | + ,(regrid, avg_b0_1, [('out_file', 'in_dwi')]) |
| 121 | + ,(inputnode, avg_b0_1, [('in_bval', 'in_bval')]) |
| 122 | + ,(avg_b0_1, bet_dwi1, [('out_file','in_file')]) |
| 123 | + ,(bet_dwi1, outputnode, [('mask_file', 'out_mask')]) |
| 124 | + ]) |
| 125 | + return wf |
| 126 | + |
| 127 | + |
13 | 128 | def hmc_pipeline(name='motion_correct'):
|
14 | 129 | """
|
15 | 130 | HMC stands for head-motion correction.
|
@@ -116,7 +231,7 @@ def ecc_pipeline(name='eddy_correct'):
|
116 | 231 | [Rohde04]_.
|
117 | 232 |
|
118 | 233 | A list of rigid transformation matrices can be provided, sourcing from a
|
119 |
| - :func:`.motion_correct` workflow, to initialize registrations in a *motion free* |
| 234 | + :func:`.hmc_pipeline` workflow, to initialize registrations in a *motion free* |
120 | 235 | framework.
|
121 | 236 |
|
122 | 237 | A list of affine transformation matrices is available as output, so that transforms
|
@@ -344,6 +459,90 @@ def sdc_fmb(name='fmb_correction',
|
344 | 459 | return wf
|
345 | 460 |
|
346 | 461 |
|
| 462 | +def sdc_peb(name='peb_correction', |
| 463 | + epi_params=dict(echospacing=0.77e-3, |
| 464 | + acc_factor=3, |
| 465 | + enc_dir='y-'), |
| 466 | + altepi_params=dict(echospacing=0.77e-3, |
| 467 | + acc_factor=3, |
| 468 | + enc_dir='y')): |
| 469 | + """ |
| 470 | + SDC stands for susceptibility distortion correction. PEB stands for phase-encoding-based. |
| 471 | +
|
| 472 | + The phase-encoding-based (PEB) method implements SDC by acquiring diffusion images with |
| 473 | + two different enconding directions [Andersson2003]_. The most typical case is acquiring |
| 474 | + with opposed phase-gradient blips (e.g. *A>>>P* and *P>>>A*, or equivalently, *-y* and *y*) |
| 475 | + as in [Chiou2000]_, but it is also possible to use orthogonal configurations [Cordes2000]_ |
| 476 | + (e.g. *A>>>P* and *L>>>R*, or equivalently *-y* and *x*). |
| 477 | + This workflow uses the implementation of FSL |
| 478 | + (`TOPUP <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/TOPUP>`_). |
| 479 | +
|
| 480 | + Example |
| 481 | + ------- |
| 482 | +
|
| 483 | + >>> from nipype.workflows.dmri.preprocess.epi import sdc_peb |
| 484 | + >>> peb = sdc_peb() |
| 485 | + >>> peb.inputs.inputnode.in_file = 'diffusion.nii' |
| 486 | + >>> peb.inputs.inputnode.in_bval = 'diffusion.bval' |
| 487 | + >>> peb.inputs.inputnode.in_mask = 'mask.nii' |
| 488 | + >>> peb.run() # doctest: +SKIP |
| 489 | +
|
| 490 | + .. admonition:: References |
| 491 | +
|
| 492 | + .. [Andersson2003] Andersson JL et al., `How to correct susceptibility distortions in |
| 493 | + spin-echo echo-planar images: application to diffusion tensor imaging |
| 494 | + <http://dx.doi.org/10.1016/S1053-8119(03)00336-7>`_. |
| 495 | + Neuroimage. 2003 Oct;20(2):870-88. doi: 10.1016/S1053-8119(03)00336-7 |
| 496 | +
|
| 497 | + .. [Cordes2000] Cordes D et al., Geometric distortion correction in EPI using two |
| 498 | + images with orthogonal phase-encoding directions, in Proc. ISMRM (8), p.1712, |
| 499 | + Denver, US, 2000. |
| 500 | +
|
| 501 | + .. [Chiou2000] Chiou JY, and Nalcioglu O, A simple method to correct off-resonance |
| 502 | + related distortion in echo planar imaging, in Proc. ISMRM (8), p.1712, Denver, US, |
| 503 | + 2000. |
| 504 | +
|
| 505 | + """ |
| 506 | + |
| 507 | + inputnode = pe.Node(niu.IdentityInterface(fields=['in_file', 'in_bval', 'in_mask', |
| 508 | + 'alt_file', 'ref_num']), |
| 509 | + name='inputnode') |
| 510 | + outputnode = pe.Node(niu.IdentityInterface(fields=['out_file', 'out_vsm']), |
| 511 | + name='outputnode') |
| 512 | + |
| 513 | + b0_ref = pe.Node(fsl.ExtractROI(t_size=1), name='b0_ref') |
| 514 | + b0_alt = pe.Node(fsl.ExtractROI(t_size=1), name='b0_alt') |
| 515 | + b0_comb = pe.Node(niu.Merge(2), name='b0_list') |
| 516 | + b0_merge = pe.Node(fsl.Merge(dimension='t'), name='b0_merged') |
| 517 | + |
| 518 | + topup = pe.Node(fsl.TOPUP(), name='topup') |
| 519 | + topup.inputs.encoding_direction = [epi_params['enc_dir'], altepi_params['enc_dir']] |
| 520 | + topup.inputs.readout_times = [epi_params['echospacing']/(1.0*epi_params['acc_factor']), |
| 521 | + altepi_params['echospacing']/(1.0*altepi_params['acc_factor'])] |
| 522 | + unwarp = pe.Node(fsl.ApplyTOPUP(in_index=[1,2]), name='unwarp') |
| 523 | + dwi_comb = pe.Node(niu.Merge(2), name='DWIcomb') |
| 524 | + |
| 525 | + wf = pe.Workflow(name=name) |
| 526 | + wf.connect([ |
| 527 | + (inputnode, b0_ref, [('in_file','in_file'), |
| 528 | + (('ref_num', _checkrnum),'t_min')]) |
| 529 | + ,(inputnode, b0_alt, [('alt_file','in_file'), |
| 530 | + (('ref_num', _checkrnum),'t_min')]) |
| 531 | + ,(inputnode, dwi_comb, [('in_file','in1'), |
| 532 | + ('alt_file','in2') ] ) |
| 533 | + ,(b0_ref, b0_comb, [('roi_file','in1')] ) |
| 534 | + ,(b0_alt, b0_comb, [('roi_file','in2')] ) |
| 535 | + ,(b0_comb, b0_merge, [('out', 'in_files')] ) |
| 536 | + ,(b0_merge, topup, [('merged_file','in_file')]) |
| 537 | + ,(topup, unwarp, [('out_fieldcoef','in_topup_fieldcoef'), |
| 538 | + ('out_movpar','in_topup_movpar'), |
| 539 | + ('out_enc_file','encoding_file')]) |
| 540 | + ,(dwi_comb, unwarp, [('out','in_files')]) |
| 541 | + ,(unwarp, outputnode, [('out_corrected','out_file')]) |
| 542 | + ]) |
| 543 | + return wf |
| 544 | + |
| 545 | + |
347 | 546 | def _checkrnum(ref_num):
|
348 | 547 | from nipype.interfaces.base import isdefined
|
349 | 548 | if (ref_num is None) or not isdefined(ref_num):
|
|
0 commit comments