196 def onApply(self, maskImage=None, maskExtent=None):
197 """maskImage: contains nonzero where smoothing will be applied
199 smoothingMethod = self.scriptedEffect.parameter(
"SmoothingMethod")
200 applyToAllVisibleSegments = int(self.scriptedEffect.parameter(
"ApplyToAllVisibleSegments")) != 0 \
201 if self.scriptedEffect.parameter(
"ApplyToAllVisibleSegments")
else False
203 if smoothingMethod != JOINT_TAUBIN:
205 if not self.scriptedEffect.confirmCurrentSegmentVisible():
210 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
211 self.scriptedEffect.saveStateForUndo()
213 if smoothingMethod == JOINT_TAUBIN:
215 elif applyToAllVisibleSegments:
217 inputSegmentIDs = vtk.vtkStringArray()
218 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
219 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(inputSegmentIDs)
221 selectedStartSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
222 if inputSegmentIDs.GetNumberOfValues() == 0:
223 logging.info(
"Smoothing operation skipped: there are no visible segments.")
225 for index
in range(inputSegmentIDs.GetNumberOfValues()):
226 segmentID = inputSegmentIDs.GetValue(index)
227 self.
showStatusMessage(f
'Smoothing {segmentationNode.GetSegmentation().GetSegment(segmentID).GetName()}...')
228 self.scriptedEffect.parameterSetNode().SetSelectedSegmentID(segmentID)
231 self.scriptedEffect.parameterSetNode().SetSelectedSegmentID(selectedStartSegmentID)
235 qt.QApplication.restoreOverrideCursor()
238 clipper = vtk.vtkImageClip()
239 clipper.SetOutputWholeExtent(maskExtent[0] - margin[0], maskExtent[1] + margin[0],
240 maskExtent[2] - margin[1], maskExtent[3] + margin[1],
241 maskExtent[4] - margin[2], maskExtent[5] + margin[2])
242 clipper.SetInputData(inputImage)
243 clipper.SetClipData(
True)
245 clippedImage = slicer.vtkOrientedImageData()
246 clippedImage.ShallowCopy(clipper.GetOutput())
247 clippedImage.CopyDirections(inputImage)
252 smoothedClippedSelectedSegmentLabelmap = slicer.vtkOrientedImageData()
253 smoothedClippedSelectedSegmentLabelmap.ShallowCopy(smoothedImage)
254 smoothedClippedSelectedSegmentLabelmap.CopyDirections(modifierLabelmap)
258 slicer.vtkOrientedImageDataResample.ApplyImageMask(smoothedClippedSelectedSegmentLabelmap, maskImage, fillValue,
False)
260 slicer.vtkOrientedImageDataResample.ModifyImage(maskImage, selectedSegmentLabelmap,
261 slicer.vtkOrientedImageDataResample.OPERATION_MAXIMUM)
262 slicer.vtkOrientedImageDataResample.ModifyImage(maskImage, smoothedClippedSelectedSegmentLabelmap,
263 slicer.vtkOrientedImageDataResample.OPERATION_MINIMUM)
265 updateExtent = [0, -1, 0, -1, 0, -1]
266 modifierExtent = modifierLabelmap.GetExtent()
268 updateExtent[2 * i] = min(maskExtent[2 * i], modifierExtent[2 * i])
269 updateExtent[2 * i + 1] = max(maskExtent[2 * i + 1], modifierExtent[2 * i + 1])
272 slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet,
275 modifierLabelmap.DeepCopy(smoothedImage)
281 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
282 selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
284 smoothingMethod = self.scriptedEffect.parameter(
"SmoothingMethod")
286 if smoothingMethod == GAUSSIAN:
289 standardDeviationMM = self.scriptedEffect.doubleParameter(
"GaussianStandardDeviationMm")
290 spacing = modifierLabelmap.GetSpacing()
291 standardDeviationPixel = [1.0, 1.0, 1.0]
292 radiusPixel = [3, 3, 3]
294 standardDeviationPixel[idx] = standardDeviationMM / spacing[idx]
295 radiusPixel[idx] = int(standardDeviationPixel[idx] * radiusFactor) + 1
297 clippedSelectedSegmentLabelmap = self.
clipImage(selectedSegmentLabelmap, maskExtent, radiusPixel)
299 clippedSelectedSegmentLabelmap = selectedSegmentLabelmap
301 thresh = vtk.vtkImageThreshold()
302 thresh.SetInputData(clippedSelectedSegmentLabelmap)
303 thresh.ThresholdByLower(0)
305 thresh.SetOutValue(maxValue)
306 thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
308 gaussianFilter = vtk.vtkImageGaussianSmooth()
309 gaussianFilter.SetInputConnection(thresh.GetOutputPort())
310 gaussianFilter.SetStandardDeviation(*standardDeviationPixel)
311 gaussianFilter.SetRadiusFactor(radiusFactor)
313 thresh2 = vtk.vtkImageThreshold()
314 thresh2.SetInputConnection(gaussianFilter.GetOutputPort())
315 thresh2.ThresholdByUpper(int(maxValue / 2))
316 thresh2.SetInValue(1)
317 thresh2.SetOutValue(0)
318 thresh2.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
328 clippedSelectedSegmentLabelmap = self.
clipImage(selectedSegmentLabelmap, maskExtent, kernelSizePixel)
330 clippedSelectedSegmentLabelmap = selectedSegmentLabelmap
332 if smoothingMethod == MEDIAN:
334 smoothingFilter = vtk.vtkImageMedian3D()
335 smoothingFilter.SetInputData(clippedSelectedSegmentLabelmap)
341 thresh = vtk.vtkImageThreshold()
342 thresh.SetInputData(clippedSelectedSegmentLabelmap)
343 thresh.ThresholdByLower(0)
344 thresh.SetInValue(backgroundValue)
345 thresh.SetOutValue(labelValue)
346 thresh.SetOutputScalarType(clippedSelectedSegmentLabelmap.GetScalarType())
348 smoothingFilter = vtk.vtkImageOpenClose3D()
349 smoothingFilter.SetInputConnection(thresh.GetOutputPort())
350 if smoothingMethod == MORPHOLOGICAL_OPENING:
351 smoothingFilter.SetOpenValue(labelValue)
352 smoothingFilter.SetCloseValue(backgroundValue)
354 smoothingFilter.SetOpenValue(backgroundValue)
355 smoothingFilter.SetCloseValue(labelValue)
357 smoothingFilter.SetKernelSize(kernelSizePixel[0], kernelSizePixel[1], kernelSizePixel[2])
358 smoothingFilter.Update()
363 logging.error(
'apply: Failed to apply smoothing')
366 import vtkSegmentationCorePython
as vtkSegmentationCore
370 segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
371 visibleSegmentIds = vtk.vtkStringArray()
372 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(visibleSegmentIds)
373 if visibleSegmentIds.GetNumberOfValues() == 0:
374 logging.info(
"Smoothing operation skipped: there are no visible segments")
377 mergedImage = slicer.vtkOrientedImageData()
378 if not segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage,
379 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS_PADDED,
380 None, visibleSegmentIds):
381 logging.error(
'Failed to apply smoothing: cannot get list of visible segments')
384 segmentLabelValues = []
385 for i
in range(visibleSegmentIds.GetNumberOfValues()):
386 segmentId = visibleSegmentIds.GetValue(i)
387 segmentLabelValues.append([segmentId, i + 1])
390 ici = vtk.vtkImageChangeInformation()
391 ici.SetInputData(mergedImage)
392 ici.SetOutputSpacing(1, 1, 1)
393 ici.SetOutputOrigin(0, 0, 0)
399 convertToPolyData = vtk.vtkDiscreteMarchingCubes()
400 convertToPolyData.SetInputConnection(ici.GetOutputPort())
401 convertToPolyData.SetNumberOfContours(len(segmentLabelValues))
404 for segmentId, labelValue
in segmentLabelValues:
405 convertToPolyData.SetValue(contourIndex, labelValue)
409 smoothingFactor = self.scriptedEffect.doubleParameter(
"JointTaubinSmoothingFactor")
410 smoothingIterations = 100
411 passBand = pow(10.0, -4.0 * smoothingFactor)
412 smoother = vtk.vtkWindowedSincPolyDataFilter()
413 smoother.SetInputConnection(convertToPolyData.GetOutputPort())
414 smoother.SetNumberOfIterations(smoothingIterations)
415 smoother.BoundarySmoothingOff()
416 smoother.FeatureEdgeSmoothingOff()
417 smoother.SetFeatureAngle(90.0)
418 smoother.SetPassBand(passBand)
419 smoother.NonManifoldSmoothingOn()
420 smoother.NormalizeCoordinatesOn()
423 threshold = vtk.vtkThreshold()
424 threshold.SetInputConnection(smoother.GetOutputPort())
427 geometryFilter = vtk.vtkGeometryFilter()
428 geometryFilter.SetInputConnection(threshold.GetOutputPort())
431 polyDataToImageStencil = vtk.vtkPolyDataToImageStencil()
432 polyDataToImageStencil.SetInputConnection(geometryFilter.GetOutputPort())
433 polyDataToImageStencil.SetOutputSpacing(1, 1, 1)
434 polyDataToImageStencil.SetOutputOrigin(0, 0, 0)
435 polyDataToImageStencil.SetOutputWholeExtent(mergedImage.GetExtent())
438 stencil = vtk.vtkImageStencil()
439 emptyBinaryLabelMap = vtk.vtkImageData()
440 emptyBinaryLabelMap.SetExtent(mergedImage.GetExtent())
441 emptyBinaryLabelMap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
442 vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(emptyBinaryLabelMap, 0)
443 stencil.SetInputData(emptyBinaryLabelMap)
444 stencil.SetStencilConnection(polyDataToImageStencil.GetOutputPort())
445 stencil.ReverseStencilOn()
446 stencil.SetBackgroundValue(1)
448 imageToWorldMatrix = vtk.vtkMatrix4x4()
449 mergedImage.GetImageToWorldMatrix(imageToWorldMatrix)
454 oldOverwriteMode = self.scriptedEffect.parameterSetNode().GetOverwriteMode()
455 self.scriptedEffect.parameterSetNode().SetOverwriteMode(slicer.vtkMRMLSegmentEditorNode.OverwriteVisibleSegments)
456 for segmentId, labelValue
in segmentLabelValues:
457 threshold.SetLowerThreshold(labelValue)
458 threshold.SetUpperThreshold(labelValue)
459 threshold.SetThresholdFunction(vtk.vtkThreshold.THRESHOLD_BETWEEN)
461 smoothedBinaryLabelMap = slicer.vtkOrientedImageData()
462 smoothedBinaryLabelMap.ShallowCopy(stencil.GetOutput())
463 smoothedBinaryLabelMap.SetImageToWorldMatrix(imageToWorldMatrix)
464 self.scriptedEffect.modifySegmentByLabelmap(segmentationNode, segmentId, smoothedBinaryLabelMap,
465 slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet,
False)
466 self.scriptedEffect.parameterSetNode().SetOverwriteMode(oldOverwriteMode)
471 smoothingMethod = self.scriptedEffect.parameter(
"SmoothingMethod")
472 if smoothingMethod == JOINT_TAUBIN:
473 self.scriptedEffect.clearBrushes()
474 self.scriptedEffect.forceRender(viewWidget)
475 slicer.util.messageBox(
"Smoothing brush is not available for 'joint smoothing' method.")
478 modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
479 maskImage = slicer.vtkOrientedImageData()
480 maskImage.DeepCopy(modifierLabelmap)
481 maskExtent = self.scriptedEffect.paintBrushesIntoLabelmap(maskImage, viewWidget)
482 self.scriptedEffect.clearBrushes()
483 self.scriptedEffect.forceRender(viewWidget)
484 if maskExtent[0] > maskExtent[1]
or maskExtent[2] > maskExtent[3]
or maskExtent[4] > maskExtent[5]:
487 self.scriptedEffect.saveStateForUndo()