diff --git a/.maint/CONTRIBUTORS.md b/.maint/CONTRIBUTORS.md index da6823b8c..bb3cf9f8f 100644 --- a/.maint/CONTRIBUTORS.md +++ b/.maint/CONTRIBUTORS.md @@ -32,6 +32,7 @@ Before every release, unlisted contributors will be invited again to add their n | Liem | Franz | | 0000-0003-0646-4810 | URPP Dynamics of Healthy Aging, University of Zurich | | Lurie | Daniel J. | | 0000-0001-8012-6399 | Department of Psychology, University of California, Berkeley | | Ma | Feilong | | 0000-0002-6838-3971 | Dartmouth College: Hanover, NH, United States | +| Madison | Thomas | | 0000-0003-3030-6580 | Department of Pediatrics, University of Minnesota, MN, USA | | Mentch | Jeff | | 0000-0002-7762-8678 | Speech & Hearing Bioscience & Technology Program, Harvard University | | Moodie | Craig A. | | 0000-0003-0867-1469 | Department of Psychology, Stanford University | | Naveau | Mikaël | | 0000-0001-6948-9068 | Cyceron, UMS 3408 (CNRS - UCBN), France | diff --git a/.zenodo.json b/.zenodo.json index 4f6a7ed83..d9b5542d9 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -8,12 +8,24 @@ "orcid": "0000-0002-1668-9629", "type": "Researcher" }, + { + "affiliation": "Department of Pediatrics, University of Minnesota, MN, USA", + "name": "Madison, Thomas", + "orcid": "0000-0003-3030-6580", + "type": "Researcher" + }, { "affiliation": "Florey Institute of Neuroscience and Mental Health", "name": "Smith, Robert E.", "orcid": "0000-0003-3636-4642", "type": "Researcher" }, + { + "affiliation": "Neurospin, CEA", + "name": "Papadopoulos, Dimitri", + "orcid": "0000-0002-1242-8990", + "type": "Researcher" + }, { "affiliation": "Centre for Modern Interdisciplinary Technologies, Nicolaus Copernicus University in Toruń", "name": "Finc, Karolina", @@ -26,18 +38,6 @@ "orcid": "0000-0002-6533-2909", "type": "Researcher" }, - { - "affiliation": "Neurospin, CEA", - "name": "Papadopoulos, Dimitri", - "orcid": "0000-0002-1242-8990", - "type": "Researcher" - }, - { - "affiliation": "Dartmouth College: Hanover, NH, United States", - "name": "Halchenko, Yaroslav O.", - "orcid": "0000-0003-3456-2493", - "type": "Researcher" - }, { "affiliation": "Department of Neuroscience, University of Pennsylvania, PA, USA", "name": "Tooley, Ursula A.", @@ -51,9 +51,9 @@ "type": "Researcher" }, { - "affiliation": "University of Texas at Austin", - "name": "de la Vega, Alejandro", - "orcid": "0000-0001-9062-3778", + "affiliation": "Dartmouth College: Hanover, NH, United States", + "name": "Halchenko, Yaroslav O.", + "orcid": "0000-0003-3456-2493", "type": "Researcher" }, { @@ -63,9 +63,9 @@ "type": "Researcher" }, { - "affiliation": "Montreal Neurological Institute, McGill University", - "name": "Urchs, Sebastian", - "orcid": "0000-0001-5504-8579", + "affiliation": "University of Texas at Austin", + "name": "de la Vega, Alejandro", + "orcid": "0000-0001-9062-3778", "type": "Researcher" }, { @@ -75,9 +75,15 @@ "type": "Researcher" }, { - "affiliation": "Charite Universitatsmedizin Berlin, Germany", - "name": "Waller, Lea", - "orcid": "0000-0002-3239-6957", + "affiliation": "Montreal Neurological Institute, McGill University", + "name": "Urchs, Sebastian", + "orcid": "0000-0001-5504-8579", + "type": "Researcher" + }, + { + "affiliation": "Machine Learning Team, National Institute of Mental Health, USA", + "name": "Nielson, Dylan M.", + "orcid": "0000-0003-4613-6643", "type": "Researcher" }, { @@ -86,6 +92,12 @@ "orcid": "0000-0002-2050-0614", "type": "Researcher" }, + { + "affiliation": "Charite Universitatsmedizin Berlin, Germany", + "name": "Waller, Lea", + "orcid": "0000-0002-3239-6957", + "type": "Researcher" + }, { "affiliation": "Speech & Hearing Bioscience & Technology Program, Harvard University", "name": "Mentch, Jeff", @@ -98,12 +110,6 @@ "orcid": "0000-0001-8012-6399", "type": "Researcher" }, - { - "affiliation": "Department of Psychology, Columbia University", - "name": "Jacoby, Nir", - "orcid": "0000-0001-7936-9991", - "type": "Researcher" - }, { "affiliation": "Computational Neuroimaging Lab, BioCruces Health Research Institute", "name": "Erramuzpe, Asier", @@ -194,6 +200,18 @@ "orcid": "0000-0002-1652-9297", "type": "Researcher" }, + { + "affiliation": "Department of Psychology, Columbia University", + "name": "Jacoby, Nir", + "orcid": "0000-0001-7936-9991", + "type": "Researcher" + }, + { + "affiliation": "Department of Psychology, University of Washington", + "name": "Kruper, John", + "orcid": "0000-0003-0081-391X", + "type": "Researcher" + }, { "affiliation": "URPP Dynamics of Healthy Aging, University of Zurich", "name": "Liem, Franz", @@ -301,9 +319,9 @@ "orcid": "0000-0002-1668-9629" }, { - "affiliation": "Montreal Neurological Institute, McGill University", - "name": "DuPre, Elizabeth", - "orcid": "0000-0003-1358-196X" + "affiliation": "Department of Psychology, Florida International University", + "name": "Salo, Taylor", + "orcid": "0000-0001-9813-3167" }, { "affiliation": "Neuroscience Program, University of Iowa", @@ -311,9 +329,9 @@ "orcid": "0000-0002-4892-2659" }, { - "affiliation": "Department of Psychology, Florida International University", - "name": "Salo, Taylor", - "orcid": "0000-0001-9813-3167" + "affiliation": "Montreal Neurological Institute, McGill University", + "name": "DuPre, Elizabeth", + "orcid": "0000-0003-1358-196X" }, { "affiliation": "Department of Psychology, Stanford University", @@ -330,11 +348,6 @@ "name": "Blair, Ross W.", "orcid": "0000-0003-3007-1056" }, - { - "affiliation": "Machine Learning Team, National Institute of Mental Health", - "name": "Nielson, Dylan M.", - "orcid": "0000-0003-4613-6643" - }, { "affiliation": "Department of Psychology, Stanford University", "name": "Poldrack, Russell A.", diff --git a/CHANGES.rst b/CHANGES.rst index 0ae69a4b7..b67a2e7f3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,4 @@ -23.2.0 (To be determined) +23.2.0 (January 10, 2024) ========================= New feature release in the 23.2.x series. @@ -33,7 +33,8 @@ This release resolves a number of issues with fieldmaps inducing distortions during correction. Phase difference and direct fieldmaps are now masked correctly, preventing the overestimation of distortions outside the brain. Additionally, we now implement Jacobian weighting during unwarping, which corrects for compression -and expansion effects on signal intensity. +and expansion effects on signal intensity. To disable Jacobian weighting, use +``--ignore fmap-jacobian``. Finally, a new resampling method has been added, to better account for susceptibility distortion and motion in a single shot resampling to a volumetric @@ -50,6 +51,7 @@ target space. We anticipate extending this to surface targets in the future. * FIX: Connect EPI-to-fieldmap transform (#3099) * FIX: Use Py2-compatible version file template for fmriprep-docker (#3101) * FIX: Update connections to unwarp_wf, convert ITK transforms to text (#3077) +* ENH: Allow --ignore fmap-jacobian to disable Jacobian determinant modulation during fieldmap correction (#3186) * ENH: Exclude non-steady-state volumes from confound correlation plot (#3171) * ENH: Pass FLAIR images to anatomical workflow builder to include in boilerplate (#3146) * ENH: Restore carpetplot and other final adjustments (#3131) diff --git a/fmriprep/cli/parser.py b/fmriprep/cli/parser.py index 69ac637e4..5da912240 100644 --- a/fmriprep/cli/parser.py +++ b/fmriprep/cli/parser.py @@ -285,7 +285,7 @@ def _slice_time_ref(value, parser): action="store", nargs="+", default=[], - choices=["fieldmaps", "slicetiming", "sbref", "t2w", "flair"], + choices=["fieldmaps", "slicetiming", "sbref", "t2w", "flair", "fmap-jacobian"], help="Ignore selected aspects of the input dataset to disable corresponding " "parts of the workflow (a space delimited list)", ) diff --git a/fmriprep/interfaces/resampling.py b/fmriprep/interfaces/resampling.py index f997765ea..6111623cb 100644 --- a/fmriprep/interfaces/resampling.py +++ b/fmriprep/interfaces/resampling.py @@ -49,6 +49,7 @@ class ResampleSeriesInputSpec(TraitedSpec): "k-", desc="the phase-encoding direction corresponding to in_data", ) + jacobian = traits.Bool(mandatory=True, desc="Whether to apply Jacobian correction") num_threads = traits.Int(1, usedefault=True, desc="Number of threads to use for resampling") output_data_type = traits.Str("float32", usedefault=True, desc="Data type of output image") order = traits.Int(3, usedefault=True, desc="Order of interpolation (0=nearest, 3=cubic)") @@ -105,6 +106,7 @@ def _run_interface(self, runtime): transforms=transforms, fieldmap=fieldmap, pe_info=pe_info, + jacobian=self.inputs.jacobian, nthreads=self.inputs.num_threads, output_dtype=self.inputs.output_data_type, order=self.inputs.order, @@ -217,6 +219,7 @@ def resample_vol( data: np.ndarray, coordinates: np.ndarray, pe_info: tuple[int, float], + jacobian: bool, hmc_xfm: np.ndarray | None, fmap_hz: np.ndarray, output: np.dtype | np.ndarray | None = None, @@ -282,8 +285,6 @@ def resample_vol( vsm = fmap_hz * pe_info[1] coordinates[pe_info[0], ...] += vsm - jacobian = 1 + np.gradient(vsm, axis=pe_info[0]) - result = ndi.map_coordinates( data, coordinates, @@ -293,7 +294,10 @@ def resample_vol( cval=cval, prefilter=prefilter, ) - result *= jacobian + + if jacobian: + result *= 1 + np.gradient(vsm, axis=pe_info[0]) + return result @@ -301,6 +305,7 @@ async def resample_series_async( data: np.ndarray, coordinates: np.ndarray, pe_info: list[tuple[int, float]], + jacobian: bool, hmc_xfms: list[np.ndarray] | None, fmap_hz: np.ndarray, output_dtype: np.dtype | None = None, @@ -361,6 +366,7 @@ async def resample_series_async( data, coordinates, pe_info[0], + jacobian, hmc_xfms[0] if hmc_xfms else None, fmap_hz, output_dtype, @@ -384,6 +390,7 @@ async def resample_series_async( data=volume, coordinates=coordinates, pe_info=pe_info[volid], + jacobian=jacobian, hmc_xfm=hmc_xfms[volid] if hmc_xfms else None, fmap_hz=fmap_hz, output=out_array[..., volid], @@ -407,6 +414,7 @@ def resample_series( data: np.ndarray, coordinates: np.ndarray, pe_info: list[tuple[int, float]], + jacobian: bool, hmc_xfms: list[np.ndarray] | None, fmap_hz: np.ndarray, output_dtype: np.dtype | None = None, @@ -467,6 +475,7 @@ def resample_series( data=data, coordinates=coordinates, pe_info=pe_info, + jacobian=jacobian, hmc_xfms=hmc_xfms, fmap_hz=fmap_hz, output_dtype=output_dtype, @@ -485,6 +494,7 @@ def resample_image( transforms: nt.TransformChain, fieldmap: nb.Nifti1Image | None, pe_info: list[tuple[int, float]] | None, + jacobian: bool = True, nthreads: int = 1, output_dtype: np.dtype | str | None = 'f4', order: int = 3, @@ -566,6 +576,7 @@ def resample_image( data=source.get_fdata(dtype='f4'), coordinates=mapped_coordinates.T.reshape((3, *target.shape[:3])), pe_info=pe_info, + jacobian=jacobian, hmc_xfms=hmc_xfms, fmap_hz=fieldmap.get_fdata(dtype='f4'), output_dtype=output_dtype, diff --git a/fmriprep/workflows/bold/apply.py b/fmriprep/workflows/bold/apply.py index 90aecfcbf..c5ff2fd8b 100644 --- a/fmriprep/workflows/bold/apply.py +++ b/fmriprep/workflows/bold/apply.py @@ -16,6 +16,7 @@ def init_bold_volumetric_resample_wf( *, metadata: dict, mem_gb: dict[str, float], + jacobian: bool, fieldmap_id: str | None = None, omp_nthreads: int = 1, name: str = 'bold_volumetric_resample_wf', @@ -123,7 +124,7 @@ def init_bold_volumetric_resample_wf( boldref2target = pe.Node(niu.Merge(2), name='boldref2target', run_without_submitting=True) bold2target = pe.Node(niu.Merge(2), name='bold2target', run_without_submitting=True) resample = pe.Node( - ResampleSeries(), + ResampleSeries(jacobian=jacobian), name="resample", n_procs=omp_nthreads, mem_gb=mem_gb['resampled'], diff --git a/fmriprep/workflows/bold/base.py b/fmriprep/workflows/bold/base.py index 7066cb671..f0ab6a305 100644 --- a/fmriprep/workflows/bold/base.py +++ b/fmriprep/workflows/bold/base.py @@ -327,6 +327,7 @@ def init_bold_wf( fieldmap_id=fieldmap_id if not multiecho else None, omp_nthreads=omp_nthreads, mem_gb=mem_gb, + jacobian='fmap-jacobian' not in config.workflow.ignore, name='bold_anat_wf', ) bold_anat_wf.inputs.inputnode.resolution = "native" diff --git a/fmriprep/workflows/bold/fit.py b/fmriprep/workflows/bold/fit.py index 9c584b7b1..f81285e62 100644 --- a/fmriprep/workflows/bold/fit.py +++ b/fmriprep/workflows/bold/fit.py @@ -838,7 +838,7 @@ def init_bold_native_wf( # Resample to boldref boldref_bold = pe.Node( - ResampleSeries(), + ResampleSeries(jacobian="fmap-jacobian" not in config.workflow.ignore), name="boldref_bold", n_procs=omp_nthreads, mem_gb=mem_gb["resampled"], diff --git a/pyproject.toml b/pyproject.toml index 95ea8368f..75322491e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ dependencies = [ "psutil >= 5.4", "pybids >= 0.15.2", "requests", - "sdcflows >= 2.6.0", + "sdcflows >= 2.8.0", "smriprep >= 0.13.2", "tedana >= 23.0.2", "templateflow >= 23.0.0", diff --git a/requirements.txt b/requirements.txt index 0973720b1..60ea70687 100644 --- a/requirements.txt +++ b/requirements.txt @@ -101,7 +101,6 @@ importlib-resources==6.1.1 # via # nireports # niworkflows - # sdcflows # templateflow indexed-gzip==1.8.7 # via smriprep @@ -165,6 +164,7 @@ matplotlib==3.8.2 migas==0.4.0 # via # fmriprep + # sdcflows more-itertools==10.1.0 # via jaraco-classes msgpack==1.0.7 @@ -302,7 +302,7 @@ psutil==5.9.6 # fmriprep (pyproject.toml) py-cpuinfo==9.0.0 # via codecarbon -pybids==0.16.3 +pybids==0.16.4 # via # fmriprep # fmriprep (pyproject.toml) @@ -382,7 +382,7 @@ scipy==1.11.4 # scikit-learn # sdcflows # tedana -sdcflows==2.6.0 +sdcflows==2.8.0 # via # fmriprep # fmriprep (pyproject.toml) @@ -393,8 +393,7 @@ seaborn==0.13.0 secretstorage==3.3.3 # via keyring sentry-sdk==1.39.0 - # via - # fmriprep + # via fmriprep simplejson==3.19.2 # via nipype six==1.16.0 @@ -434,7 +433,7 @@ tifffile==2023.9.26 toml==0.10.2 # via # fmriprep - # fmriprep (pyproject.toml) + # sdcflows tornado==6.3.3 # via bokeh tqdm==4.66.1