Slicer  4.11
Slicer is a multi-platform, free and open source software package for visualization and medical image computing
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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"