Skip to content

Commit 9fbdbca

Browse files
authored
Docker support (#8)
* Starting to include Docker support [skip ci] * Docker unit test running, but permission issues to delete the results folder (created folders/files belong to root....) [skip ci] * Updating the readme [skip ci] * Adding Docker call parameter for read/write permissions [skip ci]
1 parent ef2516e commit 9fbdbca

File tree

5 files changed

+203
-12
lines changed

5 files changed

+203
-12
lines changed

.dockerignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
venv/
2+
venv*
3+
*unit_tests_results_dir/
4+
notebooks/
5+
tests/

Dockerfile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FROM python:3.8-slim
2+
3+
MAINTAINER David Bouget <david.bouget@sintef.no>
4+
5+
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
6+
7+
RUN apt-get update -y
8+
RUN apt-get upgrade -y
9+
RUN apt-get -y install sudo
10+
RUN apt-get update && apt-get install -y git
11+
12+
WORKDIR /workspace
13+
14+
RUN git clone https://github.com/dbouget/validation_metrics_computation.git
15+
RUN pip3 install --upgrade pip
16+
RUN pip3 install -e validation_metrics_computation/
17+
18+
RUN mkdir /workspace/resources
19+
20+
# CMD ["/bin/bash"]
21+
ENTRYPOINT ["python3", "/workspace/validation_metrics_computation/main.py"]

README.md

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,21 +192,60 @@ An additional explanation of all parameters specified in the configuration file
192192
found in _/Utils/resources.py_.
193193

194194
</details>
195-
195+
196196
<details>
197197
<summary>
198198

199199
### [3. Process](https://github.com/dbouget/validation_metrics_computation#3-process)
200200
</summary>
201-
To run, you need to supply the configuration file as parameter.
202201

202+
After filling in the configuration file specifying all runtime parameters,
203+
according to the pattern from [**blank_main_config.ini**](https://github.com/dbouget/validation_metrics_computation/blob/master/blank_main_config.ini),
204+
you should run first the __validation__ task and then the __study__ task.
205+
206+
207+
#### [CLI](https://github.com/dbouget/validation_metrics_computation#cli)
208+
```
209+
raidionicsval -c CONFIG (-v debug)
210+
```
211+
212+
CONFIG should point to a configuration file (*.ini).
213+
214+
#### [Python module](https://github.com/dbouget/validation_metrics_computation#python-module)
215+
```
216+
from raidionicsval import compute
217+
compute(config_filename="/path/to/main_config.ini")
218+
```
219+
220+
"/path/to/main_config.ini" should point to a valid configuration file.
221+
222+
#### [Docker](https://github.com/dbouget/validation_metrics_computation#docker)
223+
When calling Docker images, the --user flag must be properly used in order for the folders and files created inside
224+
the container to inherit the proper read/write permissions. The user ID is retrieved on-the-fly in the following
225+
examples, but it can be given in a more hard-coded fashion if known by the user.
226+
227+
```
228+
docker pull dbouget/raidionics-val:v1.0-py38-cpu
229+
```
230+
231+
For opening the Docker image and interacting with it, run:
232+
```
233+
docker run --entrypoint /bin/bash -v /home/<username>/<resources_path>:/workspace/resources -t -i --network=host --ipc=host --user $(id -u) dbouget/raidionics-val:v1.0-py38-cpu
234+
```
235+
236+
The `/home/<username>/<resources_path>` before the column sign has to be changed to match a directory on your local
237+
machine containing the data to expose to the docker image. Namely, it must contain folder(s) with data to use as input
238+
for the validation studies, and it will contain the destination folder where the results will be saved.
239+
240+
For launching the Docker image as a CLI, run:
203241
```
204-
python main.py -c main_config.ini (-v debug)
242+
docker run -v /home/<username>/<resources_path>:/workspace/resources -t -i --network=host --ipc=host --user $(id -u) dbouget/raidionics-val:v1.0-py38-cpu -c /workspace/resources/<path>/<to>/main_config.ini -v <verbose>
205243
```
206244

207-
After filling in the configuration file, you should run first the
208-
__validation__ task and then the __study__ task.
209-
N.B. If no study fits your need, you can create a new study file in _/Studies/_.
245+
The `<path>/<to>/main_config.ini` must point to a valid configuration file on your machine, as a relative path to the `/home/<username>/<resources_path>` described above.
246+
For example, if the file is located on my machine under `/home/myuser/Data/Validation/main_config.ini`,
247+
and that `/home/myuser/Data` is the mounted resources partition mounted on the Docker image, the new relative path will be `Validation/main_config.ini`.
248+
The `<verbose>` level can be selected from [debug, info, warning, error].
210249

211250
</details>
212251

tests/validation_pipeline_test.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ def validation_pipeline_test():
4343
raise ValueError('Resources download or extraction failed, content not available on disk.')
4444
except Exception as e:
4545
logging.error("Error during resources download with: \n {}.\n".format(traceback.format_exc()))
46-
shutil.rmtree(test_dir)
46+
if os.path.exists(test_dir):
47+
shutil.rmtree(test_dir)
4748
raise ValueError("Error during resources download.\n")
4849

4950
logging.info("Preparing configuration file.\n")
@@ -80,7 +81,8 @@ def validation_pipeline_test():
8081
scores_filename = os.path.join(test_dir, 'StudyResults', 'Validation', 'all_dice_scores.csv')
8182
if not os.path.exists(scores_filename):
8283
logging.error("k-fold cross-validation unit test failed, no scores were generated.\n")
83-
shutil.rmtree(test_dir)
84+
if os.path.exists(test_dir):
85+
shutil.rmtree(test_dir)
8486
raise ValueError("k-fold cross-validation unit test failed, no scores were generated.\n")
8587

8688
logging.info("k-fold cross-validation CLI unit test started.\n")
@@ -100,23 +102,27 @@ def validation_pipeline_test():
100102
'--verbose', 'debug'])
101103
except Exception as e:
102104
logging.error("Error during k-fold cross-validation CLI unit test with: \n {}.\n".format(traceback.format_exc()))
103-
shutil.rmtree(test_dir)
105+
if os.path.exists(test_dir):
106+
shutil.rmtree(test_dir)
104107
raise ValueError("Error during k-fold cross-validation CLI unit test.\n")
105108

106109
logging.info("Collecting and comparing results.\n")
107110
scores_filename = os.path.join(test_dir, 'StudyResults', 'Validation', 'all_dice_scores.csv')
108111
if not os.path.exists(scores_filename):
109112
logging.error("k-fold cross-validation CLI unit test failed, no scores were generated.\n")
110-
shutil.rmtree(test_dir)
113+
if os.path.exists(test_dir):
114+
shutil.rmtree(test_dir)
111115
raise ValueError("k-fold cross-validation CLI unit test failed, no scores were generated.\n")
112116
logging.info("k-fold cross-validation CLI unit test succeeded.\n")
113117
except Exception as e:
114118
logging.error("Error during k-fold cross-validation unit test with: \n {}.\n".format(traceback.format_exc()))
115-
shutil.rmtree(test_dir)
119+
if os.path.exists(test_dir):
120+
shutil.rmtree(test_dir)
116121
raise ValueError("Error during k-fold cross-validation unit test with.\n")
117122

118123
logging.info("k-fold cross-validation unit test succeeded.\n")
119-
shutil.rmtree(test_dir)
124+
if os.path.exists(test_dir):
125+
shutil.rmtree(test_dir)
120126

121127

122128
validation_pipeline_test()
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import os
2+
import shutil
3+
import configparser
4+
import logging
5+
import sys
6+
import subprocess
7+
import traceback
8+
import zipfile
9+
10+
try:
11+
import requests
12+
except:
13+
subprocess.check_call([sys.executable, "-m", "pip", "install", 'requests'])
14+
import requests
15+
16+
17+
def inference_test_docker():
18+
"""
19+
Testing the CLI within a Docker container for the validation unit test, running on CPU.
20+
The latest Docker image is being hosted at: dbouget/raidionics-val:v1.0-py38-cpu
21+
22+
Returns
23+
-------
24+
25+
"""
26+
logging.basicConfig()
27+
logging.getLogger().setLevel(logging.DEBUG)
28+
logging.info("Running validation unit test in Docker container.\n")
29+
logging.info("Downloading unit test resources.\n")
30+
test_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'unit_tests_results_dir')
31+
if os.path.exists(test_dir):
32+
shutil.rmtree(test_dir)
33+
os.makedirs(test_dir)
34+
35+
try:
36+
resources_url = 'https://github.com/raidionics/Raidionics-models/releases/download/1.2.0/Samples-RaidionicsValLib_UnitTest1.zip'
37+
38+
archive_dl_dest = os.path.join(test_dir, 'resources.zip')
39+
headers = {}
40+
response = requests.get(resources_url, headers=headers, stream=True)
41+
response.raise_for_status()
42+
if response.status_code == requests.codes.ok:
43+
with open(archive_dl_dest, "wb") as f:
44+
for chunk in response.iter_content(chunk_size=1048576):
45+
f.write(chunk)
46+
with zipfile.ZipFile(archive_dl_dest, 'r') as zip_ref:
47+
zip_ref.extractall(test_dir)
48+
49+
if not os.path.exists(os.path.join(test_dir, 'Input_dataset')):
50+
raise ValueError('Resources download or extraction failed, content not available on disk.')
51+
except Exception as e:
52+
logging.error("Error during resources download with: \n {}.\n".format(traceback.format_exc()))
53+
if os.path.exists(test_dir):
54+
shutil.rmtree(test_dir)
55+
raise ValueError("Error during resources download.\n")
56+
57+
logging.info("Preparing configuration file.\n")
58+
try:
59+
val_config = configparser.ConfigParser()
60+
val_config.add_section('Default')
61+
val_config.set('Default', 'task', "validation")
62+
val_config.set('Default', 'data_root', '/workspace/resources/Input_dataset')
63+
val_config.set('Default', 'number_processes', '1')
64+
val_config.add_section('Validation')
65+
val_config.set('Validation', 'input_folder', '/workspace/resources/StudyResults')
66+
val_config.set('Validation', 'output_folder', '/workspace/resources/StudyResults')
67+
val_config.set('Validation', 'gt_files_suffix', 'label_tumor.nii.gz')
68+
val_config.set('Validation', 'prediction_files_suffix', 'pred_tumor.nii.gz')
69+
val_config.set('Validation', 'use_index_naming_convention', 'false')
70+
val_config.set('Validation', 'nb_folds', '1')
71+
val_config.set('Validation', 'split_way', 'three-way')
72+
val_config.set('Validation', 'detection_overlap_thresholds', '0.')
73+
val_config.set('Validation', 'metrics_space', 'pixelwise, objectwise')
74+
val_config.set('Validation', 'class_names', 'tumor')
75+
val_config.set('Validation', 'extra_metrics', 'IOU')
76+
val_config.set('Validation', 'tiny_objects_removal_threshold', '25')
77+
val_config.set('Validation', 'true_positive_volume_thresholds', '0.1')
78+
val_config.set('Validation', 'use_brats_data', 'false')
79+
val_config_filename = os.path.join(test_dir, 'test_val_config.ini')
80+
with open(val_config_filename, 'w') as outfile:
81+
val_config.write(outfile)
82+
83+
logging.info("Running validation unit test in Docker container.\n")
84+
try:
85+
import platform
86+
cmd_docker = ['docker', 'run', '-v', '{}:/workspace/resources'.format(test_dir),
87+
'--network=host', '--ipc=host', '--user', str(os.geteuid()),
88+
'dbouget/raidionics-val:v1.0-py38-cpu',
89+
'-c', '/workspace/resources/test_val_config.ini', '-v', 'debug']
90+
logging.info("Executing the following Docker call: {}".format(cmd_docker))
91+
if platform.system() == 'Windows':
92+
subprocess.check_call(cmd_docker, shell=True)
93+
else:
94+
subprocess.check_call(cmd_docker)
95+
except Exception as e:
96+
logging.error("Error during validation test in Docker container with: \n {}.\n".format(traceback.format_exc()))
97+
if os.path.exists(test_dir):
98+
shutil.rmtree(test_dir)
99+
raise ValueError("Error during validation test in Docker container.\n")
100+
101+
logging.info("Collecting and comparing results.\n")
102+
scores_segmentation_filename = os.path.join(test_dir, 'StudyResults', 'Validation', 'all_dice_scores.csv')
103+
if not os.path.exists(scores_segmentation_filename):
104+
logging.error("Validation in Docker container failed, no dice scores were generated.\n")
105+
if os.path.exists(test_dir):
106+
shutil.rmtree(test_dir)
107+
raise ValueError("Validation in Docker container failed, no dice scores were generated.\n")
108+
logging.info("Validation unit test in Docker container succeeded.\n")
109+
except Exception as e:
110+
logging.error("Error during validation in Docker container with: \n {}.\n".format(traceback.format_exc()))
111+
if os.path.exists(test_dir):
112+
shutil.rmtree(test_dir)
113+
raise ValueError("Error during validation in Docker container with.\n")
114+
115+
logging.info("Validation unit test in Docker container succeeded.\n")
116+
if os.path.exists(test_dir):
117+
shutil.rmtree(test_dir)
118+
119+
120+
inference_test_docker()

0 commit comments

Comments
 (0)