Skip to content
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
35 changes: 34 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,41 @@ on:
pull_request:
schedule: [{cron: '4 3 1 * *'}] # M H d m w (monthly at 3:04)
jobs:
test:
if: github.event_name != 'pull_request' || !contains('OWNER,MEMBER,COLLABORATOR', github.event.pull_request.author_association)
strategy:
matrix:
python: [3.8, 3.14]
os: [ubuntu]
include: [{os: macos, python: 3.14}, {os: windows, python: 3.14}]
runs-on: ${{ matrix.os }}-latest
timeout-minutes: 5
defaults: {run: {shell: bash}}
steps:
- uses: actions/checkout@v7
with: {fetch-depth: 0}
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python }}
- run: pip install -e .[full,dev]
- run: pytest --cov=brainweb --cov-fail-under=50 --cov-report=xml --cov-report=term-missing
- name: codacy
run: |
bash <(curl -fsL https://coverage.codacy.com/get.sh) report -l Python -r coverage.xml --partial || :
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
finish:
continue-on-error: ${{ github.event_name != 'push' }}
needs: test
runs-on: ubuntu-slim
steps:
- name: codacy
run: |
bash <(curl -fsL https://coverage.codacy.com/get.sh) final || :
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
deploy:
name: PyPI Deploy
needs: test
runs-on: ubuntu-latest
environment: pypi
permissions: {contents: write, id-token: write, packages: write}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ __pycache__/
/*.egg*/
/build/
/dist/

/.coverage*
/coverage.xml
21 changes: 21 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ repos:
args: [-i]
types: [text]
exclude: ^(.pre-commit-config.yaml|.github/workflows/test.yml)$
- id: pytest
name: pytest
language: python
entry: env PYTHONPATH=. pytest
args: [--cov=brainweb, -vv, -k=quick]
types: [python]
pass_filenames: false
additional_dependencies:
- pytest-cov
- numpy
- requests
- scikit-image
- tqdm>=4.42.0
- repo: https://github.com/PyCQA/bandit
rev: 1.9.4
hooks:
- id: bandit
args: [-c, pyproject.toml]
- repo: https://github.com/PyCQA/flake8
rev: 7.3.0
hooks:
Expand Down Expand Up @@ -53,3 +71,6 @@ repos:
rev: 9.0.0b1
hooks:
- id: isort
ci:
autoupdate_schedule: monthly
skip: [pytest]
4 changes: 2 additions & 2 deletions README.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
"[PyPI-target]: https://pypi.org/project/brainweb\n",
"[CI]: https://img.shields.io/github/actions/workflow/status/casperdcl/brainweb/test.yml?branch=main&label=brainweb&logo=GitHub\n",
"[CI-target]: https://github.com/casperdcl/brainweb/actions/workflows/test.yml\n",
"[Quality]: https://api.codacy.com/project/badge/Grade/cdad13693b0141199c31d5b44c7ab185\n",
"[Quality-target]: https://www.codacy.com/app/casper-dcl/brainweb\n",
"[Quality]: https://app.codacy.com/project/badge/Grade/cdad13693b0141199c31d5b44c7ab185\n",
"[Quality-target]: https://app.codacy.com/gh/casperdcl/brainweb\n",
"[DOI]: https://img.shields.io/badge/DOI-10.5281/zenodo.3269888-blue.svg\n",
"[DOI-target]: https://doi.org/10.5281/zenodo.3269888\n",
"[LICENCE]: https://img.shields.io/pypi/l/brainweb.svg?label=licence\n",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ coregistration.

[![PyPI](https://img.shields.io/pypi/v/brainweb.svg)](https://pypi.org/project/brainweb)
[![CI](https://img.shields.io/github/actions/workflow/status/casperdcl/brainweb/test.yml?branch=main&label=brainweb&logo=GitHub)](https://github.com/casperdcl/brainweb/actions/workflows/test.yml)
[![Quality](https://api.codacy.com/project/badge/Grade/cdad13693b0141199c31d5b44c7ab185)](https://www.codacy.com/app/casper-dcl/brainweb)
[![Quality](https://app.codacy.com/project/badge/Grade/cdad13693b0141199c31d5b44c7ab185)](https://app.codacy.com/gh/casperdcl/brainweb)
[![DOI](https://img.shields.io/badge/DOI-10.5281/zenodo.3269888-blue.svg)](https://doi.org/10.5281/zenodo.3269888)
[![LICENCE](https://img.shields.io/pypi/l/brainweb.svg?label=licence)](https://www.mozilla.org/MPL/2.0)

Expand Down
30 changes: 21 additions & 9 deletions brainweb/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
import numpy as np
import requests
from numpy.random import seed
from scipy.ndimage.filters import gaussian_filter

try:
from scipy.ndimage import gaussian_filter
except ImportError: # scikit-image<2
from scipy.ndimage.filters import gaussian_filter

from skimage.transform import resize
from tqdm.auto import tqdm, trange
from tqdm.contrib import tenumerate
Expand Down Expand Up @@ -169,7 +174,8 @@ def get_file(fname, origin, cache_dir=None, chunk_size=None):
except Exception:
log.warning("cannot create:" + cache_dir)
if not os.access(cache_dir, os.W_OK):
cache_dir = path.join('/tmp', '.brainweb')
from tempfile import gettempdir
cache_dir = path.join(gettempdir(), '.brainweb')
if not path.exists(cache_dir):
os.makedirs(cache_dir)

Expand All @@ -178,7 +184,7 @@ def get_file(fname, origin, cache_dir=None, chunk_size=None):
if not path.exists(fpath):
log.debug(f"Downloading {fpath} from {origin}")
try:
d = requests.get(origin, stream=True)
d = requests.get(origin, stream=True, timeout=(10, None))
with tqdm(total=float(d.headers.get('Content-length') or 0), desc=fname, unit="B", unit_scale=True,
unit_divisor=1024, leave=False) as fprog:
with open(fpath, 'wb') as fo:
Expand Down Expand Up @@ -583,12 +589,16 @@ def register(src, target=None, ROI=None, target_shape=Shape.mMR, src_resolution=
"""
from dipy.align.imaffine import AffineMap, transform_centers_of_mass

assert src.ndim == 3
def assert3D(ndim, name):
if ndim != 3:
raise ValueError(f"{name} must be 3D, got {ndim}D")

assert3D(src.ndim, "src")
if target is not None:
assert target.ndim == 3
assert len(target_shape) == 3
assert len(src_resolution) == 3
assert len(target_resolution) == 3
assert3D(target.ndim, "target")
assert3D(len(target_shape), "target_shape")
assert3D(len(src_resolution), "src_resolution")
assert3D(len(target_resolution), "target_resolution")

if ROI is None:
ROI = ((0, None),)
Expand All @@ -605,7 +615,9 @@ def register(src, target=None, ROI=None, target_shape=Shape.mMR, src_resolution=
affine_init[:3, -1] -= moving.shape * src_resolution / target_resolution
affine_init[:3, -1] /= 2
affine_init[:3, -1] += src_offset
affine_map = AffineMap(np.eye(4), target_shape, np.eye(4), moving.shape, affine_init) # unmoved target
# unmoved target
affine_map = AffineMap(np.eye(4), domain_grid_shape=target_shape, domain_grid2world=np.eye(4),
codomain_grid_shape=moving.shape, codomain_grid2world=affine_init)
src = affine_map.transform(moving)

if target is not None:
Expand Down
21 changes: 18 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,19 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3"]
dependencies = [
"tqdm>=4.42.0",
"numpy",
"requests",
"scikit-image"]
"scikit-image",
"tqdm>=4.42.0"]
[project.optional-dependencies]
dev = ["pytest>=6", "pytest-cov"]
plot = ["matplotlib"]
register = ["dipy"]
full = ["matplotlib", "dipy"]

[tool.bandit]
exclude_dirs = ["tests"]

[tool.flake8]
max_line_length = 120
extend_ignore = ["E261"]
Expand All @@ -55,4 +59,15 @@ blank_line_before_nested_class_or_def = false

[tool.isort]
line_length = 120
known_first_party = ["brainweb"]
known_first_party = ["brainweb", "tests"]

[tool.pytest.ini_options]
log_level = "INFO"
testpaths = ["tests"]
markers = ["quick"]
addopts = "-v --tb=short -rxs -W=error --durations=0 --durations-min=0.1"

[tool.coverage.run]
branch = true
[tool.coverage.report]
show_missing = true
45 changes: 45 additions & 0 deletions tests/test_brainweb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import pytest

import brainweb as bw


@pytest.mark.quick
def test_import():
assert bw.__version__


@pytest.fixture(scope="session")
def subject_file(tmp_path_factory):
f, url = next(iter(bw.LINKS.items()))
return bw.get_file(f, url, cache_dir=tmp_path_factory.mktemp("brainweb"))


def test_download(subject_file):
from pathlib import Path
assert Path(subject_file).exists()


def test_load(subject_file):
data = bw.load_file(subject_file)
assert data.shape == (362, 434, 362)


def test_mMR(subject_file):
vol = bw.get_mmr_fromfile(subject_file)
assert not {'T1', 'T2', 'uMap', 'PET'} - set(vol.keys())
assert vol['PET'].shape == (127, 344, 344)


def test_lesions(subject_file):
vol = bw.get_mmr_fromfile(subject_file)
im3d = bw.add_lesions(vol['PET'])
assert im3d.shape == vol['PET'].shape
assert (im3d != vol['PET']).any()


def test_registration(subject_file):
pytest.importorskip("dipy")
data = bw.load_file(subject_file)
vol = bw.get_mmr_fromfile(subject_file)
reg = bw.register(data[:, ::-1], target=vol['PET'], src_resolution=bw.Res.brainweb, target_resolution=bw.Res.mMR)
assert reg.shape == vol['PET'].shape