26
26
def main ():
27
27
"""Set an entrypoint."""
28
28
opts = get_parser ().parse_args ()
29
+ if opts .longitudinal :
30
+ opts .subject_anatomical_reference = 'unbiased'
31
+ print (
32
+ 'The "--longitudinal" flag is deprecated. Use '
33
+ '"--subject-anatomical-reference unbiased" instead.'
34
+ )
35
+
36
+ if opts .subject_anatomical_reference == 'unbiased' :
37
+ opts ['longitudinal' ] = True
29
38
return build_opts (opts )
30
39
31
40
@@ -53,6 +62,9 @@ def get_parser():
53
62
54
63
import smriprep
55
64
65
+ def _drop_ses (value ):
66
+ return value .removeprefix ('ses-' )
67
+
56
68
parser = ArgumentParser (
57
69
description = 'sMRIPrep: Structural MRI PREProcessing workflows' ,
58
70
formatter_class = RawTextHelpFormatter ,
@@ -97,6 +109,13 @@ def get_parser():
97
109
help = 'a space delimited list of participant identifiers or a single '
98
110
'identifier (the sub- prefix can be removed)' ,
99
111
)
112
+ g_bids .add_argument (
113
+ '--session-label' ,
114
+ nargs = '+' ,
115
+ type = _drop_ses ,
116
+ help = 'A space delimited list of session identifiers or a single '
117
+ 'identifier (the ses- prefix can be removed)' ,
118
+ )
100
119
g_bids .add_argument (
101
120
'-d' ,
102
121
'--derivatives' ,
@@ -115,6 +134,17 @@ def get_parser():
115
134
'{<suffix>:{<entity>:<filter>,...},...} '
116
135
'(https://github.com/bids-standard/pybids/blob/master/bids/layout/config/bids.json)' ,
117
136
)
137
+ g_bids .add_argument (
138
+ '--subject-anatomical-reference' ,
139
+ choices = ['first-lex' , 'unbiased' , 'sessionwise' ],
140
+ default = 'first' ,
141
+ help = 'Method to produce the reference anatomical space:\n '
142
+ '\t "first-lex" will use the first image in lexicographical order\n '
143
+ '\t "unbiased" will construct an unbiased template from all images '
144
+ '(previously "--longitudinal")\n '
145
+ '\t "sessionwise" will independently process each session. If multiple runs are '
146
+ 'found, the behavior will be similar to "first-lex"' ,
147
+ )
118
148
119
149
g_perfm = parser .add_argument_group ('Options to handle performance' )
120
150
g_perfm .add_argument (
@@ -175,7 +205,7 @@ def get_parser():
175
205
g_conf .add_argument (
176
206
'--longitudinal' ,
177
207
action = 'store_true' ,
178
- help = 'treat dataset as longitudinal - may increase runtime ' ,
208
+ help = 'DEPRECATED: use "--subject-anatomical-reference unbiased" instead ' ,
179
209
)
180
210
181
211
# ANTs options
@@ -389,7 +419,7 @@ def _warn_redirect(message, category, filename, lineno, file=None, line=None):
389
419
plugin_settings = retval ['plugin_settings' ]
390
420
bids_dir = retval ['bids_dir' ]
391
421
output_dir = retval ['output_dir' ]
392
- subject_list = retval ['subject_list ' ]
422
+ subject_session_list = retval ['subject_session_list ' ]
393
423
run_uuid = retval ['run_uuid' ]
394
424
retcode = retval ['return_code' ]
395
425
@@ -438,15 +468,25 @@ def _warn_redirect(message, category, filename, lineno, file=None, line=None):
438
468
_copy_any (dseg_tsv , str (Path (output_dir ) / 'smriprep' / 'desc-aparcaseg_dseg.tsv' ))
439
469
logger .log (25 , 'sMRIPrep finished without errors' )
440
470
finally :
441
- from niworkflows . reports import generate_reports
471
+ from nireports . assembler . tools import generate_reports
442
472
443
- from ..utils .bids import write_bidsignore , write_derivative_description
473
+ from smriprep import data
474
+ from smriprep .utils .bids import write_bidsignore , write_derivative_description
444
475
445
- logger .log (25 , 'Writing reports for participants: %s' , ', ' .join (subject_list ))
476
+ logger .log (
477
+ 25 , 'Writing reports for participants: %s' , _pprint_subses (subject_session_list )
478
+ )
446
479
# Generate reports phase
447
- errno += generate_reports (subject_list , output_dir , run_uuid , packagename = 'smriprep' )
448
- write_derivative_description (bids_dir , str (Path (output_dir ) / 'smriprep' ))
449
- write_bidsignore (Path (output_dir ) / 'smriprep' )
480
+ smriprep_dir = Path (output_dir ) / 'smriprep'
481
+ bootstrap_file = data .load ('reports-spec.yml' )
482
+ errno += generate_reports (
483
+ subject_session_list ,
484
+ smriprep_dir ,
485
+ run_uuid ,
486
+ bootstrap_file = bootstrap_file ,
487
+ )
488
+ write_derivative_description (bids_dir , smriprep_dir )
489
+ write_bidsignore (smriprep_dir )
450
490
sys .exit (int (errno > 0 ))
451
491
452
492
@@ -469,7 +509,7 @@ def build_workflow(opts, retval):
469
509
from subprocess import CalledProcessError , TimeoutExpired , check_call
470
510
from time import strftime
471
511
472
- from bids import BIDSLayout
512
+ from bids . layout import BIDSLayout , Query
473
513
from nipype import config as ncfg
474
514
from nipype import logging
475
515
from niworkflows .utils .bids import collect_participants
@@ -482,7 +522,7 @@ def build_workflow(opts, retval):
482
522
INIT_MSG = """
483
523
Running sMRIPrep version {version}:
484
524
* BIDS dataset path: {bids_dir}.
485
- * Participant list : {subject_list }.
525
+ * Participants & Sessions : {subject_session_list }.
486
526
* Run identifier: {uuid}.
487
527
488
528
{spaces}
@@ -495,6 +535,31 @@ def build_workflow(opts, retval):
495
535
bids_dir = opts .bids_dir .resolve ()
496
536
layout = BIDSLayout (str (bids_dir ), validate = False )
497
537
subject_list = collect_participants (layout , participant_label = opts .participant_label )
538
+ session_list = opts .session_label or []
539
+
540
+ subject_session_list = []
541
+ for subject in subject_list :
542
+ sessions = (
543
+ layout .get_sessions (
544
+ scope = 'raw' ,
545
+ subject = subject ,
546
+ session = session_list or Query .OPTIONAL ,
547
+ suffix = ['T1w' , 'T2w' ], # TODO: Track supported modalities globally
548
+ )
549
+ or None
550
+ )
551
+
552
+ if opts .subject_anatomical_reference == 'sessionwise' :
553
+ if not sessions :
554
+ raise RuntimeError (
555
+ '--subject-anatomical-reference "sessionwise" was requested, but no sessions '
556
+ f'found for subject { subject } .'
557
+ )
558
+ for session in sessions :
559
+ subject_session_list .append ((subject , session ))
560
+ else :
561
+ # This will use all sessions either found by layout or passed in via --session-id
562
+ subject_session_list .append ((subject , sessions ))
498
563
499
564
bids_filters = json .loads (opts .bids_filter_file .read_text ()) if opts .bids_filter_file else None
500
565
@@ -576,19 +641,29 @@ def build_workflow(opts, retval):
576
641
retval ['bids_dir' ] = str (bids_dir )
577
642
retval ['output_dir' ] = str (output_dir )
578
643
retval ['work_dir' ] = str (work_dir )
579
- retval ['subject_list ' ] = subject_list
644
+ retval ['subject_session_list ' ] = subject_session_list
580
645
retval ['run_uuid' ] = run_uuid
581
646
retval ['workflow' ] = None
582
647
583
648
# Called with reports only
584
649
if opts .reports_only :
585
- from niworkflows . reports import generate_reports
650
+ from nireports . assembler . tools import generate_reports
586
651
587
- logger .log (25 , 'Running --reports-only on participants %s' , ', ' .join (subject_list ))
652
+ from smriprep import data
653
+
654
+ logger .log (
655
+ 25 , 'Running --reports-only on participants %s' , _pprint_subses (subject_session_list )
656
+ )
588
657
if opts .run_uuid is not None :
589
658
run_uuid = opts .run_uuid
659
+
660
+ smriprep_dir = output_dir / 'smriprep'
661
+ bootstrap_file = data .load ('reports-spec.yml' )
590
662
retval ['return_code' ] = generate_reports (
591
- subject_list , str (output_dir ), run_uuid , packagename = 'smriprep'
663
+ subject_session_list ,
664
+ smriprep_dir ,
665
+ run_uuid ,
666
+ bootstrap_file = bootstrap_file ,
592
667
)
593
668
return retval
594
669
@@ -601,7 +676,7 @@ def build_workflow(opts, retval):
601
676
INIT_MSG (
602
677
version = smriprep .__version__ ,
603
678
bids_dir = bids_dir ,
604
- subject_list = subject_list ,
679
+ subject_session_list = _pprint_subses ( subject_session_list ) ,
605
680
uuid = run_uuid ,
606
681
spaces = output_spaces ,
607
682
),
@@ -612,7 +687,6 @@ def build_workflow(opts, retval):
612
687
# XXX Makes strong assumption of legacy layout
613
688
smriprep_dir = str (output_dir / 'smriprep' )
614
689
warnings .warn (
615
- f'Received DEPRECATED --fast-track flag. Adding { smriprep_dir } to --derivatives list.'
616
690
f'Received DEPRECATED --fast-track flag. Adding { smriprep_dir } to --derivatives list.' ,
617
691
stacklevel = 1 ,
618
692
)
@@ -638,7 +712,7 @@ def build_workflow(opts, retval):
638
712
skull_strip_mode = opts .skull_strip_mode ,
639
713
skull_strip_template = opts .skull_strip_template [0 ],
640
714
spaces = output_spaces ,
641
- subject_list = subject_list ,
715
+ subject_session_list = subject_session_list ,
642
716
work_dir = str (work_dir ),
643
717
bids_filters = bids_filters ,
644
718
cifti_output = opts .cifti_output ,
@@ -694,6 +768,30 @@ def build_workflow(opts, retval):
694
768
return retval
695
769
696
770
771
+ def _pprint_subses (subses : list ):
772
+ """
773
+ Pretty print a list of subjects and sessions.
774
+
775
+ Example
776
+ -------
777
+ >>> _pprint_subses([('01', 'A'), ('02', ['A', 'B']), ('03', None), ('04', ['A'])])
778
+ 'sub-01 ses-A, sub-02 (2 sessions), sub-03, sub-04 ses-A'
779
+ """
780
+ output = []
781
+ for subject , session in subses :
782
+ if isinstance (session , list ):
783
+ if len (session ) > 1 :
784
+ output .append (f'sub-{ subject } ({ len (session )} sessions)' )
785
+ continue
786
+ session = session [0 ]
787
+ if session is None :
788
+ output .append (f'sub-{ subject } ' )
789
+ else :
790
+ output .append (f'sub-{ subject } ses-{ session } ' )
791
+
792
+ return ', ' .join (output )
793
+
794
+
697
795
if __name__ == '__main__' :
698
796
raise RuntimeError (
699
797
'smriprep/cli/run.py should not be run directly;\n '
0 commit comments