@@ -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
+ n_volumes_to_discard
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' , 'n_volumes_to_discard ' ,
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 , [('n_volumes_to_discard' , '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 , [('n_volumes_to_discard' , 'ignore_initial_volumes' )]),
265
263
(acc_tfm , acc_msk , [('output_image' , 'roi_file' )]),
266
264
(acc_msk , acompcor , [('out' , 'mask_files' )]),
267
265
@@ -494,6 +492,7 @@ def init_ica_aroma_wf(template, metadata, mem_gb, omp_nthreads,
494
492
'itk_bold_to_t1' ,
495
493
't1_2_mni_forward_transform' ,
496
494
'name_source' ,
495
+ 'n_volumes_to_discard' ,
497
496
'bold_split' ,
498
497
'bold_mask' ,
499
498
'hmc_xforms' ,
@@ -516,6 +515,10 @@ def init_ica_aroma_wf(template, metadata, mem_gb, omp_nthreads,
516
515
)
517
516
bold_mni_trans_wf .__desc__ = None
518
517
518
+ rm_non_steady_state = pe .Node (niu .Function (function = _remove_volumes ,
519
+ output_names = ['bold_cut' ]),
520
+ name = 'rm_nonsteady' )
521
+
519
522
calc_median_val = pe .Node (fsl .ImageStats (op_string = '-k %s -p 50' ), name = 'calc_median_val' )
520
523
calc_bold_mean = pe .Node (fsl .MeanImage (), name = 'calc_bold_mean' )
521
524
@@ -539,6 +542,10 @@ def _getusans_func(image, thresh):
539
542
denoise_type = 'nonaggr' , generate_report = True , TR = metadata ['RepetitionTime' ]),
540
543
name = 'ica_aroma' )
541
544
545
+ add_non_steady_state = pe .Node (niu .Function (function = _add_volumes ,
546
+ output_names = ['bold_add' ]),
547
+ name = 'add_nonsteady' )
548
+
542
549
# extract the confound ICs from the results
543
550
ica_aroma_confound_extraction = pe .Node (ICAConfounds (ignore_aroma_err = ignore_aroma_err ),
544
551
name = 'ica_aroma_confound_extraction' )
@@ -562,16 +569,21 @@ def _getbtthresh(medianval):
562
569
('t1_2_mni_forward_transform' , 'inputnode.t1_2_mni_forward_transform' ),
563
570
('fieldwarp' , 'inputnode.fieldwarp' )]),
564
571
(inputnode , ica_aroma , [('movpar_file' , 'motion_parameters' )]),
572
+ (inputnode , rm_non_steady_state , [
573
+ ('n_volumes_to_discard' , 'n_volumes' )]),
574
+ (bold_mni_trans_wf , rm_non_steady_state , [
575
+ ('outputnode.bold_mni' , 'bold_file' )]),
565
576
(bold_mni_trans_wf , calc_median_val , [
566
- ('outputnode.bold_mni' , 'in_file' ),
567
577
('outputnode.bold_mask_mni' , 'mask_file' )]),
568
- (bold_mni_trans_wf , calc_bold_mean , [
569
- ('outputnode.bold_mni' , 'in_file' )]),
578
+ (rm_non_steady_state , calc_median_val , [
579
+ ('bold_cut' , 'in_file' )]),
580
+ (rm_non_steady_state , calc_bold_mean , [
581
+ ('bold_cut' , 'in_file' )]),
570
582
(calc_bold_mean , getusans , [('out_file' , 'image' )]),
571
583
(calc_median_val , getusans , [('out_stat' , 'thresh' )]),
572
584
# Connect input nodes to complete smoothing
573
- (bold_mni_trans_wf , smooth , [
574
- ('outputnode.bold_mni ' , 'in_file' )]),
585
+ (rm_non_steady_state , smooth , [
586
+ ('bold_cut ' , 'in_file' )]),
575
587
(getusans , smooth , [('usans' , 'usans' )]),
576
588
(calc_median_val , smooth , [(('out_stat' , _getbtthresh ), 'brightness_threshold' )]),
577
589
# connect smooth to melodic
@@ -585,17 +597,65 @@ def _getbtthresh(medianval):
585
597
(melodic , ica_aroma , [('out_dir' , 'melodic_dir' )]),
586
598
# generate tsvs from ICA-AROMA
587
599
(ica_aroma , ica_aroma_confound_extraction , [('out_dir' , 'in_directory' )]),
600
+ (inputnode , ica_aroma_confound_extraction , [
601
+ ('n_volumes_to_discard' , 'n_volumes' )]),
588
602
# output for processing and reporting
589
603
(ica_aroma_confound_extraction , outputnode , [('aroma_confounds' , 'aroma_confounds' ),
590
604
('aroma_noise_ics' , 'aroma_noise_ics' ),
591
605
('melodic_mix' , 'melodic_mix' )]),
592
606
# TODO change melodic report to reflect noise and non-noise components
593
- (ica_aroma , outputnode , [('nonaggr_denoised_file' , 'nonaggr_denoised_file' )]),
607
+ (ica_aroma , add_non_steady_state , [
608
+ ('nonaggr_denoised_file' , 'bold_cut_file' )]),
609
+ (bold_mni_trans_wf , add_non_steady_state , [
610
+ ('outputnode.bold_mni' , 'bold_file' )]),
611
+ (inputnode , add_non_steady_state , [
612
+ ('n_volumes_to_discard' , 'n_volumes' )]),
613
+ (add_non_steady_state , outputnode , [('bold_add' , 'nonaggr_denoised_file' )]),
594
614
(ica_aroma , ds_report_ica_aroma , [('out_report' , 'in_file' )]),
595
615
])
596
616
597
617
return workflow
598
618
619
+ def _remove_volumes (bold_file , n_volumes ):
620
+ import nibabel as nb
621
+ from nipype .utils .filemanip import fname_presuffix
622
+
623
+ # load the bold file and get the 4d matrix
624
+ bold_img = nb .load (bold_file )
625
+ bold_data = bold_img .get_data ()
626
+
627
+ # cut off the beginning volumes
628
+ bold_data_cut = bold_data [..., n_volumes :]
629
+
630
+ # modify header with new shape (fewer volumes)
631
+ data_shape = list (bold_img .header .get_data_shape ())
632
+ data_shape [- 1 ] -= n_volumes
633
+ bold_img .header .set_data_shape (tuple (data_shape ))
634
+
635
+ # save the resulting bold file
636
+ out = fname_presuffix (bold_file , suffix = '_cut' )
637
+ bold_img .__class__ (bold_data_cut , bold_img .affine , bold_img .header ).to_filename (out )
638
+ return out
639
+
640
+
641
+ def _add_volumes (bold_file , bold_cut_file , n_volumes ):
642
+ """prepend n_volumes from bold_file onto bold_cut_file"""
643
+ import nibabel as nb
644
+ from nipype .utils .filemanip import fname_presuffix
645
+
646
+ # load the data
647
+ bold_img = nb .load (bold_file )
648
+ bold_data = bold_img .get_data ()
649
+ bold_cut_img = nb .load (bold_cut_file )
650
+ bold_cut_data = bold_cut_img .get_data ()
651
+
652
+ # assign everything from n_volumes foward to bold_cut_data
653
+ bold_data [..., n_volumes :] = bold_cut_data
654
+
655
+ out = fname_presuffix (bold_cut_file , suffix = '_addnonsteady' )
656
+ bold_img .__class__ (bold_data , bold_img .affine , bold_img .header ).to_filename (out )
657
+ return out
658
+
599
659
600
660
def _maskroi (in_mask , roi_file ):
601
661
import numpy as np
0 commit comments