@@ -92,6 +92,8 @@ def init_bold_confs_wf(mem_gb, metadata, name="bold_confs_wf"):
92
92
BOLD series mask
93
93
movpar_file
94
94
SPM-formatted motion parameters file
95
+ skip_vols
96
+ number of non steady state volumes
95
97
t1_mask
96
98
Mask of the skull-stripped template image
97
99
t1_tpms
@@ -136,8 +138,8 @@ def init_bold_confs_wf(mem_gb, metadata, name="bold_confs_wf"):
136
138
placed within the corresponding confounds file.
137
139
"""
138
140
inputnode = pe .Node (niu .IdentityInterface (
139
- fields = ['bold' , 'bold_mask' , 'movpar_file' , 't1_mask' , 't1_tpms ' ,
140
- 't1_bold_xform' ]),
141
+ fields = ['bold' , 'bold_mask' , 'movpar_file' , 'skip_vols ' ,
142
+ 't1_mask' , 't1_tpms' , ' t1_bold_xform' ]),
141
143
name = 'inputnode' )
142
144
outputnode = pe .Node (niu .IdentityInterface (
143
145
fields = ['confounds_file' ]),
@@ -178,7 +180,6 @@ def init_bold_confs_wf(mem_gb, metadata, name="bold_confs_wf"):
178
180
name = "fdisp" , mem_gb = mem_gb )
179
181
180
182
# a/t-CompCor
181
- non_steady_state = pe .Node (nac .NonSteadyStateDetector (), name = 'non_steady_state' )
182
183
tcompcor = pe .Node (TCompCor (
183
184
components_file = 'tcompcor.tsv' , pre_filter = 'cosine' , save_pre_filter = True ,
184
185
percentile_threshold = .05 ), name = "tcompcor" , mem_gb = mem_gb )
@@ -250,18 +251,15 @@ def _pick_wm(files):
250
251
('bold_mask' , 'in_mask' )]),
251
252
(inputnode , fdisp , [('movpar_file' , 'in_file' )]),
252
253
253
- # Calculate nonsteady state
254
- (inputnode , non_steady_state , [('bold' , 'in_file' )]),
255
-
256
254
# tCompCor
257
255
(inputnode , tcompcor , [('bold' , 'realigned_file' )]),
258
- (non_steady_state , tcompcor , [('n_volumes_to_discard ' , 'ignore_initial_volumes' )]),
256
+ (inputnode , tcompcor , [('skip_vols ' , 'ignore_initial_volumes' )]),
259
257
(tcc_tfm , tcc_msk , [('output_image' , 'roi_file' )]),
260
258
(tcc_msk , tcompcor , [('out' , 'mask_files' )]),
261
259
262
260
# aCompCor
263
261
(inputnode , acompcor , [('bold' , 'realigned_file' )]),
264
- (non_steady_state , acompcor , [('n_volumes_to_discard ' , 'ignore_initial_volumes' )]),
262
+ (inputnode , acompcor , [('skip_vols ' , 'ignore_initial_volumes' )]),
265
263
(acc_tfm , acc_msk , [('output_image' , 'roi_file' )]),
266
264
(acc_msk , acompcor , [('out' , 'mask_files' )]),
267
265
@@ -399,6 +397,7 @@ def init_ica_aroma_wf(template, metadata, mem_gb, omp_nthreads,
399
397
400
398
The following steps are performed:
401
399
400
+ #. Remove non-steady state volumes from the bold series.
402
401
#. Smooth data using FSL `susan`, with a kernel width FWHM=6.0mm.
403
402
#. Run FSL `melodic` outside of ICA-AROMA to generate the report
404
403
#. Run ICA-AROMA
@@ -453,15 +452,27 @@ def init_ica_aroma_wf(template, metadata, mem_gb, omp_nthreads,
453
452
Set the dimensionality of the Melodic ICA decomposition
454
453
If None, MELODIC automatically estimates dimensionality.
455
454
456
-
457
455
**Inputs**
458
456
459
- bold_mni
460
- BOLD series, resampled to template space
457
+ itk_bold_to_t1
458
+ Affine transform from ``ref_bold_brain`` to T1 space (ITK format)
459
+ t1_2_mni_forward_transform
460
+ ANTs-compatible affine-and-warp transform file
461
+ name_source
462
+ BOLD series NIfTI file
463
+ Used to recover original information lost during processing
464
+ skip_vols
465
+ number of non steady state volumes
466
+ bold_split
467
+ Individual 3D BOLD volumes, not motion corrected
468
+ bold_mask
469
+ BOLD series mask in template space
470
+ hmc_xforms
471
+ List of affine transforms aligning each volume to ``ref_image`` in ITK format
472
+ fieldwarp
473
+ a :abbr:`DFM (displacements field map)` in ITK format
461
474
movpar_file
462
475
SPM-formatted motion parameters file
463
- bold_mask_mni
464
- BOLD series mask in template space
465
476
466
477
**Outputs**
467
478
@@ -474,15 +485,15 @@ def init_ica_aroma_wf(template, metadata, mem_gb, omp_nthreads,
474
485
nonaggr_denoised_file
475
486
BOLD series with non-aggressive ICA-AROMA denoising applied
476
487
477
- .. _ICA-AROMA: https://github.com/rhr-pruim /ICA-AROMA
488
+ .. _ICA-AROMA: https://github.com/maartenmennes /ICA-AROMA
478
489
479
490
"""
480
491
workflow = Workflow (name = name )
481
492
workflow .__postdesc__ = """\
482
493
Automatic removal of motion artifacts using independent component analysis
483
494
[ICA-AROMA, @aroma] was performed on the *preprocessed BOLD on MNI space*
484
- time-series after a spatial smoothing with an isotropic, Gaussian kernel
485
- of 6mm FWHM (full-width half-maximum).
495
+ time-series after removal of non-steady state volumes and spatial smoothing
496
+ with an isotropic, Gaussian kernel of 6mm FWHM (full-width half-maximum).
486
497
Corresponding "non-aggresively" denoised runs were produced after such
487
498
smoothing.
488
499
Additionally, the "aggressive" noise-regressors were collected and placed
@@ -494,6 +505,7 @@ def init_ica_aroma_wf(template, metadata, mem_gb, omp_nthreads,
494
505
'itk_bold_to_t1' ,
495
506
't1_2_mni_forward_transform' ,
496
507
'name_source' ,
508
+ 'skip_vols' ,
497
509
'bold_split' ,
498
510
'bold_mask' ,
499
511
'hmc_xforms' ,
@@ -516,6 +528,10 @@ def init_ica_aroma_wf(template, metadata, mem_gb, omp_nthreads,
516
528
)
517
529
bold_mni_trans_wf .__desc__ = None
518
530
531
+ rm_non_steady_state = pe .Node (niu .Function (function = _remove_volumes ,
532
+ output_names = ['bold_cut' ]),
533
+ name = 'rm_nonsteady' )
534
+
519
535
calc_median_val = pe .Node (fsl .ImageStats (op_string = '-k %s -p 50' ), name = 'calc_median_val' )
520
536
calc_bold_mean = pe .Node (fsl .MeanImage (), name = 'calc_bold_mean' )
521
537
@@ -539,6 +555,10 @@ def _getusans_func(image, thresh):
539
555
denoise_type = 'nonaggr' , generate_report = True , TR = metadata ['RepetitionTime' ]),
540
556
name = 'ica_aroma' )
541
557
558
+ add_non_steady_state = pe .Node (niu .Function (function = _add_volumes ,
559
+ output_names = ['bold_add' ]),
560
+ name = 'add_nonsteady' )
561
+
542
562
# extract the confound ICs from the results
543
563
ica_aroma_confound_extraction = pe .Node (ICAConfounds (ignore_aroma_err = ignore_aroma_err ),
544
564
name = 'ica_aroma_confound_extraction' )
@@ -562,16 +582,21 @@ def _getbtthresh(medianval):
562
582
('t1_2_mni_forward_transform' , 'inputnode.t1_2_mni_forward_transform' ),
563
583
('fieldwarp' , 'inputnode.fieldwarp' )]),
564
584
(inputnode , ica_aroma , [('movpar_file' , 'motion_parameters' )]),
585
+ (inputnode , rm_non_steady_state , [
586
+ ('skip_vols' , 'skip_vols' )]),
587
+ (bold_mni_trans_wf , rm_non_steady_state , [
588
+ ('outputnode.bold_mni' , 'bold_file' )]),
565
589
(bold_mni_trans_wf , calc_median_val , [
566
- ('outputnode.bold_mni' , 'in_file' ),
567
590
('outputnode.bold_mask_mni' , 'mask_file' )]),
568
- (bold_mni_trans_wf , calc_bold_mean , [
569
- ('outputnode.bold_mni' , 'in_file' )]),
591
+ (rm_non_steady_state , calc_median_val , [
592
+ ('bold_cut' , 'in_file' )]),
593
+ (rm_non_steady_state , calc_bold_mean , [
594
+ ('bold_cut' , 'in_file' )]),
570
595
(calc_bold_mean , getusans , [('out_file' , 'image' )]),
571
596
(calc_median_val , getusans , [('out_stat' , 'thresh' )]),
572
597
# Connect input nodes to complete smoothing
573
- (bold_mni_trans_wf , smooth , [
574
- ('outputnode.bold_mni ' , 'in_file' )]),
598
+ (rm_non_steady_state , smooth , [
599
+ ('bold_cut ' , 'in_file' )]),
575
600
(getusans , smooth , [('usans' , 'usans' )]),
576
601
(calc_median_val , smooth , [(('out_stat' , _getbtthresh ), 'brightness_threshold' )]),
577
602
# connect smooth to melodic
@@ -586,18 +611,63 @@ def _getbtthresh(medianval):
586
611
(melodic , ica_aroma , [('out_dir' , 'melodic_dir' )]),
587
612
# generate tsvs from ICA-AROMA
588
613
(ica_aroma , ica_aroma_confound_extraction , [('out_dir' , 'in_directory' )]),
614
+ (inputnode , ica_aroma_confound_extraction , [
615
+ ('skip_vols' , 'skip_vols' )]),
589
616
# output for processing and reporting
590
617
(ica_aroma_confound_extraction , outputnode , [('aroma_confounds' , 'aroma_confounds' ),
591
618
('aroma_noise_ics' , 'aroma_noise_ics' ),
592
619
('melodic_mix' , 'melodic_mix' )]),
593
620
# TODO change melodic report to reflect noise and non-noise components
594
- (ica_aroma , outputnode , [('nonaggr_denoised_file' , 'nonaggr_denoised_file' )]),
621
+ (ica_aroma , add_non_steady_state , [
622
+ ('nonaggr_denoised_file' , 'bold_cut_file' )]),
623
+ (bold_mni_trans_wf , add_non_steady_state , [
624
+ ('outputnode.bold_mni' , 'bold_file' )]),
625
+ (inputnode , add_non_steady_state , [
626
+ ('skip_vols' , 'skip_vols' )]),
627
+ (add_non_steady_state , outputnode , [('bold_add' , 'nonaggr_denoised_file' )]),
595
628
(ica_aroma , ds_report_ica_aroma , [('out_report' , 'in_file' )]),
596
629
])
597
630
598
631
return workflow
599
632
600
633
634
+ def _remove_volumes (bold_file , skip_vols ):
635
+ """remove skip_vols from bold_file"""
636
+ import nibabel as nb
637
+ from nipype .utils .filemanip import fname_presuffix
638
+
639
+ if skip_vols == 0 :
640
+ return bold_file
641
+
642
+ out = fname_presuffix (bold_file , suffix = '_cut' )
643
+ bold_img = nb .load (bold_file )
644
+ bold_img .__class__ (bold_img .dataobj [..., skip_vols :],
645
+ bold_img .affine , bold_img .header ).to_filename (out )
646
+
647
+ return out
648
+
649
+
650
+ def _add_volumes (bold_file , bold_cut_file , skip_vols ):
651
+ """prepend skip_vols from bold_file onto bold_cut_file"""
652
+ import nibabel as nb
653
+ import numpy as np
654
+ from nipype .utils .filemanip import fname_presuffix
655
+
656
+ if skip_vols == 0 :
657
+ return bold_cut_file
658
+
659
+ bold_img = nb .load (bold_file )
660
+ bold_cut_img = nb .load (bold_cut_file )
661
+
662
+ bold_data = np .concatenate ((bold_img .dataobj [..., :skip_vols ],
663
+ bold_cut_img .dataobj ), axis = 3 )
664
+
665
+ out = fname_presuffix (bold_cut_file , suffix = '_addnonsteady' )
666
+ bold_img .__class__ (bold_data , bold_img .affine , bold_img .header ).to_filename (out )
667
+
668
+ return out
669
+
670
+
601
671
def _maskroi (in_mask , roi_file ):
602
672
import numpy as np
603
673
import nibabel as nb
0 commit comments