diff --git a/.circleci/config.yml b/.circleci/config.yml index 59ec4dfb..209d4ed6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -348,6 +348,56 @@ jobs: - store_artifacts: path: /src/qsiprep/.circleci/out/drbuddi_rpe/ + maternal_brain_project: + resource_class: xlarge + environment: + CIRCLE_CPUS: 4 + <<: *dockersetup + steps: + - checkout + - run: *runinstall + - run: + name: Run QSIPrep on multi-shell dataset with GRE field maps + no_output_timeout: 3h + command: | + pytest -rP -o log_cli=true -m "maternal_brain_project" --cov-config=/src/qsiprep/pyproject.toml --cov-append --cov-report term-missing --cov=qsiprep --data_dir=/src/qsiprep/.circleci/data --output_dir=/src/qsiprep/.circleci/out --working_dir=/src/qsiprep/.circleci/work qsiprep + mkdir /src/coverage + mv /src/qsiprep/.coverage /src/coverage/.coverage.maternal_brain_project + # remove nifti files before uploading artifacts + find /src/qsiprep/.circleci/out/ -name "*.nii.gz" -type f -delete + find /src/qsiprep/.circleci/out/ -name "*.fib.gz" -type f -delete + - persist_to_workspace: + root: /src/coverage/ + paths: + - .coverage.maternal_brain_project + - store_artifacts: + path: /src/qsiprep/.circleci/out/maternal_brain_project/ + + forrest_gump: + resource_class: xlarge + environment: + CIRCLE_CPUS: 4 + <<: *dockersetup + steps: + - checkout + - run: *runinstall + - run: + name: Run QSIPrep on single-shell dataset with GRE field maps + no_output_timeout: 3h + command: | + pytest -rP -o log_cli=true -m "forrest_gump" --cov-config=/src/qsiprep/pyproject.toml --cov-append --cov-report term-missing --cov=qsiprep --data_dir=/src/qsiprep/.circleci/data --output_dir=/src/qsiprep/.circleci/out --working_dir=/src/qsiprep/.circleci/work qsiprep + mkdir /src/coverage + mv /src/qsiprep/.coverage /src/coverage/.coverage.forrest_gump + # remove nifti files before uploading artifacts + find /src/qsiprep/.circleci/out/ -name "*.nii.gz" -type f -delete + find /src/qsiprep/.circleci/out/ -name "*.fib.gz" -type f -delete + - persist_to_workspace: + root: /src/coverage/ + paths: + - .coverage.forrest_gump + - store_artifacts: + path: /src/qsiprep/.circleci/out/forrest_gump/ + IntramodalTemplate: resource_class: large <<: *dockersetup @@ -855,6 +905,26 @@ workflows: tags: only: /.*/ + - maternal_brain_project: + requires: + - build + filters: + branches: + ignore: + - /recon\/.*/ + tags: + only: /.*/ + + - forrest_gump: + requires: + - build + filters: + branches: + ignore: + - /recon\/.*/ + tags: + only: /.*/ + - IntramodalTemplate: requires: - build @@ -961,6 +1031,8 @@ workflows: - DRBUDDI_SHORELine_EPI - DRBUDDI_eddy_rpe_series - DRBUDDI_TENSORLine_EPI + - maternal_brain_project + - forrest_gump - IntramodalTemplate - MultiT1w - Recon_3Tissue_Singleshell_ACT @@ -991,6 +1063,8 @@ workflows: - DRBUDDI_SHORELine_EPI - DRBUDDI_eddy_rpe_series - DRBUDDI_TENSORLine_EPI + - maternal_brain_project + - forrest_gump - IntramodalTemplate - MultiT1w - Recon_3Tissue_Singleshell_ACT diff --git a/pyproject.toml b/pyproject.toml index ca22085f..95891de1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -191,6 +191,8 @@ markers = [ "mrtrix3_recon: test 20", "tortoise_recon: test 21", "multi_t1w: test 22", + "maternal_brain_project: multi-shell with GRE field map", + "forrest_gump: single-shell with GRE field map", ] env = [ "RUNNING_PYTEST = 1", diff --git a/qsiprep/interfaces/niworkflows.py b/qsiprep/interfaces/niworkflows.py index 19f74e7c..ecebb412 100644 --- a/qsiprep/interfaces/niworkflows.py +++ b/qsiprep/interfaces/niworkflows.py @@ -8,8 +8,11 @@ """ + from __future__ import absolute_import, division, print_function, unicode_literals +from mimetypes import guess_type + import matplotlib.pyplot as plt import nibabel as nb import numpy as np @@ -18,8 +21,15 @@ from nipype import logging from nipype.interfaces import ants from nipype.interfaces.ants import Registration -from nipype.interfaces.base import traits +from nipype.interfaces.base import ( + BaseInterfaceInputSpec, + File, + SimpleInterface, + TraitedSpec, + traits, +) from nipype.interfaces.mixins import reporting +from nipype.utils.filemanip import fname_presuffix from niworkflows.interfaces.norm import ( SpatialNormalization, _SpatialNormalizationInputSpec, @@ -112,7 +122,9 @@ def plot(self, figure=None): grid_id += 1 plot_sliceqc( - self.qc_data["slice_scores"].T, self.qc_data["slice_counts"], subplot=grid[-1] + self.qc_data["slice_scores"].T, + self.qc_data["slice_counts"], + subplot=grid[-1], ) return figure @@ -231,7 +243,6 @@ def confoundplot( cutoff=None, ylims=None, ): - # Define TR and number of frames notr = False if tr is None: @@ -439,3 +450,182 @@ def _post_run_hook(self, runtime): ) return super(RobustMNINormalizationRPT, self)._post_run_hook(runtime) + + +class FUGUEvsm2ANTSwarpInputSpec(BaseInterfaceInputSpec): + in_file = File(exists=True, mandatory=True, desc="input displacements field map") + pe_dir = traits.Enum("i", "i-", "j", "j-", "k", "k-", desc="phase-encoding axis") + + +class FUGUEvsm2ANTSwarpOutputSpec(TraitedSpec): + out_file = File(desc="the output warp field") + + +class FUGUEvsm2ANTSwarp(SimpleInterface): + """Convert a voxel-shift-map to ants warp.""" + + input_spec = FUGUEvsm2ANTSwarpInputSpec + output_spec = FUGUEvsm2ANTSwarpOutputSpec + + def _run_interface(self, runtime): + nii = nb.load(self.inputs.in_file) + + phaseEncDim = {"i": 0, "j": 1, "k": 2}[self.inputs.pe_dir[0]] + + if len(self.inputs.pe_dir) == 2: + phaseEncSign = 1.0 + else: + phaseEncSign = -1.0 + + # Fix header + hdr = nii.header.copy() + hdr.set_data_dtype(np.dtype("