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