200 def onApply(self, maskImage=None, maskExtent=None):
201 """maskImage: contains nonzero where smoothing will be applied"""
202 smoothingMethod = self.
scriptedEffect.parameter(
"SmoothingMethod")
203 applyToAllVisibleSegments = int(self.
scriptedEffect.parameter(
"ApplyToAllVisibleSegments")) != 0 \
204 if self.
scriptedEffect.parameter(
"ApplyToAllVisibleSegments")
else False
206 if smoothingMethod != JOINT_TAUBIN:
213 qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)
216 if smoothingMethod == JOINT_TAUBIN:
218 elif applyToAllVisibleSegments:
220 inputSegmentIDs = vtk.vtkStringArray()
221 segmentationNode = self.
scriptedEffect.parameterSetNode().GetSegmentationNode()
222 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(inputSegmentIDs)
224 selectedStartSegmentID = self.
scriptedEffect.parameterSetNode().GetSelectedSegmentID()
225 if inputSegmentIDs.GetNumberOfValues() == 0:
226 logging.info(
"Smoothing operation skipped: there are no visible segments.")
228 for index
in range(inputSegmentIDs.GetNumberOfValues()):
229 segmentID = inputSegmentIDs.GetValue(index)
230 self.
showStatusMessage(_(
"Smoothing {segmentName}...").format(segmentationNode.GetSegmentation().GetSegment(segmentID).GetName()))
231 self.
scriptedEffect.parameterSetNode().SetSelectedSegmentID(segmentID)
234 self.
scriptedEffect.parameterSetNode().SetSelectedSegmentID(selectedStartSegmentID)
238 qt.QApplication.restoreOverrideCursor()
240 def clipImage(self, inputImage, maskExtent, margin):
241 clipper = vtk.vtkImageClip()
242 clipper.SetOutputWholeExtent(maskExtent[0] - margin[0], maskExtent[1] + margin[0],
243 maskExtent[2] - margin[1], maskExtent[3] + margin[1],
244 maskExtent[4] - margin[2], maskExtent[5] + margin[2])
245 clipper.SetInputData(inputImage)
246 clipper.SetClipData(
True)
248 clippedImage = slicer.vtkOrientedImageData()
249 clippedImage.ShallowCopy(clipper.GetOutput())
250 clippedImage.CopyDirections(inputImage)
253 def modifySelectedSegmentByLabelmap(self, smoothedImage, selectedSegmentLabelmap, modifierLabelmap, maskImage, maskExtent):
255 smoothedClippedSelectedSegmentLabelmap = slicer.vtkOrientedImageData()
256 smoothedClippedSelectedSegmentLabelmap.ShallowCopy(smoothedImage)
257 smoothedClippedSelectedSegmentLabelmap.CopyDirections(modifierLabelmap)
261 slicer.vtkOrientedImageDataResample.ApplyImageMask(smoothedClippedSelectedSegmentLabelmap, maskImage, fillValue,
False)
263 slicer.vtkOrientedImageDataResample.ModifyImage(maskImage, selectedSegmentLabelmap,
264 slicer.vtkOrientedImageDataResample.OPERATION_MAXIMUM)
265 slicer.vtkOrientedImageDataResample.ModifyImage(maskImage, smoothedClippedSelectedSegmentLabelmap,
266 slicer.vtkOrientedImageDataResample.OPERATION_MINIMUM)
268 updateExtent = [0, -1, 0, -1, 0, -1]
269 modifierExtent = modifierLabelmap.GetExtent()
271 updateExtent[2 * i] = min(maskExtent[2 * i], modifierExtent[2 * i])
272 updateExtent[2 * i + 1] = max(maskExtent[2 * i + 1], modifierExtent[2 * i + 1])
275 slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet,
278 modifierLabelmap.DeepCopy(smoothedImage)
279 self.
scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
281 def smoothSelectedSegment(self, maskImage=None, maskExtent=None):
285 selectedSegmentLabelmap = self.
scriptedEffect.selectedSegmentLabelmap()
287 smoothingMethod = self.
scriptedEffect.parameter(
"SmoothingMethod")
289 if smoothingMethod == GAUSSIAN:
292 standardDeviationMM = self.
scriptedEffect.doubleParameter(
"GaussianStandardDeviationMm")
293 spacing = modifierLabelmap.GetSpacing()
294 standardDeviationPixel = [1.0, 1.0, 1.0]
295 radiusPixel = [3, 3, 3]
297 standardDeviationPixel[idx] = standardDeviationMM / spacing[idx]
298 radiusPixel[idx] = int(standardDeviationPixel[idx] * radiusFactor) + 1
300 clippedSelectedSegmentLabelmap = self.
clipImage(selectedSegmentLabelmap, maskExtent, radiusPixel)
302 clippedSelectedSegmentLabelmap = selectedSegmentLabelmap
304 thresh = vtk.vtkImageThreshold()
305 thresh.SetInputData(clippedSelectedSegmentLabelmap)
306 thresh.ThresholdByLower(0)
308 thresh.SetOutValue(maxValue)
309 thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
311 gaussianFilter = vtk.vtkImageGaussianSmooth()
312 gaussianFilter.SetInputConnection(thresh.GetOutputPort())
313 gaussianFilter.SetStandardDeviation(*standardDeviationPixel)
314 gaussianFilter.SetRadiusFactor(radiusFactor)
316 thresh2 = vtk.vtkImageThreshold()
317 thresh2.SetInputConnection(gaussianFilter.GetOutputPort())
318 thresh2.ThresholdByUpper(int(maxValue / 2))
319 thresh2.SetInValue(1)
320 thresh2.SetOutValue(0)
321 thresh2.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
331 clippedSelectedSegmentLabelmap = self.
clipImage(selectedSegmentLabelmap, maskExtent, kernelSizePixel)
333 clippedSelectedSegmentLabelmap = selectedSegmentLabelmap
335 if smoothingMethod == MEDIAN:
337 smoothingFilter = vtk.vtkImageMedian3D()
338 smoothingFilter.SetInputData(clippedSelectedSegmentLabelmap)
344 thresh = vtk.vtkImageThreshold()
345 thresh.SetInputData(clippedSelectedSegmentLabelmap)
346 thresh.ThresholdByLower(0)
347 thresh.SetInValue(backgroundValue)
348 thresh.SetOutValue(labelValue)
349 thresh.SetOutputScalarType(clippedSelectedSegmentLabelmap.GetScalarType())
351 smoothingFilter = vtk.vtkImageOpenClose3D()
352 smoothingFilter.SetInputConnection(thresh.GetOutputPort())
353 if smoothingMethod == MORPHOLOGICAL_OPENING:
354 smoothingFilter.SetOpenValue(labelValue)
355 smoothingFilter.SetCloseValue(backgroundValue)
357 smoothingFilter.SetOpenValue(backgroundValue)
358 smoothingFilter.SetCloseValue(labelValue)
360 smoothingFilter.SetKernelSize(kernelSizePixel[0], kernelSizePixel[1], kernelSizePixel[2])
361 smoothingFilter.Update()
366 logging.error(
"apply: Failed to apply smoothing")
368 def smoothMultipleSegments(self, maskImage=None, maskExtent=None):
369 import vtkSegmentationCorePython
as vtkSegmentationCore
373 segmentationNode = self.
scriptedEffect.parameterSetNode().GetSegmentationNode()
374 visibleSegmentIds = vtk.vtkStringArray()
375 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(visibleSegmentIds)
376 if visibleSegmentIds.GetNumberOfValues() == 0:
377 logging.info(
"Smoothing operation skipped: there are no visible segments")
380 mergedImage = slicer.vtkOrientedImageData()
381 if not segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage,
382 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS_PADDED,
383 None, visibleSegmentIds):
384 logging.error(
"Failed to apply smoothing: cannot get list of visible segments")
387 segmentLabelValues = []
388 for i
in range(visibleSegmentIds.GetNumberOfValues()):
389 segmentId = visibleSegmentIds.GetValue(i)
390 segmentLabelValues.append([segmentId, i + 1])
393 ici = vtk.vtkImageChangeInformation()
394 ici.SetInputData(mergedImage)
395 ici.SetOutputSpacing(1, 1, 1)
396 ici.SetOutputOrigin(0, 0, 0)
402 convertToPolyData = vtk.vtkDiscreteMarchingCubes()
403 convertToPolyData.SetInputConnection(ici.GetOutputPort())
404 convertToPolyData.SetNumberOfContours(len(segmentLabelValues))
407 for segmentId, labelValue
in segmentLabelValues:
408 convertToPolyData.SetValue(contourIndex, labelValue)
412 smoothingFactor = self.
scriptedEffect.doubleParameter(
"JointTaubinSmoothingFactor")
413 smoothingIterations = 100
414 passBand = pow(10.0, -4.0 * smoothingFactor)
415 smoother = vtk.vtkWindowedSincPolyDataFilter()
416 smoother.SetInputConnection(convertToPolyData.GetOutputPort())
417 smoother.SetNumberOfIterations(smoothingIterations)
418 smoother.BoundarySmoothingOff()
419 smoother.FeatureEdgeSmoothingOff()
420 smoother.SetFeatureAngle(90.0)
421 smoother.SetPassBand(passBand)
422 smoother.NonManifoldSmoothingOn()
423 smoother.NormalizeCoordinatesOn()
426 threshold = vtk.vtkThreshold()
427 threshold.SetInputConnection(smoother.GetOutputPort())
430 geometryFilter = vtk.vtkGeometryFilter()
431 geometryFilter.SetInputConnection(threshold.GetOutputPort())
434 polyDataToImageStencil = vtk.vtkPolyDataToImageStencil()
435 polyDataToImageStencil.SetInputConnection(geometryFilter.GetOutputPort())
436 polyDataToImageStencil.SetOutputSpacing(1, 1, 1)
437 polyDataToImageStencil.SetOutputOrigin(0, 0, 0)
438 polyDataToImageStencil.SetOutputWholeExtent(mergedImage.GetExtent())
441 stencil = vtk.vtkImageStencil()
442 emptyBinaryLabelMap = vtk.vtkImageData()
443 emptyBinaryLabelMap.SetExtent(mergedImage.GetExtent())
444 emptyBinaryLabelMap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
445 vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(emptyBinaryLabelMap, 0)
446 stencil.SetInputData(emptyBinaryLabelMap)
447 stencil.SetStencilConnection(polyDataToImageStencil.GetOutputPort())
448 stencil.ReverseStencilOn()
449 stencil.SetBackgroundValue(1)
451 imageToWorldMatrix = vtk.vtkMatrix4x4()
452 mergedImage.GetImageToWorldMatrix(imageToWorldMatrix)
457 oldOverwriteMode = self.
scriptedEffect.parameterSetNode().GetOverwriteMode()
458 self.
scriptedEffect.parameterSetNode().SetOverwriteMode(slicer.vtkMRMLSegmentEditorNode.OverwriteVisibleSegments)
459 for segmentId, labelValue
in segmentLabelValues:
460 threshold.SetLowerThreshold(labelValue)
461 threshold.SetUpperThreshold(labelValue)
462 threshold.SetThresholdFunction(vtk.vtkThreshold.THRESHOLD_BETWEEN)
464 smoothedBinaryLabelMap = slicer.vtkOrientedImageData()
465 smoothedBinaryLabelMap.ShallowCopy(stencil.GetOutput())
466 smoothedBinaryLabelMap.SetImageToWorldMatrix(imageToWorldMatrix)
467 self.
scriptedEffect.modifySegmentByLabelmap(segmentationNode, segmentId, smoothedBinaryLabelMap,
468 slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet,
False)
469 self.
scriptedEffect.parameterSetNode().SetOverwriteMode(oldOverwriteMode)