2 import vtk, qt, ctk, slicer
4 from SegmentEditorEffects
import *
7 """This effect fills a selected volume node inside and/or outside a segment with a chosen value. 11 scriptedEffect.name =
'Mask volume' 12 scriptedEffect.perSegment =
True 13 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
20 import qSlicerSegmentationsEditorEffectsPythonQt
as effects
21 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(
None)
22 clonedEffect.setPythonSource(__file__.replace(
'\\',
'/'))
27 iconPath = os.path.join(os.path.dirname(__file__),
'Resources/Icons/MaskVolume.png')
28 if os.path.exists(iconPath):
29 return qt.QIcon(iconPath)
33 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> 34 Fill inside and outside operation creates a binary labelmap volume as output, with the inside and outside fill values modifiable. 40 self.
visibleIcon = qt.QIcon(
":/Icons/Small/SlicerVisible.png")
53 self.
binaryMaskFillButton.setToolTip(
"Create a labelmap volume with specified inside and outside fill values.")
58 operationLayout = qt.QGridLayout()
62 self.scriptedEffect.addLabeledOptionsWidget(
"Operation:", operationLayout)
66 self.
fillValueEdit.setToolTip(
"Choose the voxel intensity that will be used to fill the masked region.")
80 fillValueEdit.decimalsOption = ctk.ctkDoubleSpinBox.DecimalsByValue + ctk.ctkDoubleSpinBox.DecimalsByKey + ctk.ctkDoubleSpinBox.InsertDecimals
81 fillValueEdit.minimum = vtk.vtkDoubleArray().GetDataTypeMin(vtk.VTK_DOUBLE)
82 fillValueEdit.maximum = vtk.vtkDoubleArray().GetDataTypeMax(vtk.VTK_DOUBLE)
86 fillValueLayout = qt.QFormLayout()
89 fillOutsideLayout = qt.QFormLayout()
92 fillInsideLayout = qt.QFormLayout()
95 binaryMaskFillLayout = qt.QHBoxLayout()
96 binaryMaskFillLayout.addLayout(fillOutsideLayout)
97 binaryMaskFillLayout.addLayout(fillInsideLayout)
98 fillValuesSpinBoxLayout = qt.QFormLayout()
99 fillValuesSpinBoxLayout.addRow(binaryMaskFillLayout)
100 fillValuesSpinBoxLayout.addRow(fillValueLayout)
101 self.scriptedEffect.addOptionsWidget(fillValuesSpinBoxLayout)
113 self.
inputVolumeSelector.setToolTip(
"Volume to mask. Default is current master volume node.")
119 inputLayout = qt.QHBoxLayout()
122 self.scriptedEffect.addLabeledOptionsWidget(
"Input Volume: ", inputLayout)
135 self.
outputVolumeSelector.setToolTip(
"Masked output volume. It may be the same as the input volume for cumulative masking.")
141 outputLayout = qt.QHBoxLayout()
144 self.scriptedEffect.addLabeledOptionsWidget(
"Output Volume: ", outputLayout)
148 self.
applyButton.objectName = self.__class__.__name__ +
'Apply' 149 self.
applyButton.setToolTip(
"Apply segment as volume mask. No undo operation available once applied.")
150 self.scriptedEffect.addOptionsWidget(self.
applyButton)
154 button.connect(
'toggled(bool)',
159 return slicer.util.mainWindow().cursor
162 self.scriptedEffect.setParameterDefault(
"FillValue",
"0")
163 self.scriptedEffect.setParameterDefault(
"BinaryMaskFillValueInside",
"1")
164 self.scriptedEffect.setParameterDefault(
"BinaryMaskFillValueOutside",
"0")
165 self.scriptedEffect.setParameterDefault(
"Operation",
"FILL_OUTSIDE")
170 volumeNodeID = volumeNode.GetID()
171 lm = slicer.app.layoutManager()
172 sliceViewNames = lm.sliceViewNames()
173 for sliceViewName
in sliceViewNames:
174 sliceWidget = lm.sliceWidget(sliceViewName)
175 if volumeNodeID == sliceWidget.mrmlSliceCompositeNode().GetBackgroundVolumeID():
182 self.
fillValueEdit.setValue(float(self.scriptedEffect.parameter(
"FillValue"))
if self.scriptedEffect.parameter(
"FillValue")
else 0)
184 if self.scriptedEffect.parameter(
"BinaryMaskFillValueOutside")
else 0)
186 if self.scriptedEffect.parameter(
"BinaryMaskFillValueInside")
else 1)
187 operationName = self.scriptedEffect.parameter(
"Operation")
190 operationButton.setChecked(
True)
192 inputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference(
"Mask volume.InputVolume")
194 outputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference(
"Mask volume.OutputVolume")
197 masterVolume = self.scriptedEffect.parameterSetNode().GetMasterVolumeNode()
198 if inputVolume
is None:
199 inputVolume = masterVolume
201 self.
fillValueEdit.setVisible(operationName
in [
"FILL_INSIDE",
"FILL_OUTSIDE"])
202 self.
fillValueLabel.setVisible(operationName
in [
"FILL_INSIDE",
"FILL_OUTSIDE"])
204 self.
fillInsideLabel.setVisible(operationName ==
"FILL_INSIDE_AND_OUTSIDE")
206 self.
fillOutsideLabel.setVisible(operationName ==
"FILL_INSIDE_AND_OUTSIDE")
207 if operationName
in [
"FILL_INSIDE",
"FILL_OUTSIDE"]:
224 self.scriptedEffect.setParameter(
"FillValue", self.
fillValueEdit.value)
227 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(
"Mask volume.InputVolume", self.
inputVolumeSelector.currentNodeID)
228 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(
"Mask volume.OutputVolume", self.
outputVolumeSelector.currentNodeID)
231 self.scriptedEffect.setParameter(
"InputVisibility",
"True")
234 if self.
outputVolumeSelector.currentNode()
is not self.scriptedEffect.parameterSetNode().GetMasterVolumeNode():
235 self.scriptedEffect.setParameter(
"OutputVisibility",
"False")
236 slicer.util.setSliceViewerLayers(background=self.scriptedEffect.parameterSetNode().GetMasterVolumeNode())
241 self.scriptedEffect.setParameter(
"Operation", operationName)
245 if inputVolume
is None:
246 inputVolume = self.scriptedEffect.parameterSetNode().GetMasterVolumeNode()
250 inputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference(
"Mask volume.InputVolume")
251 masterVolume = self.scriptedEffect.parameterSetNode().GetMasterVolumeNode()
252 if inputVolume
is None:
253 inputVolume = masterVolume
255 slicer.util.setSliceViewerLayers(background=inputVolume)
259 outputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference(
"Mask volume.OutputVolume")
261 slicer.util.setSliceViewerLayers(background=outputVolume)
265 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(
"Mask volume.InputVolume", self.
inputVolumeSelector.currentNodeID)
269 self.scriptedEffect.parameterSetNode().SetNodeReferenceID(
"Mask volume.OutputVolume", self.
outputVolumeSelector.currentNodeID)
278 operationMode = self.scriptedEffect.parameter(
"Operation")
281 volumesLogic = slicer.modules.volumes.logic()
282 scene = inputVolume.GetScene()
283 if operationMode ==
"FILL_INSIDE_AND_OUTSIDE":
284 outputVolumeName = inputVolume.GetName()+
" label" 285 outputVolume = volumesLogic.CreateAndAddLabelVolume(inputVolume, outputVolumeName)
287 outputVolumeName = inputVolume.GetName()+
" masked" 288 outputVolume = volumesLogic.CloneVolumeGeneric(scene, inputVolume, outputVolumeName,
False)
291 if operationMode
in [
"FILL_INSIDE",
"FILL_OUTSIDE"]:
296 segmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
297 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
299 slicer.app.setOverrideCursor(qt.Qt.WaitCursor)
300 SegmentEditorMaskVolumeEffect.maskVolumeWithSegment(segmentationNode, segmentID, operationMode, fillValues, inputVolume, outputVolume)
302 slicer.util.setSliceViewerLayers(background=outputVolume)
303 qt.QApplication.restoreOverrideCursor()
308 def maskVolumeWithSegment(segmentationNode, segmentID, operationMode, fillValues, inputVolumeNode, outputVolumeNode, maskExtent=None):
310 Fill voxels of the input volume inside/outside the masking model with the provided fill value 311 maskExtent: optional output to return computed mask extent (expected input is a 6-element list) 312 fillValues: list containing one or two fill values. If fill mode is inside or outside then only one value is specified in the list. 313 If fill mode is inside&outside then the list must contain two values: first is the inside fill, second is the outside fill value. 316 segmentIDs = vtk.vtkStringArray()
317 segmentIDs.InsertNextValue(segmentID)
318 maskVolumeNode = slicer.modules.volumes.logic().CreateAndAddLabelVolume(inputVolumeNode,
"TemporaryVolumeMask")
319 if not maskVolumeNode:
320 logging.error(
"maskVolumeWithSegment failed: invalid maskVolumeNode")
323 if not slicer.vtkSlicerSegmentationsModuleLogic.ExportSegmentsToLabelmapNode(segmentationNode, segmentIDs, maskVolumeNode, inputVolumeNode):
324 logging.error(
"maskVolumeWithSegment failed: ExportSegmentsToLabelmapNode error")
325 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode().GetColorNode())
326 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode())
327 slicer.mrmlScene.RemoveNode(maskVolumeNode)
331 img = slicer.modules.segmentations.logic().CreateOrientedImageDataFromVolumeNode(maskVolumeNode)
333 import vtkSegmentationCorePython
as vtkSegmentationCore
334 vtkSegmentationCore.vtkOrientedImageDataResample.CalculateEffectiveExtent(img, maskExtent, 0)
336 maskToStencil = vtk.vtkImageToImageStencil()
337 maskToStencil.ThresholdByLower(0)
338 maskToStencil.SetInputData(maskVolumeNode.GetImageData())
340 stencil = vtk.vtkImageStencil()
342 if operationMode ==
"FILL_INSIDE_AND_OUTSIDE":
344 thresh = vtk.vtkImageThreshold()
345 thresh.SetInputData(inputVolumeNode.GetImageData())
346 thresh.ThresholdByLower(0)
347 thresh.SetInValue(fillValues[1])
348 thresh.SetOutValue(fillValues[1])
349 thresh.SetOutputScalarType(inputVolumeNode.GetImageData().GetScalarType())
351 stencil.SetInputData(thresh.GetOutput())
353 stencil.SetInputData(inputVolumeNode.GetImageData())
355 stencil.SetStencilConnection(maskToStencil.GetOutputPort())
356 stencil.SetReverseStencil(operationMode ==
"FILL_OUTSIDE")
357 stencil.SetBackgroundValue(fillValues[0])
360 outputVolumeNode.SetAndObserveImageData(stencil.GetOutput())
363 ijkToRas = vtk.vtkMatrix4x4()
364 inputVolumeNode.GetIJKToRASMatrix(ijkToRas)
365 outputVolumeNode.SetIJKToRASMatrix(ijkToRas)
366 inputVolumeNode.SetAndObserveTransformNodeID(inputVolumeNode.GetTransformNodeID())
368 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode().GetColorNode())
369 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode())
370 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)