From 9d90ba01c397e3a379ff2c8d1c90f5ff4076329d Mon Sep 17 00:00:00 2001 From: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Date: Fri, 17 Jan 2020 12:11:57 +0000 Subject: [PATCH 1/7] Adding script to run unit tests and example test cases (#29) Adding script to run unit tests and example test cases --- .gitignore | 1 + monai/__init__.py | 2 +- runtests.sh | 102 ++++++++++++++++++++++++++++++++++++++ tests/__init__.py | 10 ++++ tests/testconvolutions.py | 85 +++++++++++++++++++++++++++++++ tests/utils.py | 70 ++++++++++++++++++++++++++ 6 files changed, 269 insertions(+), 1 deletion(-) create mode 100755 runtests.sh create mode 100644 tests/__init__.py create mode 100644 tests/testconvolutions.py create mode 100644 tests/utils.py diff --git a/.gitignore b/.gitignore index 9949bc981c..c30f242fd2 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,4 @@ venv.bak/ # mypy .mypy_cache/ examples/scd_lvsegs.npz +.idea/ diff --git a/monai/__init__.py b/monai/__init__.py index 9dc1300c7b..e86508dd19 100644 --- a/monai/__init__.py +++ b/monai/__init__.py @@ -12,7 +12,7 @@ import os import sys -from .utils.moduleutils import load_submodules, loadSubmodules +from .utils.moduleutils import load_submodules __copyright__ = "(c) 2020 MONAI Consortium" __version__tuple__ = (0, 0, 1) diff --git a/runtests.sh b/runtests.sh new file mode 100755 index 0000000000..102e63c68c --- /dev/null +++ b/runtests.sh @@ -0,0 +1,102 @@ +#! /bin/bash +# Test script for running all tests + + +homedir="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $homedir + +#export PYTHONPATH="$homedir:$PYTHONPATH" + +# configuration values +doCoverage=false +doQuickTests=false +doNetTests=false +doDryRun=false +doZooTests=false + +# testing command to run +cmd="python" +cmdprefix="" + + +# parse arguments +for i in "$@" +do + case $i in + --coverage) + doCoverage=true + ;; + --quick) + doQuickTests=true + doCoverage=true + export QUICKTEST=True + ;; + --net) + doNetTests=true + ;; + --dryrun) + doDryRun=true + ;; + --zoo) + doZooTests=true + ;; + *) + echo "runtests.sh [--coverage] [--quick] [--net] [--dryrun] [--zoo]" + exit 1 + ;; + esac +done + + +# commands are echoed instead of run in this case +if [ "$doDryRun" = 'true' ] +then + echo "Dry run commands:" + cmdprefix="dryrun " + + # create a dry run function which prints the command prepended with spaces for neatness + function dryrun { echo " " $* ; } +fi + + +# set command and clear previous coverage data +if [ "$doCoverage" = 'true' ] +then + cmd="coverage run -a --source ." + ${cmdprefix} coverage erase +fi + + +# # download test data if needed +# if [ ! -d testing_data ] && [ "$doDryRun" != 'true' ] +# then +# fi + + +# unit tests +${cmdprefix}${cmd} -m unittest + + +# network training/inference/eval tests +if [ "$doNetTests" = 'true' ] +then + for i in examples/*.py + do + echo $i + ${cmdprefix}${cmd} $i + done +fi + + +# # run model zoo tests +# if [ "$doZooTests" = 'true' ] +# then +# fi + + +# report on coverage +if [ "$doCoverage" = 'true' ] +then + ${cmdprefix}coverage report --omit='*/test/*' --skip-covered -m +fi + diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000..d0044e3563 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,10 @@ +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/testconvolutions.py b/tests/testconvolutions.py new file mode 100644 index 0000000000..14b189ccdd --- /dev/null +++ b/tests/testconvolutions.py @@ -0,0 +1,85 @@ +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from .utils import ImageTestCase + +from monai.networks.layers.convolutions import Convolution, ResidualUnit + + +class TestConvolution2D(ImageTestCase): + def test_conv1(self): + conv = Convolution(2, self.input_channels, self.output_channels) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0], self.im_shape[1]) + self.assertEqual(out.shape, expected_shape) + + def test_conv_only1(self): + conv = Convolution(2, self.input_channels, self.output_channels, conv_only=True) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0], self.im_shape[1]) + self.assertEqual(out.shape, expected_shape) + + def test_stride1(self): + conv = Convolution(2, self.input_channels, self.output_channels, strides=2) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0] // 2, self.im_shape[1] // 2) + self.assertEqual(out.shape, expected_shape) + + def test_dilation1(self): + conv = Convolution(2, self.input_channels, self.output_channels, dilation=3) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0], self.im_shape[1]) + self.assertEqual(out.shape, expected_shape) + + def test_dropout1(self): + conv = Convolution(2, self.input_channels, self.output_channels, dropout=0.15) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0], self.im_shape[1]) + self.assertEqual(out.shape, expected_shape) + + def test_transpose1(self): + conv = Convolution(2, self.input_channels, self.output_channels, is_transposed=True) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0], self.im_shape[1]) + self.assertEqual(out.shape, expected_shape) + + def test_transpose2(self): + conv = Convolution(2, self.input_channels, self.output_channels, strides=2, is_transposed=True) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0] * 2, self.im_shape[1] * 2) + self.assertEqual(out.shape, expected_shape) + + +class TestResidualUnit2D(ImageTestCase): + def test_conv_only1(self): + conv = ResidualUnit(2, 1, self.output_channels) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0], self.im_shape[1]) + self.assertEqual(out.shape, expected_shape) + + def test_stride1(self): + conv = ResidualUnit(2, 1, self.output_channels, strides=2) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0] // 2, self.im_shape[1] // 2) + self.assertEqual(out.shape, expected_shape) + + def test_dilation1(self): + conv = ResidualUnit(2, 1, self.output_channels, dilation=3) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0], self.im_shape[1]) + self.assertEqual(out.shape, expected_shape) + + def test_dropout1(self): + conv = ResidualUnit(2, 1, self.output_channels, dropout=0.15) + out = conv(self.imt) + expected_shape = (1, self.output_channels, self.im_shape[0], self.im_shape[1]) + self.assertEqual(out.shape, expected_shape) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000000..f780220b77 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,70 @@ +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import unittest +import torch +import numpy as np + +from monai.utils.arrayutils import rescale_array + +quick_test_var = "QUICKTEST" + + +def skip_if_quick(obj): + is_quick = os.environ.get(quick_test_var, "").lower() == "true" + + return unittest.skipIf(is_quick, "Skipping slow tests")(obj) + + +def create_test_image(width, height, num_objs=12, rad_max=30, noise_max=0.0, num_seg_classes=5): + """ + Return a noisy 2D image with `numObj' circles and a 2D mask image. The maximum radius of the circles is given as + `radMax'. The mask will have `numSegClasses' number of classes for segmentations labeled sequentially from 1, plus a + background class represented as 0. If `noiseMax' is greater than 0 then noise will be added to the image taken from + the uniform distribution on range [0,noiseMax). + """ + image = np.zeros((width, height)) + + for i in range(num_objs): + x = np.random.randint(rad_max, width - rad_max) + y = np.random.randint(rad_max, height - rad_max) + rad = np.random.randint(5, rad_max) + spy, spx = np.ogrid[-x : width - x, -y : height - y] + circle = (spx * spx + spy * spy) <= rad * rad + + if num_seg_classes > 1: + image[circle] = np.ceil(np.random.random() * num_seg_classes) + else: + image[circle] = np.random.random() * 0.5 + 0.5 + + labels = np.ceil(image).astype(np.int32) + + norm = np.random.uniform(0, num_seg_classes * noise_max, size=image.shape) + noisyimage = rescale_array(np.maximum(image, norm)) + + return noisyimage, labels + + +class ImageTestCase(unittest.TestCase): + im_shape = (128, 128) + input_channels = 1 + output_channels = 4 + num_classes = 3 + + def setUp(self): + im, msk = create_test_image(self.im_shape[0], self.im_shape[1], 4, 20, 0, self.num_classes) + + self.imt = torch.tensor(im[None, None]) + + self.seg1 = torch.tensor((msk[None, None] > 0).astype(np.float32)) + self.segn = torch.tensor(msk[None, None]) From a2fc227f8c02a154a249c93a1db766eaacc8a130 Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Fri, 17 Jan 2020 13:35:23 +0000 Subject: [PATCH 2/7] initial unit tests for dice loss (#27) * initial unit tests for 2d/3d unet * unit tests update - triggering unit tests via github workflow - renamed testconvolutions.py to test_convolutions.py - test unet test cases as variables for readability --- .github/workflows/pythonapp.yml | 7 +- requirements.txt | 1 + runtests.sh | 5 +- ...stconvolutions.py => test_convolutions.py} | 0 tests/test_dice_loss.py | 53 +++++++++++++++ tests/test_unet.py | 68 +++++++++++++++++++ 6 files changed, 128 insertions(+), 6 deletions(-) rename tests/{testconvolutions.py => test_convolutions.py} (100%) create mode 100644 tests/test_dice_loss.py create mode 100644 tests/test_unet.py diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 9251be0c36..4c8d04b8a9 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -25,7 +25,6 @@ jobs: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. flake8 . --count --statistics -# - name: Test with pytest -# run: | -# pip install pytest -# pytest + - name: Test and coverage + run: | + ./runtests.sh --coverage diff --git a/requirements.txt b/requirements.txt index 23493d8ecf..e45f176cda 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ pillow pandas coverage nibabel +parameterized diff --git a/runtests.sh b/runtests.sh index 102e63c68c..299b47dee6 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,4 +1,5 @@ #! /bin/bash +set -e # Test script for running all tests @@ -52,8 +53,8 @@ done if [ "$doDryRun" = 'true' ] then echo "Dry run commands:" - cmdprefix="dryrun " - + cmdprefix="dryrun " + # create a dry run function which prints the command prepended with spaces for neatness function dryrun { echo " " $* ; } fi diff --git a/tests/testconvolutions.py b/tests/test_convolutions.py similarity index 100% rename from tests/testconvolutions.py rename to tests/test_convolutions.py diff --git a/tests/test_dice_loss.py b/tests/test_dice_loss.py new file mode 100644 index 0000000000..0e1908b999 --- /dev/null +++ b/tests/test_dice_loss.py @@ -0,0 +1,53 @@ +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import torch +from parameterized import parameterized + +from monai.networks.losses.dice import DiceLoss + +TEST_CASE_1 = [ + { + 'include_background': False, + }, + { + 'pred': torch.tensor([[[[1., -1.], [-1., 1.]]]]), + 'ground': torch.tensor([[[[1., 0.], [1., 1.]]]]), + 'smooth': 1e-6, + }, + 0.307576, +] + +TEST_CASE_2 = [ + { + 'include_background': True, + }, + { + 'pred': torch.tensor([[[[1., -1.], [-1., 1.]]], [[[1., -1.], [-1., 1.]]]]), + 'ground': torch.tensor([[[[1., 1.], [1., 1.]]], [[[1., 0.], [1., 0.]]]]), + 'smooth': 1e-4, + }, + 0.416636, +] + + +class TestDiceLoss(unittest.TestCase): + + @parameterized.expand([TEST_CASE_1, TEST_CASE_2]) + def test_shape(self, input_param, input_data, expected_val): + result = DiceLoss(**input_param).forward(**input_data) + self.assertAlmostEqual(result.item(), expected_val, places=5) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_unet.py b/tests/test_unet.py new file mode 100644 index 0000000000..95be1bf1a1 --- /dev/null +++ b/tests/test_unet.py @@ -0,0 +1,68 @@ +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import torch +from parameterized import parameterized + +from monai.networks.nets.unet import UNet + +TEST_CASE_1 = [ # single channel 2D, batch 16 + { + 'dimensions': 2, + 'in_channels': 1, + 'num_classes': 3, + 'channels': (16, 32, 64), + 'strides': (2, 2), + 'num_res_units': 1, + }, + torch.randn(16, 1, 32, 32), + (16, 32, 32), +] + +TEST_CASE_2 = [ # single channel 3D, batch 16 + { + 'dimensions': 3, + 'in_channels': 1, + 'num_classes': 3, + 'channels': (16, 32, 64), + 'strides': (2, 2), + 'num_res_units': 1, + }, + torch.randn(16, 1, 32, 24, 48), + (16, 32, 24, 48), +] + +TEST_CASE_3 = [ # 4-channel 3D, batch 16 + { + 'dimensions': 3, + 'in_channels': 4, + 'num_classes': 3, + 'channels': (16, 32, 64), + 'strides': (2, 2), + 'num_res_units': 1, + }, + torch.randn(16, 4, 32, 64, 48), + (16, 32, 64, 48), +] + + +class TestUNET(unittest.TestCase): + + @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_3]) + def test_shape(self, input_param, input_data, expected_shape): + result = UNet(**input_param).forward(input_data)[1] + self.assertEqual(result.shape, expected_shape) + + +if __name__ == '__main__': + unittest.main() From 4bd517ae4d076b3ca6494c213d1d7f1c3d0fc92f Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Fri, 17 Jan 2020 13:37:34 +0000 Subject: [PATCH 3/7] initial unit tests for 2d/3d unet (#26) * initial unit tests for 2d/3d unet * unit tests update - triggering unit tests via github workflow - renamed testconvolutions.py to test_convolutions.py - test unet test cases as variables for readability From de1fbe027b3f298e2b20a66ea1ff1ae20e33064a Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Fri, 17 Jan 2020 16:48:15 +0000 Subject: [PATCH 4/7] 14 code examples of monai input data pipeline (#24) * fixes cardiac example * update example cardiac segmentation --- examples/cardiac_segmentation.ipynb | 101 ++++++++-------------------- 1 file changed, 29 insertions(+), 72 deletions(-) diff --git a/examples/cardiac_segmentation.ipynb b/examples/cardiac_segmentation.ipynb index f96a14a5db..112a661fb4 100644 --- a/examples/cardiac_segmentation.ipynb +++ b/examples/cardiac_segmentation.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 8, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -10,8 +10,8 @@ "output_type": "stream", "text": [ "MONAI version: 0.0.1\n", - "Python version: 3.7.3 (default, Mar 27 2019, 22:11:17) [GCC 7.3.0]\n", - "Numpy version: 1.16.4\n", + "Python version: 3.6.9 |Anaconda, Inc.| (default, Jul 30 2019, 19:07:31) [GCC 7.3.0]\n", + "Numpy version: 1.18.0\n", "Pytorch version: 1.3.1\n", "Ignite version: 0.2.1\n" ] @@ -48,7 +48,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -64,11 +64,11 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "imSrc = data.readers.NPZReader(\"scd_lvsegs.npz\", [\"images\", \"segs\"], orderType=data.streams.OrderType.CHOICE)" + "imSrc = data.readers.NPZReader(\"scd_lvsegs.npz\", [\"images\", \"segs\"], other_values=data.streams.OrderType.CHOICE)" ] }, { @@ -80,7 +80,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -93,16 +93,16 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 10, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -115,7 +115,7 @@ ], "source": [ "def normalizeImg(im, seg):\n", - " im = utils.arrayutils.rescaleArray(im)\n", + " im = utils.arrayutils.rescale_array(im)\n", " im = im[None].astype(np.float32)\n", " seg = seg[None].astype(np.int32)\n", " return im, seg\n", @@ -126,7 +126,7 @@ " augments.rot90,\n", " augments.transpose,\n", " augments.flip,\n", - " partial(augments.shift, dimFract=5, order=0, nonzeroIndex=1),\n", + " partial(augments.shift, dim_fract=5, order=0, nonzero_index=1),\n", "]\n", "\n", "src = data.augments.augmentstream.ThreadAugmentStream(imSrc, 200, augments=augs)\n", @@ -146,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -154,11 +154,11 @@ "\n", "net = networks.nets.UNet(\n", " dimensions=2,\n", - " inChannels=1,\n", - " numClasses=1,\n", + " in_channels=1,\n", + " num_classes=1,\n", " channels=(16, 32, 64, 128, 256),\n", " strides=(2, 2, 2, 2),\n", - " numResUnits=2,\n", + " num_res_units=2,\n", ")\n", "\n", "loss = networks.losses.DiceLoss()\n", @@ -174,36 +174,9 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 1 Loss: 0.8310171365737915\n", - "Epoch 2 Loss: 0.8060150742530823\n", - "Epoch 3 Loss: 0.7623872756958008\n", - "Epoch 4 Loss: 0.6729476451873779\n", - "Epoch 5 Loss: 0.6116510629653931\n", - "Epoch 6 Loss: 0.5286673903465271\n", - "Epoch 7 Loss: 0.4480087161064148\n", - "Epoch 8 Loss: 0.41203784942626953\n", - "Epoch 9 Loss: 0.3519987463951111\n", - "Epoch 10 Loss: 0.30135440826416016\n", - "Epoch 11 Loss: 0.274499773979187\n", - "Epoch 12 Loss: 0.2519426941871643\n", - "Epoch 13 Loss: 0.23030847311019897\n", - "Epoch 14 Loss: 0.22828155755996704\n", - "Epoch 15 Loss: 0.22576206922531128\n", - "Epoch 16 Loss: 0.23023653030395508\n", - "Epoch 17 Loss: 0.21913212537765503\n", - "Epoch 18 Loss: 0.22168612480163574\n", - "Epoch 19 Loss: 0.2222415804862976\n", - "Epoch 20 Loss: 0.20740610361099243\n" - ] - } - ], + "outputs": [], "source": [ "trainSteps = 100\n", "trainEpochs = 20\n", @@ -233,35 +206,12 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "im, seg = utils.mathutils.first(imSrc)\n", - "testim = utils.arrayutils.rescaleArray(im[None, None])\n", + "testim = utils.arrayutils.rescale_array(im[None, None])\n", "\n", "pred = net.cpu()(torch.from_numpy(testim))\n", "\n", @@ -269,6 +219,13 @@ "\n", "plt.imshow(np.hstack([testim[0, 0], pseg[0]]))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -287,7 +244,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.6.9" } }, "nbformat": 4, From eef2941cd20703754dcbdf40f9fa8c66e601cd4a Mon Sep 17 00:00:00 2001 From: Isaac Yang Date: Fri, 17 Jan 2020 09:01:10 -0800 Subject: [PATCH 5/7] Create .gitlab-ci.yml (#30) an initial step towards #19 --- .gitlab-ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..230eb1fe1b --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,12 @@ +stages: + - build + +.base_template : &BASE + script: + - cat README.md + +build-ci-test: + stage: build + tags: + - test + <<: *BASE From e2d69d649d57a5ec8f71bc6c19bedb9babf05c3e Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Tue, 21 Jan 2020 13:40:44 +0000 Subject: [PATCH 6/7] tests intensity normalizer - revised to support both `[key]` and `key` as an input for apply_keys - added `NumpyImageTestCase2D` and `TorchImageTestCase2D` --- monai/data/transforms/intensity_normalizer.py | 19 ++++++++----- monai/data/transforms/transform.py | 1 + tests/test_convolutions.py | 6 ++--- tests/test_intensity_normalizer.py | 27 +++++++++++++++++++ tests/utils.py | 22 ++++++++++----- 5 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 tests/test_intensity_normalizer.py diff --git a/monai/data/transforms/intensity_normalizer.py b/monai/data/transforms/intensity_normalizer.py index a25a454798..8bd6832de0 100644 --- a/monai/data/transforms/intensity_normalizer.py +++ b/monai/data/transforms/intensity_normalizer.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from collections.abc import Hashable + import numpy as np -from transform import Transform + +from .transform import Transform class IntensityNormalizer(Transform): @@ -20,17 +23,19 @@ class IntensityNormalizer(Transform): Current implementation can only support 'channel_last' format data. Args: - apply_keys (tuple or list): run transform on which field of the inout data + apply_keys (a hashable key or a tuple/list of hashable keys): run transform on which field of the input data subtrahend (ndarray): the amount to subtract by (usually the mean) divisor (ndarray): the amount to divide by (usually the standard deviation) dtype: output data format """ def __init__(self, apply_keys, subtrahend=None, divisor=None, dtype=np.float32): - Transform.__init__(self) - assert apply_keys is not None and (type(apply_keys) == tuple or type(apply_keys) == list), \ - 'must set apply_keys for this transform.' - self.apply_keys = apply_keys + self.apply_keys = apply_keys if isinstance(apply_keys, (list, tuple)) else (apply_keys,) + if not self.apply_keys: + raise ValueError('must set apply_keys for this transform.') + for key in self.apply_keys: + if not isinstance(key, Hashable): + raise ValueError('apply_keys should be a hashable or a sequence of hashables used by data[key]') if subtrahend is not None or divisor is not None: assert isinstance(subtrahend, np.ndarray) and isinstance(divisor, np.ndarray), \ 'subtrahend and divisor must be set in pair and in numpy array.' @@ -39,7 +44,7 @@ def __init__(self, apply_keys, subtrahend=None, divisor=None, dtype=np.float32): self.dtype = dtype def __call__(self, data): - assert data is not None and type(data) == dict, 'data must be in dict format with keys.' + assert data is not None and isinstance(data, dict), 'data must be in dict format with keys.' for key in self.apply_keys: img = data[key] assert key in data, 'can not find expected key={} in data.'.format(key) diff --git a/monai/data/transforms/transform.py b/monai/data/transforms/transform.py index 75cc90926b..86ebc30776 100644 --- a/monai/data/transforms/transform.py +++ b/monai/data/transforms/transform.py @@ -9,6 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. + class Transform(object): """An abstract class of a ``Transform`` A transform is callable that maps data into output data. diff --git a/tests/test_convolutions.py b/tests/test_convolutions.py index 14b189ccdd..ad945e1a92 100644 --- a/tests/test_convolutions.py +++ b/tests/test_convolutions.py @@ -10,12 +10,12 @@ # limitations under the License. -from .utils import ImageTestCase +from .utils import TorchImageTestCase2D from monai.networks.layers.convolutions import Convolution, ResidualUnit -class TestConvolution2D(ImageTestCase): +class TestConvolution2D(TorchImageTestCase2D): def test_conv1(self): conv = Convolution(2, self.input_channels, self.output_channels) out = conv(self.imt) @@ -59,7 +59,7 @@ def test_transpose2(self): self.assertEqual(out.shape, expected_shape) -class TestResidualUnit2D(ImageTestCase): +class TestResidualUnit2D(TorchImageTestCase2D): def test_conv_only1(self): conv = ResidualUnit(2, 1, self.output_channels) out = conv(self.imt) diff --git a/tests/test_intensity_normalizer.py b/tests/test_intensity_normalizer.py new file mode 100644 index 0000000000..88840ca28f --- /dev/null +++ b/tests/test_intensity_normalizer.py @@ -0,0 +1,27 @@ +import unittest + +import numpy as np + +from monai.data.transforms.intensity_normalizer import IntensityNormalizer +from tests.utils import NumpyImageTestCase2D + + +class MyTestCase(NumpyImageTestCase2D): + + def test_image_normalizer_default(self): + data_key = 'image' + normalizer = IntensityNormalizer(data_key) # test a single key + normalised = normalizer({data_key: self.imt}) + expected = (self.imt - np.mean(self.imt)) / np.std(self.imt) + self.assertTrue(np.allclose(normalised[data_key], expected)) + + def test_image_normalizer_default_1(self): + data_key = 'image' + normalizer = IntensityNormalizer([data_key]) # test list of keys + normalised = normalizer({data_key: self.imt}) + expected = (self.imt - np.mean(self.imt)) / np.std(self.imt) + self.assertTrue(np.allclose(normalised[data_key], expected)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/utils.py b/tests/utils.py index f780220b77..cc66d261fd 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -9,11 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. - import os import unittest -import torch + import numpy as np +import torch from monai.utils.arrayutils import rescale_array @@ -39,7 +39,7 @@ def create_test_image(width, height, num_objs=12, rad_max=30, noise_max=0.0, num x = np.random.randint(rad_max, width - rad_max) y = np.random.randint(rad_max, height - rad_max) rad = np.random.randint(5, rad_max) - spy, spx = np.ogrid[-x : width - x, -y : height - y] + spy, spx = np.ogrid[-x:width - x, -y:height - y] circle = (spx * spx + spy * spy) <= rad * rad if num_seg_classes > 1: @@ -55,7 +55,7 @@ def create_test_image(width, height, num_objs=12, rad_max=30, noise_max=0.0, num return noisyimage, labels -class ImageTestCase(unittest.TestCase): +class NumpyImageTestCase2D(unittest.TestCase): im_shape = (128, 128) input_channels = 1 output_channels = 4 @@ -64,7 +64,15 @@ class ImageTestCase(unittest.TestCase): def setUp(self): im, msk = create_test_image(self.im_shape[0], self.im_shape[1], 4, 20, 0, self.num_classes) - self.imt = torch.tensor(im[None, None]) + self.imt = im[None, None] + self.seg1 = (msk[None, None] > 0).astype(np.float32) + self.segn = msk[None, None] - self.seg1 = torch.tensor((msk[None, None] > 0).astype(np.float32)) - self.segn = torch.tensor(msk[None, None]) + +class TorchImageTestCase2D(NumpyImageTestCase2D): + + def setUp(self): + NumpyImageTestCase2D.setUp(self) + self.imt = torch.tensor(self.imt) + self.seg1 = torch.tensor(self.seg1) + self.segn = torch.tensor(self.segn) From b174f89e548b42bd1b4e27ba2776923ebc7751ef Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Tue, 21 Jan 2020 14:52:13 +0000 Subject: [PATCH 7/7] style updates and new test cases: - adding copyright notice - validate user input before setting class member - one line space after copyright - testing multiple keys input data --- monai/data/transforms/intensity_normalizer.py | 7 +++--- monai/data/transforms/transform.py | 1 - tests/test_convolutions.py | 1 - tests/test_intensity_normalizer.py | 22 ++++++++++++++++++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/monai/data/transforms/intensity_normalizer.py b/monai/data/transforms/intensity_normalizer.py index 8bd6832de0..5b66994972 100644 --- a/monai/data/transforms/intensity_normalizer.py +++ b/monai/data/transforms/intensity_normalizer.py @@ -30,12 +30,13 @@ class IntensityNormalizer(Transform): """ def __init__(self, apply_keys, subtrahend=None, divisor=None, dtype=np.float32): - self.apply_keys = apply_keys if isinstance(apply_keys, (list, tuple)) else (apply_keys,) - if not self.apply_keys: + _apply_keys = apply_keys if isinstance(apply_keys, (list, tuple)) else (apply_keys,) + if not _apply_keys: raise ValueError('must set apply_keys for this transform.') - for key in self.apply_keys: + for key in _apply_keys: if not isinstance(key, Hashable): raise ValueError('apply_keys should be a hashable or a sequence of hashables used by data[key]') + self.apply_keys = _apply_keys if subtrahend is not None or divisor is not None: assert isinstance(subtrahend, np.ndarray) and isinstance(divisor, np.ndarray), \ 'subtrahend and divisor must be set in pair and in numpy array.' diff --git a/monai/data/transforms/transform.py b/monai/data/transforms/transform.py index 86ebc30776..75cc90926b 100644 --- a/monai/data/transforms/transform.py +++ b/monai/data/transforms/transform.py @@ -9,7 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - class Transform(object): """An abstract class of a ``Transform`` A transform is callable that maps data into output data. diff --git a/tests/test_convolutions.py b/tests/test_convolutions.py index ad945e1a92..70644c8a9a 100644 --- a/tests/test_convolutions.py +++ b/tests/test_convolutions.py @@ -9,7 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - from .utils import TorchImageTestCase2D from monai.networks.layers.convolutions import Convolution, ResidualUnit diff --git a/tests/test_intensity_normalizer.py b/tests/test_intensity_normalizer.py index 88840ca28f..5ad2f95744 100644 --- a/tests/test_intensity_normalizer.py +++ b/tests/test_intensity_normalizer.py @@ -1,3 +1,14 @@ +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import unittest import numpy as np @@ -6,7 +17,7 @@ from tests.utils import NumpyImageTestCase2D -class MyTestCase(NumpyImageTestCase2D): +class IntensityNormTestCase(NumpyImageTestCase2D): def test_image_normalizer_default(self): data_key = 'image' @@ -22,6 +33,15 @@ def test_image_normalizer_default_1(self): expected = (self.imt - np.mean(self.imt)) / np.std(self.imt) self.assertTrue(np.allclose(normalised[data_key], expected)) + def test_image_normalizer_default_2(self): + data_keys = ['image_1', 'image_2'] + normalizer = IntensityNormalizer(data_keys) # test list of keys + normalised = normalizer(dict(zip(data_keys, (self.imt, self.seg1)))) + expected_1 = (self.imt - np.mean(self.imt)) / np.std(self.imt) + expected_2 = (self.seg1 - np.mean(self.seg1)) / np.std(self.seg1) + self.assertTrue(np.allclose(normalised[data_keys[0]], expected_1)) + self.assertTrue(np.allclose(normalised[data_keys[1]], expected_2)) + if __name__ == '__main__': unittest.main()