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