Slicer 5.4
Slicer is a multi-platform, free and open source software package for visualization and medical image computing
Loading...
Searching...
No Matches
AbstractScriptedSegmentEditorAutoCompleteEffect.py
Go to the documentation of this file.
1import logging
2
3import ctk
4import qt
5import vtk
6
7import slicer
8
9from .AbstractScriptedSegmentEditorEffect import *
10
11__all__ = ['AbstractScriptedSegmentEditorAutoCompleteEffect']
12
13
14#
15# Abstract class of python scripted segment editor auto-complete effects
16#
17# Auto-complete effects are a subtype of general effects that allow preview
18# and refinement of segmentation results before accepting them.
19#
20
22 """ AutoCompleteEffect is an effect that can create a full segmentation
23 from a partial segmentation (not all slices are segmented or only
24 part of the target structures are painted).
25 """
26
27 def __init__(self, scriptedEffect):
28 # Indicates that effect does not operate on one segment, but the whole segmentation.
29 # This means that while this effect is active, no segment can be selected
30 scriptedEffect.perSegment = False
31 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
32
36
37 # Stores merged labelmap image geometry (voxel data is not allocated)
40 self.selectedSegmentModifiedTimes = {} # map from segment ID to ModifiedTime
43
44 # Observation for auto-update
47
48 # Wait this much after the last modified event before starting aut-update:
49 autoUpdateDelaySec = 1.0
50 self.delayedAutoUpdateTimer = qt.QTimer()
51 self.delayedAutoUpdateTimer.setSingleShot(True)
52 self.delayedAutoUpdateTimer.interval = autoUpdateDelaySec * 1000
53 self.delayedAutoUpdateTimer.connect('timeout()', self.onPreviewonPreview)
54
55 self.extentGrowthRatio = 0.1 # extent of seed region will be grown outside by this much
57
59
60 def __del__(self, scriptedEffect):
61 super(SegmentEditorAutoCompleteEffect, self).__del__()
62 self.delayedAutoUpdateTimer.stop()
63 self.observeSegmentation(False)
64
65 @staticmethod
66 def isBackgroundLabelmap(labelmapOrientedImageData, label=None):
67 if labelmapOrientedImageData is None:
68 return False
69 # If five or more corner voxels of the image contain non-zero, then it is background
70 extent = labelmapOrientedImageData.GetExtent()
71 if extent[0] > extent[1] or extent[2] > extent[3] or extent[4] > extent[5]:
72 return False
73 numberOfFilledCorners = 0
74 for i in [0, 1]:
75 for j in [2, 3]:
76 for k in [4, 5]:
77 voxelValue = labelmapOrientedImageData.GetScalarComponentAsFloat(extent[i], extent[j], extent[k], 0)
78 if label is None:
79 if voxelValue > 0:
80 numberOfFilledCorners += 1
81 else:
82 if voxelValue == label:
83 numberOfFilledCorners += 1
84 if numberOfFilledCorners > 4:
85 return True
86 return False
87
89 self.autoUpdateCheckBox = qt.QCheckBox("Auto-update")
90 self.autoUpdateCheckBox.setToolTip("Auto-update results preview when input segments change.")
91 self.autoUpdateCheckBox.setChecked(True)
92 self.autoUpdateCheckBox.setEnabled(False)
93
94 self.previewButton = qt.QPushButton("Initialize")
95 self.previewButton.objectName = self.__class__.__name__ + 'Preview'
96 self.previewButton.setToolTip("Preview complete segmentation")
97 # qt.QSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding)
98 # fails on some systems, therefore set the policies using separate method calls
99 qSize = qt.QSizePolicy()
100 qSize.setHorizontalPolicy(qt.QSizePolicy.Expanding)
101 self.previewButton.setSizePolicy(qSize)
102
103 previewFrame = qt.QHBoxLayout()
104 previewFrame.addWidget(self.autoUpdateCheckBox)
105 previewFrame.addWidget(self.previewButton)
106 self.scriptedEffect.addLabeledOptionsWidget("Preview:", previewFrame)
107
108 self.previewOpacitySlider = ctk.ctkSliderWidget()
109 self.previewOpacitySlider.setToolTip("Adjust visibility of results preview.")
110 self.previewOpacitySlider.minimum = 0
111 self.previewOpacitySlider.maximum = 1.0
112 self.previewOpacitySlider.value = 0.0
113 self.previewOpacitySlider.singleStep = 0.05
114 self.previewOpacitySlider.pageStep = 0.1
115 self.previewOpacitySlider.spinBoxVisible = False
116
117 self.previewShow3DButton = qt.QPushButton("Show 3D")
118 self.previewShow3DButton.setToolTip("Preview results in 3D.")
119 self.previewShow3DButton.setCheckable(True)
120
121 displayFrame = qt.QHBoxLayout()
122 displayFrame.addWidget(qt.QLabel("inputs"))
123 displayFrame.addWidget(self.previewOpacitySlider)
124 displayFrame.addWidget(qt.QLabel("results"))
125 displayFrame.addWidget(self.previewShow3DButton)
126 self.scriptedEffect.addLabeledOptionsWidget("Display:", displayFrame)
127
128 self.cancelButton = qt.QPushButton("Cancel")
129 self.cancelButton.objectName = self.__class__.__name__ + 'Cancel'
130 self.cancelButton.setToolTip("Clear preview and cancel auto-complete")
131
132 self.applyButton = qt.QPushButton("Apply")
133 self.applyButton.objectName = self.__class__.__name__ + 'Apply'
134 self.applyButton.setToolTip("Replace segments by previewed result")
135
136 finishFrame = qt.QHBoxLayout()
137 finishFrame.addWidget(self.cancelButton)
138 finishFrame.addWidget(self.applyButton)
139 self.scriptedEffect.addOptionsWidget(finishFrame)
140
141 self.previewButton.connect('clicked()', self.onPreviewonPreview)
142 self.cancelButton.connect('clicked()', self.onCancelonCancel)
143 self.applyButton.connect('clicked()', self.onApplyonApply)
144 self.previewOpacitySlider.connect("valueChanged(double)", self.updateMRMLFromGUIupdateMRMLFromGUI)
145 self.previewShow3DButton.connect("toggled(bool)", self.updateMRMLFromGUIupdateMRMLFromGUI)
146 self.autoUpdateCheckBox.connect("stateChanged(int)", self.updateMRMLFromGUIupdateMRMLFromGUI)
147
148 def createCursor(self, widget):
149 # Turn off effect-specific cursor for this effect
150 return slicer.util.mainWindow().cursor
151
153 self.scriptedEffect.setParameterDefault("AutoUpdate", "1")
154
155 def onSegmentationModified(self, caller, event):
156 if not self.autoUpdateCheckBox.isChecked():
157 # just in case a queued request comes through
158 return
159
160 import vtkSegmentationCorePython as vtkSegmentationCore
161 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
162 segmentation = segmentationNode.GetSegmentation()
163
164 updateNeeded = False
165 for segmentIndex in range(self.selectedSegmentIds.GetNumberOfValues()):
166 segmentID = self.selectedSegmentIds.GetValue(segmentIndex)
167 segment = segmentation.GetSegment(segmentID)
168 if not segment:
169 # selected segment was deleted, cancel segmentation
170 logging.debug("Segmentation cancelled because an input segment was deleted")
171 self.onCancelonCancel()
172 return
173 segmentLabelmap = segment.GetRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName())
174 if segmentID in self.selectedSegmentModifiedTimes \
175 and segmentLabelmap and segmentLabelmap.GetMTime() == self.selectedSegmentModifiedTimes[segmentID]:
176 # this segment has not changed since last update
177 continue
178 if segmentLabelmap:
179 self.selectedSegmentModifiedTimes[segmentID] = segmentLabelmap.GetMTime()
180 elif segmentID in self.selectedSegmentModifiedTimes:
181 self.selectedSegmentModifiedTimes.pop(segmentID)
182 updateNeeded = True
183 # continue so that all segment modified times are updated
184
185 if not updateNeeded:
186 return
187
188 logging.debug("Segmentation update requested")
189 # There could be multiple update events for a single paint operation (e.g., one segment overwrites the other)
190 # therefore don't update directly, just set up/reset a timer that will perform the update when it elapses.
192 self.delayedAutoUpdateTimer.start()
193
194 def observeSegmentation(self, observationEnabled):
195 import vtkSegmentationCorePython as vtkSegmentationCore
196
197 parameterSetNode = self.scriptedEffect.parameterSetNode()
198 segmentationNode = None
199 if parameterSetNode:
200 segmentationNode = parameterSetNode.GetSegmentationNode()
201
202 segmentation = None
203 if segmentationNode:
204 segmentation = segmentationNode.GetSegmentation()
205
206 if observationEnabled and self.observedSegmentation == segmentation:
207 return
208 if not observationEnabled and not self.observedSegmentation:
209 return
210 # Need to update the observer
211 # Remove old observer
212 if self.observedSegmentation:
213 for tag in self.segmentationNodeObserverTags:
214 self.observedSegmentation.RemoveObserver(tag)
216 self.observedSegmentation = None
217 # Add new observer
218 if observationEnabled and segmentation is not None:
219 self.observedSegmentation = segmentation
220 observedEvents = [
221 vtkSegmentationCore.vtkSegmentation.SegmentAdded,
222 vtkSegmentationCore.vtkSegmentation.SegmentRemoved,
223 vtkSegmentationCore.vtkSegmentation.SegmentModified,
224 vtkSegmentationCore.vtkSegmentation.SourceRepresentationModified]
225 for eventId in observedEvents:
227
228 def getPreviewNode(self):
229 previewNode = self.scriptedEffect.parameterSetNode().GetNodeReference(ResultPreviewNodeReferenceRole)
230 if previewNode and self.scriptedEffect.parameter("SegmentationResultPreviewOwnerEffect") != self.scriptedEffect.name:
231 # another effect owns this preview node
232 return None
233 return previewNode
234
236
237 previewNode = self.getPreviewNode()
238
239 self.cancelButton.setEnabled(previewNode is not None)
240 self.applyButton.setEnabled(previewNode is not None)
241
242 self.previewOpacitySlider.setEnabled(previewNode is not None)
243 if previewNode:
244 wasBlocked = self.previewOpacitySlider.blockSignals(True)
245 self.previewOpacitySlider.value = self.getPreviewOpacity()
246 self.previewOpacitySlider.blockSignals(wasBlocked)
247 self.previewButton.text = "Update"
248 self.previewShow3DButton.setEnabled(True)
249 self.previewShow3DButton.setChecked(self.getPreviewShow3D())
250 self.autoUpdateCheckBox.setEnabled(True)
251 self.observeSegmentation(self.autoUpdateCheckBox.isChecked())
252 else:
253 self.previewButton.text = "Initialize"
254 self.autoUpdateCheckBox.setEnabled(False)
255 self.previewShow3DButton.setEnabled(False)
256 self.delayedAutoUpdateTimer.stop()
257 self.observeSegmentation(False)
258
259 autoUpdate = qt.Qt.Unchecked if self.scriptedEffect.integerParameter("AutoUpdate") == 0 else qt.Qt.Checked
260 wasBlocked = self.autoUpdateCheckBox.blockSignals(True)
261 self.autoUpdateCheckBox.setCheckState(autoUpdate)
262 self.autoUpdateCheckBox.blockSignals(wasBlocked)
263
265 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
266 previewNode = self.getPreviewNode()
267 if previewNode:
269 self.setPreviewShow3D(self.previewShow3DButton.checked)
270
271 autoUpdate = 1 if self.autoUpdateCheckBox.isChecked() else 0
272 self.scriptedEffect.setParameter("AutoUpdate", autoUpdate)
273
274 def onPreview(self):
276 return
278
279 slicer.util.showStatusMessage(f"Running {self.scriptedEffect.name} auto-complete...", 2000)
280 try:
281 # This can be a long operation - indicate it to the user
282 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
283 self.preview()
284 finally:
285 qt.QApplication.restoreOverrideCursor()
286
288
289 def reset(self):
290 self.delayedAutoUpdateTimer.stop()
291 self.observeSegmentation(False)
292 previewNode = self.scriptedEffect.parameterSetNode().GetNodeReference(ResultPreviewNodeReferenceRole)
293 if previewNode:
294 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(ResultPreviewNodeReferenceRole, None)
295 slicer.mrmlScene.RemoveNode(previewNode)
296 self.scriptedEffect.setCommonParameter("SegmentationResultPreviewOwnerEffect", "")
297 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
298 segmentationNode.GetDisplayNode().SetOpacity(1.0)
300 self.selectedSegmentIds = None
302 self.clippedMasterImageData = None
303 self.clippedMaskImageData = None
304 self.updateGUIFromMRML()
305
306 def onCancel(self):
307 self.reset()
308
309 def onApply(self):
310 self.delayedAutoUpdateTimer.stop()
311 self.observeSegmentation(False)
312
313 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
314 segmentationDisplayNode = segmentationNode.GetDisplayNode()
315 previewNode = self.getPreviewNode()
316
317 self.scriptedEffect.saveStateForUndo()
318
319 previewContainsClosedSurfaceRepresentation = previewNode.GetSegmentation().ContainsRepresentation(
320 slicer.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())
321
322 # Move segments from preview into current segmentation
323 segmentIDs = vtk.vtkStringArray()
324 previewNode.GetSegmentation().GetSegmentIDs(segmentIDs)
325 for index in range(segmentIDs.GetNumberOfValues()):
326 segmentID = segmentIDs.GetValue(index)
327 previewSegmentLabelmap = slicer.vtkOrientedImageData()
328 previewNode.GetBinaryLabelmapRepresentation(segmentID, previewSegmentLabelmap)
329 self.scriptedEffect.modifySegmentByLabelmap(segmentationNode, segmentID, previewSegmentLabelmap,
330 slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
331 if segmentationDisplayNode is not None and self.isBackgroundLabelmap(previewSegmentLabelmap):
332 # Automatically hide result segments that are background (all eight corners are non-zero)
333 segmentationDisplayNode.SetSegmentVisibility(segmentID, False)
334 previewNode.GetSegmentation().RemoveSegment(segmentID) # delete now to limit memory usage
335
336 if previewContainsClosedSurfaceRepresentation:
337 segmentationNode.CreateClosedSurfaceRepresentation()
338
339 self.reset()
340
341 def setPreviewOpacity(self, opacity):
342 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
343 segmentationNode.GetDisplayNode().SetOpacity(1.0 - opacity)
344 previewNode = self.getPreviewNode()
345 if previewNode:
346 previewNode.GetDisplayNode().SetOpacity(opacity)
347 previewNode.GetDisplayNode().SetOpacity3D(opacity)
348
349 # Make sure the GUI is up-to-date
350 wasBlocked = self.previewOpacitySlider.blockSignals(True)
351 self.previewOpacitySlider.value = opacity
352 self.previewOpacitySlider.blockSignals(wasBlocked)
353
355 previewNode = self.getPreviewNode()
356 return previewNode.GetDisplayNode().GetOpacity() if previewNode else 0.6 # default opacity for preview
357
358 def setPreviewShow3D(self, show):
359 previewNode = self.getPreviewNode()
360 if previewNode:
361 if show:
362 previewNode.CreateClosedSurfaceRepresentation()
363 else:
364 previewNode.RemoveClosedSurfaceRepresentation()
365
366 # Make sure the GUI is up-to-date
367 wasBlocked = self.previewShow3DButton.blockSignals(True)
368 self.previewShow3DButton.checked = show
369 self.previewShow3DButton.blockSignals(wasBlocked)
370
372 previewNode = self.getPreviewNode()
373 if not previewNode:
374 return False
375 containsClosedSurfaceRepresentation = previewNode.GetSegmentation().ContainsRepresentation(
376 slicer.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())
377 return containsClosedSurfaceRepresentation
378
380 if self.getPreviewNode() is None:
381 return True
382 if self.mergedLabelmapGeometryImage is None:
383 return True
384 if self.selectedSegmentIds is None:
385 return True
386
387 import vtkSegmentationCorePython as vtkSegmentationCore
388
389 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
390
391 # The effective extent for the current input segments
392 effectiveGeometryImage = slicer.vtkOrientedImageData()
393 effectiveGeometryString = segmentationNode.GetSegmentation().DetermineCommonLabelmapGeometry(
394 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.selectedSegmentIds)
395 if effectiveGeometryString is None:
396 return True
397 vtkSegmentationCore.vtkSegmentationConverter.DeserializeImageGeometry(effectiveGeometryString, effectiveGeometryImage)
398
399 masterImageData = self.scriptedEffect.sourceVolumeImageData()
400 masterImageExtent = masterImageData.GetExtent()
401
402 # The effective extent of the selected segments
403 effectiveLabelExtent = effectiveGeometryImage.GetExtent()
404 # Current extent used for auto-complete preview
405 currentLabelExtent = self.mergedLabelmapGeometryImage.GetExtent()
406
407 # Determine if the current merged labelmap extent has less than a 3 voxel margin around the effective segment extent (limited by the master image extent)
408 return ((masterImageExtent[0] != currentLabelExtent[0] and currentLabelExtent[0] > effectiveLabelExtent[0] - self.minimumExtentMargin) or
409 (masterImageExtent[1] != currentLabelExtent[1] and currentLabelExtent[1] < effectiveLabelExtent[1] + self.minimumExtentMargin) or
410 (masterImageExtent[2] != currentLabelExtent[2] and currentLabelExtent[2] > effectiveLabelExtent[2] - self.minimumExtentMargin) or
411 (masterImageExtent[3] != currentLabelExtent[3] and currentLabelExtent[3] < effectiveLabelExtent[3] + self.minimumExtentMargin) or
412 (masterImageExtent[4] != currentLabelExtent[4] and currentLabelExtent[4] > effectiveLabelExtent[4] - self.minimumExtentMargin) or
413 (masterImageExtent[5] != currentLabelExtent[5] and currentLabelExtent[5] < effectiveLabelExtent[5] + self.minimumExtentMargin))
414
415 def preview(self):
416 # Get source volume image data
417 import vtkSegmentationCorePython as vtkSegmentationCore
418
419 # Get segmentation
420 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
421
422 previewNode = self.getPreviewNode()
423 previewOpacity = self.getPreviewOpacity()
424 previewShow3D = self.getPreviewShow3D()
425
426 # If the selectedSegmentIds have been specified, then they shouldn't be overwritten here
427 currentSelectedSegmentIds = self.selectedSegmentIds
428
429 if self.effectiveExtentChanged():
430 self.reset()
431
432 # Restore the selectedSegmentIds
433 self.selectedSegmentIds = currentSelectedSegmentIds
434 if self.selectedSegmentIds is None:
435 self.selectedSegmentIds = vtk.vtkStringArray()
436 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(self.selectedSegmentIds)
437 if self.selectedSegmentIds.GetNumberOfValues() < self.minimumNumberOfSegments:
438 logging.error(f"Auto-complete operation skipped: at least {self.minimumNumberOfSegments} visible segments are required")
439 self.selectedSegmentIds = None
440 return
441
442 # Compute merged labelmap extent (effective extent slightly expanded)
443 if not self.mergedLabelmapGeometryImage:
444 self.mergedLabelmapGeometryImage = slicer.vtkOrientedImageData()
445 commonGeometryString = segmentationNode.GetSegmentation().DetermineCommonLabelmapGeometry(
446 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.selectedSegmentIds)
447 if not commonGeometryString:
448 logging.info("Auto-complete operation skipped: all visible segments are empty")
449 return
450 vtkSegmentationCore.vtkSegmentationConverter.DeserializeImageGeometry(commonGeometryString, self.mergedLabelmapGeometryImage)
451
452 masterImageData = self.scriptedEffect.sourceVolumeImageData()
453 masterImageExtent = masterImageData.GetExtent()
454 labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent()
455 # Margin size is relative to combined seed region size, but minimum of 3 voxels
456 print(f"self.extentGrowthRatio = {self.extentGrowthRatio}")
457 margin = [
458 int(max(3, self.extentGrowthRatio * (labelsEffectiveExtent[1] - labelsEffectiveExtent[0]))),
459 int(max(3, self.extentGrowthRatio * (labelsEffectiveExtent[3] - labelsEffectiveExtent[2]))),
460 int(max(3, self.extentGrowthRatio * (labelsEffectiveExtent[5] - labelsEffectiveExtent[4])))]
461 labelsExpandedExtent = [
462 max(masterImageExtent[0], labelsEffectiveExtent[0] - margin[0]),
463 min(masterImageExtent[1], labelsEffectiveExtent[1] + margin[0]),
464 max(masterImageExtent[2], labelsEffectiveExtent[2] - margin[1]),
465 min(masterImageExtent[3], labelsEffectiveExtent[3] + margin[1]),
466 max(masterImageExtent[4], labelsEffectiveExtent[4] - margin[2]),
467 min(masterImageExtent[5], labelsEffectiveExtent[5] + margin[2])]
468 print("masterImageExtent = " + repr(masterImageExtent))
469 print("labelsEffectiveExtent = " + repr(labelsEffectiveExtent))
470 print("labelsExpandedExtent = " + repr(labelsExpandedExtent))
471 self.mergedLabelmapGeometryImage.SetExtent(labelsExpandedExtent)
472
473 # Create and setup preview node
474 previewNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
475 previewNode.CreateDefaultDisplayNodes()
476 previewNode.GetDisplayNode().SetVisibility2DOutline(False)
477 if segmentationNode.GetParentTransformNode():
478 previewNode.SetAndObserveTransformNodeID(segmentationNode.GetParentTransformNode().GetID())
479 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(ResultPreviewNodeReferenceRole, previewNode.GetID())
480 self.scriptedEffect.setCommonParameter("SegmentationResultPreviewOwnerEffect", self.scriptedEffect.name)
481 self.setPreviewOpacity(0.6)
482
483 # Disable smoothing for closed surface generation to make it fast
484 previewNode.GetSegmentation().SetConversionParameter(
485 slicer.vtkBinaryLabelmapToClosedSurfaceConversionRule.GetSmoothingFactorParameterName(),
486 "-0.5")
487
488 inputContainsClosedSurfaceRepresentation = segmentationNode.GetSegmentation().ContainsRepresentation(
489 slicer.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())
490
491 self.setPreviewShow3D(inputContainsClosedSurfaceRepresentation)
492
494 self.clippedMasterImageData = slicer.vtkOrientedImageData()
495 masterImageClipper = vtk.vtkImageConstantPad()
496 masterImageClipper.SetInputData(masterImageData)
497 masterImageClipper.SetOutputWholeExtent(self.mergedLabelmapGeometryImage.GetExtent())
498 masterImageClipper.Update()
499 self.clippedMasterImageData.ShallowCopy(masterImageClipper.GetOutput())
501
502 self.clippedMaskImageData = None
504 self.clippedMaskImageData = slicer.vtkOrientedImageData()
505 intensityBasedMasking = self.scriptedEffect.parameterSetNode().GetSourceVolumeIntensityMask()
506 maskSegmentID = self.scriptedEffect.parameterSetNode().GetMaskSegmentID() if self.scriptedEffect.parameterSetNode().GetMaskSegmentID() else ""
507 intensityRange = self.scriptedEffect.parameterSetNode().GetSourceVolumeIntensityMaskRange() if intensityBasedMasking else None
508 success = segmentationNode.GenerateEditMask(self.clippedMaskImageData,
509 self.scriptedEffect.parameterSetNode().GetMaskMode(),
510 self.clippedMasterImageData, # reference geometry
511 "", # edited segment ID
512 maskSegmentID,
513 self.clippedMasterImageData if intensityBasedMasking else None,
514 intensityRange)
515 if not success:
516 logging.error("Failed to create edit mask")
517 self.clippedMaskImageData = None
518
519 previewNode.SetName(segmentationNode.GetName() + " preview")
520 previewNode.RemoveClosedSurfaceRepresentation() # Force the closed surface representation to update
521 # TODO: This will no longer be required when we can use the segment editor to set multiple segments
522 # as the closed surfaces will be converted as necessary by the segmentation logic.
523
524 mergedImage = slicer.vtkOrientedImageData()
525 segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage,
526 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.mergedLabelmapGeometryImage, self.selectedSegmentIds)
527
528 outputLabelmap = slicer.vtkOrientedImageData()
529 self.computePreviewLabelmap(mergedImage, outputLabelmap)
530
531 if previewNode.GetSegmentation().GetNumberOfSegments() != self.selectedSegmentIds.GetNumberOfValues():
532 # first update (or number of segments changed), need a full reinitialization
533 previewNode.GetSegmentation().RemoveAllSegments()
534
535 for index in range(self.selectedSegmentIds.GetNumberOfValues()):
536 segmentID = self.selectedSegmentIds.GetValue(index)
537
538 previewSegment = previewNode.GetSegmentation().GetSegment(segmentID)
539 if not previewSegment:
540 inputSegment = segmentationNode.GetSegmentation().GetSegment(segmentID)
541
542 previewSegment = vtkSegmentationCore.vtkSegment()
543 previewSegment.SetName(inputSegment.GetName())
544 previewSegment.SetColor(inputSegment.GetColor())
545 previewNode.GetSegmentation().AddSegment(previewSegment, segmentID)
546
547 labelValue = index + 1 # n-th segment label value = n + 1 (background label value is 0)
548 previewSegment.AddRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName(), outputLabelmap)
549 previewSegment.SetLabelValue(labelValue)
550
551 # Automatically hide result segments that are background (all eight corners are non-zero)
552 previewNode.GetDisplayNode().SetSegmentVisibility3D(segmentID, not self.isBackgroundLabelmap(outputLabelmap, labelValue))
553
554 # If the preview was reset, we need to restore the visibility options
555 self.setPreviewOpacity(previewOpacity)
556 self.setPreviewShow3D(previewShow3D)
557
558 self.updateGUIFromMRML()
559
560
561ResultPreviewNodeReferenceRole = "SegmentationResultPreview"