Skip to content

Commit 1af0de5

Browse files
authored
Merge pull request #11 from AMYPAD/imtrimup
2 parents 8f05c16 + d858444 commit 1af0de5

File tree

8 files changed

+192
-3
lines changed

8 files changed

+192
-3
lines changed

.github/workflows/test.yml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,28 @@ jobs:
4747
- run: pip install -U -e .[dev]
4848
- run: pytest
4949
- uses: codecov/codecov-action@v1
50+
cuda:
51+
if: github.event_name != 'pull_request' || github.repository_owner != 'AMYPAD'
52+
name: CUDA py${{ matrix.python }}
53+
runs-on: [self-hosted, python, matlab, cuda]
54+
strategy:
55+
matrix:
56+
python: [3.9]
57+
steps:
58+
- uses: actions/checkout@v2
59+
with:
60+
fetch-depth: 0
61+
- name: Run setup-python
62+
run: setup-python -p${{ matrix.python }} miutil scikit-build cmake ninja
63+
- name: Install
64+
run: pip install -U --no-binary nimpa,nipet -e .[dev,niftypet]
65+
- run: pytest
66+
- uses: codecov/codecov-action@v1
67+
- name: Post Run setup-python
68+
run: setup-python -p${{ matrix.python }} -Dr
69+
if: ${{ always() }}
5070
deploy:
51-
needs: [check, test]
71+
needs: [check, test, cuda]
5272
name: PyPI Deploy
5373
runs-on: ubuntu-latest
5474
steps:

amypad/gui.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ def main(args=None, gui_mode=True):
249249
import niftypad.api
250250
import niftypad.models
251251

252-
from amypad import centiloid, imscroll
252+
from amypad import centiloid, imscroll, imtrimup
253253

254254
parser = fix_subparser(MyParser(prog=None if gui_mode else "amypad"), gui_mode=gui_mode)
255255
sub_kwargs = {}
@@ -273,6 +273,9 @@ def argparser(prog, description=None, epilog=None, formatter_class=None):
273273
Func(imscroll.run, imscroll.__doc__, version=niftypad.__version__,
274274
python_deps=["miutil[nii,plot]", "tqdm"], argparser=argparser)
275275

276+
Func(imtrimup.run, imtrimup.__doc__, version=niftypad.__version__, python_deps=["nimpa"],
277+
argparser=argparser)
278+
276279
Func(centiloid.run, centiloid.__doc__, version=niftypad.__version__,
277280
python_deps=["miutil[nii]", "setuptools", "spm12", "tqdm"], argparser=argparser)
278281

amypad/imtrimup.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Trimming and upscaling
2+
3+
Usage:
4+
imtrimup [options] <dyndir>
5+
6+
Arguments:
7+
<dyndir> : Input folder containing dynamic scans [default: DirChooser]
8+
9+
Options:
10+
--glob PATTERN : File matching pattern [default: *.nii*]
11+
--scale FACTOR : Sampling scale factor [default: 2:int]
12+
--refim PATH : Reference image [default: FileChooser]
13+
--fcomment COMMENT : Prefix to add to outputs
14+
--no-memlim : Whether to use more memory (faster)
15+
--store-img-intrmd : Whether to write output image files
16+
--store-img : Whether to write output image sum file
17+
"""
18+
import logging
19+
from pathlib import Path
20+
21+
from niftypet import nimpa
22+
23+
log = logging.getLogger(__name__)
24+
25+
26+
def run(
27+
dyndir,
28+
glob="*.nii*",
29+
scale=2,
30+
refim="",
31+
fcomment="",
32+
no_memlim=False,
33+
store_img_intrmd=False,
34+
store_img=False,
35+
):
36+
dyndir = Path(dyndir)
37+
assert dyndir.is_dir()
38+
# get the file data
39+
fdyns = list(map(str, dyndir.glob(glob)))
40+
log.info("found %d files", len(fdyns))
41+
log.info("sort the dynamic images and output the sorted file names and numpy array")
42+
imdyn = nimpa.niisort(fdyns)
43+
log.info("trim & upscale")
44+
res = nimpa.imtrimup(imdyn["files"], scale=scale, memlim=not no_memlim, refim=refim or "",
45+
fcomment=fcomment or "", store_img_intrmd=store_img_intrmd,
46+
store_img=store_img)
47+
log.debug("trimmed and scaled images, shape %r", res['im'].shape)
48+
return res

amypad/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
try:
2+
from os import cpu_count
3+
except ImportError:
4+
try:
5+
from multiprocessing import cpu_count
6+
except ImportError:
7+
8+
def cpu_count():
9+
return 4

scripts/__init__.py

Whitespace-only changes.

setup.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ classifiers=
3838
Topic :: Utilities
3939
[options]
4040
zip_safe=False
41-
setup_requires=setuptools>=42; setuptools_scm[toml]>=3.4
41+
setup_requires=setuptools>=42; wheel; setuptools_scm[toml]>=3.4
4242
install_requires=
4343
argopt
4444
miutil[cuda,nii]>=0.8.0
4545
niftypad>=1.1.1
46+
nimpa
4647
setuptools
4748
shtab>1.3.2
4849
spm12
@@ -93,3 +94,5 @@ timeout=10
9394
log_level=INFO
9495
python_files=tests/test_*.py
9596
addopts=-v --tb=short -rxs -W=error -n=auto --durations=0 --durations-min=1 --cov=amypad --cov-report=term-missing --cov-report=xml
97+
filterwarnings=
98+
ignore:numpy.ufunc size changed.*:RuntimeWarning

tests/conftest.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
import logging
2+
from os import getenv
3+
from pathlib import Path
4+
15
import pytest
26

7+
HOME = Path(getenv("DATA_ROOT", "~")).expanduser()
8+
39

410
@pytest.fixture(scope="session")
511
def nvml():
@@ -10,3 +16,42 @@ def nvml():
1016
except pynvml.NVMLError as exc:
1117
pytest.skip(str(exc))
1218
return pynvml
19+
20+
21+
@pytest.fixture(scope="session")
22+
def mMRpars():
23+
nipet = pytest.importorskip("niftypet.nipet")
24+
params = nipet.get_mmrparams()
25+
# params["Cnt"]["VERBOSE"] = True
26+
params["Cnt"]["LOG"] = logging.INFO
27+
return params
28+
29+
30+
@pytest.fixture(scope="session")
31+
def datain(mMRpars):
32+
nipet = pytest.importorskip("niftypet.nipet")
33+
folder_in = HOME / "Ab_PET_mMR_test"
34+
if not folder_in.is_dir():
35+
pytest.skip(f"""Cannot find Ab_PET_mMR_test in
36+
${{DATA_ROOT:-~}} ({HOME}).
37+
""")
38+
return nipet.classify_input(folder_in, mMRpars)
39+
40+
41+
@pytest.fixture(scope="session")
42+
def dimin():
43+
trt = HOME / "DPUK" / "TRT"
44+
if not trt.is_dir():
45+
pytest.skip(f"""Cannot find DPUK/TRT in
46+
${{DATA_ROOT:-~}} ({HOME}).
47+
""")
48+
return trt
49+
50+
51+
@pytest.fixture(scope="session")
52+
def fimin(dimin):
53+
mprage = (dimin / "NEW002_ODE_S02442" / "TP0" /
54+
"NEW002_PETMR_V1_00015_MR_images_MPRAGE_q-_MPRAGE_20200212145346_15.nii")
55+
if not mprage.is_file():
56+
pytest.skip("fimin not found")
57+
return mprage

tests/test_imtrimup.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
5+
6+
@pytest.fixture
7+
def dyndir(datain, mMRpars):
8+
# definition of dynamic frames for kinetic analysis
9+
frmdef = ["def", [4, 15], [8, 30], [9, 60], [2, 180], [8, 300]]
10+
# output path
11+
opth = str(Path(datain["corepath"]).parent / "amypad" / "dyndir")
12+
13+
res = Path(opth) / "PET" / "multiple-frames"
14+
if res.is_dir():
15+
return res
16+
17+
nipet = pytest.importorskip("niftypet.nipet")
18+
hst = nipet.mmrhist(datain, mMRpars)
19+
# offset for the time from which meaningful events are detected
20+
toff = nipet.lm.get_time_offset(hst)
21+
# dynamic frame timings
22+
frm_timings = nipet.lm.dynamic_timings(frmdef, offset=toff)
23+
nipet.lm.draw_frames(hst, frm_timings["timings"])
24+
# hardware mu-map
25+
muhdct = nipet.hdw_mumap(datain, [1, 2, 4], mMRpars, outpath=opth, use_stored=True)
26+
27+
# object mu-map with alignment
28+
mupdct = nipet.align_mumap(
29+
datain,
30+
mMRpars,
31+
outpath=opth,
32+
store=True,
33+
hst=hst,
34+
itr=2,
35+
petopt="ac",
36+
fcomment="_mu",
37+
musrc="pct",
38+
)
39+
# object mu-map without alignment--straight from DICOM resampled to PET
40+
# muodct = nipet.obj_mumap(datain, mMRpars, outpath=opth, store=True)
41+
42+
nipet.mmrchain(
43+
datain,
44+
mMRpars,
45+
frames=frm_timings["timings"],
46+
mu_h=muhdct,
47+
mu_o=mupdct, # muodct,
48+
itr=5,
49+
fwhm=0.0,
50+
outpath=opth,
51+
fcomment="_dyn",
52+
store_img=True,
53+
store_img_intrmd=True,
54+
)
55+
return Path(opth) / "PET" / "multiple-frames"
56+
57+
58+
@pytest.mark.timeout(2 * 60 * 60) # 2h
59+
def test_rectrim(dyndir):
60+
rectrim = pytest.importorskip("amypad.rectrim")
61+
rectrim.run(dyndir)

0 commit comments

Comments
 (0)