diff --git a/.circleci/config.yml b/.circleci/config.yml index 0c171241..bf707248 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -74,8 +74,7 @@ jobs: - run: name: Initiate versioning command: | - python -m pip install -U build "setuptools >= 45" wheel "setuptools_scm >= 6.2" \ - setuptools_scm_git_archive pip twine docutils + python3 -m pip install -U build hatch hatchling pip twine docutils pip install /tmp/src/eddymotion[all] diff --git a/.github/workflows/contrib.yml b/.github/workflows/contrib.yml index 1f9246da..c0f1a005 100755 --- a/.github/workflows/contrib.yml +++ b/.github/workflows/contrib.yml @@ -11,15 +11,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 + - name: Set up Python 3 + uses: actions/setup-python@v5 with: - python-version: 3.9 - - name: Install flake8 - run: python -m pip install flake8 - - name: Check EddyMotion - run: python -m flake8 src + python-version: 3 + - name: Lint EddyMotion + run: pipx run flake8-pyproject diff --git a/.github/workflows/docs-build-pr.yml b/.github/workflows/docs-build-pr.yml index 48faa369..fc96c4e2 100644 --- a/.github/workflows/docs-build-pr.yml +++ b/.github/workflows/docs-build-pr.yml @@ -7,11 +7,11 @@ on: jobs: build: - if: "(github.repository_owner != 'nipreps') && !contains(github.event.head_commit.message, '[skip ci]')" + if: "!contains(github.event.head_commit.message, '[skip ci]')" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -39,10 +39,11 @@ jobs: - name: Install dependencies run: | - pip install --no-cache-dir "setuptools >= 45.0" "setuptools_scm[toml] >= 6.2" + pip install -U build hatch pip pip install -r docs/requirements.txt + python -m hatch version | tail -n1 - name: Build docs run: | - python -m setuptools_scm # Generate _version.py file + python -m hatch build --hooks-only make -C docs/ SPHINXOPTS="-W" BUILDDIR="$HOME/docs" OUTDIR="${CURBRANCH:-html}" html diff --git a/.github/workflows/docs-build-update.yml b/.github/workflows/docs-build-update.yml index bf73fa7c..a3be07b4 100644 --- a/.github/workflows/docs-build-update.yml +++ b/.github/workflows/docs-build-update.yml @@ -42,12 +42,13 @@ jobs: - name: Install dependencies run: | - pip install --no-cache-dir "setuptools >= 45.0" "setuptools_scm[toml] >= 6.2" + pip install -U build hatch pip pip install -r docs/requirements.txt + python -m hatch version | tail -n1 - name: Build docs run: | - python -m setuptools_scm # Generate _version.py file + python -m hatch build --hooks-only make -C docs/ SPHINXOPTS="-W" BUILDDIR="$HOME/docs" OUTDIR="${CURBRANCH:-html}" html - name: Push created tag to gh-pages diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index b3b6a132..0e583523 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -16,19 +16,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] - pip: ["pip==21.2", "pip~=22.0"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v2 - - name: Fetch all tags (for setuptools_scm to work) - run: | - /usr/bin/git -c protocol.version=2 fetch --tags --prune --unshallow origin + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/cache@v1 + - uses: actions/cache@v4 with: path: $HOME/.cache/pip key: pip-cache-v1 @@ -39,20 +38,18 @@ jobs: run: | python -m venv /tmp/buildenv source /tmp/buildenv/bin/activate - python -m pip install -U build "setuptools >= 45" wheel "setuptools_scm >= 6.2" \ - setuptools_scm_git_archive pip twine docutils + pip install -U build hatch pip twine python -m build -s -w python -m twine check dist/eddymotion-* mv dist /tmp/package - rm -rf eddymotion.egg-info/ # Interpolate version if [[ "$GITHUB_REF" == refs/tags/* ]]; then TAG=${GITHUB_REF##*/} fi - THISVERSION=$( python -m setuptools_scm ) + THISVERSION=$( python -m hatch version | tail -n1 | xargs ) THISVERSION=${TAG:-$THISVERSION} echo "Expected VERSION: \"${THISVERSION}\"" echo "THISVERSION=${THISVERSION}" >> $GITHUB_ENV @@ -61,7 +58,7 @@ jobs: run: | python -m venv /tmp/pip source /tmp/pip/bin/activate - python -m pip install -U "setuptools >= 45" "setuptools_scm >= 6.2" "${{ matrix.pip }}" + pip install -U pip python -m pip install . INSTALLED_VERSION=$(python -c 'import eddymotion as em; print(em.__version__, end="")') echo "VERSION: \"${THISVERSION}\"" @@ -72,7 +69,7 @@ jobs: run: | python -m venv /tmp/install_sdist source /tmp/install_sdist/bin/activate - python -m pip install -U "setuptools >= 45" "${{ matrix.pip }}" + pip install -U pip python -m pip install /tmp/package/eddymotion*.tar.gz INSTALLED_VERSION=$(python -c 'import eddymotion as em; print(em.__version__, end="")') echo "VERSION: \"${THISVERSION}\"" @@ -83,7 +80,7 @@ jobs: run: | python -m venv /tmp/install_wheel source /tmp/install_wheel/bin/activate - python -m pip install -U "setuptools >= 45" "${{ matrix.pip }}" + pip install -U pip python -m pip install /tmp/package/eddymotion*.whl INSTALLED_VERSION=$(python -c 'import eddymotion as em; print(em.__version__, end="")') echo "INSTALLED: \"${INSTALLED_VERSION}\"" diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 86f118e6..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,12 +0,0 @@ -recursive-exclude .circleci/ * -recursive-exclude .docker/ * -recursive-exclude .github/ * -recursive-exclude .git/ * -recursive-exclude .maint/ * -recursive-exclude docs/ * -recursive-exclude test/ * - -exclude .* -exclude Dockerfile - -recursive-include src/eddymotion/config *.* diff --git a/docs/index.rst b/docs/index.rst index 4e6dbd0b..848e3cc2 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,6 +1,8 @@ .. include:: links.rst .. include:: ../README.rst :end-line: 29 +.. include:: ../README.rst + :start-line: 34 .. image:: _static/eddymotion-flowchart.svg diff --git a/docs/requirements.txt b/docs/requirements.txt index e27fcc66..85f17b2c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,7 @@ attrs >= 20.1.0 -furo ~= 2021.10.09 +furo >= 2024.01.29 matplotlib >= 2.2.0 packaging -sphinx ~= 4.2 +sphinx >= 4.5 sphinxcontrib-apidoc +nireports diff --git a/pyproject.toml b/pyproject.toml index 78a60e81..317a96e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,15 +1,183 @@ [build-system] -requires = [ - "setuptools >= 45", - "setuptools_scm[toml] >= 6.2", - "wheel" -] -build-backend = "setuptools.build_meta" - -[tool.setuptools_scm] -write_to = "src/eddymotion/_version.py" -write_to_template = """\ -\"\"\"Version file, automatically generated by setuptools_scm.\"\"\" -__version__ = "{version}" -""" -fallback_version = "0.0" +requires = ["hatchling", "hatch-vcs", "nipreps-versions"] +build-backend = "hatchling.build" + + +[project] +name = "eddymotion" +description = "Pure python eddy-current and head-motion correction for dMRI, an extension of QSIprep's SHOREline algorithm (Cieslak, 2020) to multiple diffusion models." +readme = "README.rst" +authors = [{name = "The NiPreps Developers", email = "nipreps@gmail.com"}] +classifiers = [ + "Development Status :: 3 - Alpha", + "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", +] +license = "Apache-2.0" +requires-python = ">=3.8" +dependencies = [ + "dipy>=1.3.0", + "joblib", + "nipype>= 1.5.1, < 2.0", + "nitransforms>=21.0.0", + "nireports", + "numpy>=1.17.3", + "nest-asyncio>=1.5.1", + "scikit-image>=0.14.2", + "scipy>=1.8.0", +] +dynamic = ["version"] + +[project.urls] +Documentation = "https://www.nipreps.org/eddymotion" +Home = "https://github.com/nipreps/eddymotion" +NiPreps = "https://www.nipreps.org/" + +[project.optional-dependencies] +doc = [ + "attrs >= 20.1.0", + "furo >= 2024.01.29", + "matplotlib >= 2.2.0", + "nbsphinx", + "packaging", + "pydot >= 1.2.3", + "pydotplus", + "sphinx >= 4.5", + "sphinx-argparse", + "sphinx_rtd_theme", + "sphinxcontrib-apidoc ~= 0.3.0", + "sphinxcontrib-napoleon", + "sphinxcontrib-versioning" +] + +dev = [ + "black", + "flake8", + "flake8-pyproject", + "isort", + "pre-commit", + "pre-commit-hooks", +] + +plotting = ["nilearn"] + +resmon = ["psutil >=5.4"] + +popylar = ["popylar >= 0.2"] + +test = [ + "coverage", + "pytest >= 4.4", + "pytest-cov", + "pytest-env", + "pytest-xdist >= 1.28" +] + +# Aliases +docs = ["eddymotion[doc]"] +tests = ["eddymotion[test]"] +all = ["eddymotion[doc,test,dev,plotting,resmon,popylar]"] + +# +# Hatch configurations +# + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.hatch.build.targets.sdist] +exclude = [".git_archival.txt"] # No longer needed in sdist + +[tool.hatch.build.targets.wheel] +packages = ["src/eddymotion"] +# exclude = [ +# "eddymotion/tests/data", # Large test data directory +# ] + + +[tool.hatch.version] +validate-bump = true +source = "vcs" +raw-options = { version_scheme = "nipreps-calver" } + +[tool.hatch.build.hooks.vcs] +version-file = "src/eddymotion/_version.py" + +# +# Developer tool configurations +# + +[tool.black] +line-length = 99 +target-version = ['py39'] +skip-string-normalization = true +exclude = ''' +# Directories +/( + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | venv + | _build + | build + | dist +)/ +''' + +[tool.isort] +profile = 'black' +skip_gitignore = true + +[tool.flake8] +max-line-length = 99 +doctests = false +exclude = ["*build/", "docs/"] +select = "C,E,F,W,B,B950" +ignore = "N802,N806,W503,E203" +per-file-ignores = [ + "*/__init__.py: F401", + "docs/conf.py: E265", + "/^\\s*\\.\\. _.*?: http/: E501" +] + +[tool.pytest.ini_options] +pythonpath = "src/ test/" +norecursedirs = [".*", "_*"] +addopts = "-v --doctest-modules" +doctest_optionflags = "ALLOW_UNICODE NORMALIZE_WHITESPACE" +env = "PYTHONHASHSEED=0" +filterwarnings = ["ignore::DeprecationWarning"] + + +[tool.coverage.run] +branch = true +concurrency = 'multiprocessing' +omit = [ + '*/tests/*', + '*/__init__.py', + '*/conftest.py', + 'src/eddymotion/_version.py' +] + +[tool.coverage.report] +# Regexes for lines to exclude from consideration +exclude_lines = [ + 'raise NotImplementedError', + 'warnings\.warn', +] + +[tool.codespell] +# nd - import scipy.ndimage as nd +# mapp, reson -- Mapp. and Reson. abbreviations in citation +ignore-words-list = 'nd,mapp,reson' +skip = """ +./.git,*.pdf,*.svg,*.min.js,*.ipynb,ORIGINAL_LICENSE,\ +./docs/source/_static/example_anatreport.html""" diff --git a/setup.cfg b/setup.cfg deleted file mode 100755 index c75129a8..00000000 --- a/setup.cfg +++ /dev/null @@ -1,125 +0,0 @@ -[metadata] -name = eddymotion -author = Derek Pisner -author_email = nipreps@gmail.com -classifiers = - Development Status :: 3 - Alpha - 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 -description = Pure python eddy-current and head-motion correction for dMRI, an extension of QSIprep's SHOREline algorithm (Cieslak, 2020) to multiple diffusion models. -license = Apache License, Version 2.0 -long_description = file:README.rst -long_description_content_type = text/x-rst; charset=UTF-8 -maintainers = The NiPreps developers -maintainer_email = nipreps@gmail.com, dpysalexander@gmail.com -url = https://github.com/nipreps/eddymotion - -[options] -python_requires = >=3.8 -setup_requires = - setuptools >= 45 - setuptools_scm >= 6.2 - wheel -install_requires = - dipy>=1.3.0 - joblib - nipype>= 1.5.1, < 2.0 - nitransforms>=21.0.0 - nireports - numpy>=1.17.3 - nest-asyncio>=1.5.1 - scikit-image>=0.14.2 - scipy>=1.8.0 -test_requires = - codecov - coverage - pytest -packages = find_namespace: -package_dir= - = src -include_package_data = True -zip_safe = True - -[options.packages.find] -where = src -include = eddymotion* -exclude = - test - test.* - -[options.package_data] -eddymotion.config = - *.json - -[options.exclude_package_data] -* = test - -[options.extras_require] -doc = - nbsphinx - packaging - pydot >= 1.2.3 - pydotplus - sphinx >= 2.1.2 - sphinx-argparse - sphinx_rtd_theme - sphinxcontrib-apidoc ~= 0.3.0 - sphinxcontrib-napoleon - sphinxcontrib-versioning -docs = - %(doc)s -plotting = nilearn -resmon = psutil >=5.4 -popylar = popylar >= 0.2 -style = - flake8 >= 3.7.0 -test = - pytest >= 4.4 - pytest-xdist >= 1.28 - pytest-env - pytest-cov - coverage -tests = - %(test)s -dev = - black~=22.3 - flake8~=4.0 - isort~=5.10 - pre-commit - pre-commit-hooks - %(docs)s - %(test)s -all = - %(doc)s - %(resmon)s - %(popylar)s - %(tests)s - %(dev)s - -[flake8] -max-line-length = 99 -doctests = False -exclude= - *build/ - docs/ -select = C,E,F,W,B,B950 -ignore = N802,N806,W503,E203 -putty-ignore = - */__init__.py : +F401 - docs/conf.py : +E265 - /^\s*\.\. _.*?: http/ : +E501 - -[tool:pytest] -pythonpath = src/ test/ -norecursedirs = .* _* -addopts = -v --doctest-modules -doctest_optionflags = ALLOW_UNICODE NORMALIZE_WHITESPACE -env = - PYTHONHASHSEED=0 -filterwarnings = - ignore::DeprecationWarning diff --git a/setup.py b/setup.py deleted file mode 100644 index 5c773cf1..00000000 --- a/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python -"""eddymotion's PEP518 setup.py shim.""" -from setuptools import setup - -if __name__ == "__main__": - """ Install entry-point """ - setup() diff --git a/src/eddymotion/__init__.py b/src/eddymotion/__init__.py index 90e1b6f0..dd1f0ac7 100644 --- a/src/eddymotion/__init__.py +++ b/src/eddymotion/__init__.py @@ -27,6 +27,4 @@ __copyright__ = "Copyright 2021, The eddymotion developers" __url__ = "https://github.com/nipreps/EddyMotionCorrection" -DOWNLOAD_URL = ( - f"https://github.com/nipreps/{__packagename__}/archive/{__version__}.tar.gz" -) +DOWNLOAD_URL = f"https://github.com/nipreps/{__packagename__}/archive/{__version__}.tar.gz" diff --git a/src/eddymotion/data/dmri.py b/src/eddymotion/data/dmri.py index dfefd01a..55d634a5 100644 --- a/src/eddymotion/data/dmri.py +++ b/src/eddymotion/data/dmri.py @@ -228,9 +228,7 @@ def from_filename(cls, filename): """Read an HDF5 file from disk.""" with h5py.File(filename, "r") as in_file: root = in_file["/0"] - data = { - k: np.asanyarray(v) for k, v in root.items() if not k.startswith("_") - } + data = {k: np.asanyarray(v) for k, v in root.items() if not k.startswith("_")} return cls(**data) diff --git a/src/eddymotion/data/pet.py b/src/eddymotion/data/pet.py index 44a97b89..4591962d 100644 --- a/src/eddymotion/data/pet.py +++ b/src/eddymotion/data/pet.py @@ -132,9 +132,7 @@ def from_filename(cls, filename): """Read an HDF5 file from disk.""" with h5py.File(filename, "r") as in_file: root = in_file["/0"] - data = { - k: np.asanyarray(v) for k, v in root.items() if not k.startswith("_") - } + data = {k: np.asanyarray(v) for k, v in root.items() if not k.startswith("_")} return cls(**data) diff --git a/src/eddymotion/data/utils.py b/src/eddymotion/data/utils.py index 4d5e00d3..07cdd329 100644 --- a/src/eddymotion/data/utils.py +++ b/src/eddymotion/data/utils.py @@ -28,9 +28,7 @@ def apply_affines(nii, em_affines, output_filename=None): for ii, bvecnii in enumerate(nib.four_to_three(nii)): xfms = nt.linear.Affine(em_affines[ii]) - transformed_nii[..., ii] = np.asanyarray( - (~xfms).apply(bvecnii, reference=nii).dataobj - ) + transformed_nii[..., ii] = np.asanyarray((~xfms).apply(bvecnii, reference=nii).dataobj) nii_t_img = nii.__class__(transformed_nii, nii.affine, nii.header) diff --git a/src/eddymotion/estimator.py b/src/eddymotion/estimator.py index 7efff9d0..137b6dba 100644 --- a/src/eddymotion/estimator.py +++ b/src/eddymotion/estimator.py @@ -95,18 +95,16 @@ def fit( n_iter = len(models) for i_iter, model in enumerate(models): reg_target_type = ( - "dwi" - if model.lower() not in ("b0", "s0", "avg", "average", "mean") - else "b0" + "dwi" if model.lower() not in ("b0", "s0", "avg", "average", "mean") else "b0" ) # When downsampling these need to be set per-level bmask_img = None if dwdata.brainmask is not None: _, bmask_img = mkstemp(suffix="_bmask.nii.gz") - nb.Nifti1Image( - dwdata.brainmask.astype("uint8"), dwdata.affine, None - ).to_filename(bmask_img) + nb.Nifti1Image(dwdata.brainmask.astype("uint8"), dwdata.affine, None).to_filename( + bmask_img + ) kwargs["mask"] = dwdata.brainmask if hasattr(dwdata, "bzero") and dwdata.bzero is not None: @@ -203,9 +201,7 @@ def fit( if bmask_img: registration.inputs.fixed_image_masks = ["NULL", bmask_img] - if dwdata.em_affines is not None and np.any( - dwdata.em_affines[i, ...] - ): + if dwdata.em_affines is not None and np.any(dwdata.em_affines[i, ...]): reference = namedtuple("ImageGrid", ("shape", "affine"))( shape=dwdata.dataobj.shape[:3], affine=dwdata.affine ) @@ -243,9 +239,7 @@ def fit( return dwdata.em_affines -def _advanced_clip( - data, p_min=35, p_max=99.98, nonnegative=True, dtype="int16", invert=False -): +def _advanced_clip(data, p_min=35, p_max=99.98, nonnegative=True, dtype="int16", invert=False): """ Remove outliers at both ends of the intensity distribution and fit into a given dtype. diff --git a/src/eddymotion/model/__init__.py b/src/eddymotion/model/__init__.py index e2eb4565..7e32df8b 100644 --- a/src/eddymotion/model/__init__.py +++ b/src/eddymotion/model/__init__.py @@ -22,12 +22,12 @@ # """Data models.""" from eddymotion.model.base import ( - ModelFactory, AverageDWModel, DKIModel, DTIModel, - TrivialB0Model, + ModelFactory, PETModel, + TrivialB0Model, ) __all__ = ( diff --git a/src/eddymotion/model/base.py b/src/eddymotion/model/base.py index e0e8d2e9..fe8aca40 100644 --- a/src/eddymotion/model/base.py +++ b/src/eddymotion/model/base.py @@ -22,9 +22,10 @@ # """A factory class that adapts DIPY's dMRI models.""" import warnings -from joblib import Parallel, delayed + import numpy as np from dipy.core.gradients import gradient_table +from joblib import Parallel, delayed def _exec_fit(model, data, chunk=None): @@ -97,7 +98,11 @@ def __init__(self, gtab, S0=None, mask=None, b_max=None, **kwargs): # Setup B0 map self._S0 = None if S0 is not None: - self._S0 = np.clip(S0.astype("float32") / S0.max(), a_min=1e-5, a_max=1.0,) + self._S0 = np.clip( + S0.astype("float32") / S0.max(), + a_min=1e-5, + a_max=1.0, + ) # Setup brain mask self._mask = mask @@ -124,9 +129,7 @@ def __init__(self, gtab, S0=None, mask=None, b_max=None, **kwargs): from importlib import import_module module_name, class_name = model_str.rsplit(".", 1) - self._model = getattr( - import_module(module_name), class_name - )(_rasb2dipy(gtab), **kwargs) + self._model = getattr(import_module(module_name), class_name)(_rasb2dipy(gtab), **kwargs) def fit(self, data, n_jobs=None, **kwargs): """Fit the model chunk-by-chunk asynchronously""" @@ -136,9 +139,7 @@ def fit(self, data, n_jobs=None, **kwargs): # Select voxels within mask or just unravel 3D if no mask data = ( - data[self._mask, ...] - if self._mask is not None - else data.reshape(-1, data.shape[-1]) + data[self._mask, ...] if self._mask is not None else data.reshape(-1, data.shape[-1]) ) # One single CPU - linear execution (full model) @@ -154,8 +155,7 @@ def fit(self, data, n_jobs=None, **kwargs): # Parallelize process with joblib with Parallel(n_jobs=n_jobs) as executor: results = executor( - delayed(_exec_fit)(self._model, dchunk, i) - for i, dchunk in enumerate(data_chunks) + delayed(_exec_fit)(self._model, dchunk, i) for i, dchunk in enumerate(data_chunks) ) for submodel, index in results: self._models[index] = submodel @@ -182,10 +182,7 @@ def predict(self, gradient, **kwargs): if n_models == 1: predicted, _ = _exec_predict(self._model, gradient, S0=S0, **kwargs) else: - S0 = ( - np.array_split(S0, n_models) if S0 is not None - else [None] * n_models - ) + S0 = np.array_split(S0, n_models) if S0 is not None else [None] * n_models predicted = [None] * n_models @@ -345,10 +342,7 @@ def fit(self, data, *args, **kwargs): self._shape = data.shape[:3] # Convert data into V (voxels) x T (timepoints) - data = ( - data.reshape((-1, data.shape[-1])) - if self._mask is None else data[self._mask] - ) + data = data.reshape((-1, data.shape[-1])) if self._mask is None else data[self._mask] # A.shape = (T, K - 4); T= n. timepoints, K= n. knots (with padding) A = BSpline.design_matrix(x, self._t, k=self._order) @@ -357,17 +351,12 @@ def fit(self, data, *args, **kwargs): # One single CPU - linear execution (full model) if n_jobs == 1: - self._coeff = np.array([ - cg(ATdotA, AT @ v)[0] for v in data - ]) + self._coeff = np.array([cg(ATdotA, AT @ v)[0] for v in data]) return # Parallelize process with joblib with Parallel(n_jobs=n_jobs) as executor: - results = executor( - delayed(cg)(ATdotA, AT @ v) - for v in data - ) + results = executor(delayed(cg)(ATdotA, AT @ v) for v in data) self._coeff = np.array([r[0] for r in results]) diff --git a/src/eddymotion/viz.py b/src/eddymotion/viz.py index 38d112b0..eeeaee88 100644 --- a/src/eddymotion/viz.py +++ b/src/eddymotion/viz.py @@ -49,11 +49,13 @@ def plot_dwi(dataobj, affine, gradient=None, **kwargs): nb.Nifti1Image(dataobj, affine, None), vmax=vmax, cut_coords=cut_coords, - title=r"Reference $b$=0" - if gradient is None - else f"""\ + title=( + r"Reference $b$=0" + if gradient is None + else f"""\ $b$={gradient[3].astype(int)}, \ -$\\vec{{b}}$ = ({', '.join(str(v) for v in gradient[:3])})""", +$\\vec{{b}}$ = ({', '.join(str(v) for v in gradient[:3])})""" + ), **kwargs, ) @@ -289,8 +291,7 @@ def plot_carpet( segment_labels=None, detrend=False, ): - """ - Return carpet plot using niworkflows carpet_plot + """Return carpet plot using niworkflows carpet_plot Parameters ---------- @@ -305,10 +306,8 @@ def plot_carpet( output_file : :obj:`string` Path to save the plot segment_labels : :obj:`dict` - Dictionary of segment labels - e.g. {'Cerebral_White_Matter': [2, 41], - 'Cerebral_Cortex': [3, 42], - 'Ventricle': [4, 14, 15, 43, 72]} + Dictionary of segment labels, mapping segment name to list of integers + e.g. {'Cerebral_White_Matter': [2, 41], ...} detrend : :obj:`bool` niworkflows plot_carpet detrend flag @@ -328,9 +327,7 @@ def plot_carpet( nii_data_div_b0 = dw_data / bzero[..., np.newaxis] sort_inds = np.argsort( - gtab.bvals[~gtab.b0s_mask] - if sort_by_bval - else np.arange(len(gtab.bvals[~gtab.b0s_mask])) + gtab.bvals[~gtab.b0s_mask] if sort_by_bval else np.arange(len(gtab.bvals[~gtab.b0s_mask])) ) nii_data_div_b0 = nii_data_div_b0[..., sort_inds] @@ -351,9 +348,7 @@ def plot_carpet( for label in labels: indices = np.array([], dtype=int) for ii in segment_labels[label]: - indices = np.concatenate( - [indices, np.where(segmentation_masked == ii)[0]] - ) + indices = np.concatenate([indices, np.where(segmentation_masked == ii)[0]]) segments[label] = indices else: @@ -372,9 +367,7 @@ def plot_carpet( ) -def get_segment_labels( - filepath, keywords, delimiter=" ", index_position=0, label_position=1 -): +def get_segment_labels(filepath, keywords, delimiter=" ", index_position=0, label_position=1): """ Return segment labels for plot_carpet function diff --git a/test/test_estimator.py b/test/test_estimator.py index b592fe57..84a77475 100644 --- a/test/test_estimator.py +++ b/test/test_estimator.py @@ -77,4 +77,4 @@ def test_ANTs_config_b0(datadir, tmp_path, r_x, r_y, r_z, t_x, t_y, t_z): coords = xfm.reference.ndcoords.T rms = np.sqrt(((xfm.map(coords) - xform.map(coords)) ** 2).sum(1)).mean() - assert rms < 0.8 + assert rms < 0.8