From 8cdc6a46db90808a17324a882b6ed1168a5f8a65 Mon Sep 17 00:00:00 2001 From: Tom Close Date: Mon, 24 Oct 2022 17:46:00 +1100 Subject: [PATCH 1/3] moved sample images into dummy sub-package and added openeuro download of real T1w --- conftest.py | 4 +- medimages4tests/dummy/__init__.py | 0 medimages4tests/{ => dummy}/dicom/base.py | 4 +- .../dicom/mri/dwi/siemens/skyra/syngo_d13c.py | 4 +- .../ge/discovery_mr888/dv26_0_r05_2008a.py | 4 +- .../mri/fmap/siemens/skyra/syngo_d13c.py | 4 +- .../dicom/mri/t1w/siemens/skyra/syngo_d13c.py | 4 +- medimages4tests/{ => dummy}/nifti.py | 2 +- medimages4tests/mri/__init__.py | 0 medimages4tests/mri/neuro/__init__.py | 0 medimages4tests/mri/neuro/base.py | 10 +++++ medimages4tests/mri/neuro/t1w.py | 39 +++++++++++++++++++ scripts/import_dicom.py | 4 +- setup.cfg | 23 ++++++++++- setup.py | 4 +- tests/test_dicom.py | 2 +- tests/test_nifti.py | 6 +-- 17 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 medimages4tests/dummy/__init__.py rename medimages4tests/{ => dummy}/dicom/base.py (96%) rename medimages4tests/{ => dummy}/dicom/mri/dwi/siemens/skyra/syngo_d13c.py (99%) rename medimages4tests/{ => dummy}/dicom/mri/fmap/ge/discovery_mr888/dv26_0_r05_2008a.py (99%) rename medimages4tests/{ => dummy}/dicom/mri/fmap/siemens/skyra/syngo_d13c.py (99%) rename medimages4tests/{ => dummy}/dicom/mri/t1w/siemens/skyra/syngo_d13c.py (99%) rename medimages4tests/{ => dummy}/nifti.py (98%) create mode 100644 medimages4tests/mri/__init__.py create mode 100644 medimages4tests/mri/neuro/__init__.py create mode 100644 medimages4tests/mri/neuro/base.py create mode 100644 medimages4tests/mri/neuro/t1w.py diff --git a/conftest.py b/conftest.py index 4a2aeda..c03990a 100644 --- a/conftest.py +++ b/conftest.py @@ -4,7 +4,7 @@ from importlib import import_module import pytest -base_dicom_dir = Path(__file__).parent / 'medimages4tests' / 'dicom' +base_dicom_dir = Path(__file__).parent / 'medimages4tests' / 'dummy' / 'dicom' dicom_modules = ['__'.join(p.relative_to(base_dicom_dir).with_suffix('').parts) for p in base_dicom_dir.glob('**/*.py') if p.stem != 'base'] @@ -17,7 +17,7 @@ def work_dir(): @pytest.fixture(params=dicom_modules) def dicom_module(request): - module_path = 'medimages4tests.dicom.' + '.'.join(request.param.split('__')) + module_path = 'medimages4tests.dummy.dicom.' + '.'.join(request.param.split('__')) return import_module(module_path) diff --git a/medimages4tests/dummy/__init__.py b/medimages4tests/dummy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/medimages4tests/dicom/base.py b/medimages4tests/dummy/dicom/base.py similarity index 96% rename from medimages4tests/dicom/base.py rename to medimages4tests/dummy/dicom/base.py index f9ff915..a381045 100644 --- a/medimages4tests/dicom/base.py +++ b/medimages4tests/dummy/dicom/base.py @@ -2,9 +2,9 @@ import shutil from copy import copy import pydicom.dataset -from .. import base_cache_dir +from ... import base_cache_dir -cache_dir = base_cache_dir / "dicom" +cache_dir = base_cache_dir / "dummy" / "dicom" dicom_pkg_dir = Path(__file__).parent diff --git a/medimages4tests/dicom/mri/dwi/siemens/skyra/syngo_d13c.py b/medimages4tests/dummy/dicom/mri/dwi/siemens/skyra/syngo_d13c.py similarity index 99% rename from medimages4tests/dicom/mri/dwi/siemens/skyra/syngo_d13c.py rename to medimages4tests/dummy/dicom/mri/dwi/siemens/skyra/syngo_d13c.py index 218e806..24cbc5f 100644 --- a/medimages4tests/dicom/mri/dwi/siemens/skyra/syngo_d13c.py +++ b/medimages4tests/dummy/dicom/mri/dwi/siemens/skyra/syngo_d13c.py @@ -1,8 +1,8 @@ -from medimages4tests.dicom.base import generate_dicom, default_dicom_dir +from medimages4tests.dummy.dicom.base import generate_dicom, default_dicom_dir -def sample_image(out_dir=default_dicom_dir(__file__)): +def get_image(out_dir=default_dicom_dir(__file__)): return generate_dicom(out_dir, num_vols, constant_hdr, collated_data, varying_hdr) diff --git a/medimages4tests/dicom/mri/fmap/ge/discovery_mr888/dv26_0_r05_2008a.py b/medimages4tests/dummy/dicom/mri/fmap/ge/discovery_mr888/dv26_0_r05_2008a.py similarity index 99% rename from medimages4tests/dicom/mri/fmap/ge/discovery_mr888/dv26_0_r05_2008a.py rename to medimages4tests/dummy/dicom/mri/fmap/ge/discovery_mr888/dv26_0_r05_2008a.py index c747e06..6a27148 100644 --- a/medimages4tests/dicom/mri/fmap/ge/discovery_mr888/dv26_0_r05_2008a.py +++ b/medimages4tests/dummy/dicom/mri/fmap/ge/discovery_mr888/dv26_0_r05_2008a.py @@ -1,7 +1,7 @@ -from medimages4tests.dicom.base import generate_dicom, default_dicom_dir +from medimages4tests.dummy.dicom.base import generate_dicom, default_dicom_dir -def sample_image(out_dir=default_dicom_dir(__file__)): +def get_image(out_dir=default_dicom_dir(__file__)): return generate_dicom(out_dir, num_vols, constant_hdr, collated_data, varying_hdr) diff --git a/medimages4tests/dicom/mri/fmap/siemens/skyra/syngo_d13c.py b/medimages4tests/dummy/dicom/mri/fmap/siemens/skyra/syngo_d13c.py similarity index 99% rename from medimages4tests/dicom/mri/fmap/siemens/skyra/syngo_d13c.py rename to medimages4tests/dummy/dicom/mri/fmap/siemens/skyra/syngo_d13c.py index 3da49d2..ca99b1b 100644 --- a/medimages4tests/dicom/mri/fmap/siemens/skyra/syngo_d13c.py +++ b/medimages4tests/dummy/dicom/mri/fmap/siemens/skyra/syngo_d13c.py @@ -1,8 +1,8 @@ -from medimages4tests.dicom.base import generate_dicom, default_dicom_dir +from medimages4tests.dummy.dicom.base import generate_dicom, default_dicom_dir -def sample_image(out_dir=default_dicom_dir(__file__)): +def get_image(out_dir=default_dicom_dir(__file__)): return generate_dicom(out_dir, num_vols, constant_hdr, collated_data, varying_hdr) diff --git a/medimages4tests/dicom/mri/t1w/siemens/skyra/syngo_d13c.py b/medimages4tests/dummy/dicom/mri/t1w/siemens/skyra/syngo_d13c.py similarity index 99% rename from medimages4tests/dicom/mri/t1w/siemens/skyra/syngo_d13c.py rename to medimages4tests/dummy/dicom/mri/t1w/siemens/skyra/syngo_d13c.py index ffada82..f59d595 100644 --- a/medimages4tests/dicom/mri/t1w/siemens/skyra/syngo_d13c.py +++ b/medimages4tests/dummy/dicom/mri/t1w/siemens/skyra/syngo_d13c.py @@ -1,7 +1,7 @@ -from medimages4tests.dicom.base import generate_dicom, default_dicom_dir +from medimages4tests.dummy.dicom.base import generate_dicom, default_dicom_dir -def sample_image(out_dir=default_dicom_dir(__file__)): +def get_image(out_dir=default_dicom_dir(__file__)): return generate_dicom(out_dir, num_vols, constant_hdr, collated_data, varying_hdr) diff --git a/medimages4tests/nifti.py b/medimages4tests/dummy/nifti.py similarity index 98% rename from medimages4tests/nifti.py rename to medimages4tests/dummy/nifti.py index bea8a9e..4b73abe 100644 --- a/medimages4tests/nifti.py +++ b/medimages4tests/dummy/nifti.py @@ -5,7 +5,7 @@ import nibabel as nb -def sample_image( +def get_image( out_file: Path, data: np.ndarray = None, vox_sizes=(1.0, 1.0, 1.0), diff --git a/medimages4tests/mri/__init__.py b/medimages4tests/mri/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/medimages4tests/mri/neuro/__init__.py b/medimages4tests/mri/neuro/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/medimages4tests/mri/neuro/base.py b/medimages4tests/mri/neuro/base.py new file mode 100644 index 0000000..fa31586 --- /dev/null +++ b/medimages4tests/mri/neuro/base.py @@ -0,0 +1,10 @@ +from pathlib import Path +import attrs + + +@attrs.define +class OpenneuroSpec: + + dataset: str + tag: str + path: Path diff --git a/medimages4tests/mri/neuro/t1w.py b/medimages4tests/mri/neuro/t1w.py new file mode 100644 index 0000000..7426553 --- /dev/null +++ b/medimages4tests/mri/neuro/t1w.py @@ -0,0 +1,39 @@ +from tempfile import mkdtemp +import shutil +from pathlib import Path +import openneuro +from medimages4tests import base_cache_dir +from ..neuro import OpenneuroSpec + + +cache_dir = base_cache_dir / "mri" / "neuro" / "t1w" + + +SAMPLES = { + "ds004130-ON01016": OpenneuroSpec( + dataset="ds004130", + tag="1.0.0", + path="sub-ON01016/anat/sub-ON01016_acq-fspgr_run-01_T1w", + ) +} + + +def get_image(sample_name): + sample = SAMPLES[sample_name] + if cache_dir.exists(): + cache_dir.mkdir(parents=True) + out_path = (cache_dir / sample_name).with_suffix(".nii.gz") + if not out_path.exists(): + tmpdir = Path(mkdtemp()) + openneuro.download( + dataset=sample.dataset, + tag=sample.tag, + target_dir=tmpdir, + include=[sample.path], + ) + for ext in (".nii.gz", ".json"): + shutil.copyfile( + (tmpdir / sample.path).with_suffix(ext) + (cache_dir / sample_name).with_suffix(ext) + ) + return out_path diff --git a/scripts/import_dicom.py b/scripts/import_dicom.py index d3166b3..e93df6e 100644 --- a/scripts/import_dicom.py +++ b/scripts/import_dicom.py @@ -77,10 +77,10 @@ def generate_python_code(dpath: Path, image_type: str): collated_data=json.dumps(collated_data)) -FILE_TEMPLATE = """from medimages4tests.dicom.base import generate_dicom, default_dicom_dir +FILE_TEMPLATE = """from medimages4tests.dummy.dicom.base import generate_dicom, default_dicom_dir -def sample_image(out_dir=default_dicom_dir(__file__)): +def get_image(out_dir=default_dicom_dir(__file__)): return generate_dicom(out_dir, num_vols, constant_hdr, collated_data, varying_hdr) diff --git a/setup.cfg b/setup.cfg index 7b47453..ae8f728 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,4 +16,25 @@ VCS = git style = pep440 versionfile_source = medimages4tests/_version.py versionfile_build = medimages4tests/_version.py -tag_prefix = \ No newline at end of file +tag_prefix = + + +[flake8] +doctests = True +exclude = + **/__init__.py + *build/ + docs/sphinxext/ + docs/tools/ + docs/conf.py + arcana/_version.py + versioneer.py + docs/source/conf.py +max-line-length = 88 +select = C,E,F,W,B,B950 +extend-ignore = E203,E501 +per-file-ignores = + setup.py:F401 + +[codespell] +ignore-words = .codespell-ignorewords \ No newline at end of file diff --git a/setup.py b/setup.py index bccec77..82262b8 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,9 @@ long_description=open('README.rst').read(), install_requires=[ 'pydicom>=1.0.2', - "nibabel>=4.0.1"], + "nibabel>=4.0.1", + "openeuro-py>=2022.2.0", + "attrs>=21.4.0"], extras_require={ 'test': [ 'pytest>=5.4.3', diff --git a/tests/test_dicom.py b/tests/test_dicom.py index 9c526ea..9464978 100644 --- a/tests/test_dicom.py +++ b/tests/test_dicom.py @@ -8,7 +8,7 @@ def test_dicom_creation(dicom_module): out_dir = Path(tempfile.mkdtemp()) # Generate DICOM files - dicom_module.sample_image(out_dir) + dicom_module.get_image(out_dir) assert len(list(out_dir.glob('*.dcm'))) == dicom_module.num_vols diff --git a/tests/test_nifti.py b/tests/test_nifti.py index 4560b57..e2e5308 100644 --- a/tests/test_nifti.py +++ b/tests/test_nifti.py @@ -2,12 +2,12 @@ import shutil import nibabel as nb import numpy as np -from medimages4tests.nifti import sample_image +from medimages4tests.dummy.nifti import get_image def test_nifti(work_dir): - nifti_fpath = sample_image(work_dir / "sample.nii") + nifti_fpath = get_image(work_dir / "sample.nii") nifti = nb.load(nifti_fpath) @@ -16,7 +16,7 @@ def test_nifti(work_dir): def test_nifti_compressed(work_dir): - gz_fpath = sample_image(work_dir / "sample.nii.gz", compressed=True) + gz_fpath = get_image(work_dir / "sample.nii.gz", compressed=True) uncompressed_fpath = work_dir / "nifti.nii" with gzip.open(gz_fpath, 'rb') as f_in: From d772fe0b088e00fb7effeab24377a2f1e8d6a538 Mon Sep 17 00:00:00 2001 From: Tom Close Date: Tue, 25 Oct 2022 12:24:26 +1100 Subject: [PATCH 2/3] added openneuro download of t1w --- README.rst | 21 +++++++++++++++++-- medimages4tests/dummy/nifti.py | 6 +++++- medimages4tests/mri/neuro/base.py | 10 --------- medimages4tests/mri/neuro/t1w.py | 27 +++--------------------- medimages4tests/utils.py | 34 +++++++++++++++++++++++++++++++ tests/test_openneuro.py | 11 ++++++++++ 6 files changed, 72 insertions(+), 37 deletions(-) create mode 100644 medimages4tests/utils.py create mode 100644 tests/test_openneuro.py diff --git a/README.rst b/README.rst index 19e7bd9..06d2006 100644 --- a/README.rst +++ b/README.rst @@ -30,12 +30,29 @@ or include in your package's ``test_requires``. Usage ----- +Create a pytest fixture consisting of a dummy image with field-map metadata in DICOM format + .. code-block:: python # Import medimages4tests generator functions - from medimages4tests.dicom.mri.fmap.ge.discovery_mr888.dv26_0_r05_2008a import sample_image + from medimages4tests.dummy.dicom.mri.fmap.ge.discovery_mr888.dv26_0_r05_2008a import get_image # Return generated images in pytest fixtures (or alternative test framework) @pytest.fixture() def ge_dicom_fmap(): - return sample_image() + return get_image() + +Create a dummy NIfTI image + +.. code-block:: python + + import numpy + # Import `get_image` function + from medimages4tests.dummy.nifti import get_image + + # Create dummy nifti image of 10x10x10 containing all ones + @pytest.fixture() + def ones_nifti(): + return get_image( + data=numpy.ones((10, 10, 10)) + ) diff --git a/medimages4tests/dummy/nifti.py b/medimages4tests/dummy/nifti.py index 4b73abe..c7fca75 100644 --- a/medimages4tests/dummy/nifti.py +++ b/medimages4tests/dummy/nifti.py @@ -1,3 +1,4 @@ +import tempfile from pathlib import Path import gzip import shutil @@ -6,13 +7,16 @@ def get_image( - out_file: Path, + out_file: Path = None, data: np.ndarray = None, vox_sizes=(1.0, 1.0, 1.0), qform=(1, 2, 3, 1), compressed=False, ) -> Path: """Create a random Nifti file to satisfy BIDS parsers""" + if out_file is None: + out_file = Path(tempfile.mkdtemp()) / "sample.nii" + if data is None: data = np.random.randint(0, 1, size=[10, 10, 10]) diff --git a/medimages4tests/mri/neuro/base.py b/medimages4tests/mri/neuro/base.py index fa31586..e69de29 100644 --- a/medimages4tests/mri/neuro/base.py +++ b/medimages4tests/mri/neuro/base.py @@ -1,10 +0,0 @@ -from pathlib import Path -import attrs - - -@attrs.define -class OpenneuroSpec: - - dataset: str - tag: str - path: Path diff --git a/medimages4tests/mri/neuro/t1w.py b/medimages4tests/mri/neuro/t1w.py index 7426553..6a9bad2 100644 --- a/medimages4tests/mri/neuro/t1w.py +++ b/medimages4tests/mri/neuro/t1w.py @@ -1,9 +1,5 @@ -from tempfile import mkdtemp -import shutil -from pathlib import Path -import openneuro from medimages4tests import base_cache_dir -from ..neuro import OpenneuroSpec +from medimages4tests.utils import retrieve_from_openneuro, OpenneuroSpec cache_dir = base_cache_dir / "mri" / "neuro" / "t1w" @@ -18,22 +14,5 @@ } -def get_image(sample_name): - sample = SAMPLES[sample_name] - if cache_dir.exists(): - cache_dir.mkdir(parents=True) - out_path = (cache_dir / sample_name).with_suffix(".nii.gz") - if not out_path.exists(): - tmpdir = Path(mkdtemp()) - openneuro.download( - dataset=sample.dataset, - tag=sample.tag, - target_dir=tmpdir, - include=[sample.path], - ) - for ext in (".nii.gz", ".json"): - shutil.copyfile( - (tmpdir / sample.path).with_suffix(ext) - (cache_dir / sample_name).with_suffix(ext) - ) - return out_path +def get_image(sample="ds004130-ON01016"): + return retrieve_from_openneuro(SAMPLES[sample], cache_dir / sample) diff --git a/medimages4tests/utils.py b/medimages4tests/utils.py new file mode 100644 index 0000000..6c9107e --- /dev/null +++ b/medimages4tests/utils.py @@ -0,0 +1,34 @@ +from tempfile import mkdtemp +import shutil +from pathlib import Path +import openneuro +import attrs + + +@attrs.define +class OpenneuroSpec: + + dataset: str + tag: str + path: Path + + +def retrieve_from_openneuro( + sample, cache_path, suffixes=(".nii.gz", ".json"), force_download=False +): + if not cache_path.parent.exists(): + cache_path.parent.mkdir(parents=True) + out_path = cache_path.with_suffix(suffixes[0]) + if not out_path.exists() or force_download: + tmpdir = Path(mkdtemp()) + openneuro.download( + dataset=sample.dataset, + tag=sample.tag, + target_dir=tmpdir, + include=[sample.path], + ) + for ext in suffixes: + shutil.copyfile( + (tmpdir / sample.path).with_suffix(ext), cache_path.with_suffix(ext) + ) + return out_path diff --git a/tests/test_openneuro.py b/tests/test_openneuro.py new file mode 100644 index 0000000..edb9397 --- /dev/null +++ b/tests/test_openneuro.py @@ -0,0 +1,11 @@ +import nibabel as nb +from medimages4tests.mri.neuro.t1w import get_image + + +def test_openneuro_retrieve(): + + nifti_fpath = get_image() + + nifti = nb.load(nifti_fpath) + + assert nifti.shape == (204, 256, 256) From 2fa2d18f861e2723216a66dee2353ee7df3b14bc Mon Sep 17 00:00:00 2001 From: Tom Close Date: Wed, 26 Oct 2022 10:26:22 +1100 Subject: [PATCH 3/3] fixed openneuro dependency --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 82262b8..a6cfddc 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ install_requires=[ 'pydicom>=1.0.2', "nibabel>=4.0.1", - "openeuro-py>=2022.2.0", + "openneuro-py>=2022.2.0", "attrs>=21.4.0"], extras_require={ 'test': [