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)
231 segmentName=segmentationNode.GetSegmentation().GetSegment(segmentID).GetName()))
232 self.
scriptedEffect.parameterSetNode().SetSelectedSegmentID(segmentID)
235 self.
scriptedEffect.parameterSetNode().SetSelectedSegmentID(selectedStartSegmentID)
239 qt.QApplication.restoreOverrideCursor()
241 def clipImage(self, inputImage, maskExtent, margin):
242 clipper = vtk.vtkImageClip()
243 clipper.SetOutputWholeExtent(maskExtent[0] - margin[0], maskExtent[1] + margin[0],
244 maskExtent[2] - margin[1], maskExtent[3] + margin[1],
245 maskExtent[4] - margin[2], maskExtent[5] + margin[2])
246 clipper.SetInputData(inputImage)
247 clipper.SetClipData(
True)
249 clippedImage = slicer.vtkOrientedImageData()
250 clippedImage.ShallowCopy(clipper.GetOutput())
251 clippedImage.CopyDirections(inputImage)
254 def modifySelectedSegmentByLabelmap(self, smoothedImage, selectedSegmentLabelmap, modifierLabelmap, maskImage, maskExtent):
256 smoothedClippedSelectedSegmentLabelmap = slicer.vtkOrientedImageData()
257 smoothedClippedSelectedSegmentLabelmap.ShallowCopy(smoothedImage)
258 smoothedClippedSelectedSegmentLabelmap.CopyDirections(modifierLabelmap)
262 slicer.vtkOrientedImageDataResample.ApplyImageMask(smoothedClippedSelectedSegmentLabelmap, maskImage, fillValue,
False)
264 slicer.vtkOrientedImageDataResample.ModifyImage(maskImage, selectedSegmentLabelmap,
265 slicer.vtkOrientedImageDataResample.OPERATION_MAXIMUM)
266 slicer.vtkOrientedImageDataResample.ModifyImage(maskImage, smoothedClippedSelectedSegmentLabelmap,
267 slicer.vtkOrientedImageDataResample.OPERATION_MINIMUM)
269 updateExtent = [0, -1, 0, -1, 0, -1]
270 modifierExtent = modifierLabelmap.GetExtent()
272 updateExtent[2 * i] = min(maskExtent[2 * i], modifierExtent[2 * i])
273 updateExtent[2 * i + 1] = max(maskExtent[2 * i + 1], modifierExtent[2 * i + 1])
276 slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet,
279 modifierLabelmap.DeepCopy(smoothedImage)
280 self.
scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
282 def smoothSelectedSegment(self, maskImage=None, maskExtent=None):
286 selectedSegmentLabelmap = self.
scriptedEffect.selectedSegmentLabelmap()
288 smoothingMethod = self.
scriptedEffect.parameter(
"SmoothingMethod")
290 if smoothingMethod == GAUSSIAN:
293 standardDeviationMM = self.
scriptedEffect.doubleParameter(
"GaussianStandardDeviationMm")
294 spacing = modifierLabelmap.GetSpacing()
295 standardDeviationPixel = [1.0, 1.0, 1.0]
296 radiusPixel = [3, 3, 3]
298 standardDeviationPixel[idx] = standardDeviationMM / spacing[idx]
299 radiusPixel[idx] = int(standardDeviationPixel[idx] * radiusFactor) + 1
301 clippedSelectedSegmentLabelmap = self.
clipImage(selectedSegmentLabelmap, maskExtent, radiusPixel)
303 clippedSelectedSegmentLabelmap = selectedSegmentLabelmap
305 thresh = vtk.vtkImageThreshold()
306 thresh.SetInputData(clippedSelectedSegmentLabelmap)
307 thresh.ThresholdByLower(0)
309 thresh.SetOutValue(maxValue)
310 thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
312 gaussianFilter = vtk.vtkImageGaussianSmooth()
313 gaussianFilter.SetInputConnection(thresh.GetOutputPort())
314 gaussianFilter.SetStandardDeviation(*standardDeviationPixel)
315 gaussianFilter.SetRadiusFactor(radiusFactor)
317 thresh2 = vtk.vtkImageThreshold()
318 thresh2.SetInputConnection(gaussianFilter.GetOutputPort())
319 thresh2.ThresholdByUpper(int(maxValue / 2))
320 thresh2.SetInValue(1)
321 thresh2.SetOutValue(0)
322 thresh2.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
332 clippedSelectedSegmentLabelmap = self.
clipImage(selectedSegmentLabelmap, maskExtent, kernelSizePixel)
334 clippedSelectedSegmentLabelmap = selectedSegmentLabelmap
336 if smoothingMethod == MEDIAN:
338 smoothingFilter = vtk.vtkImageMedian3D()
339 smoothingFilter.SetInputData(clippedSelectedSegmentLabelmap)
345 thresh = vtk.vtkImageThreshold()
346 thresh.SetInputData(clippedSelectedSegmentLabelmap)
347 thresh.ThresholdByLower(0)
348 thresh.SetInValue(backgroundValue)
349 thresh.SetOutValue(labelValue)
350 thresh.SetOutputScalarType(clippedSelectedSegmentLabelmap.GetScalarType())
352 smoothingFilter = vtk.vtkImageOpenClose3D()
353 smoothingFilter.SetInputConnection(thresh.GetOutputPort())
354 if smoothingMethod == MORPHOLOGICAL_OPENING:
355 smoothingFilter.SetOpenValue(labelValue)
356 smoothingFilter.SetCloseValue(backgroundValue)
358 smoothingFilter.SetOpenValue(backgroundValue)
359 smoothingFilter.SetCloseValue(labelValue)
361 smoothingFilter.SetKernelSize(kernelSizePixel[0], kernelSizePixel[1], kernelSizePixel[2])
362 smoothingFilter.Update()
367 logging.error(
"apply: Failed to apply smoothing")
369 def smoothMultipleSegments(self, maskImage=None, maskExtent=None):
370 import vtkSegmentationCorePython
as vtkSegmentationCore
374 segmentationNode = self.
scriptedEffect.parameterSetNode().GetSegmentationNode()
375 visibleSegmentIds = vtk.vtkStringArray()
376 segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(visibleSegmentIds)
377 if visibleSegmentIds.GetNumberOfValues() == 0:
378 logging.info(
"Smoothing operation skipped: there are no visible segments")
381 mergedImage = slicer.vtkOrientedImageData()
382 if not segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage,
383 vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS_PADDED,
384 None, visibleSegmentIds):
385 logging.error(
"Failed to apply smoothing: cannot get list of visible segments")
388 segmentLabelValues = []
389 for i
in range(visibleSegmentIds.GetNumberOfValues()):
390 segmentId = visibleSegmentIds.GetValue(i)
391 segmentLabelValues.append([segmentId, i + 1])
394 ici = vtk.vtkImageChangeInformation()
395 ici.SetInputData(mergedImage)
396 ici.SetOutputSpacing(1, 1, 1)
397 ici.SetOutputOrigin(0, 0, 0)
403 convertToPolyData = vtk.vtkDiscreteMarchingCubes()
404 convertToPolyData.SetInputConnection(ici.GetOutputPort())
405 convertToPolyData.SetNumberOfContours(len(segmentLabelValues))
408 for segmentId, labelValue
in segmentLabelValues:
409 convertToPolyData.SetValue(contourIndex, labelValue)
413 smoothingFactor = self.
scriptedEffect.doubleParameter(
"JointTaubinSmoothingFactor")
414 smoothingIterations = 100
415 passBand = pow(10.0, -4.0 * smoothingFactor)
416 smoother = vtk.vtkWindowedSincPolyDataFilter()
417 smoother.SetInputConnection(convertToPolyData.GetOutputPort())
418 smoother.SetNumberOfIterations(smoothingIterations)
419 smoother.BoundarySmoothingOff()
420 smoother.FeatureEdgeSmoothingOff()
421 smoother.SetFeatureAngle(90.0)
422 smoother.SetPassBand(passBand)
423 smoother.NonManifoldSmoothingOn()
424 smoother.NormalizeCoordinatesOn()
427 threshold = vtk.vtkThreshold()
428 threshold.SetInputConnection(smoother.GetOutputPort())
431 geometryFilter = vtk.vtkGeometryFilter()
432 geometryFilter.SetInputConnection(threshold.GetOutputPort())
435 polyDataToImageStencil = vtk.vtkPolyDataToImageStencil()
436 polyDataToImageStencil.SetInputConnection(geometryFilter.GetOutputPort())
437 polyDataToImageStencil.SetOutputSpacing(1, 1, 1)
438 polyDataToImageStencil.SetOutputOrigin(0, 0, 0)
439 polyDataToImageStencil.SetOutputWholeExtent(mergedImage.GetExtent())
442 stencil = vtk.vtkImageStencil()
443 emptyBinaryLabelMap = vtk.vtkImageData()
444 emptyBinaryLabelMap.SetExtent(mergedImage.GetExtent())
445 emptyBinaryLabelMap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
446 vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(emptyBinaryLabelMap, 0)
447 stencil.SetInputData(emptyBinaryLabelMap)
448 stencil.SetStencilConnection(polyDataToImageStencil.GetOutputPort())
449 stencil.ReverseStencilOn()
450 stencil.SetBackgroundValue(1)
452 imageToWorldMatrix = vtk.vtkMatrix4x4()
453 mergedImage.GetImageToWorldMatrix(imageToWorldMatrix)
458 oldOverwriteMode = self.
scriptedEffect.parameterSetNode().GetOverwriteMode()
459 self.
scriptedEffect.parameterSetNode().SetOverwriteMode(slicer.vtkMRMLSegmentEditorNode.OverwriteVisibleSegments)
460 for segmentId, labelValue
in segmentLabelValues:
461 threshold.SetLowerThreshold(labelValue)
462 threshold.SetUpperThreshold(labelValue)
463 threshold.SetThresholdFunction(vtk.vtkThreshold.THRESHOLD_BETWEEN)
465 smoothedBinaryLabelMap = slicer.vtkOrientedImageData()
466 smoothedBinaryLabelMap.ShallowCopy(stencil.GetOutput())
467 smoothedBinaryLabelMap.SetImageToWorldMatrix(imageToWorldMatrix)
468 self.
scriptedEffect.modifySegmentByLabelmap(segmentationNode, segmentId, smoothedBinaryLabelMap,
469 slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet,
False)
470 self.
scriptedEffect.parameterSetNode().SetOverwriteMode(oldOverwriteMode)