diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index cfc9dc256f..5290534c06 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -65,7 +65,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ["3.10", "3.11"]
+ python-version: ["3.10", "3.11", "3.12"]
install: [repo]
include:
- python-version: "3.11"
diff --git a/.zenodo.json b/.zenodo.json
index eec1b4536c..e58ab57f93 100644
--- a/.zenodo.json
+++ b/.zenodo.json
@@ -52,6 +52,18 @@
"name": "Feczko, Eric",
"orcid": "0000-0003-1337-5517",
"type": "Researcher"
+ },
+ {
+ "affiliation": "Department of Biostatistics, Johns Hopkins Bloomberg School of Public Health, MD, USA",
+ "name": "Sadil, Patrick",
+ "orcid": "0000-0003-4141-1343",
+ "type": "Researcher"
+ },
+ {
+ "affiliation": "Neurospin, CEA, Université Paris-Saclay, CNRS, 91191 Gif-sur-Yvette, France",
+ "name": "Papadopoulos Orfanos, Dimitri",
+ "orcid": "0000-0002-1242-8990",
+ "type": "Researcher"
}
],
"keywords": [
diff --git a/CHANGES.rst b/CHANGES.rst
index ea2c4f7a13..8df7f4fbd4 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,53 @@
+0.13.0 (November 20, 2023)
+==========================
+New feature release in the 0.13.x series.
+
+This release adds support for MSM-Sulc, improving the alignment of subject
+surfaces to the fsLR template. This process is enabled by default, but may
+be disabled with the ``--no-msm`` flag.
+
+The ``--fast-track`` flag has been deprecated in favor of a more flexible
+``--derivatives`` flag. This flag can be used to specify one or more
+directories to search for derivatives. Derivatives found in these
+directories can be used to skip corresponding workflows. For derivatives
+that can be deterministically generated from other derivatives, sMRIPrep
+will regenerate the derivatives to avoid inconsistencies.
+
+This supports the 23.2.x series of fMRIPrep, which introduces a ``--level``
+flag to control the level of processing. This feature is not currently
+available in sMRIPrep, but will be in a future release. To preview this
+functionality, use fMRIPrep's ``--anat-only`` flag to run only structural
+workflows.
+
+* FIX: Add missing fsLR reg sphere to io_spec (#382)
+* FIX: Invert sulcal depth metric before passing to MSM, use HCP atlas files (#383)
+* FIX: Update surfaces with fsnative2t1w_xfm (#384)
+* FIX: Add surface-modify-sphere call to catch potential sphere elongation (#375)
+* ENH: Add T2w/FLAIR usage to boilerplate (#392)
+* ENH: Annotate mris_expand with thread usage (#386)
+* ENH: Add sphere registration to fit workflow, check for precomputed (#370)
+* ENH: Save msm registration sphere as desc-msm_sphere.surf.gii (#365)
+* ENH: Add Multimodal Surface Matching (#358)
+* ENH: Run pytest on CircleCI (#364)
+* ENH: Separate surfaces and morphometrics into standalone outputs (#359)
+* RF: Split template and fsLR resampling and sinking into isolated workflows (#388)
+* RF: Replace most of anat_ribbon_wf with a Python function (#363)
+* RF: Break up surface workflows for easier mix-and-match in fMRIPrep (#360)
+* TEST: Add smoke tests for main anatomical workflows (#390)
+* TEST: Add sloppy MSM configuration for use in debugging/CI (#366)
+* DOC: http:// → https:// (#377)
+* DOC: Fix misspelling found by codespell (#378)
+* MNT: Remove AFNI from smriprep docker container (#387)
+* MNT: Use a set literal, not a list literal (#379)
+* MNT: Update installation environment (#361)
+* MNT: Include 3T18yoSchwartzReactN32 FreeSurfer atlas in image (#357)
+* MNT: Infrastructure updates (#351)
+* MNT: fix flake8 warning (#349)
+* MNT: apply pyupgrade suggestions (#348)
+* MNT: fix typos found by codespell (#346)
+* MNT: Python 3.11 should be supported (#347)
+
+
0.12.2 (August 16, 2023)
========================
Bug-fix release in the 0.12.x series.
diff --git a/Dockerfile b/Dockerfile
index 47d63718f6..08da746b9b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -74,16 +74,14 @@ RUN curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bi
ENV MAMBA_ROOT_PREFIX="/opt/conda"
COPY env.yml /tmp/env.yml
+COPY requirements.txt /tmp/requirements.txt
RUN micromamba create -y -f /tmp/env.yml && \
micromamba clean -y -a
ENV PATH="/opt/conda/envs/smriprep/bin:$PATH"
-RUN /opt/conda/envs/smriprep/bin/npm install -g svgo@^2.8 bids-validator@1.11.0 && \
+RUN /opt/conda/envs/smriprep/bin/npm install -g svgo@^3.0 bids-validator@^1.13 && \
rm -r ~/.npm
-COPY requirements.txt /tmp/requirements.txt
-RUN /opt/conda/envs/smriprep/bin/pip install --no-cache-dir -r /tmp/requirements.txt
-
#
# Main stage
#
diff --git a/README.rst b/README.rst
index 0d7f2f12f4..aff2b2e7fa 100644
--- a/README.rst
+++ b/README.rst
@@ -39,7 +39,7 @@ a combination of tools from well-known software packages, including
`FSL `__,
`ANTs `__,
`FreeSurfer `__,
-and `AFNI `__.
+and `Connectome Workbench `__.
More information and documentation can be found at
https://www.nipreps.org/smriprep/.
diff --git a/env.yml b/env.yml
index 02098ea736..2e99487e37 100644
--- a/env.yml
+++ b/env.yml
@@ -4,28 +4,31 @@ channels:
- conda-forge
# Update this ~yearly; last updated April 2023
dependencies:
- - python >=3.10,<3.11
+ - python=3.10
# Needed for svgo and bids-validator; consider moving to deno
- - nodejs=16
+ - nodejs=18
# Intel Math Kernel Library for numpy
- mkl=2022.1
- mkl-service=2.4
# Base scientific python stack; required by FSL, so pinned here
- - numpy=1.25
+ - numpy=1.26
- scipy=1.11
- - matplotlib=3.7,!=3.7.2
- - pandas=2.0
- - h5py=3.8
+ - matplotlib=3.8
+ - pandas=2.1
+ - h5py=3.10
# Dependencies compiled against numpy, best to stick with conda
- - scikit-image=0.21
+ - scikit-image=0.22
- scikit-learn=1.3
# Utilities
- graphviz=6.0
- pandoc=3.1
# Workflow dependencies: ANTs
- - ants=2.4.4
+ - ants=2.5.0
# Workflow dependencies: FSL (versions pinned in 6.0.6.2)
- - fsl-bet2=2111.0
- - fsl-flirt=2111.0
- - fsl-fast4=2111.0
+ - fsl-bet2=2111.4
+ - fsl-flirt=2111.2
+ - fsl-fast4=2111.3
- fsl-miscmaths=2203.2
+ - pip
+ - pip:
+ - -r requirements.txt
diff --git a/pyproject.toml b/pyproject.toml
index f844c0cdd6..e4c476672d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,14 +12,13 @@ classifiers = [
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering :: Image Recognition",
"License :: OSI Approved :: Apache Software License",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
]
license = {file = "LICENSE"}
requires-python = ">=3.10"
dependencies = [
- "importlib_resources >= 1.3; python_version < '3.9'",
"indexed_gzip >= 0.8.8",
"lockfile",
"looseversion",
diff --git a/requirements.txt b/requirements.txt
index 9f3e9466a1..fe4fbe0067 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,18 +1,18 @@
#
-# This file is autogenerated by pip-compile with Python 3.10
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
-# pip-compile --resolver=backtracking
+# pip-compile --strip-extras
#
astor==0.8.1
# via formulaic
attrs==23.1.0
# via niworkflows
-bids-validator==1.12.0
+bids-validator==1.13.1
# via pybids
-certifi==2023.7.22
+certifi==2023.11.17
# via requests
-charset-normalizer==3.2.0
+charset-normalizer==3.3.2
# via requests
ci-info==0.3.0
# via etelemetry
@@ -20,29 +20,29 @@ click==8.1.7
# via
# nipype
# pybids
-contourpy==1.1.0
+contourpy==1.2.0
# via matplotlib
-cycler==0.11.0
+cycler==0.12.1
# via matplotlib
docopt==0.6.2
# via num2words
-etelemetry==0.3.0
+etelemetry==0.3.1
# via nipype
-filelock==3.12.2
+filelock==3.13.1
# via nipype
-fonttools==4.42.1
+fonttools==4.44.3
# via matplotlib
formulaic==0.5.2
# via pybids
-greenlet==2.0.2
+greenlet==3.0.1
# via sqlalchemy
-h5py==3.9.0
+h5py==3.10.0
# via nitransforms
idna==3.4
# via requests
-imageio==2.31.2
+imageio==2.32.0
# via scikit-image
-indexed-gzip==1.8.3
+indexed-gzip==1.8.7
# via smriprep (pyproject.toml)
interface-meta==1.3.0
# via formulaic
@@ -72,12 +72,12 @@ lxml==4.9.3
# svgutils
markupsafe==2.1.3
# via jinja2
-matplotlib==3.7.1
+matplotlib==3.8.2
# via
# niworkflows
# seaborn
# smriprep (pyproject.toml)
-networkx==3.1
+networkx==3.2.1
# via
# nipype
# prov
@@ -90,7 +90,7 @@ nibabel==5.1.0
# niworkflows
# pybids
# smriprep (pyproject.toml)
-nilearn==0.10.1
+nilearn==0.10.2
# via niworkflows
nipype==1.8.6
# via
@@ -98,11 +98,11 @@ nipype==1.8.6
# smriprep (pyproject.toml)
nitransforms==23.0.1
# via niworkflows
-niworkflows==1.8.1
+niworkflows==1.9.0
# via smriprep (pyproject.toml)
-num2words==0.5.12
+num2words==0.5.13
# via pybids
-numpy==1.25.2
+numpy==1.26.2
# via
# contourpy
# formulaic
@@ -116,15 +116,15 @@ numpy==1.25.2
# niworkflows
# pandas
# pybids
- # pywavelets
# scikit-image
# scikit-learn
# scipy
# seaborn
# smriprep (pyproject.toml)
# tifffile
-packaging==23.1
+packaging==23.2
# via
+ # etelemetry
# matplotlib
# nibabel
# nilearn
@@ -132,14 +132,14 @@ packaging==23.1
# niworkflows
# scikit-image
# smriprep (pyproject.toml)
-pandas==2.0.3
+pandas==2.1.3
# via
# formulaic
# nilearn
# niworkflows
# pybids
# seaborn
-pillow==10.0.0
+pillow==10.0.1
# via
# imageio
# matplotlib
@@ -153,7 +153,7 @@ pybids==0.16.3
# templateflow
pydot==1.4.2
# via nipype
-pyparsing==3.0.9
+pyparsing==3.1.1
# via
# matplotlib
# pydot
@@ -164,10 +164,8 @@ python-dateutil==2.8.2
# nipype
# pandas
# prov
-pytz==2023.3
+pytz==2023.3.post1
# via pandas
-pywavelets==1.4.1
- # via scikit-image
pyyaml==6.0.1
# via
# niworkflows
@@ -181,11 +179,11 @@ requests==2.31.0
# etelemetry
# nilearn
# templateflow
-scikit-image==0.21.0
+scikit-image==0.22.0
# via niworkflows
-scikit-learn==1.3.0
+scikit-learn==1.3.2
# via nilearn
-scipy==1.11.2
+scipy==1.11.4
# via
# formulaic
# nilearn
@@ -195,25 +193,25 @@ scipy==1.11.2
# pybids
# scikit-image
# scikit-learn
-seaborn==0.12.2
+seaborn==0.13.0
# via niworkflows
-simplejson==3.19.1
+simplejson==3.19.2
# via nipype
six==1.16.0
# via
# isodate
# python-dateutil
-sqlalchemy==2.0.20
+sqlalchemy==2.0.23
# via pybids
svgutils==0.3.4
# via niworkflows
-templateflow==23.0.0
+templateflow==23.1.0
# via
# niworkflows
# smriprep (pyproject.toml)
threadpoolctl==3.2.0
# via scikit-learn
-tifffile==2023.8.25
+tifffile==2023.9.26
# via scikit-image
tqdm==4.66.1
# via templateflow
@@ -223,13 +221,13 @@ traits==6.3.2
# niworkflows
transforms3d==0.4.1
# via niworkflows
-typing-extensions==4.7.1
+typing-extensions==4.8.0
# via
# formulaic
# sqlalchemy
tzdata==2023.3
# via pandas
-urllib3==2.0.4
+urllib3==2.1.0
# via requests
-wrapt==1.15.0
+wrapt==1.16.0
# via formulaic
diff --git a/scripts/fetch_templates.py b/scripts/fetch_templates.py
index 5987cced57..3d65ffc143 100755
--- a/scripts/fetch_templates.py
+++ b/scripts/fetch_templates.py
@@ -89,11 +89,28 @@ def fetch_fsaverage():
tf.get(template, density='164k', suffix='sulc', extension='.shape.gii')
+def fetch_fsLR():
+ """
+ Expected templates:
+
+ tpl-fsLR/tpl-fsLR_hemi-L_den-32k_desc-nomedialwall_dparc.label.gii
+ tpl-fsLR/tpl-fsLR_hemi-L_den-32k_desc-vaavg_midthickness.shape.gii
+ tpl-fsLR/tpl-fsLR_hemi-L_den-32k_sphere.surf.gii
+ tpl-fsLR/tpl-fsLR_hemi-R_den-32k_desc-nomedialwall_dparc.label.gii
+ tpl-fsLR/tpl-fsLR_hemi-R_den-32k_desc-vaavg_midthickness.shape.gii
+ tpl-fsLR/tpl-fsLR_hemi-R_den-32k_sphere.surf.gii
+ tpl-fsLR/tpl-fsLR_space-fsaverage_hemi-L_den-32k_sphere.surf.gii
+ tpl-fsLR/tpl-fsLR_space-fsaverage_hemi-R_den-32k_sphere.surf.gii
+ """
+ tf.get("fsLR", density="32k")
+
+
def fetch_all():
fetch_MNI2009()
fetch_MNI6()
fetch_OASIS()
fetch_fsaverage()
+ fetch_fsLR()
if __name__ == "__main__":
diff --git a/smriprep/cli/run.py b/smriprep/cli/run.py
index 10ce182814..9647dba1b8 100644
--- a/smriprep/cli/run.py
+++ b/smriprep/cli/run.py
@@ -445,13 +445,13 @@ def build_workflow(opts, retval):
import warnings
from time import strftime
from subprocess import check_call, CalledProcessError, TimeoutExpired
- from pkg_resources import resource_filename as pkgrf
import json
from bids import BIDSLayout
from nipype import logging, config as ncfg
from niworkflows.utils.bids import collect_participants
from ..__about__ import __version__
+ from ..data import load_resource
from ..workflows.base import init_smriprep_wf
logger = logging.getLogger("nipype.workflow")
@@ -628,12 +628,14 @@ def build_workflow(opts, retval):
boilerplate,
)
+ boilerplate_bib = load_resource("boilerplate.bib")
+
# Generate HTML file resolving citations
cmd = [
"pandoc",
"-s",
"--bibliography",
- pkgrf("smriprep", "data/boilerplate.bib"),
+ str(boilerplate_bib),
"--citeproc",
"--metadata",
'pagetitle="sMRIPrep citation boilerplate"',
@@ -651,7 +653,7 @@ def build_workflow(opts, retval):
"pandoc",
"-s",
"--bibliography",
- pkgrf("smriprep", "data/boilerplate.bib"),
+ str(boilerplate_bib),
"--natbib",
str(log_dir / "CITATION.md"),
"-o",
@@ -662,7 +664,7 @@ def build_workflow(opts, retval):
except (FileNotFoundError, CalledProcessError, TimeoutExpired):
logger.warning("Could not generate CITATION.tex file:\n%s", " ".join(cmd))
else:
- copyfile(pkgrf("smriprep", "data/boilerplate.bib"), str(log_dir / "CITATION.bib"))
+ copyfile(str(boilerplate_bib), str(log_dir / "CITATION.bib"))
return retval
diff --git a/smriprep/utils/bids.py b/smriprep/utils/bids.py
index 5b9174d6dd..8513696c55 100644
--- a/smriprep/utils/bids.py
+++ b/smriprep/utils/bids.py
@@ -23,16 +23,15 @@
"""Utilities to handle BIDS inputs."""
from pathlib import Path
from json import loads
-from pkg_resources import resource_filename as pkgrf
from bids.layout import BIDSLayout
+from ..data import load_resource
+
def collect_derivatives(derivatives_dir, subject_id, std_spaces, spec=None, patterns=None):
"""Gather existing derivatives and compose a cache."""
if spec is None or patterns is None:
- _spec, _patterns = tuple(
- loads(Path(pkgrf("smriprep", "data/io_spec.json")).read_text()).values()
- )
+ _spec, _patterns = tuple(loads(load_resource("io_spec.json").read_text()).values())
if spec is None:
spec = _spec
@@ -92,11 +91,11 @@ def write_derivative_description(bids_dir, deriv_dir):
.. testsetup::
- >>> from pkg_resources import resource_filename
+ >>> from smriprep.data import load_resource
>>> from pathlib import Path
>>> from tempfile import TemporaryDirectory
>>> tmpdir = TemporaryDirectory()
- >>> bids_dir = resource_filename('smriprep', 'data/tests')
+ >>> bids_dir = load_resource('tests')
>>> deriv_desc = Path(tmpdir.name) / 'dataset_description.json'
.. doctest::
diff --git a/smriprep/workflows/anatomical.py b/smriprep/workflows/anatomical.py
index 73428edeed..76a19b9f7a 100644
--- a/smriprep/workflows/anatomical.py
+++ b/smriprep/workflows/anatomical.py
@@ -23,8 +23,6 @@
"""Anatomical reference preprocessing workflows."""
import typing as ty
-from pkg_resources import resource_filename as pkgr
-
from nipype import logging
from nipype.pipeline import engine as pe
from nipype.interfaces import (
@@ -50,6 +48,7 @@
from niworkflows.utils.spaces import SpatialReferences, Reference
from niworkflows.utils.misc import add_suffix
from niworkflows.anat.ants import init_brain_extraction_wf, init_n4_only_wf
+from ..data import load_resource
from ..interfaces import DerivativesDataSink
from ..utils.misc import apply_lut as _apply_bids_lut, fs_isRunning as _fs_isRunning
from .fit.registration import init_register_template_wf
@@ -1454,7 +1453,7 @@ def init_anat_template_wf(
if num_files == 1:
get1st = pe.Node(niu.Select(index=[0]), name="get1st")
- outputnode.inputs.anat_realign_xfm = [pkgr("smriprep", "data/itkIdentityTransform.txt")]
+ outputnode.inputs.anat_realign_xfm = [str(load_resource("itkIdentityTransform.txt"))]
# fmt:off
workflow.connect([