diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index 909f2d48..96463f6e 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -30,13 +30,6 @@ jobs: CIBW_SKIP: "pp* cp36-* cp37-* *musllinux* cp38-macosx_arm64" CIBW_TEST_REQUIRES: pytest CIBW_TEST_COMMAND: pytest --pyargs nlsam --verbose - CIBW_TEST_COMMAND_LINUX: > - pytest --pyargs nlsam --verbose && - cd {package}/example && - chmod +x {package}/nlsam/tests/test_scripts1.sh && - bash {package}/nlsam/tests/test_scripts1.sh && - chmod +x {package}/nlsam/tests/test_scripts2.sh && - bash {package}/nlsam/tests/test_scripts2.sh - uses: actions/upload-artifact@v4 with: @@ -85,9 +78,9 @@ jobs: path=$(python -c "import distributed; print(distributed.__path__[0]);") echo "version=v$(python -c "from importlib.metadata import version; print(version('nlsam'));")" >> $GITHUB_ENV if [ "$RUNNER_OS" == "Windows" ]; then - pyinstaller ./scripts/nlsam_denoising -F --clean --add-data "${path};.\\distributed\\" --noupx + pyinstaller ./nlsam/scripts.py -n nlsam_denoising -F --clean --add-data "${path};.\\distributed\\" --noupx else - pyinstaller ./scripts/nlsam_denoising -F --clean --add-data ${path}:./distributed/ --noupx + pyinstaller ./nlsam/scripts.py -n nlsam_denoising -F --clean --add-data ${path}:./distributed/ --noupx fi - uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 2c919592..06c91ed6 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,7 @@ target/ nlsam.zip *.code-workspace +log +*.nii +*.nii.gz +wheelhouse/* diff --git a/scripts/nlsam_denoising b/nlsam/scripts.py old mode 100755 new mode 100644 similarity index 99% rename from scripts/nlsam_denoising rename to nlsam/scripts.py index bc1fc5b1..55634fe4 --- a/scripts/nlsam_denoising +++ b/nlsam/scripts.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - import os import argparse import logging diff --git a/nlsam/tests/test_script.py b/nlsam/tests/test_script.py new file mode 100644 index 00000000..0bc257d1 --- /dev/null +++ b/nlsam/tests/test_script.py @@ -0,0 +1,42 @@ +import subprocess +import pytest + +from pathlib import Path + +cwd = Path(__file__).parents[2] / Path("example") +commands_crop = [ + '''python -c "import nibabel as nib; import numpy as np; d = nib.load('dwi.nii.gz').get_fdata(); nib.save(nib.Nifti1Image(d[40:50, 80:85],np.eye(4)), 'dwi_crop.nii.gz')"''', + '''python -c "import nibabel as nib; import numpy as np; d = nib.load('mask.nii.gz').get_fdata(); nib.save(nib.Nifti1Image(d[40:50, 80:85],np.eye(4)), 'mask_crop.nii.gz')"''', + '''python -c "import nibabel as nib; import numpy as np; d = nib.load('mask.nii.gz').get_fdata(); nib.save(nib.Nifti1Image(np.random.rayleigh(10, d[40:50, 80:85].shape),np.eye(4)), 'noise.nii.gz')"''', +] + +commands_nlsam = [ + # Test on example dataset + 'nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -f -N 1 --noise_est local_std --sh_order 0 --log log --cores 1 -m mask_crop.nii.gz', + 'nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f -N 1 --noise_est local_std --sh_order 6 --iterations 5 --verbose --save_sigma sigma.nii.gz', + 'nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --noise_est auto --sh_order 6 --iterations 5 --verbose --save_sigma sigma.nii.gz --save_N N.nii.gz', + 'nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 --b0_threshold 10 --noise_mask pmask.nii.gz', + # Test on niftis + "gunzip dwi_crop.nii.gz mask_crop.nii.gz sigma.nii.gz" + "nlsam_denoising dwi_crop.nii dwi_nlsam.nii bvals bvecs -m mask_crop.nii -f --verbose --sh_order 0 -N 1 --no_stabilization --load_sigma sigma.nii --is_symmetric --use_threading --save_difference diff.nii", + "nlsam_denoising dwi_crop.nii dwi_nlsam.nii bvals bvecs -m mask_crop.nii -f --verbose --sh_order 0 --no_denoising --save_sigma sigma.nii --save_stab stab.nii --load_mhat dwi_crop.nii --save_eta eta.nii", + # Test on example dataset + "nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose -N 1 --noise_est local_std --sh_order 0 --block_size 2,2,2 --save_sigma sigma.nii.gz --cores 1", + "nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 -N 1 --load_sigma sigma.nii.gz --no_subsample --fix_implausible --no_clip_eta", + "nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 --noise_map noise.nii.gz --noise_mask pmask.nii.gz --use_f32", + "nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 -N 1 --load_sigma sigma.nii.gz --no_stabilization --no_denoising", + "nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 -N 1 --load_sigma sigma.nii.gz --no_denoising --no_clip_eta", + "nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 -N 1 --noise_est local_std --no_denoising", + "nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 --noise_est auto --no_denoising --cores 4", + "nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 --no_denoising", +] + + +@pytest.mark.parametrize("command", commands_crop) +def test_crop(command): + subprocess.run([command], shell=True, cwd=cwd, check=True) + + +@pytest.mark.parametrize("command", commands_nlsam) +def test_script_nlsam(command): + subprocess.run([command], shell=True, cwd=cwd, check=True) diff --git a/nlsam/tests/test_scripts1.sh b/nlsam/tests/test_scripts1.sh deleted file mode 100755 index a7039bb4..00000000 --- a/nlsam/tests/test_scripts1.sh +++ /dev/null @@ -1,36 +0,0 @@ -check_return_code () -{ - if [ $1 -ne 0 ]; then - echo 'Something failed in python apparently' - exit 1 - fi - - echo 'stuff finished' -} - -# Crop example dataset -python -c 'import nibabel as nib; import numpy as np; d = nib.load("dwi.nii.gz").get_fdata(); nib.save(nib.Nifti1Image(d[40:50, 80:85],np.eye(4)), "dwi_crop.nii.gz")' -python -c 'import nibabel as nib; import numpy as np; d = nib.load("mask.nii.gz").get_fdata(); nib.save(nib.Nifti1Image(d[40:50, 80:85],np.eye(4)), "mask_crop.nii.gz")' -python -c 'import nibabel as nib; import numpy as np; d = nib.load("mask.nii.gz").get_fdata(); nib.save(nib.Nifti1Image(np.random.rayleigh(10, d[40:50, 80:85].shape),np.eye(4)), "noise.nii.gz")' - -# Test on example dataset -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -f -N 1 --noise_est local_std --sh_order 0 --log log --cores 1 -m mask_crop.nii.gz -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f -N 1 --noise_est local_std --sh_order 6 --iterations 5 --verbose --save_sigma sigma.nii.gz -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --noise_est auto --sh_order 6 --iterations 5 --verbose --save_sigma sigma.nii.gz --save_N N.nii.gz -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 --b0_threshold 10 --noise_mask pmask.nii.gz -check_return_code $? - -# Test on niftis -gunzip dwi_crop.nii.gz mask_crop.nii.gz sigma.nii.gz - -nlsam_denoising dwi_crop.nii dwi_nlsam.nii bvals bvecs -m mask_crop.nii -f --verbose --sh_order 0 -N 1 --no_stabilization --load_sigma sigma.nii --is_symmetric --use_threading --save_difference diff.nii -check_return_code $? - -nlsam_denoising dwi_crop.nii dwi_nlsam.nii bvals bvecs -m mask_crop.nii -f --verbose --sh_order 0 --no_denoising --save_sigma sigma.nii --save_stab stab.nii --load_mhat dwi_crop.nii --save_eta eta.nii -check_return_code $? diff --git a/nlsam/tests/test_scripts2.sh b/nlsam/tests/test_scripts2.sh deleted file mode 100755 index 2b7f589d..00000000 --- a/nlsam/tests/test_scripts2.sh +++ /dev/null @@ -1,39 +0,0 @@ -check_return_code () -{ - if [ $1 -ne 0 ]; then - echo 'Something failed in python apparently' - exit 1 - fi - - echo 'stuff finished' -} - -# Crop example dataset -python -c 'import nibabel as nib; import numpy as np; d = nib.load("dwi.nii.gz").get_fdata(); nib.save(nib.Nifti1Image(d[40:50, 80:85],np.eye(4)), "dwi_crop.nii.gz")' -python -c 'import nibabel as nib; import numpy as np; d = nib.load("mask.nii.gz").get_fdata(); nib.save(nib.Nifti1Image(d[40:50, 80:85],np.eye(4)), "mask_crop.nii.gz")' -python -c 'import nibabel as nib; import numpy as np; d = nib.load("mask.nii.gz").get_fdata(); nib.save(nib.Nifti1Image(np.random.rayleigh(10, d[40:50, 80:85].shape),np.eye(4)), "noise.nii.gz")' - -# Test on example dataset -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose -N 1 --noise_est local_std --sh_order 0 --block_size 2,2,2 --save_sigma sigma.nii.gz --cores 1 -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 -N 1 --load_sigma sigma.nii.gz --no_subsample --fix_implausible --no_clip_eta -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 --noise_map noise.nii.gz --noise_mask pmask.nii.gz --use_f32 -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 -N 1 --load_sigma sigma.nii.gz --no_stabilization --no_denoising -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 -N 1 --load_sigma sigma.nii.gz --no_denoising --no_clip_eta -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 -N 1 --noise_est local_std --no_denoising -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 --noise_est auto --no_denoising --cores 4 -check_return_code $? - -nlsam_denoising dwi_crop.nii.gz dwi_nlsam.nii.gz bvals bvecs -m mask_crop.nii.gz -f --verbose --sh_order 0 --no_denoising -check_return_code $? diff --git a/pyproject.toml b/pyproject.toml index 27085c20..18d75584 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,11 +2,10 @@ requires = ["Cython>=0.29.33", "scipy>=1.5", "oldest-supported-numpy", - "setuptools", + "setuptools>=60", "wheel"] build-backend = "setuptools.build_meta" - [project] name = "nlsam" version = '0.7.1' @@ -32,3 +31,7 @@ dependencies = [ homepage = "https://github.com/samuelstjean/nlsam" documentation = "https://nlsam.readthedocs.io/en/latest/" changelog = "https://github.com/samuelstjean/nlsam/blob/master/CHANGELOG.md" + + +[project.scripts] +nlsam_denoising = "nlsam.scripts:main" diff --git a/setup.py b/setup.py index 59160b6f..b1d2d725 100644 --- a/setup.py +++ b/setup.py @@ -5,10 +5,8 @@ ext_modules = cythonize("nlsam/*.pyx") include_dirs = [numpy.get_include()] -scripts = ['scripts/nlsam_denoising'] setup( - scripts=scripts, - packages=find_packages(), include_dirs=include_dirs, + packages=find_packages(), ext_modules=ext_modules)