diff --git a/.circleci/config.yml b/.circleci/config.yml
index 2b69d2dd..179f0312 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,7 +2,7 @@
# docker pull circleci/picard
# docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd):$(pwd) -v ~/.circleci/:/root/.circleci --workdir $(pwd) circleci/picard circleci build
-version: 2
+version: 2.1
jobs:
test-notebooks:
working_directory: /pyradiomics
@@ -32,10 +32,72 @@ jobs:
command: |
jupyter nbconvert --ExecutePreprocessor.kernel_name=python3 --ExecutePreprocessor.timeout=-1 --to notebook --output-dir /tmp --execute notebooks/helloRadiomics.ipynb notebooks/helloFeatureClass.ipynb notebooks/PyRadiomicsExample.ipynb
- build-3.5: &build_template
+ build-mac-37: &build_mac_template
+ working_directory: ~/pyradiomics
+ macos:
+ xcode: 12.5.1
+ environment:
+ PYTHON_VERSION: 3.7.10
+ PYTHON_SHORT_VERSION: 3.7
+ steps:
+ - run:
+ name: Setup MAC OS environment
+ # Workaround the following error occurring because python installation is cached but gettext dependency is not
+ # dyld: Library not loaded: /usr/local/opt/gettext/lib/libintl.8.dylib
+ # Referenced from: /Users/travis/.pyenv/versions/3.7.2/bin/python
+ # Reason: Incompatible library version: python requires version 11.0.0 or later, but libintl.8.dylib provides version 10.0.0
+ # See https://github.com/scikit-build/cmake-python-distributions/issues/112 and
+ # https://github.com/scikit-build/cmake-python-distributions/pull/113
+ command: |
+ echo "HOMEBREW_NO_AUTO_UPDATE=1" >> $BASH_ENV
+ brew install gettext
+ brew install pyenv
+ echo 'export PATH=$HOME/.pyenv/versions/$PYTHON_VERSION/bin:$HOME/bin:$PATH' >> $BASH_ENV
+ mkdir -p $HOME/bin
+ ln -s $(which pip3) $HOME/bin/pip
+ ln -s $(which python3) $HOME/bin/python
+ pyenv install --list
+ pyenv install $PYTHON_VERSION
+ - run:
+ name: Setup SciKit-CI
+ command: |
+ pip install scikit-ci scikit-ci-addons
+ ci_addons --install ../addons
+ - run:
+ name: Setup PyEnv
+ command: python ../addons/travis/install_pyenv.py
+ - checkout
+ - attach_workspace:
+ at: ~/pyradiomics
+ - run:
+ name: Install
+ command: ci install
+ - run:
+ name: Test
+ command: ci test
+ - run:
+ name: Build Distribution
+ command: ci after_test
+ - persist_to_workspace:
+ root: .
+ paths: [dist]
+
+ build-mac-38:
+ <<: *build_mac_template
+ environment:
+ PYTHON_VERSION: 3.8.10
+ PYTHON_SHORT_VERSION: 3.7
+
+ build-mac-39:
+ <<: *build_mac_template
+ environment:
+ PYTHON_VERSION: 3.9.5
+ PYTHON_SHORT_VERSION: 3.9
+
+ build-37: &build_template
working_directory: /pyradiomics
docker:
- - image: circleci/python:3.5-jessie
+ - image: cimg/python:3.7
user: root
steps:
- checkout
@@ -57,34 +119,79 @@ jobs:
command: ci after_test
- persist_to_workspace:
root: .
- paths: dist
+ paths: [dist]
- build-3.6:
+ build-38:
<<: *build_template
docker:
- - image: circleci/python:3.6-jessie
+ - image: cimg/python:3.8
user: root
- build-3.7:
+ build-39:
<<: *build_template
docker:
- - image: circleci/python:3.7
- user: root
+ - image: cimg/python:3.9
+ user: root
+
+ test_deploy:
+ working_directory: /pyradiomics
+ docker:
+ - image: cimg/python:3.8
+ user: root
+ steps:
+ - run:
+ name: Check Repo User
+ command: if [[ $CIRCLE_PROJECT_USERNAME != "AIM-Harvard" ]]; then circleci step halt; fi
+ - checkout
+ - run:
+ name: Setup SciKit-CI
+ command: |
+ pip install scikit-ci scikit-ci-addons
+ ci_addons --install ../addons
+ - run:
+ name: Install
+ command: ci install
+ - run:
+ name: Install patchelf auditwheel, twine
+ command: |
+ apt update
+ apt-get install patchelf # needed to run auditwheel
+ python -m pip install "auditwheel<3.2.0"
+ python -m pip install twine
+ # only attach the workspace at this point to prevent the removal of source distributions
+ - attach_workspace:
+ at: /pyradiomics
+ - run:
+ name: Create sdist
+ command: python setup.py sdist
+ - run:
+ name: Fix Distribution Wheels
+ command: |
+ ls ./dist/*-linux_$(uname -m).whl # This will prevent further deployment if no wheels are found
+ # Since there are no external shared libraries to bundle into the wheels
+ # this step will fixup the wheel switching from 'linux' to 'manylinux1' tag
+ for whl in $(ls ./dist/*-linux_$(uname -m).whl); do
+ python -m auditwheel repair $whl -w ./dist/
+ rm $whl
+ done
+ - run:
+ name: Deploy source and linux wheels
+ command: python -m twine upload ./dist/*.whl ./dist/*.tar.gz -u $PYPI_TEST_USER -p $PYPI_TEST_PASSWORD -r testpypi
deploy:
working_directory: /pyradiomics
docker:
- - image: circleci/python:3.6-jessie
+ - image: cimg/python:3.6
user: root
steps:
- run:
name: Check Repo User
- command: if [[ $CIRCLE_PROJECT_USERNAME != "Radiomics" ]]; then circleci step halt; fi
+ command: if [[ $CIRCLE_PROJECT_USERNAME != "AIM-Harvard" ]]; then circleci step halt; fi
- checkout
- run:
name: Setup SciKit-CI
command: |
- pip install scikit-ci==0.13.0 scikit-ci-addons==0.11.0
+ pip install scikit-ci scikit-ci-addons
ci_addons --install ../addons
- run:
name: Install
@@ -92,8 +199,9 @@ jobs:
- run:
name: Install patchelf auditwheel, twine
command: |
+ apt update
apt-get install patchelf # needed to run auditwheel
- python -m pip install auditwheel
+ python -m pip install "auditwheel<3.2.0"
python -m pip install twine
# only attach the workspace at this point to prevent the removal of source distributions
- attach_workspace:
@@ -108,22 +216,22 @@ jobs:
# Since there are no external shared libraries to bundle into the wheels
# this step will fixup the wheel switching from 'linux' to 'manylinux1' tag
for whl in $(ls ./dist/*-linux_$(uname -m).whl); do
- auditwheel repair $whl -w ./dist/
+ python -m auditwheel repair $whl -w ./dist/
rm $whl
done
- run:
name: Deploy source and linux wheels
- command: twine upload ./dist/*.whl ./dist/*.tar.gz -u $PYPI_USER -p $PYPI_PASSWORD
+ command: python -m twine upload ./dist/*.whl ./dist/*.tar.gz -u $PYPI_USER -p $PYPI_PASSWORD
deploy_conda:
working_directory: /pyradiomics
docker:
- - image: circleci/python:3.6-jessie
+ - image: cimg/python:3.8
user: root
steps:
- run:
name: Check Repo User
- command: if [[ $CIRCLE_PROJECT_USERNAME != "Radiomics" ]]; then circleci step halt; fi
+ command: if [[ $CIRCLE_PROJECT_USERNAME != "AIM-Harvard" ]]; then circleci step halt; fi
- checkout
- run:
name: Install Miniconda
@@ -141,9 +249,10 @@ jobs:
name: Build Conda packages
command: |
mkdir /conda-bld
- conda build ./conda --python=3.5 --croot /conda-bld
conda build ./conda --python=3.6 --croot /conda-bld
conda build ./conda --python=3.7 --croot /conda-bld
+ conda build ./conda --python=3.8 --croot /conda-bld
+ conda build ./conda --python=3.9 --croot /conda-bld
- run:
name: Deploy Conda packages
command: |
@@ -153,31 +262,53 @@ workflows:
version: 2
build_and_deploy:
jobs:
- - build-3.5: &build_job_template
+ - build-mac-37: &build_job_template
filters:
tags:
only:
- - /^v?[0-9]+(\.[0-9]+)*(rc[0-9]+)?/
- - build-3.6:
+ - /^v?[0-9]+(\.[0-9]+)*((a|b|rc)[0-9]+)?/
+ - build-mac-38:
<<: *build_job_template
- - build-3.7:
+ - build-mac-39:
<<: *build_job_template
- - test-notebooks:
- requires:
- - build-3.5
- - build-3.6
- - build-3.7
- - deploy: &deploy_template
+ - build-37:
+ <<: *build_job_template
+ - build-38:
+ <<: *build_job_template
+ - build-39:
+ <<: *build_job_template
+ - test-notebooks: &requires_template
requires:
- - build-3.5
- - build-3.6
- - build-3.7
+ - build-37
+ - build-38
+ - build-39
+ - build-mac-37
+ - build-mac-38
+ - build-mac-39
+ - test_deploy:
+ <<: *requires_template
filters:
branches:
ignore:
- /.*/
tags:
only:
- - /^v?[0-9]+(\.[0-9]+)*(rc[0-9]+)?/
+ - /^v?[0-9]+(\.[0-9]+)*((a|b|rc)[0-9]+)/
+ - deploy:
+ <<: *requires_template
+ filters:
+ branches:
+ ignore:
+ - /.*/
+ tags:
+ only:
+ - /^v?[0-9]+(\.[0-9]+)*/
- deploy_conda:
- <<: *deploy_template
+ <<: *requires_template
+ filters:
+ branches:
+ ignore:
+ - /.*/
+ tags:
+ only:
+ - /^v?[0-9]+(\.[0-9]+)*((a|b|rc)[0-9]+)?/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 3a0c0929..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,88 +0,0 @@
-# Config file for automatic testing at travis-ci.org
-
-language: python
-
-matrix:
- include:
-
- - os: osx
- language: generic
- env:
- - PYTHON_VERSION=3.5.5
- - PYTHON_SHORT_VERSION=3.5
-
- - os: osx
- language: generic
- env:
- - PYTHON_VERSION=3.6.5
- - PYTHON_SHORT_VERSION=3.6
-
- - os: osx
- language: generic
- env:
- - PYTHON_VERSION=3.7.8
- - PYTHON_SHORT_VERSION=3.7
-
-before_cache:
- # Cleanup to avoid the cache to grow indefinitely as new package versions are released
- # see https://stackoverflow.com/questions/39930171/cache-brew-builds-with-travis-ci
- - brew cleanup
-
-cache:
- directories:
- # Cache downloaded bottles
- - $HOME/Library/Caches/Homebrew
- # pyenv
- - $HOME/.pyenv_cache
- - $HOME/.pyenv/versions/3.7.8
- - $HOME/.pyenv/versions/3.6.5
- - $HOME/.pyenv/versions/3.5.5
- # scikit-ci-addons
- - $HOME/downloads
-
-before_install:
- # Workaround the following error occuring because python installation is cached but gettext dependency is not
- # dyld: Library not loaded: /usr/local/opt/gettext/lib/libintl.8.dylib
- # Referenced from: /Users/travis/.pyenv/versions/3.7.2/bin/python
- # Reason: Incompatible library version: python requires version 11.0.0 or later, but libintl.8.dylib provides version 10.0.0
- # See https://github.com/scikit-build/cmake-python-distributions/issues/112 and
- # https://github.com/scikit-build/cmake-python-distributions/pull/113
- - brew update
- - brew install gettext
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir $HOME/bin; ln -s $(which pip2) $HOME/bin/pip; fi
- - pip install scikit-ci scikit-ci-addons
- - ci_addons --install ../addons
-
-install:
- - ci install
-
-script:
- - ci test
-
-after_success:
- - ci after_test
-
-before_deploy:
- - sudo pip install twine # Twine installation requires sudo to get access to /usr/local/man
-
-deploy:
- - provider: script
- skip_cleanup: true
- script: twine upload dist/*.whl -u $PYPI_USER -p $PYPI_PASSWORD
- on:
- tags: true
- condition: $TRAVIS_TAG =~ ^v?[0-9]+(\.[0-9]+)*(rc[0-9]+)?$ && $TRAVIS_REPO_SLUG == Radiomics/pyradiomics
- - provider: script
- script:
- wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh;
- bash miniconda.sh -b -p $HOME/miniconda;
- hash -r;
- export PATH=$HOME/miniconda/bin:$PATH;
- conda config --set always_yes yes;
- conda install gcc libgcc;
- bash ./conda/configure_conda.sh;
- conda build ./conda --python=$PYTHON_SHORT_VERSION --croot $HOME/conda-bld;
- anaconda -t $ANACONDA_TOKEN upload -u Radiomics $HOME/conda-bld/osx-64/pyradiomics-*.tar.bz2 --force
- on:
- tags: true
- condition: $TRAVIS_TAG =~ ^v?[0-9]+(\.[0-9]+)*(rc[0-9]+)?$ && $TRAVIS_REPO_SLUG == Radiomics/pyradiomics
diff --git a/MANIFEST.in b/MANIFEST.in
index fb19cd77..f50e20be 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -6,7 +6,7 @@ include requirements-dev.txt
include requirements-setup.txt
include versioneer.py
-recursive-include radiomics *
+recursive-include src/radiomics *
recursive-include data/baseline *
recursive-include data *_image.nrrd
@@ -24,4 +24,3 @@ recursive-include bin *.py
recursive-exclude * __pycache__
recursive-exclude * *.py[cod]
-recursive-exclude * nosetests.xml
diff --git a/README.md b/README.md
index fdf28f9c..23b56f1d 100644
--- a/README.md
+++ b/README.md
@@ -2,19 +2,16 @@
## Build Status
-| Linux | macOS | Windows |
-|--------------------------------|-------------------------------|-------------------------------|
-| [![][circleci]][circleci-lnk] | [![][travisci]][travisci-lnk] | [![][appveyor]][appveyor-lnk] |
+| Linux / MacOS | Windows |
+| ----------------------------- | ----------------------------- |
+| [![][circleci]][circleci-lnk] | [![][appveyor]][appveyor-lnk] |
-[appveyor]: https://ci.appveyor.com/api/projects/status/tw69xbbeyluk7fl7/branch/master?svg=true
-[appveyor-lnk]: https://ci.appveyor.com/project/Radiomics/pyradiomics/branch/master
+[appveyor]: https://ci.appveyor.com/api/projects/status/kvu7897q0v4imwdc?svg=true
+[appveyor-lnk]: https://ci.appveyor.com/project/AIM-Harvard/pyradiomics-k4sto
-[circleci]: https://circleci.com/gh/Radiomics/pyradiomics.svg?style=svg&circle-token=a4748cf0de5fad2c12bc93a485282378551c3584
-[circleci-lnk]: https://circleci.com/gh/Radiomics/pyradiomics
-
-[travisci]: https://travis-ci.org/Radiomics/pyradiomics.svg?branch=master
-[travisci-lnk]: https://travis-ci.org/Radiomics/pyradiomics
+[circleci]: https://dl.circleci.com/status-badge/img/gh/AIM-Harvard/pyradiomics/tree/master.svg?style=shield
+[circleci-lnk]: https://circleci.com/gh/AIM-Harvard/pyradiomics
## Radiomics feature extraction in Python
This is an open-source python package for the extraction of Radiomics features from medical imaging.
@@ -120,7 +117,7 @@ For more information on using docker, see
PyRadiomics can be easily used in a Python script through the `featureextractor`
module. Furthermore, PyRadiomics provides a commandline script, `pyradiomics`, for both single image extraction and
batchprocessing. Finally, a convenient front-end interface is provided as the 'Radiomics'
-extension for 3D Slicer, available [here](https://github.com/Radiomics/SlicerRadiomics).
+extension for 3D Slicer, available [here](https://github.com/AIM-Harvard/SlicerRadiomics).
### 3rd-party packages used in pyradiomics:
- SimpleITK (Image loading and preprocessing)
@@ -135,7 +132,7 @@ extension for 3D Slicer, available [here](https://github.com/Radiomics/SlicerRad
See also the [requirements file](requirements.txt).
### 3D Slicer
-PyRadiomics is also available as an [extension](https://github.com/Radiomics/SlicerRadiomics) to [3D Slicer](slicer.org).
+PyRadiomics is also available as an [extension](https://github.com/AIM-Harvard/SlicerRadiomics) to [3D Slicer](slicer.org).
Download and install the 3D slicer [nightly build](http://download.slicer.org/), the extension is then available in the
extension manager under "SlicerRadiomics".
diff --git a/appveyor.yml b/appveyor.yml
index 39c7c1ab..cfcda140 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,47 +1,47 @@
-version: "0.0.1.{build}"
+version: "3.0.{build}"
+image: Visual Studio 2019
environment:
+ PYTHON_ARCH: "64"
+ BLOCK: "0"
+
matrix:
# Visual Studio (Python 3, 64 bit)
- - PYTHON_DIR: "C:\\Python35-x64"
- PYTHON_VERSION: "3.5.x"
- PYTHON_SHORT_VERSION: "3.5"
- PYTHON_ARCH: "64"
- BLOCK: "0"
-
- - PYTHON_DIR: "C:\\Python36-x64"
- PYTHON_VERSION: "3.6.x"
- PYTHON_SHORT_VERSION: "3.6"
- PYTHON_ARCH: "64"
- BLOCK: "0"
-
- PYTHON_DIR: "C:\\Python37-x64"
PYTHON_VERSION: "3.7.x"
PYTHON_SHORT_VERSION: "3.7"
- PYTHON_ARCH: "64"
- BLOCK: "0"
+
+ - PYTHON_DIR: "C:\\Python38-x64"
+ PYTHON_VERSION: "3.8.x"
+ PYTHON_SHORT_VERSION: "3.8"
+
+ - PYTHON_DIR: "C:\\Python39-x64"
+ PYTHON_VERSION: "3.9.x"
+ PYTHON_SHORT_VERSION: "3.9"
init:
- - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
- - python -m pip install scikit-ci==0.13.0 scikit-ci-addons==0.11.0
+ - ps: $env:PATH=$env:PYTHON_DIR + ";" + $env:PYTHON_DIR + "\\Scripts;" + $env:PATH
+ # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
+ - ps: python -c "import sys;print(sys.version, sys.executable)"
+ - python -m pip install scikit-ci scikit-ci-addons
- python -m ci_addons --install ../addons
- python -m pip install twine
- ps: ../addons/appveyor/rolling-build.ps1
install:
- - python -m ci install
+ - ci install
build_script:
- - python -m ci build
+ - ci build
test_script:
- - python -m ci test
+ - ci test
after_test:
- - python -m ci after_test
+ - ci after_test
artifacts:
- path: dist/*
@@ -52,13 +52,19 @@ on_finish:
deploy_script:
- echo checking deployment
- - ps: if ($env:APPVEYOR_REPO_NAME -notmatch 'Radiomics/pyradiomics') { appveyor exit }
- - ps: if ($env:APPVEYOR_REPO_TAG_NAME -notmatch '^v?[0-9]+(\.[0-9]+)*(rc\d+)?$') { appveyor exit }
+ - ps: if ($env:APPVEYOR_REPO_NAME -notmatch 'AIM-Harvard/pyradiomics') { appveyor exit }
+ - ps: if ($env:APPVEYOR_REPO_TAG_NAME -notmatch '^v?[0-9]+(\.[0-9]+)*((a|b|rc)[0-9]+)?$') { appveyor exit }
- echo starting PyPi deployment
- - twine upload dist/*.whl -u %PYPI_USER% -p %PYPI_PASSWORD%
+ - ps: if ($env:APPVEYOR_REPO_TAG_NAME -match '^v?[0-9]+(\.[0-9]+)*$') { twine upload dist/*.whl -u $Env:PYPI_USER -p $Env:PYPI_PASSWORD }
+ - ps: if ($env:APPVEYOR_REPO_TAG_NAME -match '^v?[0-9]+(\.[0-9]+)*((a|b|rc)[0-9]+)$') { twine upload dist/*.whl -u $Env:PYPI_TEST_USER -p $Env:PYPI_TEST_PASSWORD -r testpypi }
- echo starting Anaconda deployment
- - SET PATH=C:\Miniconda-x64\Scripts;%PATH%
- - ./conda/configure_conda.bat && conda build ./conda --python=%PYTHON_SHORT_VERSION% --croot C:/conda-bld
+ - CALL C:\Miniconda3-x64\condabin\conda.bat activate
+ - conda config --set always_yes yes --set changeps1 no --set anaconda_upload no
+ - conda config --add channels simpleitk --add channels conda-forge
+ - conda install conda-build
+ - conda install anaconda-client
+ - conda update -q conda
+ - conda build ./conda --python=%PYTHON_SHORT_VERSION% --croot C:/conda-bld
- anaconda -t %ANACONDA_TOKEN% upload -u Radiomics C:/conda-bld/win-64/pyradiomics-*.tar.bz2 --force
- echo finished deployment
diff --git a/conda/meta.yaml b/conda/meta.yaml
index 1534c833..6872cc82 100644
--- a/conda/meta.yaml
+++ b/conda/meta.yaml
@@ -12,6 +12,7 @@ build:
requirements:
build:
+ - {{ compiler('c') }}
- python
- git
host:
diff --git a/labs/pyradiomics-dcm/pyradiomics-dcm.py b/labs/pyradiomics-dcm/pyradiomics-dcm.py
index 8b459e97..e27eaca6 100644
--- a/labs/pyradiomics-dcm/pyradiomics-dcm.py
+++ b/labs/pyradiomics-dcm/pyradiomics-dcm.py
@@ -319,17 +319,18 @@ def saveJSONToFile(self, fileName):
def main():
parser = argparse.ArgumentParser(
- usage="%(prog)s --input-image
--input-seg --output-sr \n\n"
- + "Warning: This is a \"pyradiomics labs\" script, which means it is an experimental feature in development!\n"
- + "The intent of this helper script is to enable pyradiomics feature extraction directly from/to DICOM data.\n"
- + "The segmentation defining the region of interest must be defined as a DICOM Segmentation image.\n"
- + "Support for DICOM Radiotherapy Structure Sets for defining region of interest may be added in the future.\n")
+ usage="""%(prog)s --input-image --input-seg --output-sr \n\n
+
+Warning: This is a \"pyradiomics labs\" script, which means it is an experimental feature in development!
+The intent of this helper script is to enable pyradiomics feature extraction directly from/to DICOM data.
+The segmentation defining the region of interest must be defined as a DICOM Segmentation image.
+Support for DICOM Radiotherapy Structure Sets for defining region of interest may be added in the future.""")
parser.add_argument(
'--input-image-dir',
dest="inputDICOMImageDir",
metavar="",
help="Path to the directory with the input DICOM series."
- + " It is expected that a single series is corresponding to a single scalar volume.",
+ " It is expected that a single series is corresponding to a single scalar volume.",
required=True)
parser.add_argument(
'--input-seg-file',
@@ -363,8 +364,8 @@ def main():
dest="volumeReconstructor",
metavar="",
help="Choose the tool to be used for reconstructing image volume from the DICOM image series."
- + " Allowed options are plastimatch or dcm2niix (should be installed on the system). plastimatch"
- + " will be used by default.",
+ " Allowed options are plastimatch or dcm2niix (should be installed on the system). plastimatch"
+ " will be used by default.",
choices=['plastimatch', 'dcm2niix'],
default="plastimatch")
parser.add_argument(
@@ -377,7 +378,7 @@ def main():
'--correct-mask',
dest="correctMask",
help="Boolean flag argument. If present, PyRadiomics will attempt to resample the mask to the image"
- + " geometry if the mask check fails.",
+ " geometry if the mask check fails.",
action='store_true',
default=False)
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..40034f9b
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,47 @@
+[build-system]
+requires = ["setuptools>=61.0", "numpy", "versioneer"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "pyradiomics"
+version = "3.0.1a1"
+authors = [
+ { name = "PyRadimiomics Community", email = "pyradiomics@googlegroups.com"}
+]
+description = "Radiomics features library for python"
+readme = "README.md"
+requires-python =">=3.5"
+license = { file = "LICENSE.txt"}
+keywords = [ "radiomics", "cancerimaging", "medicalresearch", "computationalimaging" ]
+classifiers = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Console',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: Science/Research',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: C',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
+ 'Topic :: Scientific/Engineering :: Bio-Informatics'
+]
+dependencies = [
+ "numpy",
+ "SimpleITK",
+ "PyWavelets",
+ "pykwalify",
+ "six"
+]
+
+[project.scripts]
+pyradiomics = "radiomics.scripts.__init__:parse_args"
+
+[project.urls]
+"Homepaget" = "http://github.com/AIM-Harvard/pyradiomics#readme"
+"Radiomics.io" = "https://www.radiomics.io/"
+"Documentation" = "https://pyradiomics.readthedocs.io/en/latest/index.html"
+"Docker" = "https://hub.docker.com/r/radiomics/pyradiomics/"
+"Github" = "https://github.com/AIM-Harvard/pyradiomics"
diff --git a/radiomics/generalinfo.py b/radiomics/generalinfo.py
index 0034d8cb..58c1c6e0 100644
--- a/radiomics/generalinfo.py
+++ b/radiomics/generalinfo.py
@@ -113,8 +113,8 @@ def addMaskElements(self, image, mask, label, prefix='original'):
lssif = sitk.LabelShapeStatisticsImageFilter()
lssif.Execute(mask)
- self.generalInfo[self.generalInfo_prefix + 'Mask-' + prefix + '_BoundingBox'] = lssif.GetBoundingBox(label)
- self.generalInfo[self.generalInfo_prefix + 'Mask-' + prefix + '_VoxelNum'] = lssif.GetNumberOfPixels(label)
+ self.generalInfo[self.generalInfo_prefix + 'Mask-' + prefix + '_BoundingBox'] = lssif.GetBoundingBox(int(label))
+ self.generalInfo[self.generalInfo_prefix + 'Mask-' + prefix + '_VoxelNum'] = lssif.GetNumberOfPixels(int(label))
labelMap = (mask == label)
ccif = sitk.ConnectedComponentImageFilter()
diff --git a/radiomics/imageoperations.py b/radiomics/imageoperations.py
index 82a8e28a..621996d2 100644
--- a/radiomics/imageoperations.py
+++ b/radiomics/imageoperations.py
@@ -19,6 +19,13 @@ def getMask(mask, **kwargs):
In this case, the mask at index ``label_channel`` is extracted. The resulting 3D volume is then treated as it were a
scalar input volume (i.e. with the region of interest defined by voxels with value matching ``label``).
+ .. note::
+ If only one or non-overlapping Segments are defined when using 3D Slicer, it may be the case that it is stored as a
+ labelmap (i.e. only 1 ``label_channel``, with different segmentations identified by different values for ``label``).
+ This is easy to check by loading the mask as a SimpleITK image and checking the `GetNumberOfComponentsPerPixel()`,
+ if the return value is ``1``, it is a label map (i.e. use ``label``), otherwise it is a VectorImage (i.e. use
+ ``label_channel``).
+
Finally, checks if the mask volume contains an ROI identified by ``label``. Raises a value error if the label is not
present (including a list of valid labels found).
@@ -38,7 +45,7 @@ def getMask(mask, **kwargs):
logger.info('Extracting mask at index %i', label_channel)
selector = sitk.VectorIndexSelectionCastImageFilter()
- selector.SetIndex(label_channel)
+ selector.SetIndex(int(label_channel))
mask = selector.Execute(mask)
logger.debug('Force casting mask to UInt32 to ensure correct datatype.')
@@ -216,7 +223,7 @@ def checkMask(imageNode, maskNode, **kwargs):
correctedMask = None
- label = kwargs.get('label', 1)
+ label = int(kwargs.get('label', 1))
minDims = kwargs.get('minimumROIDimensions', 2)
minSize = kwargs.get('minimumROISize', None)
@@ -316,7 +323,7 @@ def _checkROI(imageNode, maskNode, **kwargs):
returned. Otherwise, a ValueError is raised.
"""
global logger
- label = kwargs.get('label', 1)
+ label = int(kwargs.get('label', 1))
logger.debug('Checking ROI validity')
@@ -442,7 +449,7 @@ def resampleImage(imageNode, maskNode, **kwargs):
resampledPixelSpacing = kwargs['resampledPixelSpacing']
interpolator = kwargs.get('interpolator', sitk.sitkBSpline)
padDistance = kwargs.get('padDistance', 5)
- label = kwargs.get('label', 1)
+ label = int(kwargs.get('label', 1))
logger.debug('Resampling image and mask')
diff --git a/radiomics/scripts/__init__.py b/radiomics/scripts/__init__.py
index a9d3c222..fa7d9730 100644
--- a/radiomics/scripts/__init__.py
+++ b/radiomics/scripts/__init__.py
@@ -15,6 +15,7 @@
import pykwalify.core
import six.moves
+import radiomics
import radiomics.featureextractor
from . import segment, voxel
diff --git a/radiomics/scripts/segment.py b/radiomics/scripts/segment.py
index 8de06a63..8afc56f4 100644
--- a/radiomics/scripts/segment.py
+++ b/radiomics/scripts/segment.py
@@ -8,7 +8,7 @@
import SimpleITK as sitk
import six
-import radiomics.featureextractor
+import radiomics
caseLogger = logging.getLogger('radiomics.script')
_parallel_extraction_configured = False
diff --git a/radiomics/scripts/voxel.py b/radiomics/scripts/voxel.py
index e35f2d5f..60490eeb 100644
--- a/radiomics/scripts/voxel.py
+++ b/radiomics/scripts/voxel.py
@@ -7,7 +7,7 @@
import SimpleITK as sitk
import six
-import radiomics.featureextractor
+import radiomics
caseLogger = logging.getLogger('radiomics.script')
_parallel_extraction_configured = False
@@ -39,7 +39,7 @@ def extractVoxel(case_idx, case, extractor, **kwargs):
label = int(label)
label_channel = case.get('Label_channel', None) # Optional
if isinstance(label_channel, six.string_types):
- label_channel = int(label)
+ label_channel = int(label_channel)
# Extract features
result = extractor.execute(imageFilepath, maskFilepath, label, label_channel, voxelBased=True)
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 687f293d..d3315d69 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,5 +1,4 @@
-nose>=1.3.7
-parameterized
+pytest
flake8
flake8-import-order
sphinx>=1.4
diff --git a/requirements.txt b/requirements.txt
index 1d09b540..e533ab68 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
-numpy>=1.9.2
-SimpleITK>=0.9.1
-PyWavelets>=0.4.0
-pykwalify>=1.6.0
-six>=1.10.0
+numpy
+SimpleITK
+PyWavelets
+pykwalify
+six
diff --git a/scikit-ci.yml b/scikit-ci.yml
index fba7450a..8aabcda7 100644
--- a/scikit-ci.yml
+++ b/scikit-ci.yml
@@ -4,7 +4,6 @@ before_install:
appveyor:
environment:
- PATH: $;$\\Scripts;$
RUN_ENV: .\\..\\addons\\appveyor\\run-with-visual-studio.cmd
commands:
- python ../addons/appveyor/patch_vs2008.py
@@ -31,7 +30,7 @@ install:
- $ pip install wheel>=0.29.0
- $ pip install setuptools>=38.6.0
- $ pip install numpy>=1.9.2
- - $ pip install --trusted-host www.itk.org -f https://itk.org/SimpleITKDoxygen/html/PyDownloadPage.html SimpleITK>=0.9.1
+ - $ pip install SimpleITK>=0.9.1
- $ python -c "import SimpleITK; print('SimpleITK Version:' + SimpleITK.Version_VersionString())"
- $ pip install -r requirements.txt
- $ pip install -r requirements-dev.txt
@@ -42,15 +41,11 @@ before_build:
build:
commands:
- - $ python setup.py build_ext
+ - $ python setup.py develop
test:
commands:
- - $ python setup.py test --args="--with-xunit --logging-level=DEBUG"
-
- circleci:
- commands:
- - cp nosetests.xml $CIRCLE_TEST_REPORTS
+ - $ pytest
after_test:
commands:
diff --git a/setup.cfg b/setup.cfg
index d973f4c7..d4b47dff 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,9 +1,26 @@
[metadata]
-description-file = README.md
+description = Radiomics features library for python
+long_description = file: README.md
+long_description_content_type = text/markdown
+license = 3-Clause BSD
-[nosetests]
-verbosity=3
-where=tests
+[options]
+install_requires =
+ numpy
+ SimpleITK
+ PyWavelets
+ pykwalify
+ six
+include_package_data=False
+
+[options.packages.find]
+include=radiomics*
+exclude=radiomics.schemas
+
+[options.package_data]
+radiomics =
+ schemas/paramSchema.yaml
+ schemas/schemaFuncs.py
[versioneer]
VCS = git
diff --git a/setup.py b/setup.py
index d922a42d..300c1be6 100644
--- a/setup.py
+++ b/setup.py
@@ -2,59 +2,15 @@
from distutils import sysconfig
import platform
-import sys
import numpy
from setuptools import Extension, setup
-from setuptools.command.test import test as TestCommand
import versioneer
-# Check if current PyRadiomics is compatible with current python installation (> 2.6, 64 bits)
-if sys.version_info < (2, 6, 0):
- raise Exception("pyradiomics > 0.9.7 requires python 2.6 or later")
-
if platform.architecture()[0].startswith('32'):
raise Exception('PyRadiomics requires 64 bits python')
-with open('requirements.txt', 'r') as fp:
- requirements = list(filter(bool, (line.strip() for line in fp)))
-
-with open('requirements-dev.txt', 'r') as fp:
- dev_requirements = list(filter(bool, (line.strip() for line in fp)))
-
-with open('requirements-setup.txt', 'r') as fp:
- setup_requirements = list(filter(bool, (line.strip() for line in fp)))
-
-with open('README.md', 'rb') as fp:
- long_description = fp.read().decode('utf-8')
-
-
-class NoseTestCommand(TestCommand):
- """Command to run unit tests using nose driver after in-place build"""
-
- user_options = TestCommand.user_options + [
- ("args=", None, "Arguments to pass to nose"),
- ]
-
- def initialize_options(self):
- self.args = []
- TestCommand.initialize_options(self)
-
- def finalize_options(self):
- TestCommand.finalize_options(self)
- if self.args:
- self.args = __import__('shlex').split(self.args)
-
- def run_tests(self):
- # Run nose ensuring that argv simulates running nosetests directly
- nose_args = ['nosetests']
- nose_args.extend(self.args)
- __import__('nose').run_exit(argv=nose_args)
-
-
commands = versioneer.get_cmdclass()
-commands['test'] = NoseTestCommand
-
incDirs = [sysconfig.get_python_inc(), numpy.get_include()]
ext = [Extension("radiomics._cmatrices", ["radiomics/src/_cmatrices.c", "radiomics/src/cmatrices.c"],
@@ -65,56 +21,10 @@ def run_tests(self):
setup(
name='pyradiomics',
- url='http://github.com/Radiomics/pyradiomics#readme',
- project_urls={
- 'Radiomics.io': 'https://www.radiomics.io/',
- 'Documentation': 'https://pyradiomics.readthedocs.io/en/latest/index.html',
- 'Docker': 'https://hub.docker.com/r/radiomics/pyradiomics/',
- 'Github': 'https://github.com/Radiomics/pyradiomics'
- },
-
- author='pyradiomics community',
- author_email='pyradiomics@googlegroups.com',
-
version=versioneer.get_version(),
cmdclass=commands,
packages=['radiomics', 'radiomics.scripts'],
ext_modules=ext,
- zip_safe=False,
- package_data={'radiomics': ['schemas/paramSchema.yaml', 'schemas/schemaFuncs.py']},
-
- entry_points={
- 'console_scripts': [
- 'pyradiomics=radiomics.scripts.__init__:parse_args'
- ]},
-
- description='Radiomics features library for python',
- long_description=long_description,
- long_description_content_type='text/markdown',
-
- license='BSD License',
-
- classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Environment :: Console',
- 'Intended Audience :: Developers',
- 'Intended Audience :: Science/Research',
- 'License :: OSI Approved :: BSD License',
- 'Operating System :: OS Independent',
- 'Programming Language :: C',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- 'Programming Language :: Python :: 3.6',
- 'Topic :: Scientific/Engineering :: Bio-Informatics',
- ],
-
- keywords='radiomics cancerimaging medicalresearch computationalimaging',
-
- install_requires=requirements,
- test_suite='nose.collector',
- tests_require=dev_requirements,
- setup_requires=setup_requirements
+ zip_safe=False
)
diff --git a/tests/testUtils.py b/tests/testUtils.py
index 3b885782..7e9d491b 100644
--- a/tests/testUtils.py
+++ b/tests/testUtils.py
@@ -1,11 +1,9 @@
-
import ast
import csv
import logging
import math
import os
-from nose_parameterized import parameterized
import numpy
import SimpleITK as sitk
import six
@@ -16,38 +14,6 @@
logger = logging.getLogger('radiomics.testing')
-def custom_name_func(testcase_func, param_num, param):
- """
- A custom test name function that will ensure that the tests are run such that they're batched with all tests for a
- given data set are run together, avoiding re-reading the data more than necessary. Tests are run in alphabetical
- order, so put the test case first. An alternate option is to right justify the test number (param_num) with zeroes
- so that the numerical and alphabetical orders are the same. Not providing this method when there are more than 10
- tests results in tests running in an order similar to:
-
- test_*.test_scenario_0_*
-
- test_*.test_scenario_10_*
-
- test_*.test_scenario_11_*
-
- ...
-
- test_*.test_scenario_19_*
-
- test_*.test_scenario_1_*
-
- test_*.test_scenario_20_*
- """
- global logger
-
- logger.debug('custom_name_func: function name = %s, param_num = {0:0>3}, param.args = %s'.format(param_num),
- testcase_func.__name__, param.args)
- return str("%s_%s" % (
- testcase_func.__name__,
- parameterized.to_safe_name("_".join(str(x) for x in param.args)),
- ))
-
-
class RadiomicsTestUtils:
"""
This utility class reads in and stores the baseline files stored in 'data/baseline' (one per feature class)
diff --git a/tests/test_docstrings.py b/tests/test_docstrings.py
index 019f16c3..06be6364 100644
--- a/tests/test_docstrings.py
+++ b/tests/test_docstrings.py
@@ -1,39 +1,19 @@
-# to run this test, from directory above:
-# setenv PYTHONPATH /path/to/pyradiomics/radiomics
-# nosetests --nocapture -v tests/test_docstrings.py
-
import logging
-from nose_parameterized import parameterized
import six
from radiomics import getFeatureClasses
-from testUtils import custom_name_func
featureClasses = getFeatureClasses()
-def setup_module(module):
- # runs before anything in this file
- print("") # this is to get a newline after the dots
- return
+def pytest_generate_tests(metafunc):
+ metafunc.parametrize(["featureClassName", "featureName"], metafunc.cls.generate_scenarios())
class TestDocStrings:
- def setup(self):
- # setup before each test method
- print("") # this is to get a newline after the dots
-
- @classmethod
- def setup_class(cls):
- # called before any methods in this class
- print("") # this is to get a newline after the dots
-
- @classmethod
- def teardown_class(cls):
- # run after any methods in this class
- print("") # this is to get a newline after the dots
+ @staticmethod
def generate_scenarios():
global featureClasses
for featureClassName, featureClass in six.iteritems(featureClasses):
@@ -45,7 +25,6 @@ def generate_scenarios():
for f in featureNames:
yield (featureClassName, f)
- @parameterized.expand(generate_scenarios(), testcase_func_name=custom_name_func)
def test_class(self, featureClassName, featureName):
global featureClasses
logging.info('%s', featureName)
diff --git a/tests/test_exampleSettings.py b/tests/test_exampleSettings.py
index eba2c226..ceeb3b0b 100644
--- a/tests/test_exampleSettings.py
+++ b/tests/test_exampleSettings.py
@@ -1,24 +1,24 @@
-# to run this test, from directory above:
-# setenv PYTHONPATH /path/to/pyradiomics/radiomics
-# nosetests --nocapture -v tests/test_exampleSettings.py
-
import os
-from nose_parameterized import parameterized
import pykwalify.core
from radiomics import getParameterValidationFiles
+schemaFile, schemaFuncs = getParameterValidationFiles()
+
+def pytest_generate_tests(metafunc):
+ metafunc.parametrize("settingsFile", metafunc.cls.generate_scenarios())
+
+
def exampleSettings_name_func(testcase_func, param_num, param):
return '%s_%s' % (testcase_func.__name__, os.path.splitext(os.path.basename(param.args[0]))[0])
class TestExampleSettings:
- def __init__(self):
- self.schemaFile, self.schemaFuncs = getParameterValidationFiles()
- def generateScenarios():
+ @staticmethod
+ def generate_scenarios():
dataDir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'examples', 'exampleSettings')
if os.path.isdir(dataDir):
settingsFiles = [fname for fname in os.listdir(dataDir) if fname.endswith('.yaml') or fname.endswith('.yml')]
@@ -26,10 +26,10 @@ def generateScenarios():
for fname in settingsFiles:
yield os.path.join(dataDir, fname)
- @parameterized.expand(generateScenarios(), testcase_func_name=exampleSettings_name_func)
def test_scenarios(self, settingsFile):
+ global schemaFile, schemaFuncs
- assert os.path.isfile(self.schemaFile)
- assert os.path.isfile(self.schemaFuncs)
- c = pykwalify.core.Core(source_file=settingsFile, schema_files=[self.schemaFile], extensions=[self.schemaFuncs])
+ assert os.path.isfile(schemaFile)
+ assert os.path.isfile(schemaFuncs)
+ c = pykwalify.core.Core(source_file=settingsFile, schema_files=[schemaFile], extensions=[schemaFuncs])
c.validate()
diff --git a/tests/test_features.py b/tests/test_features.py
index 05e88d9b..29f94b78 100644
--- a/tests/test_features.py
+++ b/tests/test_features.py
@@ -1,15 +1,10 @@
-# to run this test, from directory above:
-# setenv PYTHONPATH /path/to/pyradiomics/radiomics
-# nosetests --nocapture -v tests/test_features.py
-
import logging
import os
-from nose_parameterized import parameterized
import six
from radiomics import getFeatureClasses
-from testUtils import custom_name_func, RadiomicsTestUtils
+from testUtils import RadiomicsTestUtils
testUtils = RadiomicsTestUtils()
tests = sorted(testUtils.getTests())
@@ -18,8 +13,13 @@
featureClasses = getFeatureClasses()
+def pytest_generate_tests(metafunc):
+ metafunc.parametrize(["testCase", "featureName"], metafunc.cls.generate_scenarios())
+
+
class TestFeatures:
+ @staticmethod
def generate_scenarios():
global tests, featureClasses
@@ -51,17 +51,16 @@ def generate_scenarios():
for featureName in baselineFeatureNames:
yield test, featureName
- @parameterized.expand(generate_scenarios(), testcase_func_name=custom_name_func)
- def test_scenario(self, test, featureName):
+ def test_scenario(self, testCase, featureName):
print("")
global testUtils, featureClass, featureClasses
featureName = featureName.split('_')
- logging.debug('test_scenario: test = %s, featureClassName = %s, featureName = %s', test, featureName[1],
+ logging.debug('test_scenario: test = %s, featureClassName = %s, featureName = %s', testCase, featureName[1],
featureName[-1])
- testOrClassChanged = testUtils.setFeatureClassAndTestCase(featureName[1], test)
+ testOrClassChanged = testUtils.setFeatureClassAndTestCase(featureName[1], testCase)
testImage = testUtils.getImage(featureName[0])
testMask = testUtils.getMask(featureName[0])
diff --git a/tests/test_matrices.py b/tests/test_matrices.py
index b4858307..00ab3969 100644
--- a/tests/test_matrices.py
+++ b/tests/test_matrices.py
@@ -1,16 +1,11 @@
-# to run this test, from directory above:
-# setenv PYTHONPATH /path/to/pyradiomics/radiomics
-# nosetests --nocapture -v tests/test_features.py
-
import logging
import os
-from nose_parameterized import parameterized
import numpy
import six
from radiomics import getFeatureClasses, testCases
-from testUtils import custom_name_func, RadiomicsTestUtils
+from testUtils import RadiomicsTestUtils
testUtils = RadiomicsTestUtils()
@@ -18,8 +13,13 @@
featureClasses = getFeatureClasses()
+def pytest_generate_tests(metafunc):
+ metafunc.parametrize(["testCase", "featureClassName"], metafunc.cls.generate_scenarios())
+
+
class TestMatrices:
+ @staticmethod
def generate_scenarios():
global featureClasses
@@ -27,23 +27,22 @@ def generate_scenarios():
if testCase.startswith('test'):
continue
for className, featureClass in six.iteritems(featureClasses):
- assert(featureClass is not None)
+ assert featureClass is not None
if "_calculateMatrix" in dir(featureClass):
logging.debug('generate_scenarios: featureClass = %s', className)
yield testCase, className
- @parameterized.expand(generate_scenarios(), testcase_func_name=custom_name_func)
- def test_scenario(self, test, featureClassName):
+ def test_scenario(self, testCase, featureClassName):
global testUtils, featureClasses
- logging.debug('test_scenario: testCase = %s, featureClassName = %s', test, featureClassName)
+ logging.debug('test_scenario: testCase = %s, featureClassName = %s', testCase, featureClassName)
- baselineFile = os.path.join(testUtils.getDataDir(), 'baseline', '%s_%s.npy' % (test, featureClassName))
+ baselineFile = os.path.join(testUtils.getDataDir(), 'baseline', '%s_%s.npy' % (testCase, featureClassName))
assert os.path.isfile(baselineFile)
baselineMatrix = numpy.load(baselineFile)
- testUtils.setFeatureClassAndTestCase(featureClassName, test)
+ testUtils.setFeatureClassAndTestCase(featureClassName, testCase)
testImage = testUtils.getImage('original')
testMask = testUtils.getMask('original')
diff --git a/tests/test_wavelet.py b/tests/test_wavelet.py
index 149f2f9e..2d2c71aa 100644
--- a/tests/test_wavelet.py
+++ b/tests/test_wavelet.py
@@ -1,11 +1,6 @@
-# to run this test, from directory above:
-# setenv PYTHONPATH /path/to/pyradiomics/radiomics
-# nosetests --nocapture -v tests/test_features.py
-
import logging
import os
-from nose_parameterized import parameterized
import numpy
import SimpleITK as sitk
@@ -13,43 +8,16 @@
logger = logging.getLogger('radiomics.testing')
testCases = ('test_wavelet_64x64x64', 'test_wavelet_37x37x37')
-baselineFile = '../data/baseline/wavelet.npy'
-
-
-def custom_name_func(testcase_func, param_num, param):
- """
- A custom test name function that will ensure that the tests are run such that they're batched with all tests for a
- given data set are run together, avoiding re-reading the data more than necessary. Tests are run in alphabetical
- order, so put the test case first. An alternate option is to right justify the test number (param_num) with zeroes
- so that the numerical and alphabetical orders are the same. Not providing this method when there are more than 10
- tests results in tests running in an order similar to:
-
- test_*.test_scenario_0_*
-
- test_*.test_scenario_10_*
-
- test_*.test_scenario_11_*
-
- ...
-
- test_*.test_scenario_19_*
-
- test_*.test_scenario_1_*
+baselineFile = os.path.join(os.path.dirname(__file__), '../data/baseline/wavelet.npy')
- test_*.test_scenario_20_*
- """
- global logger
- logger.debug('custom_name_func: function name = %s, param_num = {0:0>3}, param.args = %s'.format(param_num),
- testcase_func.__name__, param.args)
- return str("%s_%s" % (
- testcase_func.__name__,
- parameterized.to_safe_name(param.args[0]),
- ))
+def pytest_generate_tests(metafunc):
+ metafunc.parametrize(["testCase", "image", "mask", "baseline"], metafunc.cls.generate_scenarios())
class TestWavelet:
+ @staticmethod
def generate_scenarios():
global logger, testCases, baselineFile
@@ -85,11 +53,10 @@ def generate_scenarios():
level = wavelet_name.split('-')[1]
yield '_'.join((testCase, 'preCropped', wavelet_name)), image, mask, baselineDict[level]
- @parameterized.expand(generate_scenarios(), testcase_func_name=custom_name_func)
- def test_scenario(self, test, image, mask, baseline):
+ def test_scenario(self, testCase, image, mask, baseline):
global logger, testUtils, featureClasses
- logger.debug('test_scenario: testCase = %s,', test)
+ logger.debug('test_scenario: testCase = %s,', testCase)
im_arr = sitk.GetArrayFromImage(image)
ma_arr = sitk.GetArrayFromImage(mask) == 1 # Conver to boolean array, label = 1