Skip to content

Commit

Permalink
Merge a863278 into d9ad712
Browse files Browse the repository at this point in the history
  • Loading branch information
lrouhier committed Aug 17, 2020
2 parents d9ad712 + a863278 commit 5a4fd51
Show file tree
Hide file tree
Showing 23 changed files with 588 additions and 65 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
@@ -0,0 +1,2 @@
[run]
omit = setup.py
33 changes: 29 additions & 4 deletions .github/workflows/run_tests.yml
Expand Up @@ -3,15 +3,22 @@

name: Run tests

on: [push, pull_request]
on:
# Trigger the workflow on push but only for the master branch
push:
branches:
- master
pull_request:

jobs:
build:
test:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8]
test-name:
- integration-test

steps:
- uses: actions/checkout@v2
Expand All @@ -22,7 +29,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
pip install flake8 pytest coverage coveralls pytest-cov
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install -e .
- name: Lint with flake8
Expand All @@ -32,5 +39,23 @@ jobs:
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_FLAG_NAME: ${{ matrix.test-name }}
COVERALLS_PARALLEL: true
run: |
pytest
ivadomed_download_data -d data_testing -o testing_data
pytest --cov=./ --cov-report term-missing
coveralls
coveralls:
needs: test
runs-on: ubuntu-latest
container: python:3-slim
steps:
- name: Finished
run: |
pip3 install --upgrade coveralls
coveralls --finish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -2,7 +2,7 @@

Integrated framework for medical image analysis with deep learning.

[![Coverage Status](https://coveralls.io/repos/github/neuropoly/ivadomed/badge.svg?branch=master)](https://coveralls.io/github/neuropoly/ivadomed?branch=master)
[![Coverage Status](https://coveralls.io/repos/github/ivadomed/ivadomed/badge.svg?branch=master)](https://coveralls.io/github/ivadomed/ivadomed?branch=master)
![](https://github.com/neuropoly/ivadomed/workflows/Python%20package/badge.svg)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md)

Expand Down
7 changes: 4 additions & 3 deletions ivadomed/loader/loader.py
Expand Up @@ -517,7 +517,7 @@ class MRI3DSubVolumeSegmentationDataset(Dataset):
space.
"""

def __init__(self, filename_pairs, transform=None, length=(64, 64, 64), stride=(0, 0, 0), slice_axis=0,
def __init__(self, filename_pairs, transform=None, length=(64, 64, 64), stride=(0, 0, 0), slice_axis=0, task="segmentation",
soft_gt=False):
self.filename_pairs = filename_pairs
self.handlers = []
Expand All @@ -527,6 +527,7 @@ def __init__(self, filename_pairs, transform=None, length=(64, 64, 64), stride=(
self.prepro_transforms, self.transform = transform
self.slice_axis = slice_axis
self.has_bounding_box = True
self.task = task
self.soft_gt = soft_gt

self._load_filenames()
Expand Down Expand Up @@ -673,7 +674,7 @@ class Bids3DDataset(MRI3DSubVolumeSegmentationDataset):
"""
def __init__(self, root_dir, subject_lst, target_suffix, model_params, contrast_params, slice_axis=2,
cache=True, transform=None, metadata_choice=False, roi_params=None,
multichannel=False, object_detection_params=None, soft_gt=False):
multichannel=False, object_detection_params=None, task="segmentation", soft_gt=False):
dataset = BidsDataset(root_dir,
subject_lst=subject_lst,
target_suffix=target_suffix,
Expand All @@ -686,7 +687,7 @@ def __init__(self, root_dir, subject_lst, target_suffix, model_params, contrast_
object_detection_params=object_detection_params)

super().__init__(dataset.filename_pairs, length=model_params["length_3D"], stride=model_params["stride_3D"],
transform=transform, slice_axis=slice_axis, soft_gt=soft_gt)
transform=transform, slice_axis=slice_axis, task=task, soft_gt=soft_gt)


class BidsDataset(MRI2DSegmentationDataset):
Expand Down
2 changes: 1 addition & 1 deletion ivadomed/training.py
Expand Up @@ -310,7 +310,7 @@ def train(model_params, dataset_train, dataset_val, training_params, log_directo
if thr_increment:
print('\nRunning threshold analysis to find optimal threshold')
# Choice of optimisation metric
metric = "recall_specificity" if model_params["name"] in imed_utils.imed_loader.CLASSIFIER_LIST else "dice"
metric = "recall_specificity" if imed_utils.get_task(model_params["name"]) == "classification" else "dice"
# Run analysis
optimal_thr = threshold_analysis(model=model,
val_loader=val_loader,
Expand Down
14 changes: 14 additions & 0 deletions testing/ivado_test.py
@@ -1,4 +1,18 @@
#Add custom tests here
# this test is run first so we will also do a little setup here

import ivadomed.models as imed_models
import torch
import os


def test_sample():
assert 1 == 1


def test_model_creation():
# creating basic model for test
model = imed_models.Unet()
torch.save(model, "testing_data/model_unet_test.pt")
assert os.path.isfile("testing_data/model_unet_test.pt")

2 changes: 1 addition & 1 deletion testing/test_HeMIS.py
Expand Up @@ -48,7 +48,7 @@ def test_HeMIS(p=0.0001):

roi_params = {"suffix": "_seg-manual", "slice_filter_roi": None}

train_lst = ['sub-test001']
train_lst = ['sub-unf01']
contrasts = ['T1w', 'T2w', 'T2star']

print('[INFO]: Creating dataset ...\n')
Expand Down
2 changes: 1 addition & 1 deletion testing/test_adaptative.py
Expand Up @@ -20,7 +20,7 @@

def test_hdf5():
print('[INFO]: Starting test ... \n')
train_lst = ['sub-test001']
train_lst = ['sub-unf01']

training_transform_dict = {
"Resample":
Expand Down
18 changes: 16 additions & 2 deletions testing/test_bounding_box.py
Expand Up @@ -14,7 +14,7 @@
LOG_DIR = "log"


@pytest.mark.parametrize('train_lst', [['sub-test001']])
@pytest.mark.parametrize('train_lst', [['sub-unf01']])
@pytest.mark.parametrize('target_lst', [["_lesion-manual"]])
@pytest.mark.parametrize('config', [
{
Expand All @@ -23,7 +23,9 @@
"safety_factor": [1.0, 1.0, 1.0],
"log_directory": LOG_DIR
},
"transforms_params": {"NumpyToTensor": {}},
"transforms_params": {
"Resample": {"wspace": 0.75, "hspace": 0.75},
"NumpyToTensor": {}},
"roi_params": {"suffix": "_seg-manual", "slice_filter_roi": 10},
"contrast_params": {"contrast_lst": ['T2w'], "balance": {}},
"multichannel": False,
Expand Down Expand Up @@ -95,3 +97,15 @@ def test_bounding_box(train_lst, target_lst, config):
assert seg_pair['input'][0].shape[-2:] == (mx2 - mx1, my2 - my1)

shutil.rmtree(LOG_DIR)


# testing adjust bb size
def test_adjust_bb_size():
test_coord = (0, 10, 0, 10, 0, 10)
imed_obj_detect.adjust_bb_size(test_coord, (2, 2, 2), True)


# testing bb statistic
def test_compute_bb_statistics():
imed_obj_detect.compute_bb_statistics("testing_data/bounding_box_dict.json")

5 changes: 3 additions & 2 deletions testing/test_inference.py
Expand Up @@ -22,6 +22,7 @@
PATH_BIDS = 'testing_data'
PATH_OUT = 'tmp'


@pytest.mark.parametrize('transforms_dict', [{
"Resample":
{
Expand All @@ -35,7 +36,7 @@
"NumpyToTensor": {},
"NormalizeInstance": {"applied_to": ["im"]}
}])
@pytest.mark.parametrize('test_lst', [['sub-test001']])
@pytest.mark.parametrize('test_lst', [['sub-unf01']])
@pytest.mark.parametrize('target_lst', [["_lesion-manual"], ["_seg-manual"]])
@pytest.mark.parametrize('roi_params', [{"suffix": "_seg-manual", "slice_filter_roi": 10}])
@pytest.mark.parametrize('testing_params', [{
Expand All @@ -55,7 +56,7 @@ def test_inference(transforms_dict, test_lst, target_lst, roi_params, testing_pa
"data_list": test_lst,
"dataset_type": "testing",
"requires_undo": True,
"contrast_params": {"contrast_lst": ['T2w', 'T2star'], "balance": {}},
"contrast_params": {"contrast_lst": ['T2w'], "balance": {}},
"bids_path": PATH_BIDS,
"target_suffix": target_lst,
"roi_params": roi_params,
Expand Down
133 changes: 132 additions & 1 deletion testing/test_losses.py
Expand Up @@ -7,7 +7,8 @@
import pytest
import torch

from ivadomed.losses import GeneralizedDiceLoss, MultiClassDiceLoss, TverskyLoss, FocalTverskyLoss, DiceLoss
from ivadomed.losses import GeneralizedDiceLoss, MultiClassDiceLoss, TverskyLoss, FocalTverskyLoss, DiceLoss, \
AdapWingLoss, L2loss, LossCombination, FocalDiceLoss


@pytest.mark.parametrize('params', [
Expand Down Expand Up @@ -207,3 +208,133 @@ def test_focaltverskyloss(params):
input, target, expected_value, loss_fct = params
loss = loss_fct.forward(input, target)
assert isclose(loss.detach().cpu().numpy(), expected_value, rel_tol=1e-2)


@pytest.mark.parametrize('params', [
(torch.tensor([[[[[0.0, 1.0], [0.0, 0.0]], [[0.0, 1.0], [0.0, 0.0]]]]]),
torch.tensor([[[[[0.0, 1.0], [0.0, 0.0]], [[0.0, 1.0], [0.0, 0.0]]]]]),
0.,
L2loss()),
(torch.tensor([[[[1.0, 0.0], [0.0, 1.0]]]]),
torch.tensor([[[[1.0, 0.0], [0.0, 1.0]]]]),
0.,
L2loss()),
(torch.tensor([[[[0.0, 0.0], [0.0, 0.0]]]]),
torch.tensor([[[[1.0, 0.0], [0.0, 1.0]]]]),
1.0,
L2loss()),
(torch.tensor([[[[0.5, 0.5], [0.5, 0.5]]]]),
torch.tensor([[[[0.5, 1.0], [1.0, 1.0]]]]),
0.375,
L2loss()),
])
def test_L2loss(params):
"""
test L2 loss
Args:
params (tuple): containing input tensor, target tensor, expected value, loss function
"""

input, target, expected_value, loss_fct = params
loss = loss_fct.forward(input, target)
assert isclose(loss.detach().cpu().numpy(), expected_value, rel_tol=1e-2)


@pytest.mark.parametrize('params', [
(torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
0.,
AdapWingLoss()),
(torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
torch.tensor([[[[1.0, 0.0], [1.0, 1.0]]]]),
29.0147,
AdapWingLoss()),
(torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
torch.tensor([[[[1.0, 0.0], [1.0, 1.0]]]]),
41.4496,
AdapWingLoss(omega=20)),
(torch.tensor([[[[0.5, 0.5], [0.5, 0.5]]]]),
torch.tensor([[[[0.5, 1.0], [1.0, 1.0]]]]),
8.2703,
AdapWingLoss(epsilon=2)),
])
def test_adapwingloss(params):
"""
test AdapWingLoss
Args:
params (tuple): containing input tensor, target tensor, expected value, loss function
"""

input, target, expected_value, loss_fct = params
loss = loss_fct.forward(input, target)
assert isclose(loss.detach().cpu().numpy(), expected_value, rel_tol=1e-2)


@pytest.mark.parametrize('params', [
(torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
-1.0,
LossCombination(["DiceLoss", "L2loss"], [None, None])),
(torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
torch.tensor([[[[1.0, 0.0], [1.0, 1.0]]]]),
1.0,
LossCombination(["DiceLoss", "L2loss"], [None, None])),
(torch.tensor([[[[0.5, 0.5], [0.5, 0.5]]]]),
torch.tensor([[[[0.5, 1.0], [1.0, 1.0]]]]),
-0.3173,
LossCombination(["DiceLoss", "L2loss"], [None, None])),
])
def test_losscombination(params):
"""
test LossCombination
Args:
params (tuple): containing input tensor, target tensor, expected value, loss function
"""

input, target, expected_value, loss_fct = params
loss = loss_fct.forward(input, target)
assert isclose(loss.detach().cpu().numpy(), expected_value, rel_tol=1e-2)


@pytest.mark.parametrize('params', [
(torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
-16.1181,
FocalDiceLoss()),
(torch.tensor([[[[1.0, 1.0], [0.0, 0.0]]]]),
torch.tensor([[[[1.0, 0.0], [1.0, 1.0]]]]),
3.6897,
FocalDiceLoss()),
(torch.tensor([[[[0.5, 0.5], [0.5, 0.5]]]]),
torch.tensor([[[[0.5, 1.0], [1.0, 1.0]]]]),
-1.1619,
FocalDiceLoss()),
])
def test_focaldiceloss(params):
"""
test focaldiceloss
Args:
params (tuple): containing input tensor, target tensor, expected value, loss function
"""

input, target, expected_value, loss_fct = params
loss = loss_fct.forward(input, target)
assert isclose(loss.detach().cpu().numpy(), expected_value, rel_tol=1e-2)

0 comments on commit 5a4fd51

Please sign in to comment.