mosamatic2 2.0.10__py3-none-any.whl → 2.0.12__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.

@@ -3,12 +3,12 @@ import click
3
3
  from mosamatic2.core.tasks import Dicom2NiftiTask
4
4
 
5
5
 
6
- @click.command(help='Converts DICOM series to NIFTI')
6
+ @click.command(help='Converts root directory with DICOM series to NIFTI format')
7
7
  @click.option(
8
- '--images',
8
+ '--scans',
9
9
  required=True,
10
10
  type=click.Path(exists=True),
11
- help='Directory with images',
11
+ help='Root directory with DICOM scans (one for each patient)',
12
12
  )
13
13
  @click.option(
14
14
  '--output',
@@ -22,14 +22,14 @@ from mosamatic2.core.tasks import Dicom2NiftiTask
22
22
  default=False,
23
23
  help='Overwrite [true|false]'
24
24
  )
25
- def dicom2nifti(images, output, overwrite):
25
+ def dicom2nifti(scans, output, overwrite):
26
26
  """
27
- Converts single DICOM series (scan) to NIFTI
27
+ Converts DICOM scans to NIFTI format.
28
28
 
29
29
  Parameters
30
30
  ----------
31
- --images : str
32
- Directory with DICOM images of a single series
31
+ --scans : str
32
+ Root directory with DICOM scans (one subdirectory for each patient)
33
33
 
34
34
  --output : str
35
35
  Path to output directory
@@ -38,7 +38,7 @@ def dicom2nifti(images, output, overwrite):
38
38
  Overwrite contents output directory [true|false]
39
39
  """
40
40
  task = Dicom2NiftiTask(
41
- inputs={'images': images},
41
+ inputs={'scans': scans},
42
42
  params=None,
43
43
  output=output,
44
44
  overwrite=overwrite,
@@ -0,0 +1,26 @@
1
+ import os
2
+ from mosamatic2.core.managers.logmanager import LogManager
3
+ from mosamatic2.core.data.filedata import FileData
4
+ from mosamatic2.core.data.niftiimage import NiftiImage
5
+
6
+ LOG = LogManager()
7
+
8
+
9
+ class MultiNiftiImage(FileData):
10
+ def __init__(self):
11
+ super(MultiNiftiImage, self).__init__()
12
+ self._images = []
13
+
14
+ def images(self):
15
+ return self._images
16
+
17
+ def load(self):
18
+ if self.path():
19
+ for f in os.listdir(self.path()):
20
+ f_path = os.path.join(self.path(), f)
21
+ image = NiftiImage()
22
+ image.set_path(f_path)
23
+ if image.load():
24
+ self._images.append(image)
25
+ return True
26
+ return False
@@ -1,14 +1,14 @@
1
1
  import os
2
2
  from mosamatic2.core.managers.logmanager import LogManager
3
3
  from mosamatic2.core.data.filedata import FileData
4
- from mosamatic2.core.data.numpyimage import NumPyImage
4
+ from mosamatic2.core.data.numpyimage import NumpyImage
5
5
 
6
6
  LOG = LogManager()
7
7
 
8
8
 
9
- class MultiNumPyImage(FileData):
9
+ class MultiNumpyImage(FileData):
10
10
  def __init__(self):
11
- super(MultiNumPyImage, self).__init__()
11
+ super(MultiNumpyImage, self).__init__()
12
12
  self._images = []
13
13
 
14
14
  def images(self):
@@ -18,7 +18,7 @@ class MultiNumPyImage(FileData):
18
18
  if self.path():
19
19
  for f in os.listdir(self.path()):
20
20
  f_path = os.path.join(self.path(), f)
21
- image = NumPyImage()
21
+ image = NumpyImage()
22
22
  image.set_path(f_path)
23
23
  if image.load():
24
24
  self._images.append(image)
@@ -0,0 +1,13 @@
1
+ from mosamatic2.core.data.filedata import FileData
2
+ from mosamatic2.core.utils import (
3
+ is_nifti,
4
+ load_nifti,
5
+ )
6
+
7
+ class NiftiImage(FileData):
8
+ def load(self):
9
+ if self.path():
10
+ if is_nifti(self.path()):
11
+ self.set_object(load_nifti(self.path()))
12
+ return True
13
+ return False
@@ -4,7 +4,7 @@ from mosamatic2.core.utils import (
4
4
  load_numpy_array,
5
5
  )
6
6
 
7
- class NumPyImage(FileData):
7
+ class NumpyImage(FileData):
8
8
  def load(self):
9
9
  if self.path():
10
10
  if is_numpy(self.path()):
@@ -1 +1,2 @@
1
- from mosamatic2.core.pipelines.defaultpipeline.defaultpipeline import DefaultPipeline
1
+ from mosamatic2.core.pipelines.defaultpipeline.defaultpipeline import DefaultPipeline
2
+ from mosamatic2.core.pipelines.defaultdockerpipeline.defaultdockerpipeline import DefaultDockerPipeline
@@ -0,0 +1,42 @@
1
+ import os
2
+
3
+ from mosamatic2.core.pipelines.pipeline import Pipeline
4
+ from mosamatic2.core.managers.logmanager import LogManager
5
+
6
+ LOG = LogManager()
7
+ DOCKER_SCRIPT = """
8
+ docker run --rm ^
9
+ -v "%IMAGES%":/data/images ^
10
+ -v "%MODEL_FILES%":/data/model_files ^
11
+ -v "%OUTPUT%":/data/output ^
12
+ brecheisen/mosamatic2-cli:%VERSION% defaultpipeline ^
13
+ --images /data/images --model_files /data/model_files --output /data/output --overwrite true
14
+ """
15
+
16
+
17
+ class DefaultDockerPipeline(Pipeline):
18
+ INPUTS = [
19
+ 'images',
20
+ 'model_files',
21
+ ]
22
+ PARAMS = [
23
+ 'target_size',
24
+ 'file_type',
25
+ 'fig_width',
26
+ 'fig_height',
27
+ 'model_type',
28
+ 'model_version',
29
+ 'version',
30
+ ]
31
+ def __init__(self, inputs, params, output, overwrite):
32
+ super(DefaultDockerPipeline, self).__init__(inputs, params, output, overwrite)
33
+
34
+ def run(self):
35
+ docker_script = 'docker run --rm ' + \
36
+ '-v "{}":/data/images '.format(self.input('images')) + \
37
+ '-v "{}":/data/model_files '.format(self.input('model_files')) + \
38
+ '-v "{}":/data/output '.format(self.output()) + \
39
+ 'brecheisen/mosamatic2-cli:{} defaultpipeline '.format(self.param('version')) + \
40
+ '--images /data/images --model_files /data/model_files --output /data/output --overwrite true'
41
+ LOG.info(f'Running Docker script: {docker_script}')
42
+ os.system(docker_script)
@@ -4,7 +4,7 @@ import pandas as pd
4
4
  from mosamatic2.core.tasks.task import Task
5
5
  from mosamatic2.core.managers.logmanager import LogManager
6
6
  from mosamatic2.core.data.multidicomimage import MultiDicomImage
7
- from mosamatic2.core.data.numpyimage import NumPyImage
7
+ from mosamatic2.core.data.numpyimage import NumpyImage
8
8
  from mosamatic2.core.utils import (
9
9
  get_pixels_from_dicom_object,
10
10
  calculate_area,
@@ -74,7 +74,7 @@ class CalculateScoresTask(Task):
74
74
 
75
75
  def load_segmentation(self, f, file_type='npy'):
76
76
  if file_type == 'npy':
77
- segmentation = NumPyImage()
77
+ segmentation = NumpyImage()
78
78
  segmentation.set_path(f)
79
79
  if segmentation.load():
80
80
  return segmentation.object()
@@ -2,23 +2,37 @@ import os
2
2
  import dicom2nifti
3
3
  from mosamatic2.core.tasks.task import Task
4
4
  from mosamatic2.core.managers.logmanager import LogManager
5
+ from mosamatic2.core.utils import is_dicom
5
6
 
6
7
  LOG = LogManager()
7
8
 
8
9
 
9
10
  class Dicom2NiftiTask(Task):
10
- INPUTS = ['images']
11
+ INPUTS = ['scans']
11
12
  PARAMS = []
12
13
 
13
14
  def __init__(self, inputs, params, output, overwrite):
14
15
  super(Dicom2NiftiTask, self).__init__(inputs, params, output, overwrite)
15
16
 
17
+ def load_scan_dirs(self):
18
+ scan_dirs = []
19
+ for d in os.listdir(self.input('scans')):
20
+ scan_dir = os.path.join(self.input('scans'), d)
21
+ if os.path.isdir(scan_dir):
22
+ scan_dirs.append(scan_dir)
23
+ return scan_dirs
24
+
16
25
  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)
26
+ scan_dirs = self.load_scan_dirs()
27
+ nr_steps = len(scan_dirs)
28
+ for step in range(nr_steps):
29
+ scan_dir = scan_dirs[step]
30
+ scan_name = os.path.split(scan_dir)[1]
31
+ nifti_file_name = scan_name + '.nii.gz'
32
+ LOG.info(f'Converting DICOM series in {scan_dir} to {nifti_file_name}')
33
+ dicom2nifti.dicom_series_to_nifti(
34
+ scan_dir,
35
+ os.path.join(self.output(), nifti_file_name),
36
+ reorient_nifti=True,
37
+ )
38
+ self.set_progress(step, nr_steps)
@@ -9,6 +9,7 @@ from mosamatic2.core.tasks.task import Task
9
9
  from mosamatic2.core.tasks.segmentmusclefatl3tensorflowtask.paramloader import ParamLoader
10
10
  from mosamatic2.core.data.multidicomimage import MultiDicomImage
11
11
  from mosamatic2.core.data.dicomimage import DicomImage
12
+ from mosamatic2.core.managers.logmanager import LogManager
12
13
  from mosamatic2.core.utils import (
13
14
  normalize_between,
14
15
  get_pixels_from_dicom_object,
@@ -16,6 +17,8 @@ from mosamatic2.core.utils import (
16
17
  )
17
18
 
18
19
  DEVICE = 'cpu'
20
+ L3_INDEX = 167
21
+ LOG = LogManager()
19
22
 
20
23
 
21
24
  class SegmentMuscleFatL3TensorFlowTask(Task):
@@ -25,6 +25,18 @@ class SelectSliceFromScansTask(Task):
25
25
 
26
26
  def __init__(self, inputs, params, output, overwrite):
27
27
  super(SelectSliceFromScansTask, self).__init__(inputs, params, output, overwrite)
28
+ self._error_dir = os.path.split(self.output())[0]
29
+ self._error_dir = os.path.join(self._error_dir, 'selectslicefromscanstask_errors')
30
+ os.makedirs(self._error_dir, exist_ok=True)
31
+ self._error_file = os.path.join(self._error_dir, 'errors.txt')
32
+ with open(self._error_file, 'w') as f:
33
+ f.write('Errors:\n\n')
34
+ LOG.info(f'Error directory: {self._error_dir}')
35
+
36
+ def write_error(self, message):
37
+ LOG.error(message)
38
+ with open(self._error_file, 'a') as f:
39
+ f.write(message + '\n')
28
40
 
29
41
  def load_scan_dirs(self):
30
42
  scan_dirs = []
@@ -37,6 +49,8 @@ class SelectSliceFromScansTask(Task):
37
49
  def extract_masks(self, scan_dir):
38
50
  os.makedirs(TOTAL_SEGMENTATOR_OUTPUT_DIR, exist_ok=True)
39
51
  totalsegmentator(input=scan_dir, output=TOTAL_SEGMENTATOR_OUTPUT_DIR, fast=True)
52
+ if not os.path.isfile(os.path.join(TOTAL_SEGMENTATOR_OUTPUT_DIR, 'vertebrae_L3.nii.gz')):
53
+ raise Exception(f'{scan_dir}: vertebrae_L3.nii.gz does not exist')
40
54
  # os.system(f'TotalSegmentator -i {scan_dir} -o {TOTAL_SEGMENTATOR_OUTPUT_DIR} --fast')
41
55
 
42
56
  def delete_total_segmentator_output(self):
@@ -54,25 +68,50 @@ class SelectSliceFromScansTask(Task):
54
68
  elif vertebra == 'T4':
55
69
  vertebral_level = 'vertebrae_T4'
56
70
  else:
57
- raise RuntimeError(f'Unknown vertbra {vertebra}. Options are "L3" and "T4"')
71
+ self.write_error(f'{scan_dir}: Unknown vertbra {vertebra}. Options are "L3" and "T4"')
72
+ return None
58
73
  # Find Z-positions DICOM images
59
74
  z_positions = {}
60
75
  for f in os.listdir(scan_dir):
61
76
  f_path = os.path.join(scan_dir, f)
62
- p = load_dicom(f_path, stop_before_pixels=True)
63
- if p is not None:
64
- z_positions[p.ImagePositionPatient[2]] = f_path
77
+ try:
78
+ p = load_dicom(f_path, stop_before_pixels=True)
79
+ if p is not None and hasattr(p, "ImagePositionPatient"):
80
+ z_positions[p.ImagePositionPatient[2]] = f_path
81
+ except Exception as e:
82
+ self.write_error(f"{scan_dir}: Failed to load DICOM {f_path}: {e}")
83
+ break
84
+ if not z_positions:
85
+ self.write_error(f"{scan_dir}: No valid DICOM z-positions found.")
86
+ return None
65
87
  # Find Z-position L3 image
66
88
  mask_file = os.path.join(TOTAL_SEGMENTATOR_OUTPUT_DIR, f'{vertebral_level}.nii.gz')
67
- mask_obj = nib.load(mask_file)
68
- mask = mask_obj.get_fdata()
69
- affine_transform = mask_obj.affine
89
+ if not os.path.exists(mask_file):
90
+ self.write_error(f"{scan_dir}: Mask file not found: {mask_file}")
91
+ return None
92
+ try:
93
+ mask_obj = nib.load(mask_file)
94
+ mask = mask_obj.get_fdata()
95
+ affine_transform = mask_obj.affine
96
+ except Exception as e:
97
+ self.write_error(f"{scan_dir}: Failed to load mask {mask_file}: {e}")
98
+ return None
70
99
  indexes = np.array(np.where(mask == 1))
71
- index_min = indexes.min(axis=1)
72
- index_max = indexes.max(axis=1)
100
+ if indexes.size == 0:
101
+ self.write_error(f"{scan_dir}: No voxels found in mask {mask_file} for {vertebral_level}")
102
+ return None
103
+ try:
104
+ index_min = indexes.min(axis=1)
105
+ index_max = indexes.max(axis=1)
106
+ except ValueError as e:
107
+ self.write_error(f"{scan_dir}: Invalid indexes array for {vertebral_level}: {e}")
108
+ return None
73
109
  world_min = nib.affines.apply_affine(affine_transform, index_min)
74
110
  world_max = nib.affines.apply_affine(affine_transform, index_max)
75
111
  z_direction = affine_transform[:3, 2][2]
112
+ if z_direction == 0:
113
+ self.write_error(f"{scan_dir}: Affine z-direction is zero.")
114
+ return None
76
115
  z_sign = math.copysign(1, z_direction)
77
116
  z_delta_offset = self.get_z_delta_offset_for_mask(vertebral_level)
78
117
  if z_delta_offset is None:
@@ -88,6 +127,8 @@ class SelectSliceFromScansTask(Task):
88
127
  closest_file = z_positions[closest_z]
89
128
  LOG.info(f'Closest image: {closest_file}')
90
129
  break
130
+ if closest_file is None:
131
+ self.write_error(f"{scan_dir}: No matching slice found.")
91
132
  return closest_file
92
133
 
93
134
  def run(self):
@@ -97,18 +138,25 @@ class SelectSliceFromScansTask(Task):
97
138
  for step in range(nr_steps):
98
139
  scan_dir = scan_dirs[step]
99
140
  scan_name = os.path.split(scan_dir)[1]
141
+ errors = False
142
+ LOG.info(f'Processing {scan_dir}...')
100
143
  try:
101
144
  self.extract_masks(scan_dir)
102
145
  except Exception as e:
103
- LOG.warning(f'Could not extract masks from {scan_dir} [{str(e)}]')
104
- self.set_progress(step, nr_steps)
105
- continue
106
- file_path = self.find_slice(scan_dir, vertebra)
107
- if file_path is not None:
108
- extension = '' if file_path.endswith('.dcm') else '.dcm'
109
- target_file_path = os.path.join(self.output(), vertebra + '_' + scan_name + extension)
110
- shutil.copyfile(file_path, target_file_path)
111
- else:
112
- LOG.error(f'Could not find slice for vertebral level: {vertebra}')
113
- self.delete_total_segmentator_output()
146
+ self.write_error(f'{scan_dir}: Could not extract masks [{str(e)}]. Skipping scan...')
147
+ errors = True
148
+ if not errors:
149
+ file_path = self.find_slice(scan_dir, vertebra)
150
+ if file_path is not None:
151
+ extension = '' if file_path.endswith('.dcm') else '.dcm'
152
+ target_file_path = os.path.join(self.output(), vertebra + '_' + scan_name + extension)
153
+ shutil.copyfile(file_path, target_file_path)
154
+ else:
155
+ self.write_error(f'{scan_dir}: Could not find slice for vertebral level: {vertebra}')
156
+ errors = True
157
+ self.delete_total_segmentator_output()
158
+ if errors:
159
+ LOG.info(f'Copying problematic scan {scan_dir} to error directory: {self._error_dir}')
160
+ scan_error_dir = os.path.join(self._error_dir, scan_name)
161
+ shutil.copytree(scan_dir, scan_error_dir)
114
162
  self.set_progress(step, nr_steps)
mosamatic2/core/utils.py CHANGED
@@ -5,6 +5,7 @@ import textwrap
5
5
  import math
6
6
  import pendulum
7
7
  import numpy as np
8
+ import nibabel as nb
8
9
  import struct
9
10
  import binascii
10
11
  import pydicom
@@ -134,6 +135,16 @@ def is_jpeg2000_compressed(p):
134
135
  return False
135
136
 
136
137
 
138
+ def is_nifti(f):
139
+ return f.endswith('.nii') or f.endswith('.nii.gz')
140
+
141
+
142
+ def load_nifti(f):
143
+ if is_nifti(f):
144
+ return nb.load(f)
145
+ return None
146
+
147
+
137
148
  def is_numpy_array(value):
138
149
  return isinstance(value, np.array)
139
150
 
@@ -149,8 +160,7 @@ def is_numpy(f):
149
160
  def load_numpy_array(f):
150
161
  if is_numpy(f):
151
162
  return np.load(f)
152
- else:
153
- return None
163
+ return None
154
164
 
155
165
 
156
166
  def get_pixels_from_tag_file(tag_file_path):
@@ -22,6 +22,7 @@ from mosamatic2.ui.widgets.panels.tasks.dicom2niftitaskpanel import Dicom2NiftiT
22
22
  from mosamatic2.ui.widgets.panels.tasks.createdicomsummarytaskpanel import CreateDicomSummaryTaskPanel
23
23
  from mosamatic2.ui.widgets.panels.tasks.selectslicefromscanstaskpanel import SelectSliceFromScansTaskPanel
24
24
  from mosamatic2.ui.widgets.panels.pipelines.defaultpipelinepanel import DefaultPipelinePanel
25
+ from mosamatic2.ui.widgets.panels.pipelines.defaultdockerpipelinepanel import DefaultDockerPipelinePanel
25
26
  from mosamatic2.ui.widgets.panels.visualizations.slicevisualization.slicevisualization import SliceVisualization
26
27
 
27
28
  LOG = LogManager()
@@ -42,6 +43,7 @@ class MainWindow(QMainWindow):
42
43
  self._create_dicom_summary_task_panel = None
43
44
  self._select_slice_from_scans_task_panel = None
44
45
  self._default_pipeline_panel = None
46
+ self._default_docker_pipeline_panel = None
45
47
  self._slice_visualization = None
46
48
  self.init_window()
47
49
 
@@ -98,8 +100,11 @@ class MainWindow(QMainWindow):
98
100
  def init_pipelines_menu(self):
99
101
  default_pipeline_action = QAction('DefaultPipeline', self)
100
102
  default_pipeline_action.triggered.connect(self.handle_default_pipeline_action)
103
+ default_docker_pipeline_action = QAction('DefaultDockerPipeline', self)
104
+ default_docker_pipeline_action.triggered.connect(self.handle_default_docker_pipeline_action)
101
105
  pipelines_menu = self.menuBar().addMenu('Pipelines')
102
106
  pipelines_menu.addAction(default_pipeline_action)
107
+ pipelines_menu.addAction(default_docker_pipeline_action)
103
108
 
104
109
  def init_visualizations_menu(self):
105
110
  slice_visualization_action = QAction('SliceVisualization', self)
@@ -128,6 +133,7 @@ class MainWindow(QMainWindow):
128
133
  self._main_panel.add_panel(self.create_dicom_summary_task_panel(), 'createdicomsummarytaskpanel')
129
134
  self._main_panel.add_panel(self.select_slice_from_scans_task_panel(), 'selectslicefromscanstaskpanel')
130
135
  self._main_panel.add_panel(self.default_pipeline_panel(), 'defaultpipelinepanel')
136
+ self._main_panel.add_panel(self.default_docker_pipeline_panel(), 'defaultdockerpipelinepanel')
131
137
  self._main_panel.add_panel(self.slice_visualization(), 'slicevisualization')
132
138
  self._main_panel.select_panel('defaultpipelinepanel')
133
139
  return self._main_panel
@@ -179,6 +185,11 @@ class MainWindow(QMainWindow):
179
185
  if not self._default_pipeline_panel:
180
186
  self._default_pipeline_panel = DefaultPipelinePanel()
181
187
  return self._default_pipeline_panel
188
+
189
+ def default_docker_pipeline_panel(self):
190
+ if not self._default_docker_pipeline_panel:
191
+ self._default_docker_pipeline_panel = DefaultDockerPipelinePanel()
192
+ return self._default_docker_pipeline_panel
182
193
 
183
194
  def slice_visualization(self):
184
195
  if not self._slice_visualization:
@@ -216,6 +227,9 @@ class MainWindow(QMainWindow):
216
227
  def handle_default_pipeline_action(self):
217
228
  self.main_panel().select_panel('defaultpipelinepanel')
218
229
 
230
+ def handle_default_docker_pipeline_action(self):
231
+ self.main_panel().select_panel('defaultdockerpipelinepanel')
232
+
219
233
  def handle_slice_visualization_action(self):
220
234
  self.main_panel().select_panel('slicevisualization')
221
235
 
@@ -233,6 +247,7 @@ class MainWindow(QMainWindow):
233
247
  self.create_dicom_summary_task_panel().save_inputs_and_parameters()
234
248
  self.select_slice_from_scans_task_panel().save_inputs_and_parameters()
235
249
  self.default_pipeline_panel().save_inputs_and_parameters()
250
+ self.default_docker_pipeline_panel().save_inputs_and_parameters()
236
251
  self.slice_visualization().save_inputs_and_parameters()
237
252
  return super().closeEvent(event)
238
253
 
@@ -1 +1 @@
1
- 2.0.10
1
+ 2.0.12
@@ -0,0 +1,311 @@
1
+ import os
2
+
3
+ from PySide6.QtWidgets import (
4
+ QLineEdit,
5
+ QSpinBox,
6
+ QComboBox,
7
+ QCheckBox,
8
+ QHBoxLayout,
9
+ QVBoxLayout,
10
+ QFormLayout,
11
+ QPushButton,
12
+ QFileDialog,
13
+ QMessageBox,
14
+ )
15
+ from PySide6.QtCore import (
16
+ QThread,
17
+ Slot,
18
+ )
19
+
20
+ from mosamatic2.core.managers.logmanager import LogManager
21
+ from mosamatic2.ui.widgets.panels.pipelines.pipelinepanel import PipelinePanel
22
+ from mosamatic2.ui.settings import Settings
23
+ from mosamatic2.ui.utils import is_macos
24
+ from mosamatic2.ui.worker import Worker
25
+ from mosamatic2.core.pipelines import DefaultDockerPipeline
26
+
27
+ LOG = LogManager()
28
+
29
+ PANEL_TITLE = 'DefaultDockerPipeline'
30
+ PANEL_NAME = 'defaultdockerpipeline'
31
+ MODEL_TYPE_ITEM_NAMES = ['tensorflow', 'pytorch']
32
+ MODEL_VERSION_ITEM_NAMES = ['1.0', '2.2']
33
+
34
+
35
+ class DefaultDockerPipelinePanel(PipelinePanel):
36
+ def __init__(self):
37
+ super(DefaultDockerPipelinePanel, self).__init__()
38
+ self.set_title(PANEL_TITLE)
39
+ self._images_dir_line_edit = None
40
+ self._images_dir_select_button = None
41
+ self._model_files_dir_line_edit = None
42
+ self._model_files_dir_select_button = None
43
+ self._output_dir_line_edit = None
44
+ self._output_dir_select_button = None
45
+ self._target_size_spinbox = None
46
+ self._model_type_combobox = None
47
+ self._model_version_combobox = None
48
+ self._fig_width_spinbox = None
49
+ self._fig_height_spinbox = None
50
+ self._full_scan_checkbox = None
51
+ self._overwrite_checkbox = None
52
+ self._version_line_edit = None
53
+ self._form_layout = None
54
+ self._run_pipeline_button = None
55
+ self._settings = None
56
+ self._task = None
57
+ self._worker = None
58
+ self._thread = None
59
+ self.init_layout()
60
+
61
+ def images_dir_line_edit(self):
62
+ if not self._images_dir_line_edit:
63
+ self._images_dir_line_edit = QLineEdit()
64
+ self._images_dir_line_edit.setText(self.settings().get(f'{PANEL_NAME}/images_dir'))
65
+ return self._images_dir_line_edit
66
+
67
+ def images_dir_select_button(self):
68
+ if not self._images_dir_select_button:
69
+ self._images_dir_select_button = QPushButton('Select')
70
+ self._images_dir_select_button.clicked.connect(self.handle_images_dir_select_button)
71
+ return self._images_dir_select_button
72
+
73
+ def model_files_dir_line_edit(self):
74
+ if not self._model_files_dir_line_edit:
75
+ self._model_files_dir_line_edit = QLineEdit()
76
+ self._model_files_dir_line_edit.setText(self.settings().get(f'{PANEL_NAME}/model_files_dir'))
77
+ return self._model_files_dir_line_edit
78
+
79
+ def model_files_dir_select_button(self):
80
+ if not self._model_files_dir_select_button:
81
+ self._model_files_dir_select_button = QPushButton('Select')
82
+ self._model_files_dir_select_button.clicked.connect(self.handle_model_files_dir_select_button)
83
+ return self._model_files_dir_select_button
84
+
85
+ def output_dir_line_edit(self):
86
+ if not self._output_dir_line_edit:
87
+ self._output_dir_line_edit = QLineEdit()
88
+ self._output_dir_line_edit.setText(self.settings().get(f'{PANEL_NAME}/output_dir'))
89
+ return self._output_dir_line_edit
90
+
91
+ def output_dir_select_button(self):
92
+ if not self._output_dir_select_button:
93
+ self._output_dir_select_button = QPushButton('Select')
94
+ self._output_dir_select_button.clicked.connect(self.handle_output_dir_select_button)
95
+ return self._output_dir_select_button
96
+
97
+ def target_size_spinbox(self):
98
+ if not self._target_size_spinbox:
99
+ self._target_size_spinbox = QSpinBox()
100
+ self._target_size_spinbox.setMinimum(0)
101
+ self._target_size_spinbox.setMaximum(1024)
102
+ self._target_size_spinbox.setValue(self.settings().get_int(f'{PANEL_NAME}/target_size', 512))
103
+ return self._target_size_spinbox
104
+
105
+ def model_type_combobox(self):
106
+ if not self._model_type_combobox:
107
+ self._model_type_combobox = QComboBox()
108
+ self._model_type_combobox.addItems(MODEL_TYPE_ITEM_NAMES)
109
+ self._model_type_combobox.setCurrentText(self.settings().get(f'{PANEL_NAME}/model_type'))
110
+ self._model_type_combobox.currentTextChanged.connect(self.handle_model_type_combobox)
111
+ return self._model_type_combobox
112
+
113
+ def model_version_combobox(self):
114
+ if not self._model_version_combobox:
115
+ self._model_version_combobox = QComboBox()
116
+ self._model_version_combobox.addItems(MODEL_VERSION_ITEM_NAMES)
117
+ self._model_version_combobox.setCurrentText(self.settings().get(f'{PANEL_NAME}/model_version'))
118
+ self._model_version_combobox.currentTextChanged.connect(self.handle_model_version_combobox)
119
+ return self._model_version_combobox
120
+
121
+ def fig_width_spinbox(self):
122
+ if not self._fig_width_spinbox:
123
+ self._fig_width_spinbox = QSpinBox()
124
+ self._fig_width_spinbox.setValue(self.settings().get_int(f'{PANEL_NAME}/fig_width', default=10))
125
+ return self._fig_width_spinbox
126
+
127
+ def fig_height_spinbox(self):
128
+ if not self._fig_height_spinbox:
129
+ self._fig_height_spinbox = QSpinBox()
130
+ self._fig_height_spinbox.setValue(self.settings().get_int(f'{PANEL_NAME}/fig_height', default=10))
131
+ return self._fig_height_spinbox
132
+
133
+ def full_scan_checkbox(self):
134
+ if not self._full_scan_checkbox:
135
+ self._full_scan_checkbox = QCheckBox('')
136
+ self._full_scan_checkbox.setChecked(self.settings().get_bool(f'{PANEL_NAME}/full_scan', False))
137
+ return self._full_scan_checkbox
138
+
139
+ def overwrite_checkbox(self):
140
+ if not self._overwrite_checkbox:
141
+ self._overwrite_checkbox = QCheckBox('')
142
+ self._overwrite_checkbox.setChecked(self.settings().get_bool(f'{PANEL_NAME}/overwrite', True))
143
+ return self._overwrite_checkbox
144
+
145
+ def version_line_edit(self):
146
+ if not self._version_line_edit:
147
+ self._version_line_edit = QLineEdit()
148
+ self._version_line_edit.setText(self.settings().get(f'{PANEL_NAME}/version'))
149
+ return self._version_line_edit
150
+
151
+ def form_layout(self):
152
+ if not self._form_layout:
153
+ self._form_layout = QFormLayout()
154
+ if is_macos():
155
+ self._form_layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
156
+ return self._form_layout
157
+
158
+ def run_pipeline_button(self):
159
+ if not self._run_pipeline_button:
160
+ self._run_pipeline_button = QPushButton('Run pipeline')
161
+ self._run_pipeline_button.clicked.connect(self.handle_run_pipeline_button)
162
+ return self._run_pipeline_button
163
+
164
+ def settings(self):
165
+ if not self._settings:
166
+ self._settings = Settings()
167
+ return self._settings
168
+
169
+ def init_help_dialog(self):
170
+ self.help_dialog().set_text('Show some help information')
171
+
172
+ def init_layout(self):
173
+ images_dir_layout = QHBoxLayout()
174
+ images_dir_layout.addWidget(self.images_dir_line_edit())
175
+ images_dir_layout.addWidget(self.images_dir_select_button())
176
+ model_files_dir_layout = QHBoxLayout()
177
+ model_files_dir_layout.addWidget(self.model_files_dir_line_edit())
178
+ model_files_dir_layout.addWidget(self.model_files_dir_select_button())
179
+ output_dir_layout = QHBoxLayout()
180
+ output_dir_layout.addWidget(self.output_dir_line_edit())
181
+ output_dir_layout.addWidget(self.output_dir_select_button())
182
+ self.form_layout().addRow('Images directory', images_dir_layout)
183
+ self.form_layout().addRow('Model files directory', model_files_dir_layout)
184
+ self.form_layout().addRow('Output directory', output_dir_layout)
185
+ # self.form_layout().addRow('Version', self.version_line_edit())
186
+ self.form_layout().addRow('Overwrite', self.overwrite_checkbox())
187
+ layout = QVBoxLayout()
188
+ layout.addLayout(self.form_layout())
189
+ layout.addWidget(self.run_pipeline_button())
190
+ self.setLayout(layout)
191
+ self.setObjectName(PANEL_NAME)
192
+
193
+ def handle_images_dir_select_button(self):
194
+ last_directory = self.settings().get('last_directory')
195
+ directory = QFileDialog.getExistingDirectory(dir=last_directory)
196
+ if directory:
197
+ self.images_dir_line_edit().setText(directory)
198
+ self.settings().set('last_directory', directory)
199
+
200
+ def handle_model_files_dir_select_button(self):
201
+ last_directory = self.settings().get('last_directory')
202
+ directory = QFileDialog.getExistingDirectory(dir=last_directory)
203
+ if directory:
204
+ self.model_files_dir_line_edit().setText(directory)
205
+ self.settings().set('last_directory', directory)
206
+
207
+ def handle_output_dir_select_button(self):
208
+ last_directory = self.settings().get('last_directory')
209
+ directory = QFileDialog.getExistingDirectory(dir=last_directory)
210
+ if directory:
211
+ self.output_dir_line_edit().setText(directory)
212
+ self.settings().set('last_directory', directory)
213
+
214
+ def handle_model_type_combobox(self, text):
215
+ if text == 'tensorflow':
216
+ self.model_version_combobox().setCurrentText('1.0')
217
+ if text == 'pytorch':
218
+ self.model_version_combobox().setCurrentText('2.2')
219
+
220
+ def handle_model_version_combobox(self, text):
221
+ if text == '1.0':
222
+ self.model_type_combobox().setCurrentText('tensorflow')
223
+ if text == '2.2':
224
+ self.model_type_combobox().setCurrentText('pytorch')
225
+
226
+ def handle_run_pipeline_button(self):
227
+ errors = self.check_inputs_and_parameters()
228
+ if len(errors) > 0:
229
+ error_message = 'Following errors were encountered:\n'
230
+ for error in errors:
231
+ error_message += f' - {error}\n'
232
+ QMessageBox.information(self, 'Error', error_message)
233
+ else:
234
+ LOG.info('Running pipeline...')
235
+ self.run_pipeline_button().setEnabled(False)
236
+ self.save_inputs_and_parameters()
237
+ self._task = DefaultDockerPipeline(
238
+ inputs={
239
+ 'images': self.images_dir_line_edit().text(),
240
+ 'model_files': self.model_files_dir_line_edit().text(),
241
+ },
242
+ params={
243
+ 'file_type': 'npy',
244
+ 'model_type': 'tensorflow',
245
+ 'model_version': 1.0,
246
+ 'target_size': 512,
247
+ 'fig_width': 10,
248
+ 'fig_height': 10,
249
+ 'version': '2.0.10',
250
+ },
251
+ output=self.output_dir_line_edit().text(),
252
+ overwrite=self.overwrite_checkbox().isChecked(),
253
+ )
254
+ self._worker = Worker(self._task)
255
+ self._thread = QThread()
256
+ self._worker.moveToThread(self._thread)
257
+ self._thread.started.connect(self._worker.run)
258
+ self._worker.progress.connect(self.handle_progress)
259
+ self._worker.status.connect(self.handle_status)
260
+ self._worker.finished.connect(self.handle_finished)
261
+ self._worker.finished.connect(self._thread.quit)
262
+ self._worker.finished.connect(self._worker.deleteLater)
263
+ self._thread.finished.connect(self._thread.deleteLater)
264
+ self._thread.start()
265
+
266
+ @Slot(int)
267
+ def handle_progress(self, progress):
268
+ LOG.info(f'Progress: {progress} / 100%')
269
+
270
+ @Slot(str)
271
+ def handle_status(self, status):
272
+ LOG.info(f'Status: {status}')
273
+
274
+ @Slot()
275
+ def handle_finished(self):
276
+ self.run_pipeline_button().setEnabled(True)
277
+
278
+ def check_inputs_and_parameters(self):
279
+ errors = []
280
+ if self.images_dir_line_edit().text() == '':
281
+ errors.append('Empty images directory path')
282
+ elif not os.path.isdir(self.images_dir_line_edit().text()):
283
+ errors.append('Images directory does not exist')
284
+ if self.model_files_dir_line_edit().text() == '':
285
+ errors.append('Empty model files directory path')
286
+ elif not os.path.isdir(self.model_files_dir_line_edit().text()):
287
+ errors.append('Model files directory does not exist')
288
+ if self.output_dir_line_edit().text() == '':
289
+ errors.append('Empty output directory path')
290
+ elif os.path.isdir(self.output_dir_line_edit().text()) and not self.overwrite_checkbox().isChecked():
291
+ errors.append('Output directory exists but overwrite=False. Please remove output directory first')
292
+ # if self.version_line_edit().text() == '':
293
+ # errors.append('Empty version. Should be same as current Mosamatic version')
294
+ # if self.target_size_spinbox().value() != 512:
295
+ # errors.append('Target size must be 512')
296
+ # if self.full_scan_checkbox().isChecked():
297
+ # errors.append('Full scan support is not available yet')
298
+ return errors
299
+
300
+ def save_inputs_and_parameters(self):
301
+ self.settings().set(f'{PANEL_NAME}/images_dir', self.images_dir_line_edit().text())
302
+ self.settings().set(f'{PANEL_NAME}/model_files_dir', self.model_files_dir_line_edit().text())
303
+ self.settings().set(f'{PANEL_NAME}/output_dir', self.output_dir_line_edit().text())
304
+ # self.settings().set(f'{PANEL_NAME}/version', self.version_line_edit().text())
305
+ # self.settings().set(f'{PANEL_NAME}/target_size', self.target_size_spinbox().value())
306
+ # self.settings().set(f'{PANEL_NAME}/model_type', self.model_type_combobox().currentText())
307
+ # self.settings().set(f'{PANEL_NAME}/model_version', self.model_version_combobox().currentText())
308
+ # self.settings().set(f'{PANEL_NAME}/fig_width', self.fig_width_spinbox().value())
309
+ # self.settings().set(f'{PANEL_NAME}/fig_height', self.fig_height_spinbox().value())
310
+ # self.settings().set(f'{PANEL_NAME}/full_scan', self.full_scan_checkbox().isChecked())
311
+ self.settings().set(f'{PANEL_NAME}/overwrite', self.overwrite_checkbox().isChecked())
@@ -33,8 +33,8 @@ class Dicom2NiftiTaskPanel(TaskPanel):
33
33
  def __init__(self):
34
34
  super(Dicom2NiftiTaskPanel, self).__init__()
35
35
  self.set_title(PANEL_TITLE)
36
- self._images_dir_line_edit = None
37
- self._images_dir_select_button = None
36
+ self._scans_dir_line_edit = None
37
+ self._scans_dir_select_button = None
38
38
  self._output_dir_line_edit = None
39
39
  self._output_dir_select_button = None
40
40
  self._overwrite_checkbox = None
@@ -46,16 +46,16 @@ class Dicom2NiftiTaskPanel(TaskPanel):
46
46
  self._thread = None
47
47
  self.init_layout()
48
48
 
49
- def images_dir_line_edit(self):
50
- if not self._images_dir_line_edit:
51
- self._images_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/images_dir'))
52
- return self._images_dir_line_edit
49
+ def scans_dir_line_edit(self):
50
+ if not self._scans_dir_line_edit:
51
+ self._scans_dir_line_edit = QLineEdit(self.settings().get(f'{PANEL_NAME}/scans_dir'))
52
+ return self._scans_dir_line_edit
53
53
 
54
- def images_dir_select_button(self):
55
- if not self._images_dir_select_button:
56
- self._images_dir_select_button = QPushButton('Select')
57
- self._images_dir_select_button.clicked.connect(self.handle_images_dir_select_button)
58
- return self._images_dir_select_button
54
+ def scans_dir_select_button(self):
55
+ if not self._scans_dir_select_button:
56
+ self._scans_dir_select_button = QPushButton('Select')
57
+ self._scans_dir_select_button.clicked.connect(self.handle_scans_dir_select_button)
58
+ return self._scans_dir_select_button
59
59
 
60
60
  def output_dir_line_edit(self):
61
61
  if not self._output_dir_line_edit:
@@ -93,13 +93,13 @@ class Dicom2NiftiTaskPanel(TaskPanel):
93
93
  return self._settings
94
94
 
95
95
  def init_layout(self):
96
- images_dir_layout = QHBoxLayout()
97
- images_dir_layout.addWidget(self.images_dir_line_edit())
98
- images_dir_layout.addWidget(self.images_dir_select_button())
96
+ scans_dir_layout = QHBoxLayout()
97
+ scans_dir_layout.addWidget(self.scans_dir_line_edit())
98
+ scans_dir_layout.addWidget(self.scans_dir_select_button())
99
99
  output_dir_layout = QHBoxLayout()
100
100
  output_dir_layout.addWidget(self.output_dir_line_edit())
101
101
  output_dir_layout.addWidget(self.output_dir_select_button())
102
- self.form_layout().addRow('Images directory', images_dir_layout)
102
+ self.form_layout().addRow('Scans directory', scans_dir_layout)
103
103
  self.form_layout().addRow('Output directory', output_dir_layout)
104
104
  self.form_layout().addRow('Overwrite', self.overwrite_checkbox())
105
105
  layout = QVBoxLayout()
@@ -108,11 +108,11 @@ class Dicom2NiftiTaskPanel(TaskPanel):
108
108
  self.setLayout(layout)
109
109
  self.setObjectName(PANEL_NAME)
110
110
 
111
- def handle_images_dir_select_button(self):
111
+ def handle_scans_dir_select_button(self):
112
112
  last_directory = self.settings().get('last_directory')
113
113
  directory = QFileDialog.getExistingDirectory(dir=last_directory)
114
114
  if directory:
115
- self.images_dir_line_edit().setText(directory)
115
+ self.scans_dir_line_edit().setText(directory)
116
116
  self.settings().set('last_directory', directory)
117
117
 
118
118
  def handle_output_dir_select_button(self):
@@ -134,7 +134,7 @@ class Dicom2NiftiTaskPanel(TaskPanel):
134
134
  self.run_task_button().setEnabled(False)
135
135
  self.save_inputs_and_parameters()
136
136
  self._task = Dicom2NiftiTask(
137
- inputs={'images': self.images_dir_line_edit().text()},
137
+ inputs={'scans': self.scans_dir_line_edit().text()},
138
138
  params=None,
139
139
  output=self.output_dir_line_edit().text(),
140
140
  overwrite=self.overwrite_checkbox().isChecked(),
@@ -167,10 +167,10 @@ class Dicom2NiftiTaskPanel(TaskPanel):
167
167
 
168
168
  def check_inputs_and_parameters(self):
169
169
  errors = []
170
- if self.images_dir_line_edit().text() == '':
171
- errors.append('Empty images directory path')
172
- if not os.path.isdir(self.images_dir_line_edit().text()):
173
- errors.append('Images directory does not exist')
170
+ if self.scans_dir_line_edit().text() == '':
171
+ errors.append('Empty scans directory path')
172
+ if not os.path.isdir(self.scans_dir_line_edit().text()):
173
+ errors.append('Scans directory does not exist')
174
174
  if self.output_dir_line_edit().text() == '':
175
175
  errors.append('Empty output directory path')
176
176
  if os.path.isdir(self.output_dir_line_edit().text()) and not self.overwrite_checkbox().isChecked():
@@ -178,6 +178,6 @@ class Dicom2NiftiTaskPanel(TaskPanel):
178
178
  return errors
179
179
 
180
180
  def save_inputs_and_parameters(self):
181
- self.settings().set(f'{PANEL_NAME}/images_dir', self.images_dir_line_edit().text())
181
+ self.settings().set(f'{PANEL_NAME}/scans_dir', self.scans_dir_line_edit().text())
182
182
  self.settings().set(f'{PANEL_NAME}/output_dir', self.output_dir_line_edit().text())
183
183
  self.settings().set(f'{PANEL_NAME}/overwrite', self.overwrite_checkbox().isChecked())
@@ -25,7 +25,7 @@ from mosamatic2.core.tasks import SelectSliceFromScansTask
25
25
 
26
26
  LOG = LogManager()
27
27
 
28
- PANEL_TITLE = 'Automatically select L3 slice from full CT scan'
28
+ PANEL_TITLE = 'SelectSliceFromScansTaskPanel'
29
29
  PANEL_NAME = 'selectslicefromscanstaskpanel'
30
30
 
31
31
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mosamatic2
3
- Version: 2.0.10
3
+ Version: 2.0.12
4
4
  Summary:
5
5
  Author: Ralph Brecheisen
6
6
  Author-email: r.brecheisen@maastrichtuniversity.nl
@@ -7,7 +7,7 @@ mosamatic2/commands/calculatescores.py,sha256=Lb8Q8L2yq7Tt6VBJ6_lltRuldrev_pac6f
7
7
  mosamatic2/commands/createdicomsummary.py,sha256=qbVgWGIJPBL8vTBcAxfThloQ_q15OfQy5ohMprzZL4E,1955
8
8
  mosamatic2/commands/createpngsfromsegmentations.py,sha256=uUAQJVTqOkBCfENzi21RBNYvf6_nuesx1MeR3j_-7dM,1682
9
9
  mosamatic2/commands/defaultpipeline.py,sha256=-kQRSXbEDimAdy_kT1qMxRlZWUoXdu3PkWf70PCIS5Y,1734
10
- mosamatic2/commands/dicom2nifti.py,sha256=4hMvglxdOid7p7P_Jc-Tudsf93JRMlYaQsEQctLq2d4,1015
10
+ mosamatic2/commands/dicom2nifti.py,sha256=t0EYUqNpPs7OCtPk4KMZrppZQdiaGnfh0HJKKnYJ8CY,1084
11
11
  mosamatic2/commands/rescaledicomimages.py,sha256=25QdCzB5s0sRwkTb3o5zco2bIwy6LttNf7i97kGBDYQ,1280
12
12
  mosamatic2/commands/segmentmusclefatl3tensorflow.py,sha256=CdScmA_EQicaN4GY5bBUOYwfhDPqy9om2sxY3WrtmM0,1424
13
13
  mosamatic2/commands/selectslicefromscans.py,sha256=3398PM2uBcxF6wpb0-c-Itp_qxoAxBf0SE2nDDI3Ct4,1715
@@ -19,42 +19,46 @@ mosamatic2/core/data/dicomimageseries.py,sha256=OZkNi15crL8nEA-PGYsM0k9NMi2mMHRv
19
19
  mosamatic2/core/data/dixonseries.py,sha256=kq9fy65MSM2XwiScqp7b3rQ09JmpyGwbG6ldZsuPRrM,516
20
20
  mosamatic2/core/data/filedata.py,sha256=hCnpizGqOpxzIADJkDS2_NSmKVLL1u49TYjSJE5UXQo,515
21
21
  mosamatic2/core/data/multidicomimage.py,sha256=cdd0H4Dq49h7NLKBx51_h_HZVnH7-reu48PY8m6tXwU,1034
22
- mosamatic2/core/data/multinumpyimage.py,sha256=MU5_ec00vx9_JhkibgkcgyGFfaj69STy85ifa8MNsSc,753
23
- mosamatic2/core/data/numpyimage.py,sha256=ZbJ9JRAVOgvXzTZ6kscEuGZJqKT-MPdt42IAlmh3iXo,359
22
+ mosamatic2/core/data/multiniftiimage.py,sha256=zkHRPzKE-fyP9MtlheJyo_OoqpFXmYMxwguZXP-9bdw,753
23
+ mosamatic2/core/data/multinumpyimage.py,sha256=bISFxDHJ2aFuutCLpwQoiuPkMfVQf_Uc0WslZ4ruJ3o,753
24
+ mosamatic2/core/data/niftiimage.py,sha256=s4WGADwnzlSgHx80isM58J6cHdhKXF2e3zuGvJzew-M,347
25
+ mosamatic2/core/data/numpyimage.py,sha256=bnG6WVGSRxNdzIlb2DNj5u6Gv4BAYscIj2BuyjkDjEc,359
24
26
  mosamatic2/core/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
27
  mosamatic2/core/managers/logmanager.py,sha256=NEaXvhl0aILjBbK710GaWanVuuNvB51HpHhE5rgYvng,1391
26
28
  mosamatic2/core/managers/logmanagerlistener.py,sha256=Gaig07yjBnyQq9I8sN85olTEeDCDyCFQnEJdwzvmgvc,99
27
- mosamatic2/core/pipelines/__init__.py,sha256=Esb4TQFvY2soU2dfQPgVlhfTx1LdxYrPBiuAKDosHqA,85
29
+ mosamatic2/core/pipelines/__init__.py,sha256=2RjHG0xLO-_QK-yw0RHrJe85B-SZ4oAKUJWrDzrLdGI,190
30
+ mosamatic2/core/pipelines/defaultdockerpipeline/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ mosamatic2/core/pipelines/defaultdockerpipeline/defaultdockerpipeline.py,sha256=gb-rez0Y3jY6_c7llfwN2IDnl9l5Fz3XyvScLmWA7Y4,1490
28
32
  mosamatic2/core/pipelines/defaultpipeline/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
33
  mosamatic2/core/pipelines/defaultpipeline/defaultpipeline.py,sha256=Bme0r_shnrllWYCYDNc6cLM2fQC2yD8RJKpRdoh_6Uc,3077
30
34
  mosamatic2/core/pipelines/pipeline.py,sha256=mRxKXLKwgKDpc8R9mCI6gDKGJ2lKVxRQ__Sf0Mfn_Qc,384
31
35
  mosamatic2/core/singleton.py,sha256=FV0k_LlOCmFhlWN6gf1c2x7YXWyd8-7DsIMvOKrI6NY,224
32
36
  mosamatic2/core/tasks/__init__.py,sha256=w03AyCk2BQ6gGPBEKhtad6lt9WuwHC-nMztv7wQNwb4,759
33
37
  mosamatic2/core/tasks/calculatescorestask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py,sha256=jEbogrEnEtMbmfQ8KOGSUdGMhdeX69Bv01-82faRIlY,6617
38
+ mosamatic2/core/tasks/calculatescorestask/calculatescorestask.py,sha256=cwGVedJR_BGSYzXq6ouTgCbSC6s2VtyD8FzRC-QBXUI,6617
35
39
  mosamatic2/core/tasks/createdicomsummarytask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
40
  mosamatic2/core/tasks/createdicomsummarytask/createdicomsummarytask.py,sha256=kcdwIBnoAXexok2j6_tF_s6gwvn2DvjLwe_VhdODpbM,2957
37
41
  mosamatic2/core/tasks/createpngsfromsegmentationstask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
42
  mosamatic2/core/tasks/createpngsfromsegmentationstask/createpngsfromsegmentationstask.py,sha256=1UpOsp1CH668BQ0g4tALou_tFgisC306VcvqOKSDuTo,1884
39
43
  mosamatic2/core/tasks/dicom2niftitask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py,sha256=9VqDDCShUG09Qhppia-5X-rkVbUzI4VF8rBVia19Tvs,762
44
+ mosamatic2/core/tasks/dicom2niftitask/dicom2niftitask.py,sha256=DC6yNX0RCIJMWcFHkzV51KF8khITYvFQTCsbOr60xGI,1305
41
45
  mosamatic2/core/tasks/rescaledicomimagestask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
46
  mosamatic2/core/tasks/rescaledicomimagestask/rescaledicomimagestask.py,sha256=vGSpMJoXFtE-IHGxTEO9DMkerMJcfG5r9tXgtvkxm6Y,3053
43
47
  mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
48
  mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/paramloader.py,sha256=VxTCOYK_1VRAG83P-ulm0LPvqXI-0iT5BCr0Rdr7MWg,900
45
- mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/segmentmusclefatl3tensorflowtask.py,sha256=E3GHzGtl-v_qIdLjSy3cbDnf8vfWK6Bax3RAZcKCqXg,5149
49
+ mosamatic2/core/tasks/segmentmusclefatl3tensorflowtask/segmentmusclefatl3tensorflowtask.py,sha256=IEE-31eLmq7Y47YL5pT3QUdf9ET8Ju-Z3OxniraZUbA,5245
46
50
  mosamatic2/core/tasks/selectslicefromscanstask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
- mosamatic2/core/tasks/selectslicefromscanstask/selectslicefromscanstask.py,sha256=ST_ZMFyPNtsFsXCv7Yu7fnX2reHwom4D5DDPIkjukz0,4805
51
+ mosamatic2/core/tasks/selectslicefromscanstask/selectslicefromscanstask.py,sha256=EIEHhWoGL30rcz8qckFl465rU40P-pIkvhMOfSud7Yw,7253
48
52
  mosamatic2/core/tasks/task.py,sha256=APPnid6dpSGkPuDqU1vm2RIMR5vkpvbP1CPHUMjympg,1691
49
- mosamatic2/core/utils.py,sha256=iib_o_dD4hnqGIgOxLOfvMFOqLZOpt0iT_8Oarkmdjs,11723
53
+ mosamatic2/core/utils.py,sha256=9wY1itpLIEWV8KtwZeD5c4gH4shzOkZEkCA0rSwRI4Q,11897
50
54
  mosamatic2/server.py,sha256=-cZ9BPsZUXoINKqwhCHN8c59mlvzzDXzTVxsYt9au70,4644
51
55
  mosamatic2/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
- mosamatic2/ui/mainwindow.py,sha256=nV9RqqSZVoVbU2opJIk7E3n4GvZWtuDETxmzwzmx4mg,13099
56
+ mosamatic2/ui/mainwindow.py,sha256=H1X1pLJjqd2DhxJY0ZU6QkktX28zl0pmtFflmOxrGZA,14048
53
57
  mosamatic2/ui/resources/icons/mosamatic2.icns,sha256=OfhC-diJTIgaNMOezxKKilGsY7mRkaGdU5dGr0MOjIA,2994125
54
58
  mosamatic2/ui/resources/icons/mosamatic2.ico,sha256=ySD3RYluHK3pgS0Eas7eKrVk_AskdLQ4qs_IT-wNhq4,12229
55
59
  mosamatic2/ui/resources/icons/spinner.gif,sha256=rvaac6GUZauHSPFSOLWr0RmLfjmtZih2Q8knQ2WP3Po,16240
56
60
  mosamatic2/ui/resources/images/body-composition.jpg,sha256=KD-BudbXwThB4lJOZZN-ad5-TZRaaZ5cKTH0Ar1TOZs,21227
57
- mosamatic2/ui/resources/VERSION,sha256=rjrzwwdgM5RnMbKO12uDFNTXB8MXR6EtdrI79fSNyhA,9
61
+ mosamatic2/ui/resources/VERSION,sha256=faSzNrbz8BB4kaBpCeyQhoMmfWqpb8tKPPbsfcvfHOA,9
58
62
  mosamatic2/ui/settings.py,sha256=YEVHYJIfNsqMO3v1pjzgh7Pih9GGoUX7S9s8S-sBNUk,2121
59
63
  mosamatic2/ui/utils.py,sha256=6bbPIrh4RJ_yhQKNZrgPbL4XeUEogjIjbk_e5c3QS5g,853
60
64
  mosamatic2/ui/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -66,6 +70,7 @@ mosamatic2/ui/widgets/panels/defaultpanel.py,sha256=Ry32Xv6ibxm6NdZ7eBOCcisWNcmn
66
70
  mosamatic2/ui/widgets/panels/logpanel.py,sha256=ogswJ6_ryb6u7JeVnOsh2Ez8KWg6jtCFZwij8s87xO4,1861
67
71
  mosamatic2/ui/widgets/panels/mainpanel.py,sha256=KqI8dA7GpLFd2unqVRTBkNxdnh6AWGpVPwQuaEg8PmI,2431
68
72
  mosamatic2/ui/widgets/panels/pipelines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ mosamatic2/ui/widgets/panels/pipelines/defaultdockerpipelinepanel.py,sha256=SRS6gBhnymgRM6TmaqJgzHzHfffkWucMCjsuzDgJdBg,14415
69
74
  mosamatic2/ui/widgets/panels/pipelines/defaultpipelinepanel.py,sha256=qkfI4BnLIXqE_YvSQj4sO_FjnK0eVdIMqAZ8sktgI-8,13727
70
75
  mosamatic2/ui/widgets/panels/pipelines/pipelinepanel.py,sha256=SlkKme8Wv2Bvp2Alen98mFjv3F5eZCwJylj294gd5uU,178
71
76
  mosamatic2/ui/widgets/panels/stackedpanel.py,sha256=dK1YWuHUzxRhVb5gP0Lu9rAiW4XagjcHmGF__5Lpufk,657
@@ -73,10 +78,10 @@ mosamatic2/ui/widgets/panels/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQe
73
78
  mosamatic2/ui/widgets/panels/tasks/calculatescorestaskpanel.py,sha256=NmPLQizj4x9jgf9UA7VZSjARNHYJB_jrfB0kvaVncdw,9387
74
79
  mosamatic2/ui/widgets/panels/tasks/createdicomsummarytaskpanel.py,sha256=cRCaEmjjZP6RwEhezj2Axxuv8uAGe7ZzHoU67asoZ5s,7530
75
80
  mosamatic2/ui/widgets/panels/tasks/createpngsfromsegmentationstaskpanel.py,sha256=JFnmYjPemRtXPXV2fk2cjB45fseN5BZ8gI_T1zVLGV8,7879
76
- mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py,sha256=gIv5xo0z2EEfFzHI4Q6H87maf-nPIeC8yIxLL5iWuCw,7480
81
+ mosamatic2/ui/widgets/panels/tasks/dicom2niftitaskpanel.py,sha256=6gtaMCZMAbLGv0pOHd4fHBFOluIptFfGOA-vGvrYKGI,7450
77
82
  mosamatic2/ui/widgets/panels/tasks/rescaledicomimagestaskpanel.py,sha256=ds7JxynpMeNvqfHKtg1LQR23rb3_Y7xoDLbZ2wl-TMw,7597
78
83
  mosamatic2/ui/widgets/panels/tasks/segmentmusclefatl3tensorflowtaskpanel.py,sha256=QCEZs9lqaE-XAJuyyrfZVnFkNRyjMw6Cfa-6qP9WaV8,9630
79
- mosamatic2/ui/widgets/panels/tasks/selectslicefromscanstaskpanel.py,sha256=iZu_li_7Zr3n8nVp-FI1GFV_HuCf_I4NY9PNr9aX-oY,8077
84
+ mosamatic2/ui/widgets/panels/tasks/selectslicefromscanstaskpanel.py,sha256=meKltgxPReZ9HioSop6jW_2CFm18URBy3LX11U8tbtc,8059
80
85
  mosamatic2/ui/widgets/panels/tasks/taskpanel.py,sha256=t8lIx1P8sS1Fa-aNm6eEha6297pJQNbBRizkobBexz8,170
81
86
  mosamatic2/ui/widgets/panels/visualizations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
87
  mosamatic2/ui/widgets/panels/visualizations/slicevisualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -86,7 +91,7 @@ mosamatic2/ui/widgets/panels/visualizations/slicevisualization/slicevisualizatio
86
91
  mosamatic2/ui/widgets/panels/visualizations/visualization.py,sha256=JvqTJi7cCGYK1-wrN2oURdCOBoPS2clVUyYglhkoVJg,178
87
92
  mosamatic2/ui/widgets/splashscreen.py,sha256=MS-OczOWfwwEQNQd-JWe9_Mh57css0cSQgbu973rwQo,4056
88
93
  mosamatic2/ui/worker.py,sha256=v7e3gq7MUudgpB1BJW-P7j5wurzu6-HG5m7I6WHgJp0,699
89
- mosamatic2-2.0.10.dist-info/entry_points.txt,sha256=MCUpKkgbej1clgp8EqlLQGs0BIKwGPcBPiVWLfGz9Gw,126
90
- mosamatic2-2.0.10.dist-info/METADATA,sha256=O7XhZE4AY-lCnfN0GHXxv9GUZDYKYbhBo-5nB-oE_kk,1490
91
- mosamatic2-2.0.10.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
92
- mosamatic2-2.0.10.dist-info/RECORD,,
94
+ mosamatic2-2.0.12.dist-info/entry_points.txt,sha256=MCUpKkgbej1clgp8EqlLQGs0BIKwGPcBPiVWLfGz9Gw,126
95
+ mosamatic2-2.0.12.dist-info/METADATA,sha256=VwD0JrQc0gdfEC46CqJlD82m3K6TZ1dxAAERa2k4MsY,1490
96
+ mosamatic2-2.0.12.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
97
+ mosamatic2-2.0.12.dist-info/RECORD,,