From ec5e651e61b96306f6b575075c8af760b3a5e541 Mon Sep 17 00:00:00 2001 From: oesteban Date: Mon, 15 Oct 2018 11:43:42 -0700 Subject: [PATCH 01/15] [ENH] Template-based masking of EPI boldrefs --- docker/scripts/get_templates.sh | 8 +++- fmriprep/__about__.py | 2 + fmriprep/workflows/bold/util.py | 71 +++++++++++++++++++++++++++------ requirements.txt | 2 +- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/docker/scripts/get_templates.sh b/docker/scripts/get_templates.sh index bd3663668..d060531bb 100755 --- a/docker/scripts/get_templates.sh +++ b/docker/scripts/get_templates.sh @@ -6,10 +6,12 @@ ASYM_09C_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/ ASYM_09C_SHA256="a24699ba0d13f72d0f8934cc211cb80bfd9c9a077b481d9b64295cf5275235a9" OASIS_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/584123a29ad5a1020913609d" OASIS_SHA256="d87300e91346c16f55baf6f54f5f990bc020b61e8d5df9bcc3abb0cc4b943113" -NKI_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/59cd90f46c613b02b3d79782" -NKI_SHA256="4bba067f6675d15be96b205cb227e18a540673fd7e4577e13feedcef3a6f0ec5" +NKI_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/5bc3fad82aa873001bc5a553" +NKI_SHA256="9c08713d067bcf13baa61b01a9495e526b55d1f148d951da01e082679f076fa9" OASIS_DKT31_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/5b16f17aeca4a80012bd7542" OASIS_DKT31_SHA256="623fa7141712b1a7263331dba16eb069a4443e9640f52556c89d461611478145" +EPI_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/5bc12155ac011000176bff82" +EPI_SHA256="fcd6980ef98c9d7622c6dc2a7747ff51ba3909d98e2a740df9a8265d50920d1b" GET(){ URL=$1; SHA256=$2; @@ -36,4 +38,6 @@ echo "Getting NKI template" GET "$NKI_TEMPLATE" "$NKI_SHA256" echo "Getting OASIS DKT31 template" GET "$OASIS_DKT31_TEMPLATE" "$OASIS_DKT31_SHA256" +echo "Getting fMRIPrep's BOLDref template" +GET "$EPI_TEMPLATE" "$EPI_SHA256" echo "Done!" diff --git a/fmriprep/__about__.py b/fmriprep/__about__.py index 04a87e61a..3a844b20c 100644 --- a/fmriprep/__about__.py +++ b/fmriprep/__about__.py @@ -108,6 +108,8 @@ ] LINKS_REQUIRES = [ + "git+https://github.com/oesteban/niworkflows.git" + "@11ea24fc4f1bb93768a0cef8b4252c498534f76e#egg=niworkflows-0.4.3", ] TESTS_REQUIRES = [ diff --git a/fmriprep/workflows/bold/util.py b/fmriprep/workflows/bold/util.py index de7e3238a..73efcd18b 100644 --- a/fmriprep/workflows/bold/util.py +++ b/fmriprep/workflows/bold/util.py @@ -10,11 +10,20 @@ .. autofunction:: init_skullstrip_bold_wf """ +from packaging.version import parse as parseversion, Version +from pkg_resources import resource_filename as pkgr_fn + from nipype.pipeline import engine as pe from nipype.interfaces import utility as niu, fsl, afni, ants -from niworkflows.interfaces.utils import CopyXForm +from niworkflows.data import get_template +from niworkflows.interfaces.ants import AI +from niworkflows.interfaces.fixes import ( + FixHeaderRegistration as Registration, + FixHeaderApplyTransforms as ApplyTransforms, +) from niworkflows.interfaces.masks import SimpleShowMaskRPT from niworkflows.interfaces.registration import EstimateReferenceImage +from niworkflows.interfaces.utils import CopyXForm from ...engine import Workflow from ...interfaces.nilearn import MaskEPI @@ -105,8 +114,8 @@ def init_bold_reference_wf(omp_nthreads, bold_file=None, name='bold_reference_wf mem_gb=1) # OE: 128x128x128x50 * 64 / 8 ~ 900MB. # Re-run validation; no effect if no sbref; otherwise apply same validation to sbref as bold validate_ref = pe.Node(ValidateImage(), name='validate_ref', mem_gb=DEFAULT_MEMORY_MIN_GB) - enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads, - enhance_t2=enhance_t2) + enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf( + omp_nthreads=omp_nthreads) workflow.connect([ (inputnode, validate, [('bold_file', 'in_file')]), @@ -137,7 +146,7 @@ def init_bold_reference_wf(omp_nthreads, bold_file=None, name='bold_reference_wf def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', - omp_nthreads=1, enhance_t2=False): + omp_nthreads=1): """ This workflow takes in a :abbr:`BOLD (blood-oxygen level-dependant)` :abbr:`fMRI (functional MRI)` average/summary (e.g. a reference image @@ -177,10 +186,6 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', Name of workflow (default: ``enhance_and_skullstrip_bold_wf``) omp_nthreads : int number of threads available to parallel nodes - enhance_t2 : bool - perform logarithmic transform of input BOLD image to improve contrast - before calculating the preliminary mask - **Inputs** @@ -207,9 +212,43 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', outputnode = pe.Node(niu.IdentityInterface(fields=[ 'mask_file', 'skull_stripped_file', 'bias_corrected_file']), name='outputnode') - # Create a loose mask to avoid N4 internal's Otsu mask - n4_mask = pe.Node(MaskEPI(upper_cutoff=0.75, enhance_t2=enhance_t2, opening=1, - no_sanitize=True), name='n4_mask') + bold_template = get_template('fMRIPrep') / 'tpl-fMRIPrep_space-MNI_res-02_boldref.nii.gz' + brain_mask = get_template('MNI152NLin2009cAsym') / \ + 'tpl-MNI152NLin2009cAsym_space-MNI_res-02_brainmask.nii.gz' + + # Initialize transforms with antsAI + init_aff = pe.Node(AI( + fixed_image=str(bold_template), + fixed_image_mask=str(brain_mask), + metric=('Mattes', 32, 'Regular', 0.2), + transform=('Affine', 0.1), + search_factor=(20, 0.12), + principal_axes=False, + convergence=(10, 1e-6, 10), + verbose=True), + name='init_aff', + n_procs=omp_nthreads) + + if parseversion(Registration().version) > Version('2.2.0'): + init_aff.inputs.search_grid = (40, (0, 40, 40)) + + # Set up spatial normalization + norm = pe.Node(Registration( + from_file=pkgr_fn( + 'fmriprep.data', + 'epi_registration_settings.json')), + name='norm', + n_procs=omp_nthreads) + fixed_mask_trait = 'fixed_image_mask' + norm.inputs.fixed_image = str(bold_template) + if parseversion(Registration().version) >= Version('2.2.0'): + fixed_mask_trait += 's' + setattr(norm.inputs, fixed_mask_trait, str(brain_mask)) + + map_brainmask = pe.Node( + ApplyTransforms(interpolation='MultiLabel', float=True, input_image=str(brain_mask)), + name='map_brainmask' + ) # Run N4 normally, force num_threads=1 for stability (images are small, no need for >1) n4_correct = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), @@ -246,11 +285,17 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', apply_mask = pe.Node(fsl.ApplyMask(), name='apply_mask') workflow.connect([ - (inputnode, n4_mask, [('in_file', 'in_files')]), + (inputnode, init_aff, [('in_file', 'moving_image')]), + (inputnode, map_brainmask, [('in_file', 'reference_image')]), + (inputnode, norm, [('in_file', 'moving_image')]), + (init_aff, norm, [('output_transform', 'initial_moving_transform')]), + (norm, map_brainmask, [ + ('reverse_invert_flags', 'invert_transform_flags'), + ('reverse_transforms', 'transforms')]), (inputnode, n4_correct, [('in_file', 'input_image')]), (inputnode, fixhdr_unifize, [('in_file', 'hdr_file')]), (inputnode, fixhdr_skullstrip2, [('in_file', 'hdr_file')]), - (n4_mask, n4_correct, [('out_mask', 'mask_image')]), + (map_brainmask, n4_correct, [('output_image', 'mask_image')]), (n4_correct, skullstrip_first_pass, [('output_image', 'in_file')]), (skullstrip_first_pass, bet_dilate, [('mask_file', 'in_file')]), (bet_dilate, bet_mask, [('out_file', 'mask_file')]), diff --git a/requirements.txt b/requirements.txt index 01add8634..b0f54a5ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -niworkflows>=0.4.3 +git+https://github.com/oesteban/niworkflows.git@11ea24fc4f1bb93768a0cef8b4252c498534f76e#egg=niworkflows-0.4.3 grabbit==0.2.3 pybids==0.6.5 versioneer From 3e344dba74450a189ad2b7ee42810515a1e33fce Mon Sep 17 00:00:00 2001 From: oesteban Date: Mon, 15 Oct 2018 11:52:31 -0700 Subject: [PATCH 02/15] fix flake8 error --- fmriprep/workflows/bold/util.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fmriprep/workflows/bold/util.py b/fmriprep/workflows/bold/util.py index 73efcd18b..3123f0ee3 100644 --- a/fmriprep/workflows/bold/util.py +++ b/fmriprep/workflows/bold/util.py @@ -26,7 +26,6 @@ from niworkflows.interfaces.utils import CopyXForm from ...engine import Workflow -from ...interfaces.nilearn import MaskEPI from ...interfaces import ValidateImage From 2679533f4d538786df34fbf8052fad23e34dd195 Mon Sep 17 00:00:00 2001 From: oesteban Date: Mon, 15 Oct 2018 13:42:19 -0700 Subject: [PATCH 03/15] use poldracklab/niworkflows@0946bad69867840718d4aeeb54ef11bd535c1a9c --- fmriprep/__about__.py | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fmriprep/__about__.py b/fmriprep/__about__.py index 3a844b20c..01c62f93c 100644 --- a/fmriprep/__about__.py +++ b/fmriprep/__about__.py @@ -109,7 +109,7 @@ LINKS_REQUIRES = [ "git+https://github.com/oesteban/niworkflows.git" - "@11ea24fc4f1bb93768a0cef8b4252c498534f76e#egg=niworkflows-0.4.3", + "@0946bad69867840718d4aeeb54ef11bd535c1a9c#egg=niworkflows-0.4.3", ] TESTS_REQUIRES = [ diff --git a/requirements.txt b/requirements.txt index b0f54a5ae..37767211e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -git+https://github.com/oesteban/niworkflows.git@11ea24fc4f1bb93768a0cef8b4252c498534f76e#egg=niworkflows-0.4.3 +git+https://github.com/oesteban/niworkflows.git@0946bad69867840718d4aeeb54ef11bd535c1a9c#egg=niworkflows-0.4.3 grabbit==0.2.3 pybids==0.6.5 versioneer From af0cd52be8b0fbc6e31cf9eb9561ed57dc43d921 Mon Sep 17 00:00:00 2001 From: oesteban Date: Mon, 15 Oct 2018 14:43:06 -0700 Subject: [PATCH 04/15] update pinning to release --- docs/environment.yml | 2 +- fmriprep/__about__.py | 4 +--- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/environment.yml b/docs/environment.yml index 2c92a97dc..850916563 100644 --- a/docs/environment.yml +++ b/docs/environment.yml @@ -29,4 +29,4 @@ dependencies: - svgutils - nitime - nilearn - - niworkflows>=0.4.2 + - niworkflows>=0.4.4 diff --git a/fmriprep/__about__.py b/fmriprep/__about__.py index 01c62f93c..bae53895e 100644 --- a/fmriprep/__about__.py +++ b/fmriprep/__about__.py @@ -98,7 +98,7 @@ 'pybids==0.6.5', 'nitime', 'nipype>=1.1.3', - 'niworkflows>=0.4.3', + 'niworkflows>=0.4.4', 'statsmodels', 'seaborn', 'indexed_gzip>=0.8.2', @@ -108,8 +108,6 @@ ] LINKS_REQUIRES = [ - "git+https://github.com/oesteban/niworkflows.git" - "@0946bad69867840718d4aeeb54ef11bd535c1a9c#egg=niworkflows-0.4.3", ] TESTS_REQUIRES = [ diff --git a/requirements.txt b/requirements.txt index 37767211e..c184845fd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -git+https://github.com/oesteban/niworkflows.git@0946bad69867840718d4aeeb54ef11bd535c1a9c#egg=niworkflows-0.4.3 +niworkflows>=0.4.4 grabbit==0.2.3 pybids==0.6.5 versioneer From c088fb6fc9b33b45da72e333b977755a50f616da Mon Sep 17 00:00:00 2001 From: oesteban Date: Mon, 15 Oct 2018 23:09:29 -0700 Subject: [PATCH 05/15] new registration parameters --- fmriprep/data/epi_atlasbased_brainmask.json | 20 ++++++++++++++++++++ fmriprep/workflows/bold/tests/test_util.py | 2 +- fmriprep/workflows/bold/util.py | 10 +++++----- 3 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 fmriprep/data/epi_atlasbased_brainmask.json diff --git a/fmriprep/data/epi_atlasbased_brainmask.json b/fmriprep/data/epi_atlasbased_brainmask.json new file mode 100644 index 000000000..94066b4bd --- /dev/null +++ b/fmriprep/data/epi_atlasbased_brainmask.json @@ -0,0 +1,20 @@ +{ + "winsorize_upper_quantile": 0.98, + "winsorize_lower_quantile": 0.05, + "float": true, + "metric": ["Mattes", "Mattes"], + "metric_weight": [1, 1], + "radius_or_number_of_bins": [64, 64], + "transforms": ["Affine", "Affine"], + "transform_parameters": [[0.1], [0.1]], + "number_of_iterations": [[200], [15]], + "convergence_window_size": [10, 3], + "convergence_threshold": [1e-08, 1e-09], + "sampling_strategy": ["Random", "Random"], + "smoothing_sigmas": [[4], [2]], + "sigma_units": ["mm", "mm", "mm"], + "shrink_factors": [[2], [1]], + "sampling_percentage": [0.2, 0.2], + "use_histogram_matching": [true, true], + "use_estimate_learning_rate_once": [true, true] +} diff --git a/fmriprep/workflows/bold/tests/test_util.py b/fmriprep/workflows/bold/tests/test_util.py index a5e808b3a..dde36f382 100755 --- a/fmriprep/workflows/bold/tests/test_util.py +++ b/fmriprep/workflows/bold/tests/test_util.py @@ -49,7 +49,7 @@ def symmetric_overlap(img1, img2): ) ]) def test_masking(input_fname, expected_fname): - bold_reference_wf = init_bold_reference_wf(omp_nthreads=1, enhance_t2=True) + bold_reference_wf = init_bold_reference_wf(omp_nthreads=1) bold_reference_wf.inputs.inputnode.bold_file = input_fname # Reconstruct base_fname from above diff --git a/fmriprep/workflows/bold/util.py b/fmriprep/workflows/bold/util.py index 3123f0ee3..6a346ca80 100644 --- a/fmriprep/workflows/bold/util.py +++ b/fmriprep/workflows/bold/util.py @@ -235,14 +235,14 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', norm = pe.Node(Registration( from_file=pkgr_fn( 'fmriprep.data', - 'epi_registration_settings.json')), + 'epi_atlasbased_brainmask.json')), name='norm', n_procs=omp_nthreads) - fixed_mask_trait = 'fixed_image_mask' norm.inputs.fixed_image = str(bold_template) - if parseversion(Registration().version) >= Version('2.2.0'): - fixed_mask_trait += 's' - setattr(norm.inputs, fixed_mask_trait, str(brain_mask)) + # fixed_mask_trait = 'fixed_image_mask' + # if parseversion(Registration().version) >= Version('2.2.0'): + # fixed_mask_trait += 's' + # setattr(norm.inputs, fixed_mask_trait, str(brain_mask)) map_brainmask = pe.Node( ApplyTransforms(interpolation='MultiLabel', float=True, input_image=str(brain_mask)), From 966d4122652dd1c2c6ec93c13ecf05db80d4eead Mon Sep 17 00:00:00 2001 From: oesteban Date: Mon, 15 Oct 2018 23:10:05 -0700 Subject: [PATCH 06/15] pre-cache template getter --- docker/scripts/get_templates.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docker/scripts/get_templates.sh b/docker/scripts/get_templates.sh index d060531bb..c55f60e9d 100755 --- a/docker/scripts/get_templates.sh +++ b/docker/scripts/get_templates.sh @@ -2,8 +2,10 @@ MNI_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/580705eb594d9001ed622649" MNI_SHA256="608b1d609255424d51300e189feacd5ec74b04e244628303e802a6c0b0f9d9db" -ASYM_09C_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/580705089ad5a101f17944a9" -ASYM_09C_SHA256="a24699ba0d13f72d0f8934cc211cb80bfd9c9a077b481d9b64295cf5275235a9" +ASYM_09C_TEMPLATE_OLD="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/580705089ad5a101f17944a9" +ASYM_09C_SHA256_OLD="a24699ba0d13f72d0f8934cc211cb80bfd9c9a077b481d9b64295cf5275235a9" +ASYM_09C_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/5b0dbce20f461a000db8fa3d" +ASYM_09C_SHA256="2851302474359c2c48995155aadb48b861e5dcf87aefda71af8010f671e8ed66" OASIS_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/584123a29ad5a1020913609d" OASIS_SHA256="d87300e91346c16f55baf6f54f5f990bc020b61e8d5df9bcc3abb0cc4b943113" NKI_TEMPLATE="https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/5bc3fad82aa873001bc5a553" @@ -30,6 +32,8 @@ GET(){ set -e echo "Getting MNI template" GET "$MNI_TEMPLATE" "$MNI_SHA256" +echo "Getting (deprecated version of) MNI152NLin2009cAsym template" +GET "$ASYM_09C_TEMPLATE_OLD" "$ASYM_09C_SHA256_OLD" echo "Getting MNI152NLin2009cAsym template" GET "$ASYM_09C_TEMPLATE" "$ASYM_09C_SHA256" echo "Getting OASIS template" From be17090f88bf5a68d535116c53d68cb5fb3a9526 Mon Sep 17 00:00:00 2001 From: oesteban Date: Tue, 16 Oct 2018 07:26:22 -0700 Subject: [PATCH 07/15] fix ds005 problem --- fmriprep/interfaces/cifti.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fmriprep/interfaces/cifti.py b/fmriprep/interfaces/cifti.py index 793a00286..e9726ecc6 100644 --- a/fmriprep/interfaces/cifti.py +++ b/fmriprep/interfaces/cifti.py @@ -130,10 +130,9 @@ def _fetch_data(self): raise IOError("Freesurfer annotations for %s not found in %s" % ( self.inputs.surface_target, self.inputs.subjects_dir)) - label_space = 'tpl-OASISTRT20' - label_dir = getters.get_oasis_dkt31_mni152() - label_file = os.path.join(label_dir, - 'tpl-OASISTRT20_variant-DKT31_space-MNI152NLin2009cAsym.nii.gz') + label_space = 'OASISTRT20' + label_file = str(getters.get_template(label_space) / + 'tpl-OASISTRT20_variant-DKT31_space-MNI152NLin2009cAsym.nii.gz') download_link = getters.OSF_PROJECT_URL + getters.OSF_RESOURCES[label_space][0] return annotation_files, label_file, download_link From 76d0f701c0d719d0be3052889e0369d18eb8fe7d Mon Sep 17 00:00:00 2001 From: oesteban Date: Tue, 16 Oct 2018 12:46:42 -0700 Subject: [PATCH 08/15] 1) shorten registration; 2) accept a ``pre_mask`` --- fmriprep/data/epi_atlasbased_brainmask.json | 26 ++-- fmriprep/workflows/bold/base.py | 5 +- fmriprep/workflows/bold/registration.py | 3 +- fmriprep/workflows/bold/resampling.py | 4 +- fmriprep/workflows/bold/util.py | 138 ++++++++++++-------- fmriprep/workflows/fieldmap/unwarp.py | 5 +- scripts/generate_reference_mask.py | 5 +- 7 files changed, 110 insertions(+), 76 deletions(-) diff --git a/fmriprep/data/epi_atlasbased_brainmask.json b/fmriprep/data/epi_atlasbased_brainmask.json index 94066b4bd..31fed8bd2 100644 --- a/fmriprep/data/epi_atlasbased_brainmask.json +++ b/fmriprep/data/epi_atlasbased_brainmask.json @@ -2,19 +2,19 @@ "winsorize_upper_quantile": 0.98, "winsorize_lower_quantile": 0.05, "float": true, - "metric": ["Mattes", "Mattes"], - "metric_weight": [1, 1], - "radius_or_number_of_bins": [64, 64], - "transforms": ["Affine", "Affine"], - "transform_parameters": [[0.1], [0.1]], - "number_of_iterations": [[200], [15]], - "convergence_window_size": [10, 3], - "convergence_threshold": [1e-08, 1e-09], + "metric": ["Mattes"], + "metric_weight": [1], + "radius_or_number_of_bins": [64], + "transforms": ["Affine"], + "transform_parameters": [[0.1]], + "number_of_iterations": [[200]], + "convergence_window_size": [10], + "convergence_threshold": [1e-09], "sampling_strategy": ["Random", "Random"], - "smoothing_sigmas": [[4], [2]], + "smoothing_sigmas": [[2]], "sigma_units": ["mm", "mm", "mm"], - "shrink_factors": [[2], [1]], - "sampling_percentage": [0.2, 0.2], - "use_histogram_matching": [true, true], - "use_estimate_learning_rate_once": [true, true] + "shrink_factors": [[2]], + "sampling_percentage": [0.2], + "use_histogram_matching": [true], + "use_estimate_learning_rate_once": [true] } diff --git a/fmriprep/workflows/bold/base.py b/fmriprep/workflows/bold/base.py index b73ff02bd..ae07741f7 100755 --- a/fmriprep/workflows/bold/base.py +++ b/fmriprep/workflows/bold/base.py @@ -394,9 +394,8 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer, ]), ]) - # The first reference uses T2 contrast enhancement - bold_reference_wf = init_bold_reference_wf( - omp_nthreads=omp_nthreads, enhance_t2=True) + # Generate a tentative boldref + bold_reference_wf = init_bold_reference_wf(omp_nthreads=omp_nthreads) # Top-level BOLD splitter bold_split = pe.Node(FSLSplit(dimension='t'), name='bold_split', diff --git a/fmriprep/workflows/bold/registration.py b/fmriprep/workflows/bold/registration.py index fbd2dd6b6..60150a135 100644 --- a/fmriprep/workflows/bold/registration.py +++ b/fmriprep/workflows/bold/registration.py @@ -324,7 +324,7 @@ def init_bold_t1_trans_wf(freesurfer, mem_gb, omp_nthreads, use_fieldwarp=False, merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb) # Generate a reference on the target T1w space - gen_final_ref = init_bold_reference_wf(omp_nthreads) + gen_final_ref = init_bold_reference_wf(omp_nthreads, pre_mask=True) workflow.connect([ (inputnode, merge_xforms, [('itk_bold_to_t1', 'in1')]), @@ -334,6 +334,7 @@ def init_bold_t1_trans_wf(freesurfer, mem_gb, omp_nthreads, use_fieldwarp=False, (gen_ref, bold_to_t1w_transform, [('out_file', 'reference_image')]), (bold_to_t1w_transform, merge, [('out_files', 'in_files')]), (merge, gen_final_ref, [('out_file', 'inputnode.bold_file')]), + (mask_t1w_tfm, gen_final_ref, [('output_image', 'inputnode.bold_mask')]), (merge, outputnode, [('out_file', 'bold_t1')]), (gen_final_ref, outputnode, [('outputnode.ref_image', 'bold_t1_ref')]), ]) diff --git a/fmriprep/workflows/bold/resampling.py b/fmriprep/workflows/bold/resampling.py index 6c19c342e..cc40acfb0 100644 --- a/fmriprep/workflows/bold/resampling.py +++ b/fmriprep/workflows/bold/resampling.py @@ -301,7 +301,8 @@ def _aslist(in_value): mem_gb=mem_gb * 3) # Generate a reference on the target T1w space - gen_final_ref = init_bold_reference_wf(omp_nthreads) + gen_final_ref = init_bold_reference_wf( + omp_nthreads=omp_nthreads, pre_mask=True) workflow.connect([ (inputnode, merge_xforms, [('t1_2_mni_forward_transform', 'in1'), @@ -311,6 +312,7 @@ def _aslist(in_value): (inputnode, bold_to_mni_transform, [('bold_split', 'input_image')]), (bold_to_mni_transform, merge, [('out_files', 'in_files')]), (merge, gen_final_ref, [('out_file', 'inputnode.bold_file')]), + (mask_mni_tfm, gen_final_ref, [('output_image', 'inputnode.bold_mask')]), (merge, outputnode, [('out_file', 'bold_mni')]), (gen_final_ref, outputnode, [('outputnode.ref_image', 'bold_mni_ref')]), ]) diff --git a/fmriprep/workflows/bold/util.py b/fmriprep/workflows/bold/util.py index 6a346ca80..517fdd29f 100644 --- a/fmriprep/workflows/bold/util.py +++ b/fmriprep/workflows/bold/util.py @@ -32,8 +32,8 @@ DEFAULT_MEMORY_MIN_GB = 0.01 -def init_bold_reference_wf(omp_nthreads, bold_file=None, name='bold_reference_wf', - gen_report=False, enhance_t2=False): +def init_bold_reference_wf(omp_nthreads, bold_file=None, pre_mask=False, + name='bold_reference_wf', gen_report=False): """ This workflow generates reference BOLD images for a series @@ -66,6 +66,9 @@ def init_bold_reference_wf(omp_nthreads, bold_file=None, name='bold_reference_wf bold_file BOLD series NIfTI file + bold_mask : bool + A tentative brain mask to initialize the workflow (requires ``pre_mask`` + parameter set ``True``). **Outputs** @@ -95,7 +98,7 @@ def init_bold_reference_wf(omp_nthreads, bold_file=None, name='bold_reference_wf First, a reference volume and its skull-stripped version were generated using a custom methodology of *fMRIPrep*. """ - inputnode = pe.Node(niu.IdentityInterface(fields=['bold_file', 'sbref_file']), + inputnode = pe.Node(niu.IdentityInterface(fields=['bold_file', 'sbref_file', 'bold_mask']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['bold_file', 'raw_ref_image', 'skip_vols', 'ref_image', @@ -114,7 +117,7 @@ def init_bold_reference_wf(omp_nthreads, bold_file=None, name='bold_reference_wf # Re-run validation; no effect if no sbref; otherwise apply same validation to sbref as bold validate_ref = pe.Node(ValidateImage(), name='validate_ref', mem_gb=DEFAULT_MEMORY_MIN_GB) enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf( - omp_nthreads=omp_nthreads) + omp_nthreads=omp_nthreads, pre_mask=pre_mask) workflow.connect([ (inputnode, validate, [('bold_file', 'in_file')]), @@ -132,6 +135,11 @@ def init_bold_reference_wf(omp_nthreads, bold_file=None, name='bold_reference_wf ('outputnode.skull_stripped_file', 'ref_image_brain')]), ]) + if pre_mask: + workflow.connect([ + (inputnode, enhance_and_skullstrip_bold_wf, [('bold_mask', 'inputnode.pre_mask')]), + ]) + if gen_report: mask_reportlet = pe.Node(SimpleShowMaskRPT(), name='mask_reportlet') workflow.connect([ @@ -144,8 +152,10 @@ def init_bold_reference_wf(omp_nthreads, bold_file=None, name='bold_reference_wf return workflow -def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', - omp_nthreads=1): +def init_enhance_and_skullstrip_bold_wf( + name='enhance_and_skullstrip_bold_wf', + pre_mask=False, + omp_nthreads=1): """ This workflow takes in a :abbr:`BOLD (blood-oxygen level-dependant)` :abbr:`fMRI (functional MRI)` average/summary (e.g. a reference image @@ -156,8 +166,11 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', Steps of this workflow are: - - 1. Calculate a conservative mask using Nilearn's ``create_epi_mask``. + 1. Calculate a tentative mask by registering (9-parameters) to *fMRIPrep*'s + :abbr:`EPI (echo-planar imaging)` -*boldref* template, which + is in MNI space. + The tentative mask is obtained by resampling the MNI template's + brainmask into *boldref*-space. 2. Run ANTs' ``N4BiasFieldCorrection`` on the input :abbr:`BOLD (blood-oxygen level-dependant)` average, using the mask generated in 1) instead of the internal Otsu thresholding. @@ -171,6 +184,9 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', 6. Calculate a final mask as the intersection of 3) and 5). 7. Apply final mask on the enhanced reference. + Step 1 can be skipped if the ``pre_mask`` argument is set to ``True`` and + a tentative mask is passed in to the workflow throught the ``pre_mask`` + Nipype input. .. workflow :: @@ -183,6 +199,9 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', **Parameters** name : str Name of workflow (default: ``enhance_and_skullstrip_bold_wf``) + pre_mask : bool + Indicates whether the ``pre_mask`` input will be set (and thus, step 1 + should be skipped). omp_nthreads : int number of threads available to parallel nodes @@ -190,6 +209,9 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', in_file BOLD image (single volume) + pre_mask : bool + A tentative brain mask to initialize the workflow (requires ``pre_mask`` + parameter set ``True``). **Outputs** @@ -206,48 +228,15 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', .. _N4BiasFieldCorrection: https://hdl.handle.net/10380/3053 """ workflow = Workflow(name=name) - inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), + inputnode = pe.Node(niu.IdentityInterface(fields=['in_file', 'pre_mask']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'mask_file', 'skull_stripped_file', 'bias_corrected_file']), name='outputnode') - bold_template = get_template('fMRIPrep') / 'tpl-fMRIPrep_space-MNI_res-02_boldref.nii.gz' - brain_mask = get_template('MNI152NLin2009cAsym') / \ - 'tpl-MNI152NLin2009cAsym_space-MNI_res-02_brainmask.nii.gz' - - # Initialize transforms with antsAI - init_aff = pe.Node(AI( - fixed_image=str(bold_template), - fixed_image_mask=str(brain_mask), - metric=('Mattes', 32, 'Regular', 0.2), - transform=('Affine', 0.1), - search_factor=(20, 0.12), - principal_axes=False, - convergence=(10, 1e-6, 10), - verbose=True), - name='init_aff', - n_procs=omp_nthreads) - - if parseversion(Registration().version) > Version('2.2.0'): - init_aff.inputs.search_grid = (40, (0, 40, 40)) - - # Set up spatial normalization - norm = pe.Node(Registration( - from_file=pkgr_fn( - 'fmriprep.data', - 'epi_atlasbased_brainmask.json')), - name='norm', - n_procs=omp_nthreads) - norm.inputs.fixed_image = str(bold_template) - # fixed_mask_trait = 'fixed_image_mask' - # if parseversion(Registration().version) >= Version('2.2.0'): - # fixed_mask_trait += 's' - # setattr(norm.inputs, fixed_mask_trait, str(brain_mask)) - - map_brainmask = pe.Node( - ApplyTransforms(interpolation='MultiLabel', float=True, input_image=str(brain_mask)), - name='map_brainmask' - ) + # Dilate pre_mask + pre_dilate = pe.Node(fsl.DilateImage( + operation='max', kernel_shape='sphere', kernel_size=3.0, + internal_datatype='char'), name='pre_mask_dilate') # Run N4 normally, force num_threads=1 for stability (images are small, no need for >1) n4_correct = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), @@ -283,18 +272,59 @@ def init_enhance_and_skullstrip_bold_wf(name='enhance_and_skullstrip_bold_wf', # Compute masked brain apply_mask = pe.Node(fsl.ApplyMask(), name='apply_mask') + if not pre_mask: + bold_template = get_template('fMRIPrep') / 'tpl-fMRIPrep_space-MNI_res-02_boldref.nii.gz' + brain_mask = get_template('MNI152NLin2009cAsym') / \ + 'tpl-MNI152NLin2009cAsym_space-MNI_res-02_brainmask.nii.gz' + + # Initialize transforms with antsAI + init_aff = pe.Node(AI( + fixed_image=str(bold_template), + fixed_image_mask=str(brain_mask), + metric=('Mattes', 32, 'Regular', 0.2), + transform=('Affine', 0.1), + search_factor=(20, 0.12), + principal_axes=False, + convergence=(10, 1e-6, 10), + verbose=True), + name='init_aff', + n_procs=omp_nthreads) + + if parseversion(Registration().version) > Version('2.2.0'): + init_aff.inputs.search_grid = (40, (0, 40, 40)) + + # Set up spatial normalization + norm = pe.Node(Registration( + from_file=pkgr_fn( + 'fmriprep.data', + 'epi_atlasbased_brainmask.json')), + name='norm', + n_procs=omp_nthreads) + norm.inputs.fixed_image = str(bold_template) + map_brainmask = pe.Node( + ApplyTransforms(interpolation='MultiLabel', float=True, input_image=str(brain_mask)), + name='map_brainmask' + ) + workflow.connect([ + (inputnode, init_aff, [('in_file', 'moving_image')]), + (inputnode, map_brainmask, [('in_file', 'reference_image')]), + (inputnode, norm, [('in_file', 'moving_image')]), + (init_aff, norm, [('output_transform', 'initial_moving_transform')]), + (norm, map_brainmask, [ + ('reverse_invert_flags', 'invert_transform_flags'), + ('reverse_transforms', 'transforms')]), + (map_brainmask, pre_dilate, [('output_image', 'in_file')]), + ]) + else: + workflow.connect([ + (inputnode, pre_dilate, [('pre_mask', 'in_file')]), + ]) + workflow.connect([ - (inputnode, init_aff, [('in_file', 'moving_image')]), - (inputnode, map_brainmask, [('in_file', 'reference_image')]), - (inputnode, norm, [('in_file', 'moving_image')]), - (init_aff, norm, [('output_transform', 'initial_moving_transform')]), - (norm, map_brainmask, [ - ('reverse_invert_flags', 'invert_transform_flags'), - ('reverse_transforms', 'transforms')]), + (pre_dilate, n4_correct, [('out_file', 'mask_image')]), (inputnode, n4_correct, [('in_file', 'input_image')]), (inputnode, fixhdr_unifize, [('in_file', 'hdr_file')]), (inputnode, fixhdr_skullstrip2, [('in_file', 'hdr_file')]), - (map_brainmask, n4_correct, [('output_image', 'mask_image')]), (n4_correct, skullstrip_first_pass, [('output_image', 'in_file')]), (skullstrip_first_pass, bet_dilate, [('mask_file', 'in_file')]), (bet_dilate, bet_mask, [('out_file', 'mask_file')]), diff --git a/fmriprep/workflows/fieldmap/unwarp.py b/fmriprep/workflows/fieldmap/unwarp.py index 188572eda..cd0e5fcfb 100644 --- a/fmriprep/workflows/fieldmap/unwarp.py +++ b/fmriprep/workflows/fieldmap/unwarp.py @@ -152,7 +152,8 @@ def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): apply_fov_mask = pe.Node(fsl.ApplyMask(), name="apply_fov_mask") - enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads) + enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads, + pre_mask=True) workflow.connect([ (inputnode, fmap2ref_reg, [('fmap_ref', 'moving_image')]), @@ -187,6 +188,8 @@ def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): (fmap_fov2ref_apply, apply_fov_mask, [('output_image', 'mask_file')]), (unwarp_reference, apply_fov_mask, [('output_image', 'in_file')]), (apply_fov_mask, enhance_and_skullstrip_bold_wf, [('out_file', 'inputnode.in_file')]), + (fmap_mask2ref_apply, enhance_and_skullstrip_bold_wf, + [('output_image', 'inputnode.pre_mask')]), (apply_fov_mask, outputnode, [('out_file', 'out_reference')]), (enhance_and_skullstrip_bold_wf, outputnode, [ ('outputnode.mask_file', 'out_mask'), diff --git a/scripts/generate_reference_mask.py b/scripts/generate_reference_mask.py index 4084e9695..9f9745aa0 100755 --- a/scripts/generate_reference_mask.py +++ b/scripts/generate_reference_mask.py @@ -15,8 +15,7 @@ def sink_mask_file(in_file, orig_file, out_dir): def init_main_wf(bold_file, out_dir, base_dir=None, name='main_wf'): - wf = init_bold_reference_wf(enhance_t2=True, - omp_nthreads=4, + wf = init_bold_reference_wf(omp_nthreads=4, name=name) wf.base_dir = base_dir wf.inputs.inputnode.bold_file = bold_file @@ -27,7 +26,7 @@ def init_main_wf(bold_file, out_dir, base_dir=None, name='main_wf'): sink.inputs.orig_file = bold_file wf.connect([ (wf.get_node('outputnode'), sink, [('bold_mask', 'in_file')]), - ]) + ]) return wf From 713132f0577943e5439f99371556149afd4c9c8a Mon Sep 17 00:00:00 2001 From: oesteban Date: Tue, 16 Oct 2018 12:49:55 -0700 Subject: [PATCH 09/15] [skip ci] update documentation --- fmriprep/workflows/bold/util.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fmriprep/workflows/bold/util.py b/fmriprep/workflows/bold/util.py index 517fdd29f..219c78684 100644 --- a/fmriprep/workflows/bold/util.py +++ b/fmriprep/workflows/bold/util.py @@ -171,18 +171,19 @@ def init_enhance_and_skullstrip_bold_wf( is in MNI space. The tentative mask is obtained by resampling the MNI template's brainmask into *boldref*-space. - 2. Run ANTs' ``N4BiasFieldCorrection`` on the input + 2. Binary dilation of the tentative mask with a sphere of 3mm diameter. + 3. Run ANTs' ``N4BiasFieldCorrection`` on the input :abbr:`BOLD (blood-oxygen level-dependant)` average, using the mask generated in 1) instead of the internal Otsu thresholding. - 3. Calculate a loose mask using FSL's ``bet``, with one mathematical morphology + 4. Calculate a loose mask using FSL's ``bet``, with one mathematical morphology dilation of one iteration and a sphere of 6mm as structuring element. - 4. Mask the :abbr:`INU (intensity non-uniformity)`-corrected image + 5. Mask the :abbr:`INU (intensity non-uniformity)`-corrected image with the latest mask calculated in 3), then use AFNI's ``3dUnifize`` to *standardize* the T2* contrast distribution. - 5. Calculate a mask using AFNI's ``3dAutomask`` after the contrast + 6. Calculate a mask using AFNI's ``3dAutomask`` after the contrast enhancement of 4). - 6. Calculate a final mask as the intersection of 3) and 5). - 7. Apply final mask on the enhanced reference. + 7. Calculate a final mask as the intersection of 3) and 5). + 8. Apply final mask on the enhanced reference. Step 1 can be skipped if the ``pre_mask`` argument is set to ``True`` and a tentative mask is passed in to the workflow throught the ``pre_mask`` From 698e82142c17e21359648ccc5b5a6266a8fb1166 Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 24 Oct 2018 11:05:56 -0700 Subject: [PATCH 10/15] fix header mismatch problem --- fmriprep/interfaces/__init__.py | 2 +- fmriprep/interfaces/images.py | 45 +++++++++++++++++++++++++++++++++ fmriprep/workflows/bold/util.py | 10 ++++++-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/fmriprep/interfaces/__init__.py b/fmriprep/interfaces/__init__.py index c0f4bc22b..32cdf5d1f 100644 --- a/fmriprep/interfaces/__init__.py +++ b/fmriprep/interfaces/__init__.py @@ -5,7 +5,7 @@ ReadSidecarJSON, DerivativesDataSink, BIDSDataGrabber, BIDSFreeSurferDir, BIDSInfo ) from .images import ( - IntraModalMerge, ValidateImage, TemplateDimensions, Conform + IntraModalMerge, ValidateImage, TemplateDimensions, Conform, MatchHeader ) from .freesurfer import ( StructuralReference, MakeMidthickness, FSInjectBrainExtracted, diff --git a/fmriprep/interfaces/images.py b/fmriprep/interfaces/images.py index d58db6cef..26666d423 100644 --- a/fmriprep/interfaces/images.py +++ b/fmriprep/interfaces/images.py @@ -479,6 +479,51 @@ def _run_interface(self, runtime): return runtime +class MatchHeaderInputSpec(BaseInterfaceInputSpec): + reference = File(exists=True, mandatory=True, + desc='NIfTI file with reference header') + in_file = File(exists=True, mandatory=True, + desc='NIfTI file which header will be checked') + + +class MatchHeaderOutputSpec(TraitedSpec): + out_file = File(exists=True, desc='NIfTI file with fixed header') + + +class MatchHeader(SimpleInterface): + input_spec = MatchHeaderInputSpec + output_spec = MatchHeaderOutputSpec + + def _run_interface(self, runtime): + refhdr = nb.load(self.inputs.reference).header.copy() + imgnii = nb.load(self.inputs.in_file) + imghdr = imgnii.header.copy() + + imghdr['dim_info'] = refhdr['dim_info'] # dim_info is lost sometimes + + # Set qform + qform, qcode = refhdr.get_qform(coded=True) + if not np.allclose(qform, imghdr.get_qform()): + LOGGER.warning( + 'q-forms of reference and mask are substantially different') + imghdr.set_qform(qform, int(qcode)) + + # Set sform + sform, scode = refhdr.get_sform(coded=True) + if not np.allclose(sform, imghdr.get_sform()): + LOGGER.warning( + 's-forms of reference and mask are substantially different') + imghdr.set_sform(sform, int(scode)) + + out_file = fname_presuffix(self.inputs.in_file, suffix='_hdr', + newpath=runtime.cwd) + + imgnii.__class__(imgnii.get_data(), imgnii.affine, imghdr).to_filename( + out_file) + self._results['out_file'] = out_file + return runtime + + def reorient(in_file, newpath=None): """Reorient Nifti files to RAS""" out_file = fname_presuffix(in_file, suffix='_ras', newpath=newpath) diff --git a/fmriprep/workflows/bold/util.py b/fmriprep/workflows/bold/util.py index 219c78684..f3d5554da 100644 --- a/fmriprep/workflows/bold/util.py +++ b/fmriprep/workflows/bold/util.py @@ -26,7 +26,7 @@ from niworkflows.interfaces.utils import CopyXForm from ...engine import Workflow -from ...interfaces import ValidateImage +from ...interfaces import ValidateImage, MatchHeader DEFAULT_MEMORY_MIN_GB = 0.01 @@ -239,6 +239,10 @@ def init_enhance_and_skullstrip_bold_wf( operation='max', kernel_shape='sphere', kernel_size=3.0, internal_datatype='char'), name='pre_mask_dilate') + # Ensure mask's header matches reference's + check_hdr = pe.Node(MatchHeader(), name='check_hdr', + run_without_submitting=True) + # Run N4 normally, force num_threads=1 for stability (images are small, no need for >1) n4_correct = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), name='n4_correct', n_procs=1) @@ -322,7 +326,9 @@ def init_enhance_and_skullstrip_bold_wf( ]) workflow.connect([ - (pre_dilate, n4_correct, [('out_file', 'mask_image')]), + (inputnode, check_hdr, [('in_file', 'reference')]), + (pre_dilate, check_hdr, [('out_file', 'in_file')]), + (check_hdr, n4_correct, [('out_file', 'mask_image')]), (inputnode, n4_correct, [('in_file', 'input_image')]), (inputnode, fixhdr_unifize, [('in_file', 'hdr_file')]), (inputnode, fixhdr_skullstrip2, [('in_file', 'hdr_file')]), From e52394ed933d35e509d44432b0eda62f9e2c3c8b Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 24 Oct 2018 11:22:01 -0700 Subject: [PATCH 11/15] [skip ci] Pin niworkflows==0.4.4 --- docs/environment.yml | 2 +- fmriprep/__about__.py | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/environment.yml b/docs/environment.yml index 850916563..24d91fbba 100644 --- a/docs/environment.yml +++ b/docs/environment.yml @@ -29,4 +29,4 @@ dependencies: - svgutils - nitime - nilearn - - niworkflows>=0.4.4 + - niworkflows==0.4.4 diff --git a/fmriprep/__about__.py b/fmriprep/__about__.py index bae53895e..81590f460 100644 --- a/fmriprep/__about__.py +++ b/fmriprep/__about__.py @@ -98,7 +98,7 @@ 'pybids==0.6.5', 'nitime', 'nipype>=1.1.3', - 'niworkflows>=0.4.4', + 'niworkflows==0.4.4', 'statsmodels', 'seaborn', 'indexed_gzip>=0.8.2', diff --git a/requirements.txt b/requirements.txt index c184845fd..317e6899b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -niworkflows>=0.4.4 +niworkflows==0.4.4 grabbit==0.2.3 pybids==0.6.5 versioneer From 2a09ae8b3f49301b169f283d7e034042d229e81d Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 24 Oct 2018 11:32:23 -0700 Subject: [PATCH 12/15] address @effigies' comments on MatchHeader --- fmriprep/interfaces/images.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fmriprep/interfaces/images.py b/fmriprep/interfaces/images.py index 26666d423..bf667c2f3 100644 --- a/fmriprep/interfaces/images.py +++ b/fmriprep/interfaces/images.py @@ -502,24 +502,26 @@ def _run_interface(self, runtime): imghdr['dim_info'] = refhdr['dim_info'] # dim_info is lost sometimes # Set qform - qform, qcode = refhdr.get_qform(coded=True) + qform = refhdr.get_qform() + qcode = int(refhdr['qform_code']) if not np.allclose(qform, imghdr.get_qform()): LOGGER.warning( 'q-forms of reference and mask are substantially different') - imghdr.set_qform(qform, int(qcode)) + imghdr.set_qform(qform, qcode) # Set sform - sform, scode = refhdr.get_sform(coded=True) + sform = refhdr.get_sform() + scode = int(refhdr['sform_code']) if not np.allclose(sform, imghdr.get_sform()): LOGGER.warning( 's-forms of reference and mask are substantially different') - imghdr.set_sform(sform, int(scode)) + imghdr.set_sform(sform, scode) out_file = fname_presuffix(self.inputs.in_file, suffix='_hdr', newpath=runtime.cwd) - imgnii.__class__(imgnii.get_data(), imgnii.affine, imghdr).to_filename( - out_file) + imgnii.__class__(imgnii.get_data(), imghdr.get_best_affine(), + imghdr).to_filename(out_file) self._results['out_file'] = out_file return runtime From f86b543505b65bddab2d078157b6f1c77edd3760 Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 24 Oct 2018 16:40:37 -0700 Subject: [PATCH 13/15] fix master --- .circleci/config.yml | 24 +++++++++++++++++++++--- .circleci/ds005_outputs.txt | 8 ++++---- .circleci/ds005_partial_outputs.txt | 4 ++-- fmriprep/workflows/bold/base.py | 2 +- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 30749074d..9a7a329bb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -742,14 +742,22 @@ workflows: - get_regression_data: filters: branches: - ignore: /docs?\/.*/ + ignore: + - /docs?\/.*/ + - /ds005\/.*/ + - /ds054\/.*/ + - /ds210\/.*/ - build_docs: requires: - build filters: branches: - ignore: /tests?\/.*/ + ignore: + - /tests?\/.*/ + - /ds005\/.*/ + - /ds054\/.*/ + - /ds210\/.*/ tags: only: /.*/ @@ -770,7 +778,11 @@ workflows: - get_regression_data filters: branches: - ignore: /docs?\/.*/ + ignore: + - /docs?\/.*/ + - /ds005\/.*/ + - /ds054\/.*/ + - /ds210\/.*/ tags: only: /.*/ @@ -783,6 +795,8 @@ workflows: ignore: - /docs?\/.*/ - /tests?\/.*/ + - /ds054\/.*/ + - /ds210\/.*/ tags: only: /.*/ @@ -795,6 +809,8 @@ workflows: ignore: - /docs?\/.*/ - /tests?\/.*/ + - /ds005\/.*/ + - /ds210\/.*/ tags: only: /.*/ @@ -808,6 +824,8 @@ workflows: ignore: - /docs?\/.*/ - /tests?\/.*/ + - /ds005\/.*/ + - /ds054\/.*/ tags: only: /.*/ diff --git a/.circleci/ds005_outputs.txt b/.circleci/ds005_outputs.txt index 6e9dc34c4..6e5b1a141 100644 --- a/.circleci/ds005_outputs.txt +++ b/.circleci/ds005_outputs.txt @@ -36,8 +36,8 @@ fmriprep/sub-01/func fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_AROMAnoiseICs.csv fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_desc-confounds_regressors.tsv fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_desc-MELODIC_mixing.tsv -fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_desc-preproc_bold.dtseries.json -fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_desc-preproc_bold.dtseries.nii +fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_bold.dtseries.json +fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_bold.dtseries.nii fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_space-fsaverage5_hemi-L.func.gii fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_space-fsaverage5_hemi-R.func.gii fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_space-MNI152NLin2009cAsym_boldref.nii.gz @@ -52,8 +52,8 @@ fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_space-T1w_label-aseg_ds fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_AROMAnoiseICs.csv fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_desc-confounds_regressors.tsv fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_desc-MELODIC_mixing.tsv -fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_desc-preproc_bold.dtseries.json -fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_desc-preproc_bold.dtseries.nii +fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_bold.dtseries.json +fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_bold.dtseries.nii fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_space-fsaverage5_hemi-L.func.gii fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_space-fsaverage5_hemi-R.func.gii fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-02_space-MNI152NLin2009cAsym_boldref.nii.gz diff --git a/.circleci/ds005_partial_outputs.txt b/.circleci/ds005_partial_outputs.txt index 56cae3e9f..6afee6b3b 100644 --- a/.circleci/ds005_partial_outputs.txt +++ b/.circleci/ds005_partial_outputs.txt @@ -36,8 +36,8 @@ fmriprep/sub-01/func fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_AROMAnoiseICs.csv fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_desc-confounds_regressors.tsv fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_desc-MELODIC_mixing.tsv -fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_desc-preproc_bold.dtseries.json -fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_desc-preproc_bold.dtseries.nii +fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_bold.dtseries.json +fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_bold.dtseries.nii fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_space-fsaverage5_hemi-L.func.gii fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_space-fsaverage5_hemi-R.func.gii fmriprep/sub-01/func/sub-01_task-mixedgamblestask_run-01_space-MNI152NLin2009cAsym_boldref.nii.gz diff --git a/fmriprep/workflows/bold/base.py b/fmriprep/workflows/bold/base.py index 60b41b5b8..e05b885de 100755 --- a/fmriprep/workflows/bold/base.py +++ b/fmriprep/workflows/bold/base.py @@ -924,7 +924,7 @@ def init_func_derivatives_wf(output_dir, output_spaces, template, freesurfer, CiftiNameSource(), iterfield=['variant'], name='name_cifti', mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True) cifti_bolds = pe.MapNode( - DerivativesDataSink(base_directory=output_dir, desc='preproc', compress=False), + DerivativesDataSink(base_directory=output_dir, compress=False), iterfield=['in_file', 'suffix'], name='cifti_bolds', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) cifti_key = pe.MapNode(DerivativesDataSink( From ca32147600793524a1be4d7601cdb8a5e79ba3b4 Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 24 Oct 2018 18:08:40 -0700 Subject: [PATCH 14/15] remove one branch --- fmriprep/workflows/bold/util.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fmriprep/workflows/bold/util.py b/fmriprep/workflows/bold/util.py index f3d5554da..abdfefdf7 100644 --- a/fmriprep/workflows/bold/util.py +++ b/fmriprep/workflows/bold/util.py @@ -120,6 +120,7 @@ def init_bold_reference_wf(omp_nthreads, bold_file=None, pre_mask=False, omp_nthreads=omp_nthreads, pre_mask=pre_mask) workflow.connect([ + (inputnode, enhance_and_skullstrip_bold_wf, [('bold_mask', 'inputnode.pre_mask')]), (inputnode, validate, [('bold_file', 'in_file')]), (inputnode, gen_ref, [('sbref_file', 'sbref_file')]), (validate, gen_ref, [('out_file', 'in_file')]), @@ -135,11 +136,6 @@ def init_bold_reference_wf(omp_nthreads, bold_file=None, pre_mask=False, ('outputnode.skull_stripped_file', 'ref_image_brain')]), ]) - if pre_mask: - workflow.connect([ - (inputnode, enhance_and_skullstrip_bold_wf, [('bold_mask', 'inputnode.pre_mask')]), - ]) - if gen_report: mask_reportlet = pe.Node(SimpleShowMaskRPT(), name='mask_reportlet') workflow.connect([ From 7535a08751179a38be6e53b1895ed22f58224fdb Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Wed, 24 Oct 2018 20:06:23 -0700 Subject: [PATCH 15/15] [skip ci] fix minimal error in docs --- fmriprep/workflows/bold/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fmriprep/workflows/bold/util.py b/fmriprep/workflows/bold/util.py index abdfefdf7..652f8f499 100644 --- a/fmriprep/workflows/bold/util.py +++ b/fmriprep/workflows/bold/util.py @@ -178,7 +178,7 @@ def init_enhance_and_skullstrip_bold_wf( to *standardize* the T2* contrast distribution. 6. Calculate a mask using AFNI's ``3dAutomask`` after the contrast enhancement of 4). - 7. Calculate a final mask as the intersection of 3) and 5). + 7. Calculate a final mask as the intersection of 4) and 6). 8. Apply final mask on the enhanced reference. Step 1 can be skipped if the ``pre_mask`` argument is set to ``True`` and