Slicer  5.2
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 import logging
2 
3 import ctk
4 import qt
5 import vtk
6 
7 import slicer
8 
9 from .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)
39  self.selectedSegmentIds = None
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.onPreview)
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 
88  def setupOptionsFrame(self):
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.onPreview)
142  self.cancelButton.connect('clicked()', self.onCancel)
143  self.applyButton.connect('clicked()', self.onApply)
144  self.previewOpacitySlider.connect("valueChanged(double)", self.updateMRMLFromGUI)
145  self.previewShow3DButton.connect("toggled(bool)", self.updateMRMLFromGUI)
146  self.autoUpdateCheckBox.connect("stateChanged(int)", self.updateMRMLFromGUI)
147 
148  def createCursor(self, widget):
149  # Turn off effect-specific cursor for this effect
150  return slicer.util.mainWindow().cursor
151 
152  def setMRMLDefaults(self):
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.onCancel()
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.
191  if not self.previewComputationInProgress:
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.MasterRepresentationModified]
225  for eventId in observedEvents:
226  self.segmentationNodeObserverTags.append(self.observedSegmentation.AddObserver(eventId, self.onSegmentationModified))
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 
235  def updateGUIFromMRML(self):
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 
264  def updateMRMLFromGUI(self):
265  segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
266  previewNode = self.getPreviewNode()
267  if previewNode:
268  self.setPreviewOpacity(self.previewOpacitySlider.value)
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
277  self.previewComputationInProgress = True
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 
287  self.previewComputationInProgress = False
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)
299  self.mergedLabelmapGeometryImage = None
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 
354  def getPreviewOpacity(self):
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 
371  def getPreviewShow3D(self):
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())
500  self.clippedMasterImageData.CopyDirections(self.mergedLabelmapGeometryImage)
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 
561 ResultPreviewNodeReferenceRole = "SegmentationResultPreview"