Skip to content
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

[WIP] ENH: Add --func-only option to skip anatomical processing #808

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Editors
.*.py.swo

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
4 changes: 4 additions & 0 deletions .zenodo.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
"affiliation": "MIT",
"orcid": "0000-0002-7252-7771"
},
{
"name": "Madeleine Snyder",
"affiliation": "Harvard Medical School, Harvard University, Cambridge, Massachusetts"
},
{
"name": "Poldrack, Russell A.",
"affiliation": "Department of Psychology, Stanford University",
Expand Down
1 change: 1 addition & 0 deletions docs/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ is presented below:
debug=False,
low_mem=False,
anat_only=False,
func_only=False,
hires=True,
use_bbr=True,
bold2t1w_dof=9,
Expand Down
10 changes: 8 additions & 2 deletions fmriprep/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ def get_parser():
'in working directory)')
g_perfm.add_argument('--use-plugin', action='store', default=None,
help='nipype plugin configuration file')
g_perfm.add_argument('--anat-only', action='store_true',
help='run anatomical workflows only')

g_perfm.add_argument('--ignore-aroma-denoising-errors', action='store_true',
default=False,
help='ignores the errors ICA_AROMA returns when there '
Expand All @@ -92,6 +91,12 @@ def get_parser():
g_perfm.add_argument("-v", "--verbose", dest="verbose_count", action="count", default=0,
help="increases log verbosity for each occurence, debug level is -vvv")

# Add mutually exclusive func_only and anat_only pipeline options
g_anatfunc = parser.add_mutually_exclusive_group()
g_anatfunc.add_argument('--anat-only', action='store_true')
g_anatfunc.add_argument('--func-only', action='store_true')

# Add workflow config arguments
g_conf = parser.add_argument_group('Workflow configuration')
g_conf.add_argument(
'--ignore', required=False, action='store', nargs="+", default=[],
Expand Down Expand Up @@ -416,6 +421,7 @@ def build_workflow(opts, retval):
debug=opts.debug,
low_mem=opts.low_mem,
anat_only=opts.anat_only,
func_only=opts.func_only,
longitudinal=opts.longitudinal,
omp_nthreads=omp_nthreads,
skull_strip_template=opts.skull_strip_template,
Expand Down
17 changes: 10 additions & 7 deletions fmriprep/interfaces/bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# @Author: oesteban
# @Date: 2016-06-03 09:35:13
# @Last Modified by: oesteban
# @Last Modified time: 2017-10-10 15:37:47
# @Last Modified time: 2017-10-31 10:01:08
"""
Interfaces for handling BIDS-like neuroimaging structures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -130,25 +130,28 @@ class BIDSDataGrabber(SimpleInterface):
"""
input_spec = BIDSDataGrabberInputSpec
output_spec = BIDSDataGrabberOutputSpec
_require_funcs = True
_require_func = True
_require_anat = True

def __init__(self, *args, **kwargs):
anat_only = kwargs.pop('anat_only')
anat_only = kwargs.pop('anat_only', False)
func_only = kwargs.pop('func_only', False)
super(BIDSDataGrabber, self).__init__(*args, **kwargs)
if anat_only is not None:
self._require_funcs = not anat_only

self._require_func = anat_only is not True
self._require_anat = func_only is not True

def _run_interface(self, runtime):
bids_dict = self.inputs.subject_data

self._results['out_dict'] = bids_dict
self._results.update(bids_dict)

if not bids_dict['t1w']:
if self._require_anat and not bids_dict['t1w']:
raise FileNotFoundError('No T1w images found for subject sub-{}'.format(
self.inputs.subject_id))

if self._require_funcs and not bids_dict['bold']:
if self._require_func and not bids_dict['bold']:
raise FileNotFoundError('No functional images found for subject sub-{}'.format(
self.inputs.subject_id))

Expand Down
194 changes: 120 additions & 74 deletions fmriprep/workflows/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
from ..info import __version__

from .anatomical import init_anat_preproc_wf
from .bold import init_func_preproc_wf
from .bold import init_func_preproc_wf, init_onlyfunc_preproc_wf


def init_fmriprep_wf(subject_list, task_id, run_uuid,
ignore, debug, low_mem, anat_only, longitudinal, omp_nthreads,
ignore, debug, low_mem, anat_only, func_only, longitudinal, omp_nthreads,
skull_strip_template, work_dir, output_dir, bids_dir,
freesurfer, output_spaces, template, medial_surface_nan, hires,
use_bbr, bold2t1w_dof, fmap_bspline, fmap_demean, use_syn, force_syn,
Expand All @@ -55,6 +55,7 @@ def init_fmriprep_wf(subject_list, task_id, run_uuid,
debug=False,
low_mem=False,
anat_only=False,
func_only=False,
longitudinal=False,
omp_nthreads=1,
skull_strip_template='OASIS',
Expand Down Expand Up @@ -94,6 +95,8 @@ def init_fmriprep_wf(subject_list, task_id, run_uuid,
Write uncompressed .nii files in some cases to reduce memory usage
anat_only : bool
Disable functional workflows
func_only : bool
Disable anatomical workflows
longitudinal : bool
Treat multiple sessions as longitudinal (may increase runtime)
See sub-workflows for specific differences
Expand Down Expand Up @@ -167,6 +170,7 @@ def init_fmriprep_wf(subject_list, task_id, run_uuid,
debug=debug,
low_mem=low_mem,
anat_only=anat_only,
func_only=func_only,
longitudinal=longitudinal,
omp_nthreads=omp_nthreads,
skull_strip_template=skull_strip_template,
Expand Down Expand Up @@ -203,8 +207,8 @@ def init_fmriprep_wf(subject_list, task_id, run_uuid,


def init_single_subject_wf(subject_id, task_id, name,
ignore, debug, low_mem, anat_only, longitudinal, omp_nthreads,
skull_strip_template, reportlets_dir, output_dir,
ignore, debug, low_mem, anat_only, func_only, longitudinal,
omp_nthreads, skull_strip_template, reportlets_dir, output_dir,
bids_dir, freesurfer, output_spaces, template, medial_surface_nan,
hires, use_bbr, bold2t1w_dof, fmap_bspline, fmap_demean, use_syn,
force_syn, output_grid_ref, use_aroma, ignore_aroma_err):
Expand Down Expand Up @@ -241,6 +245,7 @@ def init_single_subject_wf(subject_id, task_id, name,
debug=False,
low_mem=False,
anat_only=False,
func_only=False,
hires=True,
use_bbr=True,
bold2t1w_dof=9,
Expand Down Expand Up @@ -268,6 +273,8 @@ def init_single_subject_wf(subject_id, task_id, name,
Write uncompressed .nii files in some cases to reduce memory usage
anat_only : bool
Disable functional workflows
func_only : bool
Disable anatomical workflows
longitudinal : bool
Treat multiple sessions as longitudinal (may increase runtime)
See sub-workflows for specific differences
Expand Down Expand Up @@ -342,7 +349,7 @@ def init_single_subject_wf(subject_id, task_id, name,
"All workflows require BOLD images.".format(
subject_id, task_id if task_id else '<all>'))

if not subject_data['t1w']:
if not func_only or not subject_data['t1w']:
raise Exception("No T1w images found for participant {}. "
"All workflows require T1w images.".format(subject_id))

Expand All @@ -351,7 +358,8 @@ def init_single_subject_wf(subject_id, task_id, name,
inputnode = pe.Node(niu.IdentityInterface(fields=['subjects_dir']),
name='inputnode')

bidssrc = pe.Node(BIDSDataGrabber(subject_data=subject_data, anat_only=anat_only),
bidssrc = pe.Node(BIDSDataGrabber(subject_data=subject_data, anat_only=anat_only,
func_only=func_only),
name='bidssrc')

bids_info = pe.Node(BIDSInfo(), name='bids_info', run_without_submitting=True)
Expand All @@ -373,79 +381,117 @@ def init_single_subject_wf(subject_id, task_id, name,
suffix='about'),
name='ds_about_report', run_without_submitting=True)

# Preprocessing of T1w (includes registration to MNI)
anat_preproc_wf = init_anat_preproc_wf(name="anat_preproc_wf",
skull_strip_template=skull_strip_template,
output_spaces=output_spaces,
template=template,
debug=debug,
longitudinal=longitudinal,
omp_nthreads=omp_nthreads,
freesurfer=freesurfer,
hires=hires,
reportlets_dir=reportlets_dir,
output_dir=output_dir,
num_t1w=len(subject_data['t1w']))

workflow.connect([
(inputnode, anat_preproc_wf, [('subjects_dir', 'inputnode.subjects_dir')]),
(bidssrc, bids_info, [(('t1w', fix_multi_T1w_source_name), 'in_file')]),
(inputnode, summary, [('subjects_dir', 'subjects_dir')]),
(bidssrc, summary, [('t1w', 't1w'),
('t2w', 't2w'),
('bold', 'bold')]),
(bids_info, summary, [('subject_id', 'subject_id')]),
(bidssrc, anat_preproc_wf, [('t1w', 'inputnode.t1w'),
('t2w', 'inputnode.t2w')]),
(summary, anat_preproc_wf, [('subject_id', 'inputnode.subject_id')]),
(bidssrc, ds_summary_report, [(('t1w', fix_multi_T1w_source_name), 'source_file')]),
(summary, ds_summary_report, [('out_report', 'in_file')]),
(bidssrc, ds_about_report, [(('t1w', fix_multi_T1w_source_name), 'source_file')]),
(about, ds_about_report, [('out_report', 'in_file')]),
])

if anat_only:
return workflow

for bold_file in subject_data['bold']:
func_preproc_wf = init_func_preproc_wf(bold_file=bold_file,
layout=layout,
ignore=ignore,
freesurfer=freesurfer,
use_bbr=use_bbr,
bold2t1w_dof=bold2t1w_dof,
reportlets_dir=reportlets_dir,
if not func_only:
# Preprocessing of T1w (includes registration to MNI)
anat_preproc_wf = init_anat_preproc_wf(name="anat_preproc_wf",
skull_strip_template=skull_strip_template,
output_spaces=output_spaces,
template=template,
medial_surface_nan=medial_surface_nan,
output_dir=output_dir,
omp_nthreads=omp_nthreads,
low_mem=low_mem,
fmap_bspline=fmap_bspline,
fmap_demean=fmap_demean,
use_syn=use_syn,
force_syn=force_syn,
debug=debug,
output_grid_ref=output_grid_ref,
use_aroma=use_aroma,
ignore_aroma_err=ignore_aroma_err)
longitudinal=longitudinal,
omp_nthreads=omp_nthreads,
freesurfer=freesurfer,
hires=hires,
reportlets_dir=reportlets_dir,
output_dir=output_dir,
num_t1w=len(subject_data['t1w']))

workflow.connect([
(anat_preproc_wf, func_preproc_wf,
[('outputnode.t1_preproc', 'inputnode.t1_preproc'),
('outputnode.t1_brain', 'inputnode.t1_brain'),
('outputnode.t1_mask', 'inputnode.t1_mask'),
('outputnode.t1_seg', 'inputnode.t1_seg'),
('outputnode.t1_tpms', 'inputnode.t1_tpms'),
('outputnode.t1_2_mni_forward_transform', 'inputnode.t1_2_mni_forward_transform'),
('outputnode.t1_2_mni_reverse_transform', 'inputnode.t1_2_mni_reverse_transform'),
# Undefined if --no-freesurfer, but this is safe
('outputnode.subjects_dir', 'inputnode.subjects_dir'),
('outputnode.subject_id', 'inputnode.subject_id'),
('outputnode.t1_2_fsnative_forward_transform',
'inputnode.t1_2_fsnative_forward_transform'),
('outputnode.t1_2_fsnative_reverse_transform',
'inputnode.t1_2_fsnative_reverse_transform')]),
(inputnode, anat_preproc_wf, [('subjects_dir', 'inputnode.subjects_dir')]),
(bidssrc, bids_info, [(('t1w', fix_multi_T1w_source_name), 'in_file')]),
(inputnode, summary, [('subjects_dir', 'subjects_dir')]),
(bidssrc, summary, [('t1w', 't1w'),
('t2w', 't2w'),
('bold', 'bold')]),
(bids_info, summary, [('subject_id', 'subject_id')]),
(bidssrc, anat_preproc_wf, [('t1w', 'inputnode.t1w'),
('t2w', 'inputnode.t2w')]),
(summary, anat_preproc_wf, [('subject_id', 'inputnode.subject_id')]),
(bidssrc, ds_summary_report, [(('t1w', fix_multi_T1w_source_name), 'source_file')]),
(summary, ds_summary_report, [('out_report', 'in_file')]),
(bidssrc, ds_about_report, [(('t1w', fix_multi_T1w_source_name), 'source_file')]),
(about, ds_about_report, [('out_report', 'in_file')]),
])

if anat_only:
return workflow

for bold_file in subject_data['bold']:
if not func_only: # Connect anatomical only if possible
func_preproc_wf = init_func_preproc_wf(
bold_file=bold_file,
layout=layout,
ignore=ignore,
freesurfer=freesurfer,
use_bbr=use_bbr,
bold2t1w_dof=bold2t1w_dof,
reportlets_dir=reportlets_dir,
output_spaces=output_spaces,
template=template,
medial_surface_nan=medial_surface_nan,
output_dir=output_dir,
omp_nthreads=omp_nthreads,
low_mem=low_mem,
fmap_bspline=fmap_bspline,
fmap_demean=fmap_demean,
use_syn=use_syn,
force_syn=force_syn,
debug=debug,
output_grid_ref=output_grid_ref,
use_aroma=use_aroma,
ignore_aroma_err=ignore_aroma_err,
)
workflow.connect([
(anat_preproc_wf, func_preproc_wf,
[('outputnode.t1_preproc', 'inputnode.t1_preproc'),
('outputnode.t1_brain', 'inputnode.t1_brain'),
('outputnode.t1_mask', 'inputnode.t1_mask'),
('outputnode.t1_seg', 'inputnode.t1_seg'),
('outputnode.t1_tpms', 'inputnode.t1_tpms'),
('outputnode.t1_2_mni_forward_transform',
'inputnode.t1_2_mni_forward_transform'),
('outputnode.t1_2_mni_reverse_transform',
'inputnode.t1_2_mni_reverse_transform'),
# Undefined if --no-freesurfer, but this is safe
('outputnode.subjects_dir', 'inputnode.subjects_dir'),
('outputnode.subject_id', 'inputnode.subject_id'),
('outputnode.t1_2_fsnative_forward_transform',
'inputnode.t1_2_fsnative_forward_transform'),
('outputnode.t1_2_fsnative_reverse_transform',
'inputnode.t1_2_fsnative_reverse_transform')]),
])

else:
func_preproc_wf = init_onlyfunc_preproc_wf(
bold_file=bold_file,
layout=layout,
ignore=ignore,
reportlets_dir=reportlets_dir,
output_dir=output_dir,
omp_nthreads=omp_nthreads,
low_mem=low_mem,
fmap_bspline=fmap_bspline,
fmap_demean=fmap_demean,
debug=debug,
use_aroma=use_aroma,
ignore_aroma_err=ignore_aroma_err,
)

workflow.connect([
(inputnode, func_preproc_wf, [('subjects_dir', 'inputnode.subjects_dir')]),
(bidssrc, bids_info, [
(('bold', fix_multi_bold_source_name), 'in_file')]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix_multi_bold_source_name is undefined

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed there was an analog to fic_multi_t1w_source_name, which pops up in the anat_only workflow.

(inputnode, summary, [('subjects_dir', 'subjects_dir')]),
(bidssrc, summary, [('bold', 'bold')]),
(bids_info, summary, [('subject_id', 'subject_id')]),
(bidssrc, func_preproc_wf, [('bold', 'inputnode.bold')]),
(summary, func_preproc_wf, [('subject_id', 'inputnode.subject_id')]),
(bidssrc, ds_summary_report, [
(('bold', fix_multi_bold_source_name), 'source_file')]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix_multi_bold_source_name is undefined

(summary, ds_summary_report, [('out_report', 'in_file')]),
(bidssrc, ds_about_report, [
(('bold', fix_multi_bold_source_name), 'source_file')]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix_multi_bold_source_name is undefined

(about, ds_about_report, [('out_report', 'in_file')]),
])

return workflow
2 changes: 1 addition & 1 deletion fmriprep/workflows/bold/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

"""

from .base import init_func_preproc_wf
from .base import init_func_preproc_wf, init_onlyfunc_preproc_wf
from .util import init_bold_reference_wf
from .hmc import init_bold_hmc_wf
from .stc import init_bold_stc_wf
Expand Down