-
Notifications
You must be signed in to change notification settings - Fork 307
ENH: Add dedicated session filtering, alternative anatomical template options #3495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 23 commits
6ea4cb4
0799a7f
0f6f664
002df3f
ae26b13
81e4903
9c8a90d
8772fd7
1c86ccb
563c936
b15f5ac
237a3bd
8b09e38
db533c7
1ea9211
9e2cf19
abf21b9
f37ae81
e48e483
7d98be6
ee82f57
99af5b9
2bc1a5b
832d651
0a013f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,10 +23,14 @@ | |
"""Parser.""" | ||
|
||
import sys | ||
import typing as ty | ||
from pathlib import Path | ||
|
||
from .. import config | ||
|
||
if ty.TYPE_CHECKING: | ||
from bids import BIDSLayout | ||
|
||
|
||
def _build_parser(**kwargs): | ||
"""Build parser object. | ||
|
@@ -51,6 +55,7 @@ def _build_parser(**kwargs): | |
'force_bbr': ('--force bbr', '26.0.0'), | ||
'force_no_bbr': ('--force no-bbr', '26.0.0'), | ||
'force_syn': ('--force syn-sdc', '26.0.0'), | ||
'longitudinal': ('--subject-anatomical-reference unbiased', '26.1.0'), | ||
} | ||
|
||
class DeprecatedAction(Action): | ||
|
@@ -220,9 +225,14 @@ def _fallback_trt(value, parser): | |
help='A space delimited list of participant identifiers or a single ' | ||
'identifier (the sub- prefix can be removed)', | ||
) | ||
# Re-enable when option is actually implemented | ||
# g_bids.add_argument('-s', '--session-id', action='store', default='single_session', | ||
# help='Select a specific session to be processed') | ||
|
||
g_bids.add_argument( | ||
'--session-label', | ||
nargs='+', | ||
type=lambda label: label.removeprefix('ses-'), | ||
help='A space delimited list of session identifiers or a single ' | ||
'identifier (the ses- prefix can be removed)', | ||
) | ||
# Re-enable when option is actually implemented | ||
# g_bids.add_argument('-r', '--run-id', action='store', default='single_run', | ||
# help='Select a specific run to be processed') | ||
|
@@ -235,6 +245,17 @@ def _fallback_trt(value, parser): | |
type=int, | ||
help='Select a specific echo to be processed in a multiecho series', | ||
) | ||
g_bids.add_argument( | ||
'--subject-anatomical-reference', | ||
choices=['first-lex', 'unbiased', 'sessionwise'], | ||
default='first-lex', | ||
help='Method to produce the reference anatomical space:\n' | ||
'\t"first-lex" will use the first image in lexicographical order\n' | ||
'\t"unbiased" will construct an unbiased template from all images ' | ||
'(previously "--longitudinal")\n' | ||
'\t"sessionwise" will independently process each session. If multiple runs are ' | ||
'found, the behavior will be similar to "first-lex" for each session.', | ||
) | ||
g_bids.add_argument( | ||
'--bids-filter-file', | ||
dest='bids_filters', | ||
|
@@ -384,8 +405,8 @@ def _fallback_trt(value, parser): | |
) | ||
g_conf.add_argument( | ||
'--longitudinal', | ||
action='store_true', | ||
help='Treat dataset as longitudinal - may increase runtime', | ||
action=DeprecatedAction, | ||
help='Deprecated - use `--subject-anatomical-reference unbiased` instead', | ||
) | ||
g_conf.add_argument( | ||
'--bold2anat-init', | ||
|
@@ -769,6 +790,7 @@ def parse_args(args=None, namespace=None): | |
"""Parse args and run further checks on the command line.""" | ||
import logging | ||
|
||
from niworkflows.utils.bids import collect_participants | ||
from niworkflows.utils.spaces import Reference, SpatialReferences | ||
|
||
parser = _build_parser() | ||
|
@@ -779,6 +801,14 @@ def parse_args(args=None, namespace=None): | |
config.load(opts.config_file, skip=skip, init=False) | ||
config.loggers.cli.info(f'Loaded previous configuration file {opts.config_file}') | ||
|
||
if opts.longitudinal: | ||
opts.subject_anatomical_reference = 'unbiased' | ||
msg = ( | ||
'The `--longitudinal` flag is deprecated - use ' | ||
'`--subject-anatomical-reference unbiased` instead.' | ||
) | ||
config.loggers.cli.warning(msg) | ||
|
||
config.execution.log_level = int(max(25 - 5 * opts.verbose_count, logging.DEBUG)) | ||
config.from_dict(vars(opts), init=['nipype']) | ||
|
||
|
@@ -927,19 +957,68 @@ def parse_args(args=None, namespace=None): | |
work_dir.mkdir(exist_ok=True, parents=True) | ||
|
||
# Force initialization of the BIDSLayout | ||
config.loggers.cli.debug('Initializing BIDS Layout') | ||
config.execution.init() | ||
all_subjects = config.execution.layout.get_subjects() | ||
|
||
# Please note this is the input folder's dataset_description.json | ||
dset_desc_path = config.execution.bids_dir / 'dataset_description.json' | ||
if dset_desc_path.exists(): | ||
from hashlib import sha256 | ||
|
||
desc_content = dset_desc_path.read_bytes() | ||
config.execution.bids_description_hash = sha256(desc_content).hexdigest() | ||
|
||
# First check that bids_dir looks like a BIDS folder | ||
subject_list = collect_participants( | ||
config.execution.layout, participant_label=config.execution.participant_label | ||
) | ||
if config.execution.participant_label is None: | ||
config.execution.participant_label = all_subjects | ||
config.execution.participant_label = subject_list | ||
|
||
participant_label = set(config.execution.participant_label) | ||
missing_subjects = participant_label - set(all_subjects) | ||
if missing_subjects: | ||
parser.error( | ||
'One or more participant labels were not found in the BIDS directory: {}.'.format( | ||
', '.join(missing_subjects) | ||
session_list = config.execution.session_label or [] | ||
subject_session_list = create_processing_groups( | ||
config.execution.layout, | ||
subject_list, | ||
session_list, | ||
config.workflow.subject_anatomical_reference, | ||
) | ||
config.execution.processing_groups = subject_session_list | ||
config.execution.participant_label = sorted(subject_list) | ||
config.workflow.skull_strip_template = config.workflow.skull_strip_template[0] | ||
|
||
|
||
def create_processing_groups( | ||
layout: 'BIDSLayout', | ||
subject_list: list, | ||
session_list: list | str | None, | ||
subject_anatomical_reference: str, | ||
) -> list[tuple[str]]: | ||
"""Generate a list of subject-session pairs to be processed.""" | ||
from bids.layout import Query | ||
|
||
subject_session_list = [] | ||
|
||
for subject in subject_list: | ||
sessions = ( | ||
layout.get_sessions( | ||
scope='raw', | ||
subject=subject, | ||
session=session_list or Query.OPTIONAL, | ||
) | ||
or None | ||
) | ||
|
||
config.execution.participant_label = sorted(participant_label) | ||
config.workflow.skull_strip_template = config.workflow.skull_strip_template[0] | ||
if subject_anatomical_reference == 'sessionwise': | ||
if not sessions: | ||
raise RuntimeError( | ||
'`--subject-anatomical-reference sessionwise` was requested, but no sessions ' | ||
f'found for subject {subject}.' | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like it shouldn't be an error. No sessions is the same as a single session, so I would expect it to work the same. I think changing this to: if subject_anatomical_reference == 'sessionwise' and sessions:
subject_session_list.extend((subject, session) for session in sessions)
else:
subject_session_list.append((subject, sessions)) should cover your use cases... That said, this isn't tested, so it's hard to figure out what's happening. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was maintaining the logic first implemented in smriprep, but i agree that raising an error is a bit too strict - imo, a warning would suffice. |
||
for session in sessions: | ||
if len(session) == 1: | ||
session = session[0] | ||
mgxd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
subject_session_list.append((subject, session)) | ||
else: | ||
subject_session_list.append((subject, sessions)) | ||
|
||
return subject_session_list |
Uh oh!
There was an error while loading. Please reload this page.