Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REL: 24.1.0 #405

Merged
merged 6 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 23 additions & 17 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
exclude: ".*/data/.*"
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
exclude: '.*\.svg'
- id: end-of-file-fixer
exclude: '.*\.svg'
- id: check-yaml
- id: check-json
- id: check-toml
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.5
hooks:
- id: ruff
args: [ --fix ]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
exclude: '.*\.svg'
- id: end-of-file-fixer
exclude: '.*\.svg'
- id: check-yaml
- id: check-json
- id: check-toml
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.5
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
additional_dependencies:
- tomli
19 changes: 19 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
24.1.0 (October 02, 2024)
=========================
This new minor release includes a few bug fixes, such as excluding MCRIBS from surface reconstruction without a precomputed segmentation and ensuring generated derivatives are not masked, as well as improvements to reporting.

### Enhancements
* ENH: Add boilerplate, errors to report (#403)
* ENH: Add age to session report (#402)
* ENH: Improvements to age parsing (#395, #398)

### Bug Fixes
* FIX: MCRIBS auto surface reconstruction logic (#399)
* FIX: Do not force masking of anatomicals when using `--derivatives` (#400)

### Internals / Maintenance
* MAINT: Revisit warnings filter (#396)
* MAINT: Automate testing with tox (#404)
* MAINT: Port over parser arguments and tests from fmriprep (#401)


24.0.1 (August 31, 2024)
========================
A patch release with a fix for the BOLD T2\* workflow. The command line argument `--me-t2s-fit-method` was added for finer control when processing multi-echo datasets.
Expand Down
48 changes: 20 additions & 28 deletions nibabies/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,36 @@

import json
from pathlib import Path
from tempfile import TemporaryDirectory
from shutil import copytree

import nibabel as nb
import numpy as np
import pytest

from nibabies.data import load as load_data

FILES = (
'functional.nii',
'anatomical.nii',
'func.dlabel.nii',
'func.dtseries.nii',
'epi.nii',
'T1w.nii',
'func_to_struct.mat',
'atlas.nii',
'label_list.txt',
'sub-01_run-01_echo-1_bold.nii.gz',
'sub-01_run-01_echo-2_bold.nii.gz',
'sub-01_run-01_echo-3_bold.nii.gz',
)


@pytest.fixture(scope='package')
def data_dir():
with TemporaryDirectory() as tmpdir:
tmp_path = Path(tmpdir)
for fname in FILES:
Path.touch(tmp_path / fname)
yield tmp_path
try:
from importlib.resources import files as ir_files
except ImportError: # PY<3.9
from importlib_resources import files as ir_files

Check warning on line 16 in nibabies/conftest.py

View check run for this annotation

Codecov / codecov/patch

nibabies/conftest.py#L15-L16

Added lines #L15 - L16 were not covered by tests


def copytree_or_skip(source, target):
data_dir = ir_files('nibabies') / source

Check warning on line 20 in nibabies/conftest.py

View check run for this annotation

Codecov / codecov/patch

nibabies/conftest.py#L20

Added line #L20 was not covered by tests
if not data_dir.exists():
pytest.skip(f'Cannot chdir into {data_dir!r}. Probably in a zipped distribution.')

Check warning on line 22 in nibabies/conftest.py

View check run for this annotation

Codecov / codecov/patch

nibabies/conftest.py#L22

Added line #L22 was not covered by tests

try:
copytree(data_dir, target / data_dir.name)
except Exception: # noqa: BLE001
pytest.skip(f'Cannot copy {data_dir!r} into {target / data_dir.name}. Probably in a zip.')

Check warning on line 27 in nibabies/conftest.py

View check run for this annotation

Codecov / codecov/patch

nibabies/conftest.py#L24-L27

Added lines #L24 - L27 were not covered by tests


@pytest.fixture(autouse=True)
def _populate_namespace(doctest_namespace, data_dir):
doctest_namespace['data_dir'] = data_dir
doctest_namespace['test_data'] = load_data.cached('../tests/data')
doctest_namespace['Path'] = Path
def _populate_namespace(doctest_namespace, tmp_path):
doctest_namespace['copytree_or_skip'] = copytree_or_skip
doctest_namespace['testdir'] = tmp_path
doctest_namespace['datadir'] = load_data()


@pytest.fixture
Expand Down
26 changes: 26 additions & 0 deletions nibabies/data/boilerplate.bib
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,29 @@ @article{mcribs
title = {Parcellation of the neonatal cortex using Surface-based Melbourne Children's Regional Infant Brain atlases (M-{CRIB}-S)},
journal = {Scientific Reports}
}

@article{patriat_improved_2017,
title = {An improved model of motion-related signal changes in {fMRI}},
volume = {144, Part A},
issn = {1053-8119},
url = {https://www.sciencedirect.com/science/article/pii/S1053811916304360},
doi = {10.1016/j.neuroimage.2016.08.051},
abstract = {Head motion is a significant source of noise in the estimation of functional connectivity from resting-state functional MRI (rs-fMRI). Current strategies to reduce this noise include image realignment, censoring time points corrupted by motion, and including motion realignment parameters and their derivatives as additional nuisance regressors in the general linear model. However, this nuisance regression approach assumes that the motion-induced signal changes are linearly related to the estimated realignment parameters, which is not always the case. In this study we develop an improved model of motion-related signal changes, where nuisance regressors are formed by first rotating and translating a single brain volume according to the estimated motion, re-registering the data, and then performing a principal components analysis (PCA) on the resultant time series of both moved and re-registered data. We show that these “Motion Simulated (MotSim)” regressors account for significantly greater fraction of variance, result in higher temporal signal-to-noise, and lead to functional connectivity estimates that are less affected by motion compared to the most common current approach of using the realignment parameters and their derivatives as nuisance regressors. This improvement should lead to more accurate estimates of functional connectivity, particularly in populations where motion is prevalent, such as patients and young children.},
urldate = {2017-01-18},
journal = {NeuroImage},
author = {Patriat, Rémi and Reynolds, Richard C. and Birn, Rasmus M.},
month = jan,
year = {2017},
keywords = {Motion, Correction, Methods, Rs-fMRI},
pages = {74--82},
}

@article{templateflow,
author = {Ciric, R. and Thompson, William H. and Lorenz, R. and Goncalves, M. and MacNicol, E. and Markiewicz, C. J. and Halchenko, Y. O. and Ghosh, S. S. and Gorgolewski, K. J. and Poldrack, R. A. and Esteban, O.},
title = {{TemplateFlow}: {FAIR}-sharing of multi-scale, multi-species brain models},
volume = {19},
pages = {1568--1571},
year = {2022},
doi = {10.1038/s41592-022-01681-2},
journal = {Nature Methods}
}
Empty file.
Empty file.
Empty file.
61 changes: 61 additions & 0 deletions nibabies/interfaces/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from pathlib import Path
from shutil import copytree
from tempfile import TemporaryDirectory

import pytest

try:
from contextlib import chdir as _chdir
except ImportError: # PY310
import os
from contextlib import contextmanager

@contextmanager # type: ignore
def _chdir(path):
cwd = os.getcwd()
os.chdir(path)
try:
yield
finally:
os.chdir(cwd)


DATA_FILES = (
'functional.nii',
'anatomical.nii',
'func.dlabel.nii',
'func.dtseries.nii',
'epi.nii',
'T1w.nii',
'func_to_struct.mat',
'atlas.nii',
'label_list.txt',
'sub-01_run-01_echo-1_bold.nii.gz',
'sub-01_run-01_echo-2_bold.nii.gz',
'sub-01_run-01_echo-3_bold.nii.gz',
)


@pytest.fixture(scope='package')
def data_dir():
with TemporaryDirectory() as tmpdir:
tmp_path = Path(tmpdir)
for fname in DATA_FILES:
Path.touch(tmp_path / fname)
yield tmp_path


@pytest.fixture(autouse=True)
def _docdir(data_dir, request, tmp_path):
# Trigger ONLY for the doctests.
doctest_plugin = request.config.pluginmanager.getplugin('doctest')
if isinstance(request.node, doctest_plugin.DoctestItem):
copytree(data_dir, tmp_path, dirs_exist_ok=True)

# Chdir only for the duration of the test.
with _chdir(tmp_path):
yield

else:
# For normal tests, we have to yield, since this is a yield-fixture.
yield
38 changes: 19 additions & 19 deletions nibabies/interfaces/workbench.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,16 +164,16 @@ class CiftiCreateDenseFromTemplate(WBCommand):

>>> from nibabies.interfaces import workbench as wb
>>> frmtpl = wb.CiftiCreateDenseFromTemplate()
>>> frmtpl.inputs.in_file = data_dir / "func.dtseries.nii"
>>> frmtpl.inputs.in_file = testdir / "func.dtseries.nii"
>>> frmtpl.inputs.series = True
>>> frmtpl.inputs.series_step = 0.8
>>> frmtpl.inputs.series_start = 0
>>> frmtpl.cmdline # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
'wb_command -cifti-create-dense-from-template .../func.dtseries.nii \
template_func.dtseries.nii -series 0.8 0.0'

>>> frmtpl.inputs.volume = [("OTHER", data_dir / 'functional.nii', True), \
("PUTAMEN_LEFT", data_dir / 'functional.nii')]
>>> frmtpl.inputs.volume = [("OTHER", testdir / 'functional.nii', True), \
("PUTAMEN_LEFT", testdir / 'functional.nii')]
>>> frmtpl.cmdline # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
'wb_command -cifti-create-dense-from-template .../func.dtseries.nii \
template_func.dtseries.nii -series 0.8 0.0 \
Expand Down Expand Up @@ -330,8 +330,8 @@ class CiftiCreateDenseTimeseries(WBCommand):

>>> from nibabies.interfaces.workbench import CiftiCreateDenseTimeseries
>>> createdts = CiftiCreateDenseTimeseries()
>>> createdts.inputs.volume_data = data_dir /'functional.nii'
>>> createdts.inputs.volume_structure_labels = data_dir / 'atlas.nii'
>>> createdts.inputs.volume_data = testdir /'functional.nii'
>>> createdts.inputs.volume_structure_labels = testdir / 'atlas.nii'
>>> createdts.cmdline # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
'wb_command -cifti-create-dense-timeseries out.dtseries.nii \
-volume .../functional.nii .../atlas.nii'
Expand Down Expand Up @@ -467,8 +467,8 @@ class CiftiCreateLabel(WBCommand):

>>> from nibabies.interfaces import workbench as wb
>>> lab = wb.CiftiCreateLabel()
>>> lab.inputs.volume_label = data_dir / "functional.nii"
>>> lab.inputs.structure_label_volume = data_dir / "functional.nii"
>>> lab.inputs.volume_label = testdir / "functional.nii"
>>> lab.inputs.structure_label_volume = testdir / "functional.nii"
>>> lab.cmdline # doctest: +ELLIPSIS
'wb_command -cifti-create-label out.dlabel.nii -volume .../functional.nii .../functional.nii'
"""
Expand Down Expand Up @@ -940,9 +940,9 @@ class CiftiResample(WBCommand):

>>> from nibabies.interfaces import workbench as wb
>>> res = wb.CiftiResample()
>>> res.inputs.in_file = data_dir / "func.dtseries.nii"
>>> res.inputs.in_file = testdir / "func.dtseries.nii"
>>> res.inputs.direction = "COLUMN"
>>> res.inputs.template = data_dir / "func.dlabel.nii"
>>> res.inputs.template = testdir / "func.dlabel.nii"
>>> res.inputs.template_direction = "COLUMN"
>>> res.inputs.surface_method = "ADAP_BARY_AREA"
>>> res.inputs.volume_method = "CUBIC"
Expand Down Expand Up @@ -1051,7 +1051,7 @@ class CiftiSeparate(WBCommand):
dimension, columns for .dtseries.

>>> separate = CiftiSeparate()
>>> separate.inputs.in_file = data_dir / "func.dtseries.nii"
>>> separate.inputs.in_file = testdir / "func.dtseries.nii"
>>> separate.inputs.direction = "COLUMN"
>>> separate.inputs.volume_all_file = "volume_all.nii.gz"
>>> separate.cmdline # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
Expand Down Expand Up @@ -1216,10 +1216,10 @@ class VolumeAffineResample(WBCommand):

>>> from nibabies.interfaces.workbench import VolumeAffineResample
>>> resample = VolumeAffineResample()
>>> resample.inputs.in_file = data_dir /'functional.nii'
>>> resample.inputs.volume_space = data_dir /'anatomical.nii'
>>> resample.inputs.in_file = testdir /'functional.nii'
>>> resample.inputs.volume_space = testdir /'anatomical.nii'
>>> resample.inputs.method = 'CUBIC'
>>> resample.inputs.affine = data_dir / 'func_to_struct.mat'
>>> resample.inputs.affine = testdir / 'func_to_struct.mat'
>>> resample.cmdline # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
'wb_command -volume-resample .../functional.nii .../anatomical.nii CUBIC \
resampled_functional.nii.gz -affine .../func_to_struct.mat'
Expand All @@ -1237,8 +1237,8 @@ class VolumeAffineResample(WBCommand):
However, if other volumes were used to calculate the affine, they can
be provided:

>>> resample.inputs.flirt_source_volume = data_dir / 'epi.nii'
>>> resample.inputs.flirt_target_volume = data_dir /'T1w.nii'
>>> resample.inputs.flirt_source_volume = testdir / 'epi.nii'
>>> resample.inputs.flirt_target_volume = testdir /'T1w.nii'
>>> resample.cmdline # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
'wb_command -volume-resample .../functional.nii .../anatomical.nii CUBIC \
resampled_functional.nii.gz -affine .../func_to_struct.mat \
Expand Down Expand Up @@ -1297,7 +1297,7 @@ class VolumeAllLabelsToROIs(WBCommand):

>>> from nibabies.interfaces.workbench import VolumeAllLabelsToROIs
>>> rois = VolumeAllLabelsToROIs()
>>> rois.inputs.in_file = data_dir / 'atlas.nii'
>>> rois.inputs.in_file = testdir / 'atlas.nii'
>>> rois.inputs.label_map = 1
>>> rois.cmdline # doctest: +ELLIPSIS
'wb_command -volume-all-labels-to-rois .../atlas.nii 1 atlas_rois.nii.gz'
Expand Down Expand Up @@ -1346,7 +1346,7 @@ class VolumeLabelExportTable(WBCommand):

>>> from nibabies.interfaces.workbench import VolumeLabelExportTable
>>> label_export = VolumeLabelExportTable()
>>> label_export.inputs.in_file = data_dir / 'atlas.nii'
>>> label_export.inputs.in_file = testdir / 'atlas.nii'
>>> label_export.inputs.label_map = 1
>>> label_export.cmdline # doctest: +ELLIPSIS
'wb_command -volume-label-export-table .../atlas.nii 1 atlas_labels.txt'
Expand Down Expand Up @@ -1434,8 +1434,8 @@ class VolumeLabelImport(WBCommand):

>>> from nibabies.interfaces.workbench import VolumeLabelImport
>>> label_import = VolumeLabelImport()
>>> label_import.inputs.in_file = data_dir / 'atlas.nii'
>>> label_import.inputs.label_list_file = data_dir / 'label_list.txt'
>>> label_import.inputs.in_file = testdir / 'atlas.nii'
>>> label_import.inputs.label_list_file = testdir / 'label_list.txt'
>>> label_import.cmdline # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
'wb_command -volume-label-import .../atlas.nii .../label_list.txt \
atlas_labels.nii.gz'
Expand Down
6 changes: 3 additions & 3 deletions nibabies/workflows/bold/alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,11 @@ def parse_roi_labels(label_file: str):

Return a list of structure names and label keys.

>>> structs, ids = parse_roi_labels(test_data / "labelfile.txt")
>>> structs, ids = parse_roi_labels(datadir / "FreeSurferSubcorticalLabelTableLut.txt")
>>> structs
['CEREBELLUM_LEFT', 'THALAMUS_LEFT', 'CAUDATE_LEFT']
['ACCUMBENS_LEFT', 'ACCUMBENS_RIGHT', 'AMYGDALA_LEFT', ...]
>>> ids
[8, 10, 11]
[26, 58, 18, ...]
"""

with open(label_file) as fp:
Expand Down
Loading