2 import vtk, qt, ctk, slicer
4 from SegmentEditorEffects
import *
8 """This effect fills a selected volume node inside and/or outside a segment with a chosen value. 12 scriptedEffect.name =
'Mask volume' 13 scriptedEffect.perSegment =
True 14 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
21 import qSlicerSegmentationsEditorEffectsPythonQt
as effects
22 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(
None)
23 clonedEffect.setPythonSource(__file__.replace(
'\\',
'/'))
28 iconPath = os.path.join(os.path.dirname(__file__),
'Resources/Icons/MaskVolume.png')
29 if os.path.exists(iconPath):
30 return qt.QIcon(iconPath)
34 return """<html>Use the currently selected segment as a mask to blank out regions in a volume.<br> The mask is applied to the master volume by default.<p> 35 Fill inside and outside operation creates a binary labelmap volume as output, with the inside and outside fill values modifiable. 41 self.
visibleIcon = qt.QIcon(
":/Icons/Small/SlicerVisible.png")
54 self.
binaryMaskFillButton.setToolTip(
"Create a labelmap volume with specified inside and outside fill values.")
59 operationLayout = qt.QGridLayout()
63 self.scriptedEffect.addLabeledOptionsWidget(
"Operation:", operationLayout)
67 self.
fillValueEdit.setToolTip(
"Choose the voxel intensity that will be used to fill the masked region.")
81 fillValueEdit.decimalsOption = ctk.ctkDoubleSpinBox.DecimalsByValue + ctk.ctkDoubleSpinBox.DecimalsByKey + ctk.ctkDoubleSpinBox.InsertDecimals
82 fillValueEdit.minimum = vtk.vtkDoubleArray().GetDataTypeMin(vtk.VTK_DOUBLE)
83 fillValueEdit.maximum = vtk.vtkDoubleArray().GetDataTypeMax(vtk.VTK_DOUBLE)
87 fillValueLayout = qt.QFormLayout()
90 fillOutsideLayout = qt.QFormLayout()
93 fillInsideLayout = qt.QFormLayout()
96 binaryMaskFillLayout = qt.QHBoxLayout()
97 binaryMaskFillLayout.addLayout(fillOutsideLayout)
98 binaryMaskFillLayout.addLayout(fillInsideLayout)
99 fillValuesSpinBoxLayout = qt.QFormLayout()
100 fillValuesSpinBoxLayout.addRow(binaryMaskFillLayout)
101 fillValuesSpinBoxLayout.addRow(fillValueLayout)
102 self.scriptedEffect.addOptionsWidget(fillValuesSpinBoxLayout)
114 self.
inputVolumeSelector.setToolTip(
"Volume to mask. Default is current master volume node.")
120 inputLayout = qt.QHBoxLayout()
123 self.scriptedEffect.addLabeledOptionsWidget(
"Input Volume: ", inputLayout)
136 self.
outputVolumeSelector.setToolTip(
"Masked output volume. It may be the same as the input volume for cumulative masking.")
142 outputLayout = qt.QHBoxLayout()
145 self.scriptedEffect.addLabeledOptionsWidget(
"Output Volume: ", outputLayout)
149 self.
applyButton.objectName = self.__class__.__name__ +
'Apply' 150 self.
applyButton.setToolTip(
"Apply segment as volume mask. No undo operation available once applied.")
151 self.scriptedEffect.addOptionsWidget(self.
applyButton)
155 button.connect(
'toggled(bool)',
160 return slicer.util.mainWindow().cursor
163 self.scriptedEffect.setParameterDefault(
"FillValue",
"0")
164 self.scriptedEffect.setParameterDefault(
"BinaryMaskFillValueInside",
"1")
165 self.scriptedEffect.setParameterDefault(
"BinaryMaskFillValueOutside",
"0")
166 self.scriptedEffect.setParameterDefault(
"Operation",
"FILL_OUTSIDE")
171 volumeNodeID = volumeNode.GetID()
172 lm = slicer.app.layoutManager()
173 sliceViewNames = lm.sliceViewNames()
174 for sliceViewName
in sliceViewNames:
175 sliceWidget = lm.sliceWidget(sliceViewName)
176 if volumeNodeID == sliceWidget.mrmlSliceCompositeNode().GetBackgroundVolumeID():
183 self.
fillValueEdit.setValue(float(self.scriptedEffect.parameter(
"FillValue"))
if self.scriptedEffect.parameter(
"FillValue")
else 0)
185 if self.scriptedEffect.parameter(
"BinaryMaskFillValueOutside")
else 0)
187 if self.scriptedEffect.parameter(
"BinaryMaskFillValueInside")
else 1)
188 operationName = self.scriptedEffect.parameter(
"Operation")
191 operationButton.setChecked(
True)
193 inputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference(
"Mask volume.InputVolume")
195 outputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference(
"Mask volume.OutputVolume")
198 masterVolume = self.scriptedEffect.parameterSetNode().GetMasterVolumeNode()
199 if inputVolume
is None:
200 inputVolume = masterVolume
202 self.
fillValueEdit.setVisible(operationName
in [
"FILL_INSIDE",
"FILL_OUTSIDE"])
203 self.
fillValueLabel.setVisible(operationName
in [
"FILL_INSIDE",
"FILL_OUTSIDE"])
205 self.
fillInsideLabel.setVisible(operationName ==
"FILL_INSIDE_AND_OUTSIDE")
207 self.
fillOutsideLabel.setVisible(operationName ==
"FILL_INSIDE_AND_OUTSIDE")
208 if operationName
in [
"FILL_INSIDE",
"FILL_OUTSIDE"]:
225 self.scriptedEffect.setParameter(
"FillValue", self.
fillValueEdit.value)
228 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(
"Mask volume.InputVolume", self.
inputVolumeSelector.currentNodeID)
229 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(
"Mask volume.OutputVolume", self.
outputVolumeSelector.currentNodeID)
232 self.scriptedEffect.setParameter(
"InputVisibility",
"True")
235 if self.
outputVolumeSelector.currentNode()
is not self.scriptedEffect.parameterSetNode().GetMasterVolumeNode():
236 self.scriptedEffect.setParameter(
"OutputVisibility",
"False")
237 slicer.util.setSliceViewerLayers(background=self.scriptedEffect.parameterSetNode().GetMasterVolumeNode())
242 self.scriptedEffect.setParameter(
"Operation", operationName)
246 if inputVolume
is None:
247 inputVolume = self.scriptedEffect.parameterSetNode().GetMasterVolumeNode()
251 inputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference(
"Mask volume.InputVolume")
252 masterVolume = self.scriptedEffect.parameterSetNode().GetMasterVolumeNode()
253 if inputVolume
is None:
254 inputVolume = masterVolume
256 slicer.util.setSliceViewerLayers(background=inputVolume)
260 outputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference(
"Mask volume.OutputVolume")
262 slicer.util.setSliceViewerLayers(background=outputVolume)
266 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(
"Mask volume.InputVolume", self.
inputVolumeSelector.currentNodeID)
270 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(
"Mask volume.OutputVolume", self.
outputVolumeSelector.currentNodeID)
279 operationMode = self.scriptedEffect.parameter(
"Operation")
282 volumesLogic = slicer.modules.volumes.logic()
283 scene = inputVolume.GetScene()
284 if operationMode ==
"FILL_INSIDE_AND_OUTSIDE":
285 outputVolumeName = inputVolume.GetName() +
" label" 286 outputVolume = volumesLogic.CreateAndAddLabelVolume(inputVolume, outputVolumeName)
288 outputVolumeName = inputVolume.GetName() +
" masked" 289 outputVolume = volumesLogic.CloneVolumeGeneric(scene, inputVolume, outputVolumeName,
False)
292 if operationMode
in [
"FILL_INSIDE",
"FILL_OUTSIDE"]:
297 segmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
298 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
300 slicer.app.setOverrideCursor(qt.Qt.WaitCursor)
301 SegmentEditorMaskVolumeEffect.maskVolumeWithSegment(segmentationNode, segmentID, operationMode, fillValues, inputVolume, outputVolume)
303 slicer.util.setSliceViewerLayers(background=outputVolume)
304 qt.QApplication.restoreOverrideCursor()
309 def maskVolumeWithSegment(segmentationNode, segmentID, operationMode, fillValues, inputVolumeNode, outputVolumeNode, maskExtent=None):
311 Fill voxels of the input volume inside/outside the masking model with the provided fill value 312 maskExtent: optional output to return computed mask extent (expected input is a 6-element list) 313 fillValues: list containing one or two fill values. If fill mode is inside or outside then only one value is specified in the list. 314 If fill mode is inside&outside then the list must contain two values: first is the inside fill, second is the outside fill value. 317 segmentIDs = vtk.vtkStringArray()
318 segmentIDs.InsertNextValue(segmentID)
319 maskVolumeNode = slicer.modules.volumes.logic().CreateAndAddLabelVolume(inputVolumeNode,
"TemporaryVolumeMask")
320 if not maskVolumeNode:
321 logging.error(
"maskVolumeWithSegment failed: invalid maskVolumeNode")
324 if not slicer.vtkSlicerSegmentationsModuleLogic.ExportSegmentsToLabelmapNode(segmentationNode, segmentIDs, maskVolumeNode, inputVolumeNode):
325 logging.error(
"maskVolumeWithSegment failed: ExportSegmentsToLabelmapNode error")
326 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode().GetColorNode())
327 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode())
328 slicer.mrmlScene.RemoveNode(maskVolumeNode)
332 img = slicer.modules.segmentations.logic().CreateOrientedImageDataFromVolumeNode(maskVolumeNode)
334 import vtkSegmentationCorePython
as vtkSegmentationCore
335 vtkSegmentationCore.vtkOrientedImageDataResample.CalculateEffectiveExtent(img, maskExtent, 0)
337 maskToStencil = vtk.vtkImageToImageStencil()
338 maskToStencil.ThresholdByLower(0)
339 maskToStencil.SetInputData(maskVolumeNode.GetImageData())
341 stencil = vtk.vtkImageStencil()
343 if operationMode ==
"FILL_INSIDE_AND_OUTSIDE":
345 thresh = vtk.vtkImageThreshold()
346 thresh.SetInputData(inputVolumeNode.GetImageData())
347 thresh.ThresholdByLower(0)
348 thresh.SetInValue(fillValues[1])
349 thresh.SetOutValue(fillValues[1])
350 thresh.SetOutputScalarType(inputVolumeNode.GetImageData().GetScalarType())
352 stencil.SetInputData(thresh.GetOutput())
354 stencil.SetInputData(inputVolumeNode.GetImageData())
356 stencil.SetStencilConnection(maskToStencil.GetOutputPort())
357 stencil.SetReverseStencil(operationMode ==
"FILL_OUTSIDE")
358 stencil.SetBackgroundValue(fillValues[0])
361 outputVolumeNode.SetAndObserveImageData(stencil.GetOutput())
364 ijkToRas = vtk.vtkMatrix4x4()
365 inputVolumeNode.GetIJKToRASMatrix(ijkToRas)
366 outputVolumeNode.SetIJKToRASMatrix(ijkToRas)
367 inputVolumeNode.SetAndObserveTransformNodeID(inputVolumeNode.GetTransformNodeID())
369 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode().GetColorNode())
370 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode())
371 slicer.mrmlScene.RemoveNode(maskVolumeNode)
def updateMRMLFromGUI(self)
def __init__(self, scriptedEffect)
def onInputVolumeChanged(self)
def isVolumeVisible(self, volumeNode)
def setupOptionsFrame(self)
def onOutputVolumeChanged(self)
binaryMaskFillOutsideEdit
def updateGUIFromMRML(self)
def createCursor(self, widget)
def fillValueChanged(self)
def onOutputVisibilityButtonClicked(self)
def maskVolumeWithSegment(segmentationNode, segmentID, operationMode, fillValues, inputVolumeNode, outputVolumeNode, maskExtent=None)
def onInputVisibilityButtonClicked(self)
def setMRMLDefaults(self)
def onOperationSelectionChanged(self, operationName, toggle)