mosamatic2 2.0.2__py3-none-any.whl → 2.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mosamatic2 might be problematic. Click here for more details.

Files changed (60) hide show
  1. mosamatic2/app.py +4 -0
  2. mosamatic2/cli.py +34 -0
  3. mosamatic2/commands/__init__.py +0 -0
  4. mosamatic2/commands/calculatescores.py +73 -0
  5. mosamatic2/commands/createpngsfromsegmentations.py +65 -0
  6. mosamatic2/commands/dicom2nifti.py +46 -0
  7. mosamatic2/commands/rescaledicomimages.py +54 -0
  8. mosamatic2/commands/segmentmusclefatl3tensorflow.py +55 -0
  9. mosamatic2/constants.py +6 -3
  10. mosamatic2/core/data/__init__.py +5 -0
  11. mosamatic2/core/data/dicomimage.py +18 -0
  12. mosamatic2/core/data/dicomimageseries.py +26 -0
  13. mosamatic2/core/data/dixonseries.py +22 -0
  14. mosamatic2/core/data/filedata.py +26 -0
  15. mosamatic2/core/data/multidicomimage.py +30 -0
  16. mosamatic2/core/managers/logmanager.py +0 -2
  17. mosamatic2/core/pipelines/__init__.py +1 -0
  18. mosamatic2/core/pipelines/defaultpipeline.py +79 -0
  19. mosamatic2/core/pipelines/pipeline.py +14 -0
  20. mosamatic2/core/tasks/__init__.py +5 -0
  21. mosamatic2/core/tasks/calculatescorestask/__init__.py +0 -0
  22. mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py +149 -0
  23. mosamatic2/core/tasks/createpngsfromsegmentationstask/__init__.py +0 -0
  24. mosamatic2/core/tasks/createpngsfromsegmentationstask/createpngsfromsegmentationstask.py +52 -0
  25. mosamatic2/core/tasks/dicom2niftitask/__init__.py +0 -0
  26. mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py +24 -0
  27. mosamatic2/core/tasks/rescaledicomimagestask/__init__.py +0 -0
  28. mosamatic2/core/tasks/rescaledicomimagestask/rescaledicomimagestask.py +64 -0
  29. mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/__init__.py +0 -0
  30. mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/paramloader.py +39 -0
  31. mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/segmentmusclefatl3tensorflowtask.py +121 -0
  32. mosamatic2/core/tasks/task.py +50 -0
  33. mosamatic2/core/utils.py +316 -0
  34. mosamatic2/server.py +112 -1
  35. mosamatic2/ui/mainwindow.py +150 -1
  36. mosamatic2/ui/resources/VERSION +1 -1
  37. mosamatic2/ui/widgets/dialogs/__init__.py +0 -0
  38. mosamatic2/ui/widgets/dialogs/dialog.py +16 -0
  39. mosamatic2/ui/widgets/dialogs/helpdialog.py +9 -0
  40. mosamatic2/ui/widgets/panels/__init__.py +0 -0
  41. mosamatic2/ui/widgets/panels/defaultpanel.py +31 -0
  42. mosamatic2/ui/widgets/panels/logpanel.py +65 -0
  43. mosamatic2/ui/widgets/panels/mainpanel.py +82 -0
  44. mosamatic2/ui/widgets/panels/pipelines/__init__.py +0 -0
  45. mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py +299 -0
  46. mosamatic2/ui/widgets/panels/stackedpanel.py +22 -0
  47. mosamatic2/ui/widgets/panels/taskpanel.py +6 -0
  48. mosamatic2/ui/widgets/panels/tasks/__init__.py +0 -0
  49. mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py +215 -0
  50. mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py +186 -0
  51. mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py +183 -0
  52. mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py +184 -0
  53. mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py +216 -0
  54. mosamatic2/ui/widgets/panels/tasks/selectslicefromscantaskpanel.py +184 -0
  55. mosamatic2/ui/worker.py +29 -0
  56. {mosamatic2-2.0.2.dist-info → mosamatic2-2.0.4.dist-info}/METADATA +6 -2
  57. mosamatic2-2.0.4.dist-info/RECORD +74 -0
  58. {mosamatic2-2.0.2.dist-info → mosamatic2-2.0.4.dist-info}/entry_points.txt +1 -0
  59. mosamatic2-2.0.2.dist-info/RECORD +0 -26
  60. {mosamatic2-2.0.2.dist-info → mosamatic2-2.0.4.dist-info}/WHEEL +0 -0
@@ -0,0 +1,149 @@
1
+ import os
2
+ import numpy as np
3
+ import pandas as pd
4
+ from mosamatic2.core.tasks.task import Task
5
+ from mosamatic2.core.managers.logmanager import LogManager
6
+ from mosamatic2.core.data.multidicomimage import MultiDicomImage
7
+ from mosamatic2.core.utils import (
8
+ is_dicom,
9
+ load_dicom,
10
+ is_jpeg2000_compressed,
11
+ get_pixels_from_dicom_object,
12
+ calculate_area,
13
+ calculate_mean_radiation_attenuation,
14
+ get_pixels_from_tag_file,
15
+ MUSCLE,
16
+ SAT,
17
+ VAT,
18
+ )
19
+
20
+ LOG = LogManager()
21
+
22
+
23
+ class CalculateScoresTask(Task):
24
+ INPUTS = [
25
+ 'images',
26
+ 'segmentations'
27
+ ]
28
+ PARAMS = ['file_type']
29
+
30
+ def __init__(self, inputs, params, output, overwrite=True):
31
+ super(CalculateScoresTask, self).__init__(inputs, params, output, overwrite)
32
+
33
+ def collect_img_seg_pairs(self, images, segmentations, file_type='npy'):
34
+ file_type = '.tag' if file_type == 'tag' else '.seg.npy'
35
+ img_seg_pairs = []
36
+ for image in images:
37
+ f_img_path = image.path()
38
+ f_img_name = os.path.split(f_img_path)[1]
39
+ for f_seg_path in segmentations:
40
+ f_seg_name = os.path.split(f_seg_path)[1]
41
+ if file_type == '.seg.npy':
42
+ f_seg_name = f_seg_name.removesuffix(file_type)
43
+ if f_seg_name == f_img_name:
44
+ # img_seg_pairs.append((f_img_path, f_seg_path))
45
+ img_seg_pairs.append((image, f_seg_path))
46
+ elif file_type == '.tag':
47
+ f_seg_name = f_seg_name.removesuffix(file_type).removesuffix('.dcm')
48
+ f_img_name = f_img_name.removesuffix('.dcm')
49
+ if f_seg_name == f_img_name:
50
+ # img_seg_pairs.append((f_img_path, f_seg_path))
51
+ img_seg_pairs.append((image, f_seg_path))
52
+ else:
53
+ raise RuntimeError('Unknown file type')
54
+ return img_seg_pairs
55
+
56
+ def load_images(self):
57
+ image_data = MultiDicomImage()
58
+ image_data.set_path(self.input('images'))
59
+ if image_data.load():
60
+ return image_data
61
+ raise RuntimeError('Could not load images')
62
+
63
+ def load_pixels_and_spacing(self, image):
64
+ p = image.object()
65
+ pixels = get_pixels_from_dicom_object(p, normalize=True)
66
+ return pixels, p.PixelSpacing
67
+
68
+ def load_segmentations(self, file_type='npy'):
69
+ file_type = '.tag' if file_type == 'tag' else '.seg.npy'
70
+ segmentations = []
71
+ for f in os.listdir(self.input('segmentations')):
72
+ f_path = os.path.join(self.input('segmentations'), f)
73
+ if f.endswith(file_type):
74
+ segmentations.append(f_path)
75
+ if len(segmentations) == 0:
76
+ raise RuntimeError('Input directory has no segmentation files')
77
+ return segmentations
78
+
79
+ def load_segmentation(self, f, file_type='npy'):
80
+ if file_type == 'npy':
81
+ return np.load(f)
82
+ if file_type == 'tag':
83
+ pixels = get_pixels_from_tag_file(f)
84
+ try:
85
+ pixels = pixels.reshape(512, 512)
86
+ return pixels
87
+ except Exception:
88
+ LOG.warning(f'Could not reshape TAG pixels to (512, 512), skipping...')
89
+ return None
90
+ raise RuntimeError('Unknown file type')
91
+
92
+ def run(self):
93
+ image_data = self.load_images()
94
+ images = image_data.images()
95
+ file_type = self.param('file_type')
96
+ segmentations = self.load_segmentations(file_type)
97
+ img_seg_pairs = self.collect_img_seg_pairs(images, segmentations, file_type)
98
+ # Create empty data dictionary
99
+ data = {
100
+ 'file': [],
101
+ 'muscle_area': [], 'muscle_idx': [], 'muscle_ra': [],
102
+ 'vat_area': [], 'vat_idx': [], 'vat_ra': [],
103
+ 'sat_area': [], 'sat_idx': [], 'sat_ra': []
104
+ }
105
+ nr_steps = len(img_seg_pairs)
106
+ for step in range(nr_steps):
107
+ # Get image and its pixel spacing
108
+ image, pixel_spacing = self.load_pixels_and_spacing(img_seg_pairs[step][0])
109
+ if image is None:
110
+ raise RuntimeError(f'Could not load DICOM image for file {img_seg_pairs[step][0]}')
111
+ # Get segmentation for this image
112
+ segmentation = self.load_segmentation(img_seg_pairs[step][1], file_type)
113
+ if segmentation is None:
114
+ LOG.warning(f'Could not load segmentation for file {img_seg_pairs[step][1]}')
115
+ continue
116
+ # Calculate metrics
117
+ file_name = os.path.split(img_seg_pairs[step][0].path())[1]
118
+ muscle_area = calculate_area(segmentation, MUSCLE, pixel_spacing)
119
+ muscle_idx = 0
120
+ muscle_ra = calculate_mean_radiation_attenuation(image, segmentation, MUSCLE)
121
+ vat_area = calculate_area(segmentation, VAT, pixel_spacing)
122
+ vat_idx = 0
123
+ vat_ra = calculate_mean_radiation_attenuation(image, segmentation, VAT)
124
+ sat_area = calculate_area(segmentation, SAT, pixel_spacing)
125
+ sat_idx = 0
126
+ sat_ra = calculate_mean_radiation_attenuation(image, segmentation, SAT)
127
+ LOG.info(f'file: {file_name}, ' +
128
+ f'muscle_area: {muscle_area}, muscle_idx: {muscle_idx}, muscle_ra: {muscle_ra}, ' +
129
+ f'vat_area: {vat_area}, vat_idx: {vat_idx}, vat_ra: {vat_ra}, ' +
130
+ f'sat_area: {sat_area}, sat_idx: {sat_idx}, sat_ra: {sat_ra}')
131
+ # Update dataframe data
132
+ data['file'].append(file_name)
133
+ data['muscle_area'].append(muscle_area)
134
+ data['muscle_idx'].append(muscle_idx)
135
+ data['muscle_ra'].append(muscle_ra)
136
+ data['vat_area'].append(vat_area)
137
+ data['vat_idx'].append(vat_idx)
138
+ data['vat_ra'].append(vat_ra)
139
+ data['sat_area'].append(sat_area)
140
+ data['sat_idx'].append(sat_idx)
141
+ data['sat_ra'].append(sat_ra)
142
+ # Update progress
143
+ self.set_progress(step, nr_steps)
144
+ # Build dataframe and return the CSV file as output
145
+ csv_file_path = os.path.join(self.output(), 'bc_scores.csv')
146
+ xls_file_path = os.path.join(self.output(), 'bc_scores.xlsx')
147
+ df = pd.DataFrame(data=data)
148
+ df.to_csv(csv_file_path, index=False, sep=';')
149
+ df.to_excel(xls_file_path, index=False, engine='openpyxl')
@@ -0,0 +1,52 @@
1
+ import os
2
+ from mosamatic2.core.tasks.task import Task
3
+ from mosamatic2.core.managers.logmanager import LogManager
4
+ from mosamatic2.core.utils import (
5
+ convert_numpy_array_to_png_image,
6
+ AlbertaColorMap,
7
+ load_numpy_array,
8
+ )
9
+
10
+ LOG = LogManager()
11
+
12
+
13
+ class CreatePngsFromSegmentationsTask(Task):
14
+ INPUTS = ['segmentations']
15
+ PARAMS = [
16
+ 'fig_width',
17
+ 'fig_height'
18
+ ]
19
+
20
+ def __init__(self, inputs, params, output, overwrite=True):
21
+ super(CreatePngsFromSegmentationsTask, self).__init__(inputs, params, output, overwrite)
22
+
23
+ def load_segmentations(self):
24
+ segmentations = []
25
+ for f in os.listdir(self.input('segmentations')):
26
+ f_path = os.path.join(self.input('segmentations'), f)
27
+ if f.endswith('.seg.npy'):
28
+ segmentations.append(f_path)
29
+ if len(segmentations) == 0:
30
+ raise RuntimeError('Input directory has no segmentation files')
31
+ return segmentations
32
+
33
+ def run(self):
34
+ segmnentations = self.load_segmentations()
35
+ nr_steps = len(segmnentations)
36
+ for step in range(nr_steps):
37
+ source = segmnentations[step]
38
+ source_name = os.path.split(source)[1]
39
+ source_image = load_numpy_array(source)
40
+ if source_image is not None:
41
+ png_file_name = source_name + '.png'
42
+ convert_numpy_array_to_png_image(
43
+ source_image,
44
+ self.output(),
45
+ AlbertaColorMap(),
46
+ png_file_name,
47
+ fig_width=int(self.param('fig_width')),
48
+ fig_height=int(self.param('fig_height')),
49
+ )
50
+ else:
51
+ LOG.warning(f'File {source} is not a valid NumPy array file')
52
+ self.set_progress(step, nr_steps)
File without changes
@@ -0,0 +1,24 @@
1
+ import os
2
+ import dicom2nifti
3
+ from mosamatic2.core.tasks.task import Task
4
+ from mosamatic2.core.managers.logmanager import LogManager
5
+
6
+ LOG = LogManager()
7
+
8
+
9
+ class Dicom2NiftiTask(Task):
10
+ INPUTS = ['images']
11
+ PARAMS = []
12
+
13
+ def __init__(self, inputs, params, output, overwrite):
14
+ super(Dicom2NiftiTask, self).__init__(inputs, params, output, overwrite)
15
+
16
+ def run(self):
17
+ nifti_file_name = os.path.split(self.input('images'))[1] + '.nii.gz'
18
+ LOG.info(f'Converting DICOM directory to {nifti_file_name}')
19
+ dicom2nifti.dicom_series_to_nifti(
20
+ self.input('images'),
21
+ os.path.join(self.output(), nifti_file_name),
22
+ reorient_nifti=True,
23
+ )
24
+ self.set_progress(0, 1)
@@ -0,0 +1,64 @@
1
+ import os
2
+ import shutil
3
+ import numpy as np
4
+ from mosamatic2.core.tasks.task import Task
5
+ from mosamatic2.core.managers.logmanager import LogManager
6
+ from mosamatic2.core.data.multidicomimage import MultiDicomImage
7
+ from mosamatic2.core.data.dicomimage import DicomImage
8
+ from mosamatic2.core.data.multidicomimage import MultiDicomImage
9
+ from scipy.ndimage import zoom
10
+
11
+ LOG = LogManager()
12
+
13
+
14
+ class RescaleDicomImagesTask(Task):
15
+ INPUTS = ['images']
16
+ PARAMS = ['target_size']
17
+
18
+ def __init__(self, inputs, params, output=None, overwrite=True):
19
+ super(RescaleDicomImagesTask, self).__init__(inputs, params, output, overwrite)
20
+
21
+ def rescale_image(self, p, target_size):
22
+ pixel_array = p.pixel_array
23
+ hu_array = pixel_array * p.RescaleSlope + p.RescaleIntercept
24
+ hu_air = -1000
25
+ new_rows = max(p.Rows, p.Columns)
26
+ new_cols = max(p.Rows, p.Columns)
27
+ padded_hu_array = np.full((new_rows, new_cols), hu_air, dtype=hu_array.dtype)
28
+ padded_hu_array[:pixel_array.shape[0], :pixel_array.shape[1]] = hu_array
29
+ pixel_array_padded = (padded_hu_array - p.RescaleIntercept) / p.RescaleSlope
30
+ pixel_array_padded = pixel_array_padded.astype(pixel_array.dtype) # Image now has largest dimensions
31
+ pixel_array_rescaled = zoom(pixel_array_padded, zoom=(target_size / new_rows), order=3) # Cubic interpolation
32
+ pixel_array_rescaled = pixel_array_rescaled.astype(pixel_array.dtype)
33
+ original_pixel_spacing = p.PixelSpacing
34
+ new_pixel_spacing = [ps * (new_rows / target_size) for ps in original_pixel_spacing]
35
+ p.PixelSpacing = new_pixel_spacing
36
+ p.PixelData = pixel_array_rescaled.tobytes()
37
+ p.Rows = target_size
38
+ p.Columns = target_size
39
+ return p
40
+
41
+ def run(self):
42
+ image_data = MultiDicomImage()
43
+ image_data.set_path(self.input('images'))
44
+ if image_data.load():
45
+ images = image_data.images()
46
+ nr_steps = len(images)
47
+ for step in range(nr_steps):
48
+ source = images[step]
49
+ assert isinstance(source, DicomImage)
50
+ p = source.object()
51
+ if len(p.pixel_array.shape) == 2:
52
+ source_name = os.path.split(source.path())[1]
53
+ if p.Rows != self.param('target_size') or p.Columns != self.param('target_size'):
54
+ p = self.rescale_image(p, self.param('target_size'))
55
+ target = os.path.join(self.output(), source_name)
56
+ p.save_as(target)
57
+ else:
58
+ target = os.path.join(self.output(), source_name)
59
+ shutil.copy(source.path(), target)
60
+ else:
61
+ LOG.warning(f'Shape of pixel data in file {source.path()} should be 2D but is {len(p.pixel_array.shape)}D')
62
+ self.set_progress(step, nr_steps)
63
+ else:
64
+ LOG.error('Error loading multi-DICOM image data')
@@ -0,0 +1,39 @@
1
+ import json
2
+
3
+
4
+ class ParamLoader:
5
+ def __init__(self, json_path):
6
+ self.update(json_path)
7
+
8
+ def save(self, json_path):
9
+ """"
10
+ Save dict to json file
11
+
12
+ Parameters
13
+ ----------
14
+ json_path : string
15
+ Path to save location
16
+ """
17
+ with open(json_path, 'w') as f:
18
+ json.dump(self.__dict__, f, indent=4)
19
+
20
+ def update(self, json_path):
21
+ """
22
+ Load parameters from json file
23
+
24
+ Parameters
25
+ ----------
26
+ json_path : string
27
+ Path to json file
28
+ """
29
+ with open(json_path) as f:
30
+ params = json.load(f)
31
+ self.__dict__.update(params)
32
+
33
+ @property
34
+ def dict(self):
35
+ """"
36
+ Give dict-like access to Params instance
37
+ by: 'params.dict['learning_rate']'
38
+ """
39
+ return self.__dict__
@@ -0,0 +1,121 @@
1
+ import os
2
+ import zipfile
3
+ import tempfile
4
+ import numpy as np
5
+
6
+ import models
7
+
8
+ from mosamatic2.core.tasks.task import Task
9
+ from mosamatic2.core.tasks.segmentmusclefatl3tensorflowtask.paramloader import ParamLoader
10
+ from mosamatic2.core.data.multidicomimage import MultiDicomImage
11
+ from mosamatic2.core.data.dicomimage import DicomImage
12
+ from mosamatic2.core.utils import (
13
+ normalize_between,
14
+ get_pixels_from_dicom_object,
15
+ convert_labels_to_157,
16
+ )
17
+
18
+ DEVICE = 'cpu'
19
+
20
+
21
+ class SegmentMuscleFatL3TensorFlowTask(Task):
22
+ INPUTS = [
23
+ 'images',
24
+ 'model_files'
25
+ ]
26
+ PARAMS = ['model_version']
27
+
28
+ def __init__(self, inputs, params, output, overwrite=True):
29
+ super(SegmentMuscleFatL3TensorFlowTask, self).__init__(inputs, params, output, overwrite)
30
+
31
+ def load_images(self):
32
+ image_data = MultiDicomImage()
33
+ image_data.set_path(self.input('images'))
34
+ if image_data.load():
35
+ return image_data
36
+ raise RuntimeError('Could not load images')
37
+
38
+ def load_model_files(self):
39
+ model_files = []
40
+ for f in os.listdir(self.input('model_files')):
41
+ f_path = os.path.join(self.input('model_files'), f)
42
+ if f_path.endswith('.zip') or f_path.endswith('.json'):
43
+ model_files.append(f_path)
44
+ if len(model_files) != 3:
45
+ raise RuntimeError(f'Found {len(model_files)} model files. This should be 3!')
46
+ return model_files
47
+
48
+ def load_models_and_params(self, model_files, model_version):
49
+ tfLoaded = False
50
+ model, contour_model, params = None, None, None
51
+ for f_path in model_files:
52
+ f_name = os.path.split(f_path)[1]
53
+ if f_name == f'model-{str(model_version)}.zip':
54
+ if not tfLoaded:
55
+ import tensorflow as tf
56
+ tfLoaded = True
57
+ with tempfile.TemporaryDirectory() as model_dir_unzipped:
58
+ # model_dir_unzipped = os.path.join(os.path.split(f_path)[0], 'model_unzipped')
59
+ os.makedirs(model_dir_unzipped, exist_ok=True)
60
+ with zipfile.ZipFile(f_path) as zipObj:
61
+ zipObj.extractall(path=model_dir_unzipped)
62
+ model = tf.keras.models.load_model(model_dir_unzipped, compile=False)
63
+ elif f_name == f'contour_model-{str(model_version)}.zip':
64
+ if not tfLoaded:
65
+ import tensorflow as tf
66
+ tfLoaded = True
67
+ with tempfile.TemporaryDirectory() as contour_model_dir_unzipped:
68
+ # contour_model_dir_unzipped = os.path.join(os.path.split(f_path)[0], 'contour_model_unzipped')
69
+ os.makedirs(contour_model_dir_unzipped, exist_ok=True)
70
+ with zipfile.ZipFile(f_path) as zipObj:
71
+ zipObj.extractall(path=contour_model_dir_unzipped)
72
+ contour_model = tf.keras.models.load_model(contour_model_dir_unzipped, compile=False)
73
+ elif f_name == f'params-{model_version}.json':
74
+ params = ParamLoader(f_path)
75
+ else:
76
+ pass
77
+ return model, contour_model, params
78
+
79
+ def extract_contour(self, image, contour_model, params):
80
+ ct = np.copy(image)
81
+ ct = normalize_between(ct, params.dict['min_bound_contour'], params.dict['max_bound_contour'])
82
+ img2 = np.expand_dims(ct, 0)
83
+ img2 = np.expand_dims(img2, -1)
84
+ pred = contour_model.predict([img2])
85
+ pred_squeeze = np.squeeze(pred)
86
+ pred_max = pred_squeeze.argmax(axis=-1)
87
+ mask = np.uint8(pred_max)
88
+ return mask
89
+
90
+ def segment_muscle_and_fat(self, image, model):
91
+ img2 = np.expand_dims(image, 0)
92
+ img2 = np.expand_dims(img2, -1)
93
+ pred = model.predict([img2])
94
+ pred_squeeze = np.squeeze(pred)
95
+ pred_max = pred_squeeze.argmax(axis=-1)
96
+ return pred_max
97
+
98
+ def process_file(self, image, output_dir, model, contour_model, params):
99
+ assert isinstance(image, DicomImage)
100
+ pixels = get_pixels_from_dicom_object(image.object(), normalize=True)
101
+ if contour_model:
102
+ mask = self.extract_contour(pixels, contour_model, params)
103
+ pixels = normalize_between(pixels, params.dict['min_bound'], params.dict['max_bound'])
104
+ pixels = pixels * mask
105
+ pixels = pixels.astype(np.float32)
106
+ segmentation = self.segment_muscle_and_fat(pixels, model)
107
+ segmentation = convert_labels_to_157(segmentation)
108
+ segmentation_file_name = os.path.split(image.path())[1]
109
+ segmentation_file_path = os.path.join(output_dir, f'{segmentation_file_name}.seg.npy')
110
+ np.save(segmentation_file_path, segmentation)
111
+
112
+ def run(self):
113
+ image_data = self.load_images()
114
+ model_files = self.load_model_files()
115
+ model_version = self.param('model_version')
116
+ model, contour_model, params = self.load_models_and_params(model_files, model_version)
117
+ images = image_data.images()
118
+ nr_steps = len(images)
119
+ for step in range(nr_steps):
120
+ self.process_file(images[step], self.output(), model, contour_model, params)
121
+ self.set_progress(step, nr_steps)
@@ -0,0 +1,50 @@
1
+ import os
2
+ import shutil
3
+ from mosamatic2.core.managers.logmanager import LogManager
4
+ from mosamatic2.core.utils import create_name_with_timestamp, mosamatic_output_dir
5
+
6
+ LOG = LogManager()
7
+
8
+
9
+ class Task:
10
+ INPUTS = []
11
+ PARAMS = []
12
+ OUTPUT = 'output'
13
+
14
+ def __init__(self, inputs, params, output, overwrite=True):
15
+ self._inputs = inputs
16
+ self._params = params
17
+ self._output = os.path.join(output, self.__class__.__name__.lower())
18
+ self._overwrite = overwrite
19
+ if self._overwrite and os.path.isdir(self._output):
20
+ shutil.rmtree(self._output)
21
+ os.makedirs(self._output, exist_ok=self._overwrite)
22
+ # Check that the inputs match specification and type
23
+ assert isinstance(self._inputs, dict)
24
+ assert len(self._inputs.keys()) == len(self.__class__.INPUTS)
25
+ for k, v in self._inputs.items():
26
+ assert k in self.__class__.INPUTS
27
+ assert isinstance(v, str)
28
+ # Check that param names match specification (if not None)
29
+ if self._params:
30
+ assert len(self._params.keys()) == len(self.__class__.PARAMS)
31
+ for k in self._params.keys():
32
+ assert k in self.__class__.PARAMS
33
+
34
+ def input(self, name):
35
+ return self._inputs[name]
36
+
37
+ def param(self, name):
38
+ return self._params[name]
39
+
40
+ def output(self):
41
+ return self._output
42
+
43
+ def overwrite(self):
44
+ return self._overwrite
45
+
46
+ def set_progress(self, step, nr_steps):
47
+ LOG.info(f'[{self.__class__.__name__}] step {step} from {nr_steps}')
48
+
49
+ def run(self):
50
+ raise NotImplementedError()