├── .gitignore
├── CMakeLists.txt
├── DeepInfer
├── CMakeLists.txt
├── DeepInfer.py
├── Resources
│ ├── .DS_Store
│ ├── Icons
│ │ ├── .DS_Store
│ │ ├── DeepInfer.png
│ │ └── Fiducials.png
│ └── json
│ │ └── .DS_Store
├── Testing
│ ├── CMakeLists.txt
│ └── Python
│ │ └── CMakeLists.txt
└── __init__.py
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled python
2 | *.pyc
3 |
4 | # IPython notebook checkpoints
5 | .ipynb_checkpoints
6 |
7 | # Editor temporaries
8 | *.swp
9 | *~
10 | *.DS_Store
11 |
12 | # pycharm
13 | .idea
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.9)
2 |
3 | project(DeepInfer)
4 |
5 | #-----------------------------------------------------------------------------
6 | set(EXTENSION_HOMEPAGE "http://www.slicer.org/slicerWiki/index.php?title=Documentation/Nightly/Extensions/DeepInfer")
7 | set(EXTENSION_CATEGORY "Machine Learning")
8 | set(EXTENSION_CONTRIBUTORS "Alireza Mehrtash(UBC, BWH), Mehran Pesteie (UBC)")
9 | set(EXTENSION_DESCRIPTION "Deep learning deployment toolkit for medical imaging.")
10 | set(EXTENSION_ICONURL "https://www.slicer.org/w/images/a/a9/Deepinfer-256.png")
11 | set(EXTENSION_SCREENSHOTURLS "://www.slicer.org/w/images/d/d3/Deepinfer-screenshot1.png")
12 | #-----------------------------------------------------------------------------
13 | find_package(Slicer REQUIRED)
14 | include(${Slicer_USE_FILE})
15 |
16 | #-----------------------------------------------------------------------------
17 | add_subdirectory(DeepInfer)
18 |
19 | #-----------------------------------------------------------------------------
20 | include(${Slicer_EXTENSION_CPACK})
21 |
--------------------------------------------------------------------------------
/DeepInfer/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #-----------------------------------------------------------------------------
2 | set(MODULE_NAME DeepInfer)
3 |
4 | #-----------------------------------------------------------------------------
5 | set(MODULE_PYTHON_SCRIPTS
6 | ${MODULE_NAME}.py
7 | )
8 |
9 | set(MODULE_PYTHON_RESOURCES
10 | Resources/Icons/${MODULE_NAME}.png
11 | )
12 |
13 | #-----------------------------------------------------------------------------
14 | slicerMacroBuildScriptedModule(
15 | NAME ${MODULE_NAME}
16 | SCRIPTS ${MODULE_PYTHON_SCRIPTS}
17 | RESOURCES ${MODULE_PYTHON_RESOURCES}
18 | WITH_GENERIC_TESTS
19 | )
20 |
21 | #-----------------------------------------------------------------------------
22 | if(BUILD_TESTING)
23 |
24 | # Register the unittest subclass in the main script as a ctest.
25 | # Note that the test will also be available at runtime.
26 | slicer_add_python_unittest(SCRIPT ${MODULE_NAME}.py)
27 |
28 | # Additional build-time testing
29 | add_subdirectory(Testing)
30 | endif()
31 |
--------------------------------------------------------------------------------
/DeepInfer/DeepInfer.py:
--------------------------------------------------------------------------------
1 | import queue
2 | import json
3 | import platform
4 | import os
5 | import re
6 | import subprocess
7 | import shutil
8 | import threading
9 | from collections import OrderedDict
10 | from glob import glob
11 | from time import sleep
12 |
13 | from __main__ import qt, ctk, slicer
14 |
15 | # To avoid the overhead of importing SimpleITK during application
16 | # startup, the import of SimpleITK is delayed until it is needed.
17 | import SimpleITK as sitk
18 | import sitkUtils
19 |
20 | from slicer.util import getNode, saveNode
21 |
22 |
23 | ICON_DIR = os.path.dirname(os.path.realpath(__file__)) + '/Resources/Icons/'
24 |
25 | from os.path import expanduser
26 | home = expanduser("~")
27 |
28 | DEEPINFER_DIR = os.path.join(home, '.deepinfer')
29 | if not os.path.isdir(DEEPINFER_DIR):
30 | os.mkdir(DEEPINFER_DIR)
31 |
32 | JSON_CLOUD_DIR = os.path.join(DEEPINFER_DIR, 'json', 'cloud')
33 | if os.path.isdir(JSON_CLOUD_DIR):
34 | shutil.rmtree(JSON_CLOUD_DIR)
35 | os.makedirs(JSON_CLOUD_DIR)
36 |
37 | JSON_LOCAL_DIR = os.path.join(DEEPINFER_DIR, 'json', 'local')
38 | if not os.path.isdir(JSON_LOCAL_DIR):
39 | os.makedirs(JSON_LOCAL_DIR)
40 |
41 | TMP_PATH = os.path.join(DEEPINFER_DIR, '.tmp')
42 | if os.path.isdir(TMP_PATH):
43 | shutil.rmtree(TMP_PATH)
44 | os.mkdir(TMP_PATH)
45 |
46 | #
47 | # DeepInfer
48 | #
49 |
50 | class DeepInfer:
51 | # Use class-level scoped variable for module consants
52 | if not __file__.endswith("DeepInfer.py"):
53 | import inspect
54 | __file__ = inspect.getframeinfo(inspect.currentframe())[0]
55 |
56 |
57 |
58 | def __init__(self, parent):
59 | parent.title = "DeepInfer"
60 | parent.categories = ["Machine Learning"]
61 | parent.dependencies = []
62 | parent.contributors = ["Alireza Mehrtash (UBC/BWH/SPL), Mehran Pesteie (UBC)"]
63 | parent.helpText = \
64 | """
65 | This modules provides a basic interface to deploy machine learning and deep learning models in Slicer using Docker.
66 | For general information about the module see the online documentation.
67 |
68 | For detailed information about a specific model please consult the DeepInfer website.
69 | """.format(parent.slicerWikiUrl, slicer.app.majorVersion, slicer.app.minorVersion)
70 |
71 | parent.acknowledgementText = """
72 | The developers would like to thank the support of the Surgical Planning Lab, the University of British Columbia, Slicer Community and the Insight Toolkit.
73 | """
74 | self.parent = parent
75 |
76 | parent.icon = qt.QIcon("%s/ITK.png" % ICON_DIR)
77 |
78 |
79 | #
80 | # qDeepInferWidget
81 | #
82 |
83 | class DeepInferWidget:
84 | def __init__(self, parent=None):
85 |
86 | # To avoid the overhead of importing SimpleITK during application
87 | # startup, the import of SimpleITK is delayed until it is needed.
88 |
89 | if not parent:
90 | self.parent = slicer.qMRMLWidget()
91 | self.parent.setLayout(qt.QVBoxLayout())
92 | self.parent.setMRMLScene(slicer.mrmlScene)
93 | else:
94 | self.parent = parent
95 | self.layout = self.parent.layout()
96 | if not parent:
97 | self.parent.show()
98 |
99 | self.modelParameters = None
100 | self.logic = None
101 |
102 | def onReload(self, moduleName="DeepInfer"):
103 | """Generic reload method for any scripted module.
104 | ModuleWizard will subsitute correct default moduleName.
105 | """
106 | globals()[moduleName] = slicer.util.reloadScriptedModule(moduleName)
107 |
108 | def setup(self):
109 |
110 | # Instantiate and connect widgets ...
111 | #
112 | # Reload and Test area
113 | #
114 | reloadCollapsibleButton = ctk.ctkCollapsibleButton()
115 | reloadCollapsibleButton.collapsed = True
116 | reloadCollapsibleButton.text = "Reload && Test"
117 | reloadFormLayout = qt.QFormLayout(reloadCollapsibleButton)
118 |
119 | # reload button
120 | # (use this during development, but remove it when delivering
121 | # your module to users)
122 | self.reloadButton = qt.QPushButton("Reload")
123 | self.reloadButton.toolTip = "Reload this module."
124 | self.reloadButton.name = "Freehand3DUltrasound Reload"
125 | reloadFormLayout.addWidget(self.reloadButton)
126 | self.reloadButton.connect('clicked()', self.onReload)
127 | # uncomment the following line for debug/development.
128 | self.layout.addWidget(reloadCollapsibleButton)
129 |
130 | # Docker Settings Area
131 | self.dockerGroupBox = ctk.ctkCollapsibleGroupBox()
132 | self.dockerGroupBox.setTitle('Docker Settings')
133 | self.layout.addWidget(self.dockerGroupBox)
134 | dockerForm = qt.QFormLayout(self.dockerGroupBox)
135 | self.dockerPath = ctk.ctkPathLineEdit()
136 | # self.dockerPath.setMaximumWidth(300)
137 | dockerForm.addRow("Docker Executable Path:", self.dockerPath)
138 | self.testDockerButton = qt.QPushButton('Test!')
139 | dockerForm.addRow("Test Docker Configuration:", self.testDockerButton)
140 | if platform.system() == 'Darwin':
141 | self.dockerPath.setCurrentPath('/usr/local/bin/docker')
142 | if platform.system() == 'Linux':
143 | self.dockerPath.setCurrentPath('/usr/bin/docker')
144 | if platform.system() == 'Windows':
145 | self.dockerPath.setCurrentPath("C:/Program Files/Docker/Docker/resources/docker.exe")
146 |
147 | ### use nvidia-docker if it is installed
148 | nvidiaDockerPath = self.dockerPath.currentPath.replace('bin/docker', 'bin/nvidia-docker')
149 | if os.path.isfile(nvidiaDockerPath):
150 | self.dockerPath.setCurrentPath(nvidiaDockerPath)
151 |
152 | # modelRepositoryVerticalLayout = qt.QVBoxLayout(modelRepositoryExpdableArea)
153 |
154 | # Model Repository Area
155 | self.modelRepoGroupBox = ctk.ctkCollapsibleGroupBox()
156 | # self.modelRepoGroupBox.collapsed = True
157 | self.modelRepoGroupBox.setTitle('Cloud Model Repository')
158 | self.layout.addWidget(self.modelRepoGroupBox)
159 | modelRepoVBLayout1 = qt.QVBoxLayout(self.modelRepoGroupBox)
160 | modelRepositoryExpdableArea = ctk.ctkExpandableWidget()
161 | modelRepoVBLayout1.addWidget(modelRepositoryExpdableArea)
162 | modelRepoVBLayout2 = qt.QVBoxLayout(modelRepositoryExpdableArea)
163 | self.modelRegistryTable = qt.QTableWidget()
164 | self.modelRegistryTable.visible = False
165 | self.modelRepositoryModel = qt.QStandardItemModel()
166 | self.modelRepositoryTableHeaderLabels = ['Model', 'Organ', 'Task', 'Status']
167 | self.modelRegistryTable.setColumnCount(4)
168 | self.modelRegistryTable.setSelectionMode(qt.QAbstractItemView.SingleSelection)
169 | self.modelRegistryTable.sortingEnabled = True
170 | self.modelRegistryTable.setHorizontalHeaderLabels(self.modelRepositoryTableHeaderLabels)
171 | self.modelRepositoryTableWidgetHeader = self.modelRegistryTable.horizontalHeader()
172 | self.modelRepositoryTableWidgetHeader.setStretchLastSection(True)
173 | # self.modelRepositoryTableWidgetHeader.setResizeMode(qt.QHeaderView.Stretch)
174 | modelRepoVBLayout2.addWidget(self.modelRegistryTable)
175 | #
176 | self.progressDownload = qt.QProgressBar()
177 | self.progressDownload.setRange(0, 100)
178 | self.progressDownload.setValue(0)
179 | modelRepoVBLayout2.addWidget(self.progressDownload)
180 | self.progressDownload.hide()
181 | #
182 | self.modelRepositoryTreeSelectionModel = self.modelRegistryTable.selectionModel()
183 | abstractItemView = qt.QAbstractItemView()
184 | self.modelRegistryTable.setSelectionBehavior(abstractItemView.SelectRows)
185 | verticalheader = self.modelRegistryTable.verticalHeader()
186 | verticalheader.setDefaultSectionSize(20)
187 | modelRepoVBLayout1.setSpacing(0)
188 | modelRepoVBLayout2.setSpacing(0)
189 | modelRepoVBLayout1.setMargin(0)
190 | modelRepoVBLayout2.setContentsMargins(7, 3, 7, 7)
191 | refreshWidget = qt.QWidget()
192 | modelRepoVBLayout2.addWidget(refreshWidget)
193 | hBoXLayout = qt.QHBoxLayout(refreshWidget)
194 | # hBoXLayout.setSpacing(0)
195 | # hBoXLayout.setMargin(0)
196 | self.connectButton = qt.QPushButton('Connect')
197 | self.downloadButton = qt.QPushButton('Download')
198 | self.downloadButton.enabled = False
199 | self.downloadButton.visible = False
200 | hBoXLayout.addStretch(1)
201 | hBoXLayout.addWidget(self.connectButton)
202 | hBoXLayout.addWidget(self.downloadButton)
203 | self.populateModelRegistryTable()
204 |
205 |
206 |
207 | #
208 | # Cancel/Apply Row
209 | #
210 | self.restoreDefaultsButton = qt.QPushButton("Restore Defaults")
211 | self.restoreDefaultsButton.toolTip = "Restore the default parameters."
212 | self.restoreDefaultsButton.enabled = True
213 |
214 | self.cancelButton = qt.QPushButton("Cancel")
215 | self.cancelButton.toolTip = "Abort the algorithm."
216 | self.cancelButton.enabled = False
217 |
218 | self.applyButton = qt.QPushButton("Apply")
219 | self.applyButton.toolTip = "Run the algorithm."
220 | self.applyButton.enabled = True
221 |
222 | hlayout = qt.QHBoxLayout()
223 |
224 | #
225 | # Local Models Area
226 | #
227 | modelsCollapsibleButton = ctk.ctkCollapsibleGroupBox()
228 | modelsCollapsibleButton.setTitle("Local Models")
229 | self.layout.addWidget(modelsCollapsibleButton)
230 | # Layout within the dummy collapsible button
231 | modelsFormLayout = qt.QFormLayout(modelsCollapsibleButton)
232 |
233 | # model search
234 | self.searchBox = ctk.ctkSearchBox()
235 | modelsFormLayout.addRow("Search:", self.searchBox)
236 | self.searchBox.connect("textChanged(QString)", self.onSearch)
237 |
238 | # model selector
239 | self.modelSelector = qt.QComboBox()
240 | modelsFormLayout.addRow("Model:", self.modelSelector)
241 | self.populateLocalModels()
242 |
243 | # connections
244 | self.modelSelector.connect('currentIndexChanged(int)', self.onModelSelect)
245 |
246 | #
247 | # Parameters Area
248 | #
249 | parametersCollapsibleButton = ctk.ctkCollapsibleGroupBox()
250 | parametersCollapsibleButton.setTitle("Model Parameters")
251 | self.layout.addWidget(parametersCollapsibleButton)
252 |
253 | # Layout within the dummy collapsible button
254 | parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)
255 | self.modelParameters = ModelParameters(parametersCollapsibleButton)
256 |
257 | # Add vertical spacer
258 | self.layout.addStretch(1)
259 |
260 | #
261 | # Status and Progress
262 | #
263 | statusLabel = qt.QLabel("Status: ")
264 | self.currentStatusLabel = qt.QLabel("Idle")
265 | hlayout = qt.QHBoxLayout()
266 | hlayout.addStretch(1)
267 | hlayout.addWidget(statusLabel)
268 | hlayout.addWidget(self.currentStatusLabel)
269 | self.layout.addLayout(hlayout)
270 |
271 | self.progress = qt.QProgressBar()
272 | self.progress.setRange(0, 1000)
273 | self.progress.setValue(0)
274 | self.layout.addWidget(self.progress)
275 | self.progress.hide()
276 |
277 | #
278 | # Cancel/Apply Row
279 | #
280 | self.restoreDefaultsButton = qt.QPushButton("Restore Defaults")
281 | self.restoreDefaultsButton.toolTip = "Restore the default parameters."
282 | self.restoreDefaultsButton.enabled = True
283 |
284 | self.cancelButton = qt.QPushButton("Cancel")
285 | self.cancelButton.toolTip = "Abort the algorithm."
286 | self.cancelButton.enabled = False
287 |
288 | self.applyButton = qt.QPushButton("Apply")
289 | self.applyButton.toolTip = "Run the algorithm."
290 | self.applyButton.enabled = True
291 |
292 | hlayout = qt.QHBoxLayout()
293 |
294 | hlayout.addWidget(self.restoreDefaultsButton)
295 | hlayout.addStretch(1)
296 | hlayout.addWidget(self.cancelButton)
297 | hlayout.addWidget(self.applyButton)
298 | self.layout.addLayout(hlayout)
299 |
300 | # connections
301 | self.connectButton.connect('clicked(bool)', self.onConnectButton)
302 | self.downloadButton.connect('clicked(bool)', self.onDownloadButton)
303 | self.testDockerButton.connect('clicked(bool)', self.onTestDockerButton)
304 | self.restoreDefaultsButton.connect('clicked(bool)', self.onRestoreDefaultsButton)
305 | self.applyButton.connect('clicked(bool)', self.onApplyButton)
306 | self.cancelButton.connect('clicked(bool)', self.onCancelButton)
307 | self.modelRegistryTable.connect('itemSelectionChanged()', self.onCloudModelSelect)
308 |
309 | # Initlial Selection
310 | self.modelSelector.currentIndexChanged(self.modelSelector.currentIndex)
311 |
312 | def cleanup(self):
313 | pass
314 |
315 | def getAllDigests(self):
316 | cmd = []
317 | cmd.append(self.dockerPath.currentPath)
318 | cmd.append('images')
319 | cmd.append('--digests')
320 | # print(cmd)
321 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
322 | digest_index = 2
323 | digests = []
324 | try:
325 | while True:
326 | slicer.app.processEvents()
327 | line = p.stdout.readline()
328 | if not line:
329 | break
330 | line = line.split()
331 | if 'DIGEST' in line:
332 | digest_index = line.index('DIGEST')
333 | else:
334 | digests.append(line[digest_index])
335 | except Exception as e:
336 | print("Exception: {}".format(e))
337 | return digests
338 |
339 | def populateLocalModels(self):
340 | digests = self.getAllDigests()
341 | jsonFiles = glob(JSON_LOCAL_DIR + "/*.json")
342 | jsonFiles.sort(cmp=lambda x, y: cmp(os.path.basename(x), os.path.basename(y)))
343 | self.jsonModels = []
344 | for fname in jsonFiles:
345 | with open(fname, "r") as fp:
346 | j = json.load(fp, object_pairs_hook=OrderedDict)
347 | self.jsonModels.append(j)
348 | # if j['docker']['digest'] in digests:
349 | # self.jsonModels.append(j)
350 | # else:
351 | # os.remove(fname)
352 | # add all the models listed in the json files
353 |
354 | for idx, j in enumerate(self.jsonModels):
355 | name = j["name"]
356 | self.modelSelector.addItem(name, idx)
357 |
358 | def onCloudModelSelect(self):
359 | self.downloadButton.enabled = False
360 | # print("on cloud model select!")
361 | for item in self.modelTableItems.keys():
362 | if item.isSelected():
363 | self.downloadButton.enabled = True
364 | self.selectedModelPath = self.modelTableItems[item]
365 |
366 | def onLogicRunStop(self):
367 | self.applyButton.setEnabled(True)
368 | self.restoreDefaultsButton.setEnabled(True)
369 | self.cancelButton.setEnabled(False)
370 | self.logic = None
371 | self.progress.hide()
372 |
373 | def onLogicRunStart(self):
374 | self.applyButton.setEnabled(False)
375 | self.restoreDefaultsButton.setEnabled(False)
376 |
377 | def onSearch(self, searchText):
378 | # add all the models listed in the json files
379 | self.modelSelector.clear()
380 | # split text on whitespace of and string search
381 | searchTextList = searchText.split()
382 | for idx, j in enumerate(self.jsonModels):
383 | lname = j["name"].lower()
384 | # require all elements in list, to add to select. case insensitive
385 | if reduce(lambda x, y: x and (lname.find(y.lower()) != -1), [True] + searchTextList):
386 | self.modelSelector.addItem(j["name"], idx)
387 |
388 | def onModelSelect(self, selectorIndex):
389 | # print("on model select")
390 | self.modelParameters.destroy()
391 | if selectorIndex < 0:
392 | return
393 | jsonIndex = self.modelSelector.itemData(selectorIndex)
394 | json_model = self.jsonModels[jsonIndex]
395 | self.modelParameters.create(json_model)
396 |
397 | if "briefdescription" in self.jsonModels[jsonIndex]:
398 | tip = self.jsonModels[jsonIndex]["briefdescription"]
399 | tip = tip.rstrip()
400 | self.modelSelector.setToolTip(tip)
401 | else:
402 | self.modelSelector.setToolTip("")
403 |
404 | def onConnectButton(self):
405 | try:
406 | self.modelRegistryTable.visible = True
407 | self.downloadButton.visible = True
408 | self.connectButton.visible = False
409 | self.connectButton.enabled = False
410 | import urllib2
411 | url = 'https://api.github.com/repos/DeepInfer/Model-Registry/contents/'
412 | response = urllib2.urlopen(url)
413 | data = json.load(response)
414 | for item in data:
415 | if 'json' in item['name']:
416 | # print(item['name'])
417 | url = item['url']
418 | response = urllib2.urlopen(url)
419 | data = json.load(response)
420 | dl_url = data['download_url']
421 | print("downloading: {}...".format(dl_url))
422 | response = urllib2.urlopen(dl_url)
423 | content = response.read()
424 | outputPath = os.path.join(JSON_CLOUD_DIR, dl_url.split('/')[-1])
425 | with open(outputPath, 'w') as f:
426 | f.write(content)
427 | self.populateModelRegistryTable()
428 | except Exception as e:
429 | print("Exception occured: {}".format(e))
430 | self.connectButton.enabled = True
431 | self.modelRegistryTable.visible = False
432 | self.downloadButton.visible = False
433 | self.connectButton.visible = True
434 | self.connectButton.enabled = True
435 |
436 | def onTestDockerButton(self):
437 | cmd = []
438 | cmd.append(self.dockerPath.currentPath)
439 | cmd.append('--version')
440 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
441 | message = p.stdout.readline().replace('\n', '')
442 | if message.startswith('Docker version'):
443 | qt.QMessageBox.information(None, 'Docker Status', 'Docker is configured correctly'
444 | ' ({}).'.format(message))
445 | else:
446 | qt.QMessageBox.critical(None, 'Docker Status', 'Docker is not configured correctly. Check your docker '
447 | 'installation and make sure that it is configured to '
448 | 'be run by non-root user.')
449 |
450 | def onDownloadButton(self):
451 | with open(self.selectedModelPath) as json_data:
452 | model = json.load(json_data)
453 | size = model['docker']['size']
454 | model_name = model['name']
455 | dockerhub_address = model['docker']['dockerhub_repository'] + '@' + model['docker']['digest']
456 | if platform.system() == 'Windows':
457 | terminal = 'Command Prompt or PowerShell, but not PowerShell ISE'
458 | else:
459 | terminal = 'terminal'
460 | message_title = '1. Copy the following command from the box below.\n' \
461 | '2. Open the {}, paste and run the copied command.\n' \
462 | '3. Wait until the docker image download is finished.\n' \
463 | '4. Restart 3D Slicer.\n' \
464 | '5. You should see the downloaded model in the \"Local Models\" section.\n' \
465 | '(Please note that the size of the \"{}\" docker image is {}.\n' \
466 | 'Make sure that you have enough storage space on your machine.)'.format(terminal, model_name, size)
467 | pull_command = 'docker pull {}'.format(dockerhub_address)
468 | mainWindow = slicer.util.mainWindow()
469 | downloadWidget = qt.QWidget()
470 | layout = qt.QVBoxLayout()
471 | downloadWidget.setLayout(layout)
472 | popupGeometry = qt.QRect()
473 | if mainWindow:
474 | width = 400
475 | height = 200
476 | popupGeometry.setWidth(width)
477 | popupGeometry.setHeight(height)
478 | downloadWidget.setGeometry(popupGeometry)
479 |
480 | pos = mainWindow.pos
481 | downloadWidget.move(pos.x() + (mainWindow.width - downloadWidget.width) / 2,
482 | pos.y() + (mainWindow.height - downloadWidget.height) / 2)
483 |
484 | titleLabel = qt.QLabel(message_title)
485 | layout.addWidget(titleLabel)
486 | te = qt.QTextEdit(pull_command)
487 | te.readOnly = True
488 | layout.addWidget(te)
489 | closeButton = qt.QPushButton('Close')
490 | layout.addWidget(closeButton)
491 | downloadWidget.show()
492 | def hide_download():
493 | downloadWidget.hide()
494 | closeButton.connect('clicked(bool)', hide_download)
495 | shutil.copy(self.selectedModelPath, os.path.join(JSON_LOCAL_DIR, os.path.basename(self.selectedModelPath)))
496 |
497 |
498 | '''
499 | def onDownloadButton(self):
500 | with open(self.selectedModelPath) as json_data:
501 | model = json.load(json_data)
502 | size = model['docker']['size']
503 | resoponse = self.Question("The size of the selected image to download is {}. Are you sure you want to proceed?".format(size),
504 | title="Download", parent=None)
505 | if resoponse:
506 | cmd = []
507 | cmd.append(self.dockerPath.currentPath)
508 | cmd.append('pull')
509 | cmd.append(model['docker']['dockerhub_repository'] + '@' + model['docker']['digest'])
510 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
511 | print(cmd)
512 | parts = dict()
513 | try:
514 | while True:
515 | self.progressDownload.show()
516 | slicer.app.processEvents()
517 | line = p.stdout.readline()
518 | if not line:
519 | break
520 | line = line.rstrip()
521 | print(line)
522 | part = line.split(':')[0]
523 | if len(part) == 12:
524 | parts[part] = line.split(':')[1]
525 | if parts.keys():
526 | print('-'*100)
527 | print(parts)
528 | n_parts = len(parts.keys())
529 | n_completed = len([status for status in parts.values() if status == ' Pull complete'])
530 | self.progressDownload.setValue(int((100*n_completed)/n_parts))
531 |
532 | except Exception as e:
533 | print("Exception: {}".format(e))
534 | print(parts)
535 | self.progressDownload.setValue(0)
536 | self.progressDownload.hide()
537 | shutil.copy(self.selectedModelPath, os.path.join(JSON_LOCAL_DIR, os.path.basename(self.selectedModelPath)))
538 | self.populateLocalModels()
539 | else:
540 | print("Download was canceled!")
541 |
542 | '''
543 | def Question(self, text, title="", parent=None):
544 | return qt.QMessageBox.question(parent, title, text,
545 | qt.QMessageBox.Yes, qt.QMessageBox.No) == qt.QMessageBox.Yes
546 |
547 | def populateModelRegistryTable(self):
548 | self.modelTableItems = dict()
549 | # print("populate Model Registry Table")
550 | model_files = glob(JSON_CLOUD_DIR+'/*.json')
551 | self.modelRegistryTable.setRowCount(len(model_files))
552 | n = 0
553 | model_files = [os.path.join(JSON_CLOUD_DIR, model_file) for model_file in model_files]
554 | for model_file in model_files:
555 | with open(model_file) as json_data:
556 | model = json.load(json_data)
557 | keys = model.keys()
558 | for key in keys:
559 | if key == 'name':
560 | nameTableItem = qt.QTableWidgetItem(str(model['name']))
561 | self.modelTableItems[nameTableItem] = model_file
562 | self.modelRegistryTable.setItem(n, 0, nameTableItem)
563 | if key == 'organ':
564 | organ = qt.QTableWidgetItem(str(model['organ']))
565 | self.modelRegistryTable.setItem(n, 1, organ)
566 | if key == 'task':
567 | task = qt.QTableWidgetItem(str(model['task']))
568 | self.modelRegistryTable.setItem(n, 2, task)
569 | n += 1
570 |
571 | def onRestoreDefaultsButton(self):
572 | self.onModelSelect(self.modelSelector.currentIndex)
573 |
574 | def onApplyButton(self):
575 | print('onApply')
576 | self.logic = DeepInferLogic()
577 | # try:
578 | self.currentStatusLabel.text = "Starting"
579 | self.modelParameters.prerun()
580 | self.logic.run(self.modelParameters)
581 |
582 | '''
583 | except:
584 | self.currentStatusLabel.text = "Exception"
585 | slicer.modules.DeepInferWidget.applyButton.enabled = True
586 | import sys
587 | msg = sys.exc_info()[0]
588 | # if there was an exception during start-up make sure to finish
589 | self.onLogicRunStop()
590 | qt.QMessageBox.critical(slicer.util.mainWindow(),
591 | "Exception before execution: {0} ".format(self.modelParameters.dockerImageName),
592 | msg)
593 | '''
594 |
595 | def onCancelButton(self):
596 | self.currentStatusLabel.text = "Aborting"
597 | if self.logic:
598 | self.logic.abort = True;
599 |
600 | def onLogicEventStart(self):
601 | self.currentStatusLabel.text = "Running"
602 | self.cancelButton.setDisabled(False)
603 | self.progress.setValue(0)
604 | self.progress.show()
605 |
606 | def onLogicEventEnd(self):
607 | self.currentStatusLabel.text = "Completed"
608 | self.progress.setValue(1000)
609 |
610 | def onLogicEventAbort(self):
611 | self.currentStatusLabel.text = "Aborted"
612 |
613 | def onLogicEventProgress(self, progress):
614 | self.currentStatusLabel.text = "Running ({0:6.5f})".format(progress)
615 | self.progress.setValue(progress * 1000)
616 |
617 | def onLogicEventIteration(self, nIter):
618 | print("Iteration ", nIter)
619 |
620 | #
621 | # DeepInferLogic
622 | #
623 |
624 | class DeepInferLogic:
625 | """This class should implement all the actual
626 | computation done by your module. The interface
627 | should be such that other python code can import
628 | this class and make use of the functionality without
629 | requiring an instance of the Widget
630 | """
631 |
632 | def __init__(self):
633 | self.main_queue = queue.Queue()
634 | self.main_queue_running = False
635 | self.thread = threading.Thread()
636 | self.abort = False
637 | modules = slicer.modules
638 | if hasattr(modules, 'DeepInferWidget'):
639 | self.dockerPath = slicer.modules.DeepInferWidget.dockerPath.currentPath
640 | else:
641 | if platform.system() == 'Darwin':
642 | defualt_path = '/usr/local/bin/docker'
643 | self.setDockerPath(defualt_path)
644 | elif platform.system() == 'Linux':
645 | defualt_path = '/usr/bin/docker'
646 | self.setDockerPath(defualt_path)
647 | elif platform.system() == 'Windows':
648 | defualt_path = "C:/Program Files/Docker/Docker/resources/docker.exe"
649 | self.setDockerPath(defualt_path)
650 | else:
651 | print('could not determine system type')
652 |
653 |
654 | def __del__(self):
655 | if self.main_queue_running:
656 | self.main_queue_stop()
657 | if self.thread.is_alive():
658 | self.thread.join()
659 |
660 | def setDockerPath(self, path):
661 | self.dockerPath = path
662 |
663 | def yieldPythonGIL(self, seconds=0):
664 | sleep(seconds)
665 |
666 | def cmdCheckAbort(self, p):
667 | if self.abort:
668 | p.kill()
669 | self.cmdAbortEvent()
670 |
671 | def cmdStartEvent(self):
672 | if hasattr(slicer.modules, 'DeepInferWidget'):
673 | widget = slicer.modules.DeepInferWidget
674 | widget.onLogicEventStart()
675 | self.yieldPythonGIL()
676 |
677 | def cmdProgressEvent(self, progress):
678 | if hasattr(slicer.modules, 'DeepInferWidget'):
679 | widget = slicer.modules.DeepInferWidget
680 | widget.onLogicEventProgress(progress)
681 | self.yieldPythonGIL()
682 |
683 | def cmdAbortEvent(self):
684 | if hasattr(slicer.modules, 'DeepInferWidget'):
685 | widget = slicer.modules.DeepInferWidget
686 | widget.onLogicEventAbort()
687 | self.yieldPythonGIL()
688 |
689 | def cmdEndEvent(self):
690 | if hasattr(slicer.modules, 'DeepInferWidget'):
691 | widget = slicer.modules.DeepInferWidget
692 | widget.onLogicEventEnd()
693 | self.yieldPythonGIL()
694 |
695 | def checkDockerDaemon(self):
696 | cmd = list()
697 | cmd.append(self.dockerPath)
698 | cmd.append('ps')
699 | print(cmd)
700 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
701 | slicer.app.processEvents()
702 | line = p.stdout.readline()
703 | if line[:9] == 'CONTAINER':
704 | return True
705 | return False
706 |
707 | def executeDocker(self, dockerName, modelName, dataPath, iodict, inputs, params):
708 | try:
709 | assert self.checkDockerDaemon(), "Docker Daemon is not running"
710 | except Exception as e:
711 | print(e.message)
712 | self.abort = True
713 |
714 | modules = slicer.modules
715 | if hasattr(modules, 'DeepInferWidget'):
716 | widgetPresent = True
717 | else:
718 | widgetPresent = False
719 |
720 | if widgetPresent:
721 | self.cmdStartEvent()
722 | inputDict = dict()
723 | outputDict = dict()
724 | paramDict = dict()
725 | for item in iodict:
726 | if iodict[item]["iotype"] == "input":
727 | if iodict[item]["type"] == "volume":
728 | # print(inputs[item])
729 | input_node_name = inputs[item].GetName()
730 | #try:
731 | img = sitk.ReadImage(sitkUtils.GetSlicerITKReadWriteAddress(input_node_name))
732 | fileName = item + '.nrrd'
733 | inputDict[item] = fileName
734 | sitk.WriteImage(img, str(os.path.join(TMP_PATH, fileName)))
735 | #except Exception as e:
736 | # print(e.message)
737 | elif iodict[item]["type"] == "point_vec":
738 | input_node_name = inputs[item].GetName()
739 | fidListNode = getNode(input_node_name)
740 | fileName = item + '.fcsv'
741 | inputDict[item] = fileName
742 | output_path = str(os.path.join(TMP_PATH, fileName))
743 | saveNode(fidListNode, output_path)
744 | elif iodict[item]["iotype"] == "output":
745 | if iodict[item]["type"] == "volume":
746 | outputDict[item] = item + '.nrrd'
747 | elif iodict[item]["type"] == "point_vec":
748 | outputDict[item] = item + '.fcsv'
749 | elif iodict[item]["iotype"] == "parameter":
750 | paramDict[item] = str(params[item])
751 |
752 | if not dataPath:
753 | dataPath = '/home/deepinfer/data'
754 |
755 | print('docker run command:')
756 | cmd = list()
757 | cmd.append(self.dockerPath)
758 | cmd.extend(('run', '-t', '-v'))
759 | cmd.append(TMP_PATH + ':' + dataPath)
760 | cmd.append(dockerName)
761 | for key in inputDict.keys():
762 | cmd.append('--' + key)
763 | cmd.append(dataPath + '/' + inputDict[key])
764 | for key in outputDict.keys():
765 | cmd.append('--' + key)
766 | cmd.append(dataPath + '/' + outputDict[key])
767 | if modelName:
768 | cmd.append('--ModelName')
769 | cmd.append(modelName)
770 | for key in paramDict.keys():
771 | if iodict[key]["type"] == "bool":
772 | if paramDict[key]:
773 | cmd.append('--' + key)
774 | else:
775 | cmd.append('--' + key)
776 | cmd.append(paramDict[key])
777 | print('-'*100)
778 | print(cmd)
779 |
780 | # TODO: add a line to check wether the docker image is present or not. If not ask user to download it.
781 | # try:
782 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
783 | progress = 0
784 | # print('executing')
785 | while True:
786 | progress += 0.15
787 | slicer.app.processEvents()
788 | self.cmdCheckAbort(p)
789 | if widgetPresent:
790 | self.cmdProgressEvent(progress)
791 | line = p.stdout.readline()
792 | if not line:
793 | break
794 | print(line)
795 |
796 |
797 | def thread_doit(self, modelParameters):
798 | iodict = modelParameters.iodict
799 | inputs = modelParameters.inputs
800 | params = modelParameters.params
801 | outputs = modelParameters.outputs
802 | dockerName = modelParameters.dockerImageName
803 | modelName = modelParameters.modelName
804 | dataPath = modelParameters.dataPath
805 | #try:
806 | self.main_queue_start()
807 | self.executeDocker(dockerName, modelName, dataPath, iodict, inputs, params)
808 | if not self.abort:
809 | self.updateOutput(iodict, outputs)
810 | self.main_queue_stop()
811 | self.cmdEndEvent()
812 |
813 | '''
814 | except Exception as e:
815 | msg = e.message
816 | qt.QMessageBox.critical(slicer.util.mainWindow(), "Exception during execution of ", msg)
817 | slicer.modules.DeepInferWidget.applyButton.enabled = True
818 | slicer.modules.DeepInferWidget.progress.hide = True
819 | self.abort = True
820 | self.yieldPythonGIL()
821 | '''
822 | def main_queue_start(self):
823 | """Begins monitoring of main_queue for callables"""
824 | self.main_queue_running = True
825 | slicer.modules.DeepInferWidget.onLogicRunStart()
826 | qt.QTimer.singleShot(0, self.main_queue_process)
827 |
828 | def main_queue_stop(self):
829 | """End monitoring of main_queue for callables"""
830 | self.main_queue_running = False
831 | if self.thread.is_alive():
832 | self.thread.join()
833 | slicer.modules.DeepInferWidget.onLogicRunStop()
834 |
835 | def main_queue_process(self):
836 | """processes the main_queue of callables"""
837 | try:
838 | while not self.main_queue.empty():
839 | f = self.main_queue.get_nowait()
840 | if callable(f):
841 | f()
842 |
843 | if self.main_queue_running:
844 | # Yield the GIL to allow other thread to do some python work.
845 | # This is needed since pyQt doesn't yield the python GIL
846 | self.yieldPythonGIL(.01)
847 | qt.QTimer.singleShot(0, self.main_queue_process)
848 |
849 | except Exception as e:
850 | import sys
851 | sys.stderr.write("ModelLogic error in main_queue: \"{0}\"".format(e))
852 |
853 | # if there was an error try to resume
854 | if not self.main_queue.empty() or self.main_queue_running:
855 | qt.QTimer.singleShot(0, self.main_queue_process)
856 |
857 | def updateOutput(self, iodict, outputs):
858 | # print('updateOutput method')
859 | output_volume_files = dict()
860 | output_fiduciallist_files = dict()
861 | for item in iodict:
862 | if iodict[item]["iotype"] == "output":
863 | if iodict[item]["type"] == "volume":
864 | fileName = str(os.path.join(TMP_PATH, item + '.nrrd'))
865 | output_volume_files[item] = fileName
866 | if iodict[item]["type"] == "point_vec":
867 | fileName = str(os.path.join(TMP_PATH, item + '.fcsv'))
868 | output_fiduciallist_files[item] = fileName
869 | for output_volume in output_volume_files.keys():
870 | result = sitk.ReadImage(output_volume_files[output_volume])
871 | output_node = outputs[output_volume]
872 | output_node_name = output_node.GetName()
873 | nodeWriteAddress = sitkUtils.GetSlicerITKReadWriteAddress(output_node_name)
874 | sitk.WriteImage(result, nodeWriteAddress)
875 | applicationLogic = slicer.app.applicationLogic()
876 | selectionNode = applicationLogic.GetSelectionNode()
877 |
878 | outputLabelMap = True
879 | if outputLabelMap:
880 | selectionNode.SetReferenceActiveLabelVolumeID(output_node.GetID())
881 | else:
882 | selectionNode.SetReferenceActiveVolumeID(output_node.GetID())
883 |
884 | applicationLogic.PropagateVolumeSelection(0)
885 | applicationLogic.FitSliceToAll()
886 | for fiduciallist in output_fiduciallist_files.keys():
887 | # information about loading markups: https://www.slicer.org/wiki/Documentation/Nightly/Modules/Markups
888 | output_node = outputs[fiduciallist]
889 | _, node = slicer.util.loadMarkupsFiducialList(output_fiduciallist_files[fiduciallist], True)
890 | output_node.Copy(node)
891 | scene = slicer.mrmlScene
892 | # todo: currently due to a bug in markups module removing the node will create some unexpected behaviors
893 | # reported bug reference: https://issues.slicer.org/view.php?id=4414
894 | # scene.RemoveNode(node)
895 |
896 |
897 | def run(self, modelParamters):
898 | """
899 | Run the actual algorithm
900 | """
901 | if self.thread.is_alive():
902 | import sys
903 | sys.stderr.write("ModelLogic is already executing!")
904 | return
905 | self.abort = False
906 | self.thread = threading.Thread(target=self.thread_doit(modelParameters=modelParamters))
907 |
908 |
909 | #
910 | # Class to manage parameters
911 | #
912 |
913 | class ModelParameters(object):
914 | """ This class is for managing the widgets for the parameters for a model
915 | """
916 |
917 | # class-scope regular expression to help covert from CamelCase
918 | reCamelCase = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')
919 |
920 | def __init__(self, parent=None):
921 | self.parent = parent
922 | self.widgets = []
923 | self.json = None
924 | # self.model = None
925 | self.inputs = []
926 | self.outputs = []
927 | self.prerun_callbacks = []
928 | self.outputLabelMap = False
929 | self.iodict = dict()
930 | self.dockerImageName = ''
931 | self.modelName = None
932 | self.dataPath = None
933 |
934 | self.outputSelector = None
935 | self.outputLabelMapBox = None
936 |
937 |
938 | def __del__(self):
939 | self.widgets = []
940 |
941 | def BeautifyCamelCase(self, str):
942 | return self.reCamelCase.sub(r' \1', str)
943 |
944 | def create_iodict(self, json_dict):
945 | iodict = dict()
946 | for member in json_dict["members"]:
947 | if "type" in member:
948 | t = member["type"]
949 | if t in ["uint8_t", "int8_t",
950 | "uint16_t", "int16_t",
951 | "uint32_t", "int32_t",
952 | "uint64_t", "int64_t",
953 | "unsigned int", "int",
954 | "double", "float"]:
955 | iodict[member["name"]] = {"type": member["type"], "iotype": member["iotype"],
956 | "value": member["default"]}
957 |
958 | else:
959 | iodict[member["name"]] = {"type": member["type"], "iotype": member["iotype"]}
960 | return iodict
961 |
962 | def create_model_info(self, json_dict):
963 | dockerImageName = json_dict['docker']['dockerhub_repository']
964 | modelName = json_dict.get('model_name')
965 | dataPath = json_dict.get('data_path')
966 | return dockerImageName, modelName, dataPath
967 |
968 | def create(self, json_dict):
969 | if not self.parent:
970 | raise "no parent"
971 | # parametersFormLayout = self.parent.layout()
972 |
973 | # You can't use exec in a function that has a subfunction, unless you specify a context.
974 | # exec ('self.model = sitk.{0}()'.format(json["name"])) in globals(), locals()
975 |
976 | self.iodict = self.create_iodict(json_dict)
977 | self.dockerImageName, self.modelName, self.dataPath = self.create_model_info(json_dict)
978 |
979 | self.prerun_callbacks = []
980 | self.inputs = dict()
981 | self.outputs = dict()
982 | self.params = dict()
983 | self.outputLabelMap = False
984 |
985 | #
986 | # Iterate over the members in the JSON to generate a GUI
987 | #
988 | for member in json_dict["members"]:
989 | w = None
990 | if "type" in member:
991 | t = member["type"]
992 |
993 | if "dim_vec" in member and int(member["dim_vec"]):
994 | if member["itk_type"].endswith("IndexType") or member["itk_type"].endswith("PointType"):
995 | isPoint = member["itk_type"].endswith("PointType")
996 |
997 | fiducialSelector = slicer.qMRMLNodeComboBox()
998 | self.widgets.append(fiducialSelector)
999 | fiducialSelector.nodeTypes = ("vtkMRMLMarkupsFiducialNode", "vtkMRMLAnnotationFiducialNode")
1000 | fiducialSelector.selectNodeUponCreation = True
1001 | fiducialSelector.addEnabled = False
1002 | fiducialSelector.removeEnabled = False
1003 | fiducialSelector.renameEnabled = True
1004 | fiducialSelector.noneEnabled = False
1005 | fiducialSelector.showHidden = False
1006 | fiducialSelector.showChildNodeTypes = True
1007 | fiducialSelector.setMRMLScene(slicer.mrmlScene)
1008 | fiducialSelector.setToolTip("Pick the Fiducial for the Point or Index")
1009 |
1010 | fiducialSelector.connect("nodeActivated(vtkMRMLNode*)",
1011 | lambda node, w=fiducialSelector, name=member["name"],
1012 | isPt=isPoint: self.onFiducialNode(name, w, isPt))
1013 | self.prerun_callbacks.append(
1014 | lambda w=fiducialSelector, name=member["name"], isPt=isPoint: self.onFiducialNode(name, w,
1015 | isPt))
1016 |
1017 | w1 = fiducialSelector
1018 |
1019 | fiducialSelectorLabel = qt.QLabel("{0}: ".format(member["name"]))
1020 | self.widgets.append(fiducialSelectorLabel)
1021 |
1022 | icon = qt.QIcon(ICON_DIR + "Fiducials.png")
1023 |
1024 | toggle = qt.QPushButton(icon, "")
1025 | toggle.setCheckable(True)
1026 | toggle.toolTip = "Toggle Fiducial Selection"
1027 | self.widgets.append(toggle)
1028 |
1029 | w2 = self.createVectorWidget(member["name"], t)
1030 |
1031 | hlayout = qt.QHBoxLayout()
1032 | hlayout.addWidget(fiducialSelector)
1033 | hlayout.setStretchFactor(fiducialSelector, 1)
1034 | hlayout.addWidget(w2)
1035 | hlayout.setStretchFactor(w2, 1)
1036 | hlayout.addWidget(toggle)
1037 | hlayout.setStretchFactor(toggle, 0)
1038 | w1.hide()
1039 |
1040 | self.widgets.append(hlayout)
1041 |
1042 | toggle.connect("clicked(bool)",
1043 | lambda checked, ptW=w2, fidW=w1: self.onToggledPointSelector(checked, ptW, fidW))
1044 |
1045 | # parametersFormLayout.addRow(fiducialSelectorLabel, hlayout)
1046 |
1047 | else:
1048 | w = self.createVectorWidget(member["name"], t)
1049 |
1050 | elif t == "point_vec":
1051 |
1052 | fiducialSelector = slicer.qMRMLNodeComboBox()
1053 | self.widgets.append(fiducialSelector)
1054 | fiducialSelector.nodeTypes = ("vtkMRMLMarkupsFiducialNode", "vtkMRMLAnnotationHierarchyNode")
1055 | fiducialSelector.addAttribute("vtkMRMLAnnotationHierarchyNode", "MainChildType",
1056 | "vtkMRMLAnnotationFiducialNode")
1057 | fiducialSelector.selectNodeUponCreation = True
1058 | fiducialSelector.addEnabled = True
1059 | fiducialSelector.removeEnabled = False
1060 | fiducialSelector.renameEnabled = True
1061 | fiducialSelector.noneEnabled = False
1062 | fiducialSelector.showHidden = False
1063 | fiducialSelector.showChildNodeTypes = True
1064 | fiducialSelector.setMRMLScene(slicer.mrmlScene)
1065 | fiducialSelector.setToolTip("Pick the Markups node for the point list.")
1066 |
1067 | fiducialSelector.connect("nodeActivated(vtkMRMLNode*)",
1068 | lambda node, name=member["name"]
1069 | : self.onFiducialListNode(name, node,
1070 | member["iotype"]))
1071 | self.prerun_callbacks.append(lambda w=fiducialSelector,
1072 | name=member["name"],
1073 | : self.onFiducialListNode(name,
1074 | w.currentNode(),
1075 | member["iotype"]))
1076 | w = fiducialSelector
1077 |
1078 | elif "enum" in member:
1079 | w = self.createEnumWidget(member["name"], member["enum"])
1080 |
1081 | elif member["name"].endswith("Direction") and "std::vector" in t:
1082 | # This member name is use for direction cosine matrix for image sources.
1083 | # We are going to ignore it
1084 | pass
1085 | elif t == "volume":
1086 | w = self.createVolumeWidget(member["name"], member["iotype"], member["voltype"], False)
1087 |
1088 | elif t == "InterpolatorEnum":
1089 | labels = ["Nearest Neighbor",
1090 | "Linear",
1091 | "BSpline",
1092 | "Gaussian",
1093 | "Label Gaussian",
1094 | "Hamming Windowed Sinc",
1095 | "Cosine Windowed Sinc",
1096 | "Welch Windowed Sinc",
1097 | "Lanczos Windowed Sinc",
1098 | "Blackman Windowed Sinc"]
1099 | values = ["sitk.sitkNearestNeighbor",
1100 | "sitk.sitkLinear",
1101 | "sitk.sitkBSpline",
1102 | "sitk.sitkGaussian",
1103 | "sitk.sitkLabelGaussian",
1104 | "sitk.sitkHammingWindowedSinc",
1105 | "sitk.sitkCosineWindowedSinc",
1106 | "sitk.sitkWelchWindowedSinc",
1107 | "sitk.sitkLanczosWindowedSinc",
1108 | "sitk.sitkBlackmanWindowedSinc"]
1109 |
1110 | w = self.createEnumWidget(member["name"], labels, values)
1111 | pass
1112 | elif t == "PixelIDValueEnum":
1113 | labels = ["int8_t",
1114 | "uint8_t",
1115 | "int16_t",
1116 | "uint16_t",
1117 | "uint32_t",
1118 | "int32_t",
1119 | "float",
1120 | "double"]
1121 | values = ["sitk.sitkInt8",
1122 | "sitk.sitkUInt8",
1123 | "sitk.sitkInt16",
1124 | "sitk.sitkUInt16",
1125 | "sitk.sitkInt32",
1126 | "sitk.sitkUInt32",
1127 | "sitk.sitkFloat32",
1128 | "sitk.sitkFloat64"]
1129 | w = self.createEnumWidget(member["name"], labels, values)
1130 | elif t in ["double", "float"]:
1131 | w = self.createDoubleWidget(member["name"], default=member["default"])
1132 | elif t == "bool":
1133 | w = self.createBoolWidget(member["name"], default=member["default"])
1134 | elif t in ["uint8_t", "int8_t",
1135 | "uint16_t", "int16_t",
1136 | "uint32_t", "int32_t",
1137 | "uint64_t", "int64_t",
1138 | "unsigned int", "int"]:
1139 | w = self.createIntWidget(member["name"], t, default=member["default"])
1140 | else:
1141 | import sys
1142 | sys.stderr.write("Unknown member \"{0}\" of type \"{1}\"\n".format(member["name"], member["type"]))
1143 |
1144 | if w:
1145 | self.addWidgetWithToolTipAndLabel(w, member)
1146 |
1147 | def createVolumeWidget(self, name, iotype, voltype, noneEnabled=False):
1148 | # print("create volume widget : {0}".format(name))
1149 | volumeSelector = slicer.qMRMLNodeComboBox()
1150 | self.widgets.append(volumeSelector)
1151 | if voltype == 'ScalarVolume':
1152 | volumeSelector.nodeTypes = ["vtkMRMLScalarVolumeNode", ]
1153 | elif voltype == 'LabelMap':
1154 | volumeSelector.nodeTypes = ["vtkMRMLLabelMapVolumeNode", ]
1155 | else:
1156 | print('Voltype must be either ScalarVolume or LabelMap!')
1157 | volumeSelector.selectNodeUponCreation = True
1158 | if iotype == "input":
1159 | volumeSelector.addEnabled = False
1160 | elif iotype == "output":
1161 | volumeSelector.addEnabled = True
1162 | volumeSelector.renameEnabled = True
1163 | volumeSelector.removeEnabled = True
1164 | volumeSelector.noneEnabled = noneEnabled
1165 | volumeSelector.showHidden = False
1166 | volumeSelector.showChildNodeTypes = False
1167 | volumeSelector.setMRMLScene(slicer.mrmlScene)
1168 | volumeSelector.setToolTip("Pick the volume.")
1169 |
1170 | # connect and verify parameters
1171 | volumeSelector.connect("currentNodeChanged(vtkMRMLNode*)",
1172 | lambda node, n=name, io=iotype: self.onVolumeSelect(node, n, io))
1173 | if iotype == "input":
1174 | self.inputs[name] = volumeSelector.currentNode()
1175 | elif iotype == "output":
1176 | self.outputs[name] = volumeSelector.currentNode()
1177 |
1178 | return volumeSelector
1179 |
1180 | def createEnumWidget(self, name, enumList, valueList=None):
1181 |
1182 | w = qt.QComboBox()
1183 | self.widgets.append(w)
1184 |
1185 | # exec 'default=self.model.Get{0}()'.format(name) in globals(), locals()
1186 |
1187 | if valueList is None:
1188 | valueList = ["self.model." + e for e in enumList]
1189 |
1190 | for e, v in zip(enumList, valueList):
1191 | w.addItem(e, v)
1192 |
1193 | self.params[name] = w.currentText
1194 | w.connect("currentIndexChanged(int)",
1195 | lambda selectorIndex, n=name, selector=w: self.onEnumChanged(n, selectorIndex, selector))
1196 | return w
1197 |
1198 | def createVectorWidget(self, name, type):
1199 | m = re.search(r"<([a-zA-Z ]+)>", type)
1200 | if m:
1201 | type = m.group(1)
1202 |
1203 | w = ctk.ctkCoordinatesWidget()
1204 | self.widgets.append(w)
1205 |
1206 | if type in ["double", "float"]:
1207 | w.setDecimals(5)
1208 | w.minimum = -3.40282e+038
1209 | w.maximum = 3.40282e+038
1210 | w.connect("coordinatesChanged(double*)",
1211 | lambda val, widget=w, name=name: self.onFloatVectorChanged(name, widget, val))
1212 | elif type == "bool":
1213 | w.setDecimals(0)
1214 | w.minimum = 0
1215 | w.maximum = 1
1216 | w.connect("coordinatesChanged(double*)",
1217 | lambda val, widget=w, name=name: self.onBoolVectorChanged(name, widget, val))
1218 | else:
1219 | w.setDecimals(0)
1220 | w.connect("coordinatesChanged(double*)",
1221 | lambda val, widget=w, name=name: self.onIntVectorChanged(name, widget, val))
1222 |
1223 | # exec ('default = self.model.Get{0}()'.format(name)) in globals(), locals()
1224 | # w.coordinates = ",".join(str(x) for x in default)
1225 | return w
1226 |
1227 | def createIntWidget(self, name, type="int", default=None):
1228 |
1229 | w = qt.QSpinBox()
1230 | self.widgets.append(w)
1231 |
1232 | if type == "uint8_t":
1233 | w.setRange(0, 255)
1234 | elif type == "int8_t":
1235 | w.setRange(-128, 127)
1236 | elif type == "uint16_t":
1237 | w.setRange(0, 65535)
1238 | elif type == "int16_t":
1239 | w.setRange(-32678, 32767)
1240 | elif type == "uint32_t" or type == "uint64_t" or type == "unsigned int":
1241 | w.setRange(0, 2147483647)
1242 | elif type == "int32_t" or type == "uint64_t" or type == "int":
1243 | w.setRange(-2147483648, 2147483647)
1244 |
1245 | # exec ('default = self.model.Get{0}()'.format(name)) in globals(), locals()
1246 | if default is not None:
1247 | w.setValue(int(default))
1248 | w.connect("valueChanged(int)", lambda val, name=name: self.onScalarChanged(name, val))
1249 | return w
1250 |
1251 | def createBoolWidget(self, name, default):
1252 | # print('create bool widget')
1253 | w = qt.QCheckBox()
1254 | self.widgets.append(w)
1255 | if default == 'false':
1256 | checked = False
1257 | else:
1258 | checked = True
1259 | w.setChecked(checked)
1260 | self.params[name] = int(w.checked)
1261 | w.connect("stateChanged(int)", lambda val, name=name: self.onScalarChanged(name, int(val)))
1262 |
1263 | return w
1264 |
1265 | def createDoubleWidget(self, name, default=None):
1266 | # exec ('default = self.model.Get{0}()'.format(name)) in globals(), locals()
1267 | w = qt.QDoubleSpinBox()
1268 | self.widgets.append(w)
1269 |
1270 | w.setRange(-3.40282e+038, 3.40282e+038)
1271 | w.decimals = 5
1272 |
1273 | if default is not None:
1274 | w.setValue(default)
1275 | w.connect("valueChanged(double)", lambda val, name=name: self.onScalarChanged(name, val))
1276 |
1277 | return w
1278 |
1279 | def addWidgetWithToolTipAndLabel(self, widget, memberJSON):
1280 | tip = ""
1281 | if "briefdescriptionSet" in memberJSON and len(memberJSON["briefdescriptionSet"]):
1282 | tip = memberJSON["briefdescriptionSet"]
1283 | elif "detaileddescriptionSet" in memberJSON:
1284 | tip = memberJSON["detaileddescriptionSet"]
1285 |
1286 | # remove trailing white space
1287 | tip = tip.rstrip()
1288 |
1289 | l = qt.QLabel(self.BeautifyCamelCase(memberJSON["name"]) + ": ")
1290 | self.widgets.append(l)
1291 |
1292 | widget.setToolTip(tip)
1293 | l.setToolTip(tip)
1294 |
1295 | parametersFormLayout = self.parent.layout()
1296 | parametersFormLayout.addRow(l, widget)
1297 |
1298 | def onToggledPointSelector(self, fidVisible, ptWidget, fiducialWidget):
1299 | ptWidget.setVisible(False)
1300 | fiducialWidget.setVisible(False)
1301 |
1302 | ptWidget.setVisible(not fidVisible)
1303 | fiducialWidget.setVisible(fidVisible)
1304 |
1305 | if ptWidget.visible:
1306 | # Update the coordinate values to envoke the changed signal.
1307 | # This will update the model from the widget
1308 | ptWidget.coordinates = ",".join(str(x) for x in ptWidget.coordinates.split(','))
1309 |
1310 | def onVolumeSelect(self, mrmlNode, n, io):
1311 | # print("on volume select:{}".format(n))
1312 | if io == "input":
1313 | self.inputs[n] = mrmlNode
1314 | elif io == "output":
1315 | self.outputs[n] = mrmlNode
1316 |
1317 | '''
1318 | def onOutputSelect(self, mrmlNode):
1319 | self.output = mrmlNode
1320 | self.onOutputLabelMapChanged(mrmlNode.IsA("vtkMRMLLabelMapVolumeNode"))
1321 |
1322 | def onOutputLabelMapChanged(self, v):
1323 | self.outputLabelMap = v
1324 | self.outputLabelMapBox.setChecked(v)
1325 | '''
1326 |
1327 | def onFiducialNode(self, name, mrmlWidget, isPoint):
1328 | if not mrmlWidget.visible:
1329 | return
1330 | annotationFiducialNode = mrmlWidget.currentNode()
1331 |
1332 | # point in physical space
1333 | coord = [0, 0, 0]
1334 |
1335 | if annotationFiducialNode.GetClassName() == "vtkMRMLMarkupsFiducialNode":
1336 | # slicer4 Markups node
1337 | if annotationFiducialNode.GetNumberOfFiducials() < 1:
1338 | return
1339 | annotationFiducialNode.GetNthFiducialPosition(0, coord)
1340 | else:
1341 | annotationFiducialNode.GetFiducialCoordinates(coord)
1342 |
1343 | # HACK transform from RAS to LPS
1344 | coord = [-coord[0], -coord[1], coord[2]]
1345 |
1346 | # FIXME: we should not need to copy the image
1347 | if not isPoint and len(self.inputs) and self.inputs[0]:
1348 | imgNodeName = self.inputs[0].GetName()
1349 | img = sitk.ReadImage(sitkUtils.GetSlicerITKReadWriteAddress(imgNodeName))
1350 | coord = img.TransformPhysicalPointToIndex(coord)
1351 | # exec ('self.model.Set{0}(coord)'.format(name))
1352 |
1353 | def onFiducialListNode(self, name, mrmlNode, io):
1354 | self.params[name] = mrmlNode
1355 | if name.lower().startswith("input"):
1356 | self.inputs[name] = mrmlNode
1357 | else:
1358 | self.outputs[name] = mrmlNode
1359 |
1360 | '''
1361 | annotationHierarchyNode = mrmlNode
1362 |
1363 | # list of points in physical space
1364 | coords = []
1365 |
1366 | if annotationHierarchyNode.GetClassName() == "vtkMRMLMarkupsFiducialNode":
1367 | # slicer4 Markups node
1368 |
1369 | for i in range(annotationHierarchyNode.GetNumberOfFiducials()):
1370 | coord = [0, 0, 0]
1371 | annotationHierarchyNode.GetNthFiducialPosition(i, coord)
1372 | coords.append(coord)
1373 | else:
1374 | # slicer4 style hierarchy nodes
1375 |
1376 | # get the first in the list
1377 | for listIndex in range(annotationHierarchyNode.GetNumberOfChildrenNodes()):
1378 | if annotationHierarchyNode.GetNthChildNode(listIndex) is None:
1379 | continue
1380 |
1381 | annotation = annotationHierarchyNode.GetNthChildNode(listIndex).GetAssociatedNode()
1382 | if annotation is None:
1383 | continue
1384 |
1385 | coord = [0, 0, 0]
1386 | annotation.GetFiducialCoordinates(coord)
1387 | coords.append(coord)
1388 |
1389 | if self.inputs[0]:
1390 | imgNodeName = self.inputs[0].GetName()
1391 | img = sitk.ReadImage(sitkUtils.GetSlicerITKReadWriteAddress(imgNodeName))
1392 |
1393 | # HACK transform from RAS to LPS
1394 | coords = [[-pt[0], -pt[1], pt[2]] for pt in coords]
1395 |
1396 | idx_coords = [img.TransformPhysicalPointToIndex(pt) for pt in coords]
1397 |
1398 | # exec ('self.model.Set{0}(idx_coords)'.format(name))
1399 | '''
1400 |
1401 | def onScalarChanged(self, name, val):
1402 | # exec ('self.model.Set{0}(val)'.format(name))
1403 | # print("onScalarChanged")
1404 | self.params[name] = val
1405 |
1406 | def onEnumChanged(self, name, selectorIndex, selector):
1407 | # data = selector.itemData(selectorIndex)
1408 | self.params[name] = selector.currentText
1409 |
1410 | def onBoolVectorChanged(self, name, widget, val):
1411 | coords = [bool(float(x)) for x in widget.coordinates.split(',')]
1412 | # exec ('self.model.Set{0}(coords)'.format(name))
1413 |
1414 | def onIntVectorChanged(self, name, widget, val):
1415 | coords = [int(float(x)) for x in widget.coordinates.split(',')]
1416 | # exec ('self.model.Set{0}(coords)'.format(name))
1417 |
1418 | def onFloatVectorChanged(self, name, widget, val):
1419 | coords = [float(x) for x in widget.coordinates.split(',')]
1420 | # exec ('self.model.Set{0}(coords)'.format(name))
1421 |
1422 | def prerun(self):
1423 | print('prerun...')
1424 | for f in self.prerun_callbacks:
1425 | f()
1426 |
1427 | def destroy(self):
1428 | self.iodict = dict()
1429 | self.inputs = dict()
1430 | self.outputs = dict()
1431 | for w in self.widgets:
1432 | # self.parent.layout().removeWidget(w)
1433 | w.deleteLater()
1434 | w.setParent(None)
1435 | self.widgets = []
1436 |
--------------------------------------------------------------------------------
/DeepInfer/Resources/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepInfer/Slicer-DeepInfer/a78b5fa90ef161a9438b525aa5fcc95cf5ce8d75/DeepInfer/Resources/.DS_Store
--------------------------------------------------------------------------------
/DeepInfer/Resources/Icons/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepInfer/Slicer-DeepInfer/a78b5fa90ef161a9438b525aa5fcc95cf5ce8d75/DeepInfer/Resources/Icons/.DS_Store
--------------------------------------------------------------------------------
/DeepInfer/Resources/Icons/DeepInfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepInfer/Slicer-DeepInfer/a78b5fa90ef161a9438b525aa5fcc95cf5ce8d75/DeepInfer/Resources/Icons/DeepInfer.png
--------------------------------------------------------------------------------
/DeepInfer/Resources/Icons/Fiducials.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepInfer/Slicer-DeepInfer/a78b5fa90ef161a9438b525aa5fcc95cf5ce8d75/DeepInfer/Resources/Icons/Fiducials.png
--------------------------------------------------------------------------------
/DeepInfer/Resources/json/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepInfer/Slicer-DeepInfer/a78b5fa90ef161a9438b525aa5fcc95cf5ce8d75/DeepInfer/Resources/json/.DS_Store
--------------------------------------------------------------------------------
/DeepInfer/Testing/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_subdirectory(Python)
2 |
--------------------------------------------------------------------------------
/DeepInfer/Testing/Python/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | #slicer_add_python_unittest(SCRIPT ${MODULE_NAME}ModuleTest.py)
3 |
--------------------------------------------------------------------------------
/DeepInfer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeepInfer/Slicer-DeepInfer/a78b5fa90ef161a9438b525aa5fcc95cf5ce8d75/DeepInfer/__init__.py
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | For more information, please see:
3 |
4 | http://www.slicer.org
5 |
6 | The 3D Slicer license below is a BSD style license, with extensions
7 | to cover contributions and other issues specific to 3D Slicer.
8 |
9 |
10 | 3D Slicer Contribution and Software License Agreement ("Agreement")
11 | Version 1.0 (December 20, 2005)
12 |
13 | This Agreement covers contributions to and downloads from the 3D
14 | Slicer project ("Slicer") maintained by The Brigham and Women's
15 | Hospital, Inc. ("Brigham"). Part A of this Agreement applies to
16 | contributions of software and/or data to Slicer (including making
17 | revisions of or additions to code and/or data already in Slicer). Part
18 | B of this Agreement applies to downloads of software and/or data from
19 | Slicer. Part C of this Agreement applies to all transactions with
20 | Slicer. If you distribute Software (as defined below) downloaded from
21 | Slicer, all of the paragraphs of Part B of this Agreement must be
22 | included with and apply to such Software.
23 |
24 | Your contribution of software and/or data to Slicer (including prior
25 | to the date of the first publication of this Agreement, each a
26 | "Contribution") and/or downloading, copying, modifying, displaying,
27 | distributing or use of any software and/or data from Slicer
28 | (collectively, the "Software") constitutes acceptance of all of the
29 | terms and conditions of this Agreement. If you do not agree to such
30 | terms and conditions, you have no right to contribute your
31 | Contribution, or to download, copy, modify, display, distribute or use
32 | the Software.
33 |
34 | PART A. CONTRIBUTION AGREEMENT - License to Brigham with Right to
35 | Sublicense ("Contribution Agreement").
36 |
37 | 1. As used in this Contribution Agreement, "you" means the individual
38 | contributing the Contribution to Slicer and the institution or
39 | entity which employs or is otherwise affiliated with such
40 | individual in connection with such Contribution.
41 |
42 | 2. This Contribution Agreement applies to all Contributions made to
43 | Slicer, including without limitation Contributions made prior to
44 | the date of first publication of this Agreement. If at any time you
45 | make a Contribution to Slicer, you represent that (i) you are
46 | legally authorized and entitled to make such Contribution and to
47 | grant all licenses granted in this Contribution Agreement with
48 | respect to such Contribution; (ii) if your Contribution includes
49 | any patient data, all such data is de-identified in accordance with
50 | U.S. confidentiality and security laws and requirements, including
51 | but not limited to the Health Insurance Portability and
52 | Accountability Act (HIPAA) and its regulations, and your disclosure
53 | of such data for the purposes contemplated by this Agreement is
54 | properly authorized and in compliance with all applicable laws and
55 | regulations; and (iii) you have preserved in the Contribution all
56 | applicable attributions, copyright notices and licenses for any
57 | third party software or data included in the Contribution.
58 |
59 | 3. Except for the licenses granted in this Agreement, you reserve all
60 | right, title and interest in your Contribution.
61 |
62 | 4. You hereby grant to Brigham, with the right to sublicense, a
63 | perpetual, worldwide, non-exclusive, no charge, royalty-free,
64 | irrevocable license to use, reproduce, make derivative works of,
65 | display and distribute the Contribution. If your Contribution is
66 | protected by patent, you hereby grant to Brigham, with the right to
67 | sublicense, a perpetual, worldwide, non-exclusive, no-charge,
68 | royalty-free, irrevocable license under your interest in patent
69 | rights covering the Contribution, to make, have made, use, sell and
70 | otherwise transfer your Contribution, alone or in combination with
71 | any other code.
72 |
73 | 5. You acknowledge and agree that Brigham may incorporate your
74 | Contribution into Slicer and may make Slicer available to members
75 | of the public on an open source basis under terms substantially in
76 | accordance with the Software License set forth in Part B of this
77 | Agreement. You further acknowledge and agree that Brigham shall
78 | have no liability arising in connection with claims resulting from
79 | your breach of any of the terms of this Agreement.
80 |
81 | 6. YOU WARRANT THAT TO THE BEST OF YOUR KNOWLEDGE YOUR CONTRIBUTION
82 | DOES NOT CONTAIN ANY CODE THAT REQURES OR PRESCRIBES AN "OPEN
83 | SOURCE LICENSE" FOR DERIVATIVE WORKS (by way of non-limiting
84 | example, the GNU General Public License or other so-called
85 | "reciprocal" license that requires any derived work to be licensed
86 | under the GNU General Public License or other "open source
87 | license").
88 |
89 | PART B. DOWNLOADING AGREEMENT - License from Brigham with Right to
90 | Sublicense ("Software License").
91 |
92 | 1. As used in this Software License, "you" means the individual
93 | downloading and/or using, reproducing, modifying, displaying and/or
94 | distributing the Software and the institution or entity which
95 | employs or is otherwise affiliated with such individual in
96 | connection therewith. The Brigham and Women?s Hospital,
97 | Inc. ("Brigham") hereby grants you, with right to sublicense, with
98 | respect to Brigham's rights in the software, and data, if any,
99 | which is the subject of this Software License (collectively, the
100 | "Software"), a royalty-free, non-exclusive license to use,
101 | reproduce, make derivative works of, display and distribute the
102 | Software, provided that:
103 |
104 | (a) you accept and adhere to all of the terms and conditions of this
105 | Software License;
106 |
107 | (b) in connection with any copy of or sublicense of all or any portion
108 | of the Software, all of the terms and conditions in this Software
109 | License shall appear in and shall apply to such copy and such
110 | sublicense, including without limitation all source and executable
111 | forms and on any user documentation, prefaced with the following
112 | words: "All or portions of this licensed product (such portions are
113 | the "Software") have been obtained under license from The Brigham and
114 | Women's Hospital, Inc. and are subject to the following terms and
115 | conditions:"
116 |
117 | (c) you preserve and maintain all applicable attributions, copyright
118 | notices and licenses included in or applicable to the Software;
119 |
120 | (d) modified versions of the Software must be clearly identified and
121 | marked as such, and must not be misrepresented as being the original
122 | Software; and
123 |
124 | (e) you consider making, but are under no obligation to make, the
125 | source code of any of your modifications to the Software freely
126 | available to others on an open source basis.
127 |
128 | 2. The license granted in this Software License includes without
129 | limitation the right to (i) incorporate the Software into
130 | proprietary programs (subject to any restrictions applicable to
131 | such programs), (ii) add your own copyright statement to your
132 | modifications of the Software, and (iii) provide additional or
133 | different license terms and conditions in your sublicenses of
134 | modifications of the Software; provided that in each case your use,
135 | reproduction or distribution of such modifications otherwise
136 | complies with the conditions stated in this Software License.
137 |
138 | 3. This Software License does not grant any rights with respect to
139 | third party software, except those rights that Brigham has been
140 | authorized by a third party to grant to you, and accordingly you
141 | are solely responsible for (i) obtaining any permissions from third
142 | parties that you need to use, reproduce, make derivative works of,
143 | display and distribute the Software, and (ii) informing your
144 | sublicensees, including without limitation your end-users, of their
145 | obligations to secure any such required permissions.
146 |
147 | 4. The Software has been designed for research purposes only and has
148 | not been reviewed or approved by the Food and Drug Administration
149 | or by any other agency. YOU ACKNOWLEDGE AND AGREE THAT CLINICAL
150 | APPLICATIONS ARE NEITHER RECOMMENDED NOR ADVISED. Any
151 | commercialization of the Software is at the sole risk of the party
152 | or parties engaged in such commercialization. You further agree to
153 | use, reproduce, make derivative works of, display and distribute
154 | the Software in compliance with all applicable governmental laws,
155 | regulations and orders, including without limitation those relating
156 | to export and import control.
157 |
158 | 5. The Software is provided "AS IS" and neither Brigham nor any
159 | contributor to the software (each a "Contributor") shall have any
160 | obligation to provide maintenance, support, updates, enhancements
161 | or modifications thereto. BRIGHAM AND ALL CONTRIBUTORS SPECIFICALLY
162 | DISCLAIM ALL EXPRESS AND IMPLIED WARRANTIES OF ANY KIND INCLUDING,
163 | BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR
164 | A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
165 | BRIGHAM OR ANY CONTRIBUTOR BE LIABLE TO ANY PARTY FOR DIRECT,
166 | INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
167 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ARISING IN ANY WAY
168 | RELATED TO THE SOFTWARE, EVEN IF BRIGHAM OR ANY CONTRIBUTOR HAS
169 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. TO THE MAXIMUM
170 | EXTENT NOT PROHIBITED BY LAW OR REGULATION, YOU FURTHER ASSUME ALL
171 | LIABILITY FOR YOUR USE, REPRODUCTION, MAKING OF DERIVATIVE WORKS,
172 | DISPLAY, LICENSE OR DISTRIBUTION OF THE SOFTWARE AND AGREE TO
173 | INDEMNIFY AND HOLD HARMLESS BRIGHAM AND ALL CONTRIBUTORS FROM AND
174 | AGAINST ANY AND ALL CLAIMS, SUITS, ACTIONS, DEMANDS AND JUDGMENTS
175 | ARISING THEREFROM.
176 |
177 | 6. None of the names, logos or trademarks of Brigham or any of
178 | Brigham's affiliates or any of the Contributors, or any funding
179 | agency, may be used to endorse or promote products produced in
180 | whole or in part by operation of the Software or derived from or
181 | based on the Software without specific prior written permission
182 | from the applicable party.
183 |
184 | 7. Any use, reproduction or distribution of the Software which is not
185 | in accordance with this Software License shall automatically revoke
186 | all rights granted to you under this Software License and render
187 | Paragraphs 1 and 2 of this Software License null and void.
188 |
189 | 8. This Software License does not grant any rights in or to any
190 | intellectual property owned by Brigham or any Contributor except
191 | those rights expressly granted hereunder.
192 |
193 | PART C. MISCELLANEOUS
194 |
195 | This Agreement shall be governed by and construed in accordance with
196 | the laws of The Commonwealth of Massachusetts without regard to
197 | principles of conflicts of law. This Agreement shall supercede and
198 | replace any license terms that you may have agreed to previously with
199 | respect to Slicer.
200 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Slicer-DeepInfer
2 | 3D Slicer [1] module to provide GUI to communicate with DeepInfer model
3 | repository and to perform image analysis using models inside Slicer.
4 |
5 | [1] [3D Slicer](https://www.slicer.org/)
6 |
--------------------------------------------------------------------------------