Slicer 5.6
Slicer is a multi-platform, free and open source software package for visualization and medical image computing
Loading...
Searching...
No Matches
SegmentEditorMaskVolumeEffect.py
Go to the documentation of this file.
1import os
2import vtk, qt, ctk, slicer
3import logging
4from SegmentEditorEffects import *
5
6from slicer.i18n import tr as _
7
8
10 """This effect fills a selected volume node inside and/or outside a segment with a chosen value."""
11
12 def __init__(self, scriptedEffect):
13 scriptedEffect.name = "Mask volume" # no tr (don't translate it because modules find effects by name)
14 scriptedEffect.title = _("Mask volume")
15 scriptedEffect.perSegment = True # this effect operates on a single selected segment
16 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
17
18 # Effect-specific members
20
21 def clone(self):
22 # It should not be necessary to modify this method
23 import qSlicerSegmentationsEditorEffectsPythonQt as effects
24
25 clonedEffect = effects.qSlicerSegmentEditorScriptedEffect(None)
26 clonedEffect.setPythonSource(__file__.replace("\\", "/"))
27 return clonedEffect
28
29 def icon(self):
30 # It should not be necessary to modify this method
31 iconPath = os.path.join(os.path.dirname(__file__), "Resources/Icons/MaskVolume.png")
32 if os.path.exists(iconPath):
33 return qt.QIcon(iconPath)
34 return qt.QIcon()
35
36 def helpText(self):
37 return "<html>" + _("""Use the currently selected segment as a mask to blank out regions in a volume<br>.
38The mask is applied to the source volume by default.<p>
39Fill inside and outside operation creates a binary labelmap volume as output, with the inside and outside fill values modifiable.
40""")
41
42 def setupOptionsFrame(self):
45 self.visibleIcon = qt.QIcon(":/Icons/Small/SlicerVisible.png")
46 self.invisibleIcon = qt.QIcon(":/Icons/Small/SlicerInvisible.png")
47
48 # Fill operation buttons
49 self.fillInsideButton = qt.QRadioButton(_("Fill inside"))
51 self.buttonToOperationNameMap[self.fillInsideButton] = "FILL_INSIDE"
52
53 self.fillOutsideButton = qt.QRadioButton(_("Fill outside"))
55 self.buttonToOperationNameMap[self.fillOutsideButton] = "FILL_OUTSIDE"
56
57 self.binaryMaskFillButton = qt.QRadioButton(_("Fill inside and outside"))
58 self.binaryMaskFillButton.setToolTip(_("Create a labelmap volume with specified inside and outside fill values."))
60 self.buttonToOperationNameMap[self.binaryMaskFillButton] = "FILL_INSIDE_AND_OUTSIDE"
61
62 # Operation buttons layout
63 operationLayout = qt.QGridLayout()
64 operationLayout.addWidget(self.fillInsideButton, 0, 0)
65 operationLayout.addWidget(self.fillOutsideButton, 1, 0)
66 operationLayout.addWidget(self.binaryMaskFillButton, 0, 1)
67 self.scriptedEffect.addLabeledOptionsWidget(_("Operation:"), operationLayout)
68
69 # fill value
70 self.fillValueEdit = ctk.ctkDoubleSpinBox()
71 self.fillValueEdit.setToolTip(_("Choose the voxel intensity that will be used to fill the masked region."))
72 self.fillValueLabel = qt.QLabel(_("Fill value: "))
73
74 # Binary mask fill outside value
75 self.binaryMaskFillOutsideEdit = ctk.ctkDoubleSpinBox()
76 self.binaryMaskFillOutsideEdit.setToolTip(_("Choose the voxel intensity that will be used to fill outside the mask."))
77 self.fillOutsideLabel = qt.QLabel(_("Outside fill value: "))
78
79 # Binary mask fill outside value
80 self.binaryMaskFillInsideEdit = ctk.ctkDoubleSpinBox()
81 self.binaryMaskFillInsideEdit.setToolTip(_("Choose the voxel intensity that will be used to fill inside the mask."))
82 self.fillInsideLabel = qt.QLabel(_(" Inside fill value: "))
83
84 for fillValueEdit in [self.fillValueEdit, self.binaryMaskFillOutsideEdit, self.binaryMaskFillInsideEdit]:
85 fillValueEdit.decimalsOption = ctk.ctkDoubleSpinBox.DecimalsByValue + ctk.ctkDoubleSpinBox.DecimalsByKey + ctk.ctkDoubleSpinBox.InsertDecimals
86 fillValueEdit.minimum = vtk.vtkDoubleArray().GetDataTypeMin(vtk.VTK_DOUBLE)
87 fillValueEdit.maximum = vtk.vtkDoubleArray().GetDataTypeMax(vtk.VTK_DOUBLE)
88 fillValueEdit.connect("valueChanged(double)", self.fillValueChangedfillValueChanged)
89
90 # Fill value layouts
91 fillValueLayout = qt.QFormLayout()
92 fillValueLayout.addRow(self.fillValueLabel, self.fillValueEdit)
93
94 fillOutsideLayout = qt.QFormLayout()
95 fillOutsideLayout.addRow(self.fillOutsideLabel, self.binaryMaskFillOutsideEdit)
96
97 fillInsideLayout = qt.QFormLayout()
98 fillInsideLayout.addRow(self.fillInsideLabel, self.binaryMaskFillInsideEdit)
99
100 binaryMaskFillLayout = qt.QHBoxLayout()
101 binaryMaskFillLayout.addLayout(fillOutsideLayout)
102 binaryMaskFillLayout.addLayout(fillInsideLayout)
103 fillValuesSpinBoxLayout = qt.QFormLayout()
104 fillValuesSpinBoxLayout.addRow(binaryMaskFillLayout)
105 fillValuesSpinBoxLayout.addRow(fillValueLayout)
106 self.scriptedEffect.addOptionsWidget(fillValuesSpinBoxLayout)
107
108 # Soft edge
109 self.softEdgeMmSpinBox = slicer.qMRMLSpinBox()
110 self.softEdgeMmSpinBox.setMRMLScene(slicer.mrmlScene)
111 self.softEdgeMmSpinBox.setToolTip(_("Standard deviation of the Gaussian function that blurs the edge of the mask."
112 " Higher value makes the edge softer."))
113 self.softEdgeMmSpinBox.quantity = "length"
114 self.softEdgeMmSpinBox.value = 0
115 self.softEdgeMmSpinBox.minimum = 0
116 self.softEdgeMmSpinBox.singleStep = 0.5
117 self.softEdgeMmLabel = self.scriptedEffect.addLabeledOptionsWidget(_("Soft edge:"), self.softEdgeMmSpinBox)
118 self.softEdgeMmSpinBox.connect("valueChanged(double)", self.softEdgeMmChangedsoftEdgeMmChanged)
119
120 # input volume selector
121 self.inputVolumeSelector = slicer.qMRMLNodeComboBox()
122 self.inputVolumeSelector.nodeTypes = ["vtkMRMLScalarVolumeNode"]
123 self.inputVolumeSelector.selectNodeUponCreation = True
124 self.inputVolumeSelector.addEnabled = True
125 self.inputVolumeSelector.removeEnabled = True
126 self.inputVolumeSelector.noneEnabled = True
127 self.inputVolumeSelector.noneDisplay = _("(Source volume)")
128 self.inputVolumeSelector.showHidden = False
129 self.inputVolumeSelector.setMRMLScene(slicer.mrmlScene)
130 self.inputVolumeSelector.setToolTip(_("Volume to mask. Default is current source volume node."))
131 self.inputVolumeSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onInputVolumeChangedonInputVolumeChanged)
132
133 self.inputVisibilityButton = qt.QToolButton()
134 self.inputVisibilityButton.setIcon(self.invisibleIcon)
136 inputLayout = qt.QHBoxLayout()
137 inputLayout.addWidget(self.inputVisibilityButton)
138 inputLayout.addWidget(self.inputVolumeSelector)
139 self.scriptedEffect.addLabeledOptionsWidget(_("Input Volume: "), inputLayout)
140
141 # output volume selector
142 self.outputVolumeSelector = slicer.qMRMLNodeComboBox()
143 self.outputVolumeSelector.nodeTypes = ["vtkMRMLScalarVolumeNode", "vtkMRMLLabelMapVolumeNode"]
144 self.outputVolumeSelector.selectNodeUponCreation = True
145 self.outputVolumeSelector.addEnabled = True
146 self.outputVolumeSelector.removeEnabled = True
147 self.outputVolumeSelector.renameEnabled = True
148 self.outputVolumeSelector.noneEnabled = True
149 self.outputVolumeSelector.noneDisplay = _("(Create new Volume)")
150 self.outputVolumeSelector.showHidden = False
151 self.outputVolumeSelector.setMRMLScene(slicer.mrmlScene)
152 self.outputVolumeSelector.setToolTip(_("Masked output volume. It may be the same as the input volume for cumulative masking."))
153 self.outputVolumeSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onOutputVolumeChangedonOutputVolumeChanged)
154
155 self.outputVisibilityButton = qt.QToolButton()
156 self.outputVisibilityButton.setIcon(self.invisibleIcon)
158 outputLayout = qt.QHBoxLayout()
159 outputLayout.addWidget(self.outputVisibilityButton)
160 outputLayout.addWidget(self.outputVolumeSelector)
161 self.scriptedEffect.addLabeledOptionsWidget(_("Output Volume: "), outputLayout)
162
163 # Apply button
164 self.applyButton = qt.QPushButton(_("Apply"))
165 self.applyButton.objectName = self.__class__.__name__ + "Apply"
166 self.applyButton.setToolTip(_("Apply segment as volume mask. No undo operation available once applied."))
167 self.scriptedEffect.addOptionsWidget(self.applyButton)
168 self.applyButton.connect("clicked()", self.onApplyonApply)
169
170 for button in self.operationRadioButtons:
171 button.connect("toggled(bool)",
172 lambda toggle, widget=self.buttonToOperationNameMap[button]: self.onOperationSelectionChanged(widget, toggle))
173
174 def createCursor(self, widget):
175 # Turn off effect-specific cursor for this effect
176 return slicer.util.mainWindow().cursor
177
178 def setMRMLDefaults(self):
179 self.scriptedEffect.setParameterDefault("FillValue", "0")
180 self.scriptedEffect.setParameterDefault("BinaryMaskFillValueInside", "1")
181 self.scriptedEffect.setParameterDefault("BinaryMaskFillValueOutside", "0")
182 self.scriptedEffect.setParameterDefault("SoftEdgeMm", "0")
183 self.scriptedEffect.setParameterDefault("Operation", "FILL_OUTSIDE")
184
185 def isVolumeVisible(self, volumeNode):
186 if not volumeNode:
187 return False
188 volumeNodeID = volumeNode.GetID()
189 lm = slicer.app.layoutManager()
190 sliceViewNames = lm.sliceViewNames()
191 for sliceViewName in sliceViewNames:
192 sliceWidget = lm.sliceWidget(sliceViewName)
193 if volumeNodeID == sliceWidget.mrmlSliceCompositeNode().GetBackgroundVolumeID():
194 return True
195 return False
196
197 def updateGUIFromMRML(self):
198 self.updatingGUIFromMRML = True
199
200 self.fillValueEdit.setValue(float(self.scriptedEffect.parameter("FillValue")) if self.scriptedEffect.parameter("FillValue") else 0)
201 self.binaryMaskFillOutsideEdit.setValue(float(self.scriptedEffect.parameter("BinaryMaskFillValueOutside"))
202 if self.scriptedEffect.parameter("BinaryMaskFillValueOutside") else 0)
203 self.binaryMaskFillInsideEdit.setValue(float(self.scriptedEffect.parameter("BinaryMaskFillValueInside"))
204 if self.scriptedEffect.parameter("BinaryMaskFillValueInside") else 1)
205 operationName = self.scriptedEffect.parameter("Operation")
206 if operationName:
207 operationButton = list(self.buttonToOperationNameMap.keys())[list(self.buttonToOperationNameMap.values()).index(operationName)]
208 operationButton.setChecked(True)
209
210 self.softEdgeMmSpinBox.setValue(float(self.scriptedEffect.parameter("SoftEdgeMm"))
211 if self.scriptedEffect.parameter("SoftEdgeMm") else 0)
212
213 inputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference("Mask volume.InputVolume")
214 self.inputVolumeSelector.setCurrentNode(inputVolume)
215 outputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference("Mask volume.OutputVolume")
216 self.outputVolumeSelector.setCurrentNode(outputVolume)
217
218 sourceVolume = self.scriptedEffect.parameterSetNode().GetSourceVolumeNode()
219 if inputVolume is None:
220 inputVolume = sourceVolume
221
222 self.fillValueEdit.setVisible(operationName in ["FILL_INSIDE", "FILL_OUTSIDE"])
223 self.fillValueLabel.setVisible(operationName in ["FILL_INSIDE", "FILL_OUTSIDE"])
224 self.binaryMaskFillInsideEdit.setVisible(operationName == "FILL_INSIDE_AND_OUTSIDE")
225 self.fillInsideLabel.setVisible(operationName == "FILL_INSIDE_AND_OUTSIDE")
226 self.binaryMaskFillOutsideEdit.setVisible(operationName == "FILL_INSIDE_AND_OUTSIDE")
227 self.fillOutsideLabel.setVisible(operationName == "FILL_INSIDE_AND_OUTSIDE")
228 if operationName in ["FILL_INSIDE", "FILL_OUTSIDE"]:
229 if self.outputVolumeSelector.noneDisplay != _("(Create new Volume)"):
230 self.outputVolumeSelector.noneDisplay = _("(Create new Volume)")
231 self.outputVolumeSelector.nodeTypes = ["vtkMRMLScalarVolumeNode", "vtkMRMLLabelMapVolumeNode"]
232 else:
233 if self.outputVolumeSelector.noneDisplay != _("(Create new Labelmap Volume)"):
234 self.outputVolumeSelector.noneDisplay = _("(Create new Labelmap Volume)")
235 self.outputVolumeSelector.nodeTypes = ["vtkMRMLLabelMapVolumeNode", "vtkMRMLScalarVolumeNode"]
236
237 self.inputVisibilityButton.setIcon(self.visibleIcon if self.isVolumeVisible(inputVolume) else self.invisibleIcon)
238 self.outputVisibilityButton.setIcon(self.visibleIcon if self.isVolumeVisible(outputVolume) else self.invisibleIcon)
239
240 self.updatingGUIFromMRML = False
241
242 def updateMRMLFromGUI(self):
243 if self.updatingGUIFromMRML:
244 return
245 self.scriptedEffect.setParameter("FillValue", self.fillValueEdit.value)
246 self.scriptedEffect.setParameter("BinaryMaskFillValueInside", self.binaryMaskFillInsideEdit.value)
247 self.scriptedEffect.setParameter("BinaryMaskFillValueOutside", self.binaryMaskFillOutsideEdit.value)
248 self.scriptedEffect.parameterSetNode().SetNodeReferenceID("Mask volume.InputVolume", self.inputVolumeSelector.currentNodeID)
249 self.scriptedEffect.parameterSetNode().SetNodeReferenceID("Mask volume.OutputVolume", self.outputVolumeSelector.currentNodeID)
250 self.scriptedEffect.setParameter("SoftEdgeMm", self.softEdgeMmSpinBox.value)
251
252 def activate(self):
253 self.scriptedEffect.setParameter("InputVisibility", "True")
254
255 def deactivate(self):
256 if self.outputVolumeSelector.currentNode() is not self.scriptedEffect.parameterSetNode().GetSourceVolumeNode():
257 self.scriptedEffect.setParameter("OutputVisibility", "False")
258 slicer.util.setSliceViewerLayers(background=self.scriptedEffect.parameterSetNode().GetSourceVolumeNode())
259
260 def onOperationSelectionChanged(self, operationName, toggle):
261 if not toggle:
262 return
263 self.scriptedEffect.setParameter("Operation", operationName)
264
265 def softEdgeMmChanged(self, edgeMm):
266 self.scriptedEffect.setParameter("SoftEdgeMm", edgeMm)
267
268 def getInputVolume(self):
269 inputVolume = self.inputVolumeSelector.currentNode()
270 if inputVolume is None:
271 inputVolume = self.scriptedEffect.parameterSetNode().GetSourceVolumeNode()
272 return inputVolume
273
274 def onInputVisibilityButtonClicked(self):
275 inputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference("Mask volume.InputVolume")
276 sourceVolume = self.scriptedEffect.parameterSetNode().GetSourceVolumeNode()
277 if inputVolume is None:
278 inputVolume = sourceVolume
279 if inputVolume:
280 slicer.util.setSliceViewerLayers(background=inputVolume)
281 self.updateGUIFromMRML()
282
283 def onOutputVisibilityButtonClicked(self):
284 outputVolume = self.scriptedEffect.parameterSetNode().GetNodeReference("Mask volume.OutputVolume")
285 if outputVolume:
286 slicer.util.setSliceViewerLayers(background=outputVolume)
287 self.updateGUIFromMRML()
288
289 def onInputVolumeChanged(self):
290 self.scriptedEffect.parameterSetNode().SetNodeReferenceID("Mask volume.InputVolume", self.inputVolumeSelector.currentNodeID)
291 self.updateGUIFromMRML() # node reference changes are not observed, update GUI manually
292
293 def onOutputVolumeChanged(self):
294 self.scriptedEffect.parameterSetNode().SetNodeReferenceID("Mask volume.OutputVolume", self.outputVolumeSelector.currentNodeID)
295 self.updateGUIFromMRML() # node reference changes are not observed, update GUI manually
296
297 def fillValueChanged(self):
298 self.updateMRMLFromGUI()
299
300 def onApply(self):
301 with slicer.util.tryWithErrorDisplay(_("Failed to apply mask to volume."), waitCursor=True):
302 inputVolume = self.getInputVolume()
303 outputVolume = self.outputVolumeSelector.currentNode()
304 operationMode = self.scriptedEffect.parameter("Operation")
305 if not outputVolume:
306 # Create new node for output
307 volumesLogic = slicer.modules.volumes.logic()
308 scene = inputVolume.GetScene()
309 if operationMode == "FILL_INSIDE_AND_OUTSIDE":
310 outputVolumeName = inputVolume.GetName() + " label"
311 outputVolume = volumesLogic.CreateAndAddLabelVolume(inputVolume, outputVolumeName)
312 else:
313 outputVolumeName = inputVolume.GetName() + " masked"
314 outputVolume = volumesLogic.CloneVolumeGeneric(scene, inputVolume, outputVolumeName, False)
315 self.outputVolumeSelector.setCurrentNode(outputVolume)
316
317 if operationMode in ["FILL_INSIDE", "FILL_OUTSIDE"]:
318 fillValues = [self.fillValueEdit.value]
319 else:
320 fillValues = [self.binaryMaskFillInsideEdit.value, self.binaryMaskFillOutsideEdit.value]
321
322 segmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
323 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
324
325 softEdgeMm = self.scriptedEffect.doubleParameter("SoftEdgeMm")
326
327 SegmentEditorMaskVolumeEffect.maskVolumeWithSegment(segmentationNode, segmentID, operationMode, fillValues, inputVolume, outputVolume,
328 softEdgeMm=softEdgeMm)
329
330 slicer.util.setSliceViewerLayers(background=outputVolume)
331
332 self.updateGUIFromMRML()
333
334 @staticmethod
335 def maskVolumeWithSegment(segmentationNode, segmentID, operationMode, fillValues, inputVolumeNode, outputVolumeNode, maskExtent=None, softEdgeMm=0.0):
336 """
337 Fill voxels of the input volume inside/outside the masking model with the provided fill value
338 maskExtent: optional output to return computed mask extent (expected input is a 6-element list)
339 fillValues: list containing one or two fill values. If fill mode is inside or outside then only one value is specified in the list.
340 If fill mode is inside&outside then the list must contain two values: first is the inside fill, second is the outside fill value.
341 """
342
343 import vtk # without this we get the error: UnboundLocalError: local variable 'vtk' referenced before assignment
344
345 segmentIDs = vtk.vtkStringArray()
346 segmentIDs.InsertNextValue(segmentID)
347 maskVolumeNode = slicer.modules.volumes.logic().CreateAndAddLabelVolume(inputVolumeNode, "TemporaryVolumeMask")
348 if not maskVolumeNode:
349 logging.error("maskVolumeWithSegment failed: invalid maskVolumeNode")
350 return False
351
352 if not slicer.vtkSlicerSegmentationsModuleLogic.ExportSegmentsToLabelmapNode(segmentationNode, segmentIDs, maskVolumeNode, inputVolumeNode):
353 logging.error("maskVolumeWithSegment failed: ExportSegmentsToLabelmapNode error")
354 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode().GetColorNode())
355 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode())
356 slicer.mrmlScene.RemoveNode(maskVolumeNode)
357 return False
358
359 if maskExtent:
360 img = slicer.modules.segmentations.logic().CreateOrientedImageDataFromVolumeNode(maskVolumeNode)
361 img.UnRegister(None)
362
363 import vtkSegmentationCorePython as vtkSegmentationCore
364
365 vtkSegmentationCore.vtkOrientedImageDataResample.CalculateEffectiveExtent(img, maskExtent, 0)
366
367 if softEdgeMm == 0:
368 # Hard edge
369 maskToStencil = vtk.vtkImageToImageStencil()
370 maskToStencil.ThresholdByLower(0)
371 maskToStencil.SetInputData(maskVolumeNode.GetImageData())
372
373 stencil = vtk.vtkImageStencil()
374
375 if operationMode == "FILL_INSIDE_AND_OUTSIDE":
376 # Set input to constant value
377 thresh = vtk.vtkImageThreshold()
378 thresh.SetInputData(inputVolumeNode.GetImageData())
379 thresh.ThresholdByLower(0)
380 thresh.SetInValue(fillValues[1])
381 thresh.SetOutValue(fillValues[1])
382 thresh.SetOutputScalarType(inputVolumeNode.GetImageData().GetScalarType())
383 thresh.Update()
384 stencil.SetInputData(thresh.GetOutput())
385 else:
386 stencil.SetInputData(inputVolumeNode.GetImageData())
387
388 stencil.SetStencilConnection(maskToStencil.GetOutputPort())
389 stencil.SetReverseStencil(operationMode == "FILL_OUTSIDE")
390 stencil.SetBackgroundValue(fillValues[0])
391 stencil.Update()
392 outputVolumeNode.SetAndObserveImageData(stencil.GetOutput())
393 else:
394 # Soft edge
395
396 thresh = vtk.vtkImageThreshold()
397 maskMin = 0
398 maskMax = 255
399 thresh.SetOutputScalarTypeToUnsignedChar()
400 thresh.SetInputData(maskVolumeNode.GetImageData())
401 thresh.ThresholdByLower(0)
402 thresh.SetInValue(maskMin)
403 thresh.SetOutValue(maskMax)
404 thresh.Update()
405
406 gaussianFilter = vtk.vtkImageGaussianSmooth()
407 spacing = maskVolumeNode.GetSpacing()
408 standardDeviationPixel = [1.0, 1.0, 1.0]
409 for idx in range(3):
410 standardDeviationPixel[idx] = softEdgeMm / spacing[idx]
411 gaussianFilter.SetInputConnection(thresh.GetOutputPort())
412 gaussianFilter.SetStandardDeviations(*standardDeviationPixel)
413 # Do not truncate the Gaussian kernel at the default 1.5 sigma,
414 # because it would result in edge artifacts.
415 # Larger value results in less edge artifact but increased computation time,
416 # so 3.0 is a good tradeoff.
417 gaussianFilter.SetRadiusFactor(3.0)
418 gaussianFilter.Update()
419
420 import vtk.util.numpy_support
421
422 maskImage = gaussianFilter.GetOutput()
423 nshape = tuple(reversed(maskImage.GetDimensions()))
424 maskArray = vtk.util.numpy_support.vtk_to_numpy(maskImage.GetPointData().GetScalars()).reshape(nshape)
425 # Normalize mask with the actual min/max values.
426 # Gaussian output is not always exactly the original minimum and maximum, so we get the actual min/max values.
427 maskMin = maskArray.min()
428 maskMax = maskArray.max()
429 mask = (maskArray.astype(float) - maskMin) / float(maskMax - maskMin)
430
431 inputArray = slicer.util.arrayFromVolume(inputVolumeNode)
432
433 if operationMode == "FILL_INSIDE_AND_OUTSIDE":
434 # Rescale the smoothed mask
435 resultArray = fillValues[0] + (fillValues[1] - fillValues[0]) * mask[:]
436 else:
437 # Compute weighted average between blanked out and input volume
438 if operationMode == "FILL_INSIDE":
439 mask = 1.0 - mask
440 resultArray = inputArray[:] * mask[:] + float(fillValues[0]) * (1.0 - mask[:])
441
442 slicer.util.updateVolumeFromArray(outputVolumeNode, resultArray.astype(inputArray.dtype))
443
444 # Set the same geometry and parent transform as the input volume
445 ijkToRas = vtk.vtkMatrix4x4()
446 inputVolumeNode.GetIJKToRASMatrix(ijkToRas)
447 outputVolumeNode.SetIJKToRASMatrix(ijkToRas)
448 inputVolumeNode.SetAndObserveTransformNodeID(inputVolumeNode.GetTransformNodeID())
449
450 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode().GetColorNode())
451 slicer.mrmlScene.RemoveNode(maskVolumeNode.GetDisplayNode())
452 slicer.mrmlScene.RemoveNode(maskVolumeNode)
453 return True