24 def __init__(self, scriptedEffect):
25 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
26 scriptedEffect.name =
"Threshold"
27 scriptedEffect.title = _(
"Threshold")
48 self.
stencil = vtk.vtkPolyDataToImageStencil()
52 self.
reslice.AutoCropOutputOff()
54 self.
reslice.SetOutputOrigin(0, 0, 0)
55 self.
reslice.SetOutputSpacing(1, 1, 1)
56 self.
reslice.SetOutputDimensionality(3)
57 self.
reslice.GenerateStencilOutputOn()
156 def setupOptionsFrame(self):
158 self.
thresholdSliderLabel.setToolTip(_(
"Set the range of the background values that should be labeled."))
172 " Threshold above/below: sets the range from the computed value to maximum/minimum."
173 " Set as lower/upper value: only modifies one side of the threshold range."))
197 " Useful for iterating through all available methods."))
202 " Useful for iterating through all available methods."))
208 qSize = qt.QSizePolicy()
209 qSize.setHorizontalPolicy(qt.QSizePolicy.Expanding)
212 autoThresholdFrame = qt.QGridLayout()
219 autoThresholdGroupBox = ctk.ctkCollapsibleGroupBox()
220 autoThresholdGroupBox.setTitle(_(
"Automatic threshold"))
221 autoThresholdGroupBox.setLayout(autoThresholdFrame)
222 autoThresholdGroupBox.collapsed =
True
225 histogramFrame = qt.QVBoxLayout()
227 histogramBrushFrame = qt.QHBoxLayout()
228 histogramFrame.addLayout(histogramBrushFrame)
264 histogramBrushFrame.addStretch()
313 histogramItemFrame = qt.QHBoxLayout()
314 histogramFrame.addLayout(histogramItemFrame)
319 lowerGroupBox = qt.QGroupBox(_(
"Lower"))
320 lowerHistogramLayout = qt.QHBoxLayout()
321 lowerHistogramLayout.setContentsMargins(0, 3, 0, 3)
322 lowerGroupBox.setLayout(lowerHistogramLayout)
323 histogramItemFrame.addWidget(lowerGroupBox)
352 upperGroupBox = qt.QGroupBox(_(
"Upper"))
353 upperHistogramLayout = qt.QHBoxLayout()
354 upperHistogramLayout.setContentsMargins(0, 3, 0, 3)
355 upperGroupBox.setLayout(upperHistogramLayout)
356 histogramItemFrame.addWidget(upperGroupBox)
382 histogramGroupBox = ctk.ctkCollapsibleGroupBox()
383 histogramGroupBox.setTitle(_(
"Local histogram"))
384 histogramGroupBox.setLayout(histogramFrame)
385 histogramGroupBox.collapsed =
True
389 self.
useForPaintButton.setToolTip(_(
"Use specified intensity range for masking and switch to Paint effect."))
393 self.
applyButton.objectName = self.__class__.__name__ +
"Apply"
394 self.
applyButton.setToolTip(_(
"Fill selected segment in regions that are in the specified intensity range."))
431 def updateGUIFromMRML(self):
447 histogramBrushType = self.
scriptedEffect.parameter(HISTOGRAM_BRUSH_TYPE_PARAMETER_NAME)
448 self.
boxROIButton.checked = histogramBrushType == HISTOGRAM_BRUSH_TYPE_BOX
449 self.
circleROIButton.checked = histogramBrushType == HISTOGRAM_BRUSH_TYPE_CIRCLE
450 self.
drawROIButton.checked = histogramBrushType == HISTOGRAM_BRUSH_TYPE_DRAW
451 self.
lineROIButton.checked = histogramBrushType == HISTOGRAM_BRUSH_TYPE_LINE
453 histogramSetModeLower = self.
scriptedEffect.parameter(HISTOGRAM_SET_LOWER_PARAMETER_NAME)
458 histogramSetModeUpper = self.
scriptedEffect.parameter(HISTOGRAM_SET_UPPER_PARAMETER_NAME)
465 def updateMRMLFromGUI(self):
466 with slicer.util.NodeModify(self.
scriptedEffect.parameterSetNode()):
472 self.
scriptedEffect.setParameter(
"AutoThresholdMethod", autoThresholdMethod)
476 self.
scriptedEffect.setParameter(
"AutoThresholdMode", autoThresholdMode)
478 histogramParameterChanged =
False
480 histogramBrushType = HISTOGRAM_BRUSH_TYPE_CIRCLE
482 histogramBrushType = HISTOGRAM_BRUSH_TYPE_BOX
484 histogramBrushType = HISTOGRAM_BRUSH_TYPE_CIRCLE
486 histogramBrushType = HISTOGRAM_BRUSH_TYPE_DRAW
488 histogramBrushType = HISTOGRAM_BRUSH_TYPE_LINE
490 if histogramBrushType != self.
scriptedEffect.parameter(HISTOGRAM_BRUSH_TYPE_PARAMETER_NAME):
491 self.
scriptedEffect.setParameter(HISTOGRAM_BRUSH_TYPE_PARAMETER_NAME, histogramBrushType)
492 histogramParameterChanged =
True
494 histogramSetModeLower = HISTOGRAM_SET_LOWER
496 histogramSetModeLower = HISTOGRAM_SET_MINIMUM
498 histogramSetModeLower = HISTOGRAM_SET_LOWER
500 histogramSetModeLower = HISTOGRAM_SET_AVERAGE
501 if histogramSetModeLower != self.
scriptedEffect.parameter(HISTOGRAM_SET_LOWER_PARAMETER_NAME):
502 self.
scriptedEffect.setParameter(HISTOGRAM_SET_LOWER_PARAMETER_NAME, histogramSetModeLower)
503 histogramParameterChanged =
True
505 histogramSetModeUpper = HISTOGRAM_SET_UPPER
507 histogramSetModeUpper = HISTOGRAM_SET_AVERAGE
509 histogramSetModeUpper = HISTOGRAM_SET_UPPER
511 histogramSetModeUpper = HISTOGRAM_SET_MAXIMUM
512 if histogramSetModeUpper != self.
scriptedEffect.parameter(HISTOGRAM_SET_UPPER_PARAMETER_NAME):
513 self.
scriptedEffect.setParameter(HISTOGRAM_SET_UPPER_PARAMETER_NAME, histogramSetModeUpper)
514 histogramParameterChanged =
True
516 if histogramParameterChanged:
552 def autoThreshold(self, autoThresholdMethod, autoThresholdMode):
553 if autoThresholdMethod == METHOD_HUANG:
555 elif autoThresholdMethod == METHOD_INTERMODES:
557 elif autoThresholdMethod == METHOD_ISO_DATA:
559 elif autoThresholdMethod == METHOD_KITTLER_ILLINGWORTH:
561 elif autoThresholdMethod == METHOD_LI:
563 elif autoThresholdMethod == METHOD_MAXIMUM_ENTROPY:
565 elif autoThresholdMethod == METHOD_MOMENTS:
567 elif autoThresholdMethod == METHOD_OTSU:
569 elif autoThresholdMethod == METHOD_RENYI_ENTROPY:
571 elif autoThresholdMethod == METHOD_SHANBHAG:
573 elif autoThresholdMethod == METHOD_TRIANGLE:
575 elif autoThresholdMethod == METHOD_YEN:
578 logging.error(f
"Unknown AutoThresholdMethod {autoThresholdMethod}")
586 sourceVolumeMin, sourceVolumeMax = masterImageData.GetScalarRange()
588 if autoThresholdMode == MODE_SET_UPPER:
589 self.
scriptedEffect.setParameter(
"MaximumThreshold", computedThreshold)
590 elif autoThresholdMode == MODE_SET_LOWER:
591 self.
scriptedEffect.setParameter(
"MinimumThreshold", computedThreshold)
592 elif autoThresholdMode == MODE_SET_MIN_UPPER:
593 self.
scriptedEffect.setParameter(
"MinimumThreshold", sourceVolumeMin)
594 self.
scriptedEffect.setParameter(
"MaximumThreshold", computedThreshold)
595 elif autoThresholdMode == MODE_SET_LOWER_MAX:
596 self.
scriptedEffect.setParameter(
"MinimumThreshold", computedThreshold)
597 self.
scriptedEffect.setParameter(
"MaximumThreshold", sourceVolumeMax)
599 logging.error(f
"Unknown AutoThresholdMode {autoThresholdMode}")
610 originalImageToWorldMatrix = vtk.vtkMatrix4x4()
611 modifierLabelmap.GetImageToWorldMatrix(originalImageToWorldMatrix)
619 thresh = vtk.vtkImageThreshold()
620 thresh.SetInputData(masterImageData)
621 thresh.ThresholdBetween(min, max)
623 thresh.SetOutValue(0)
624 thresh.SetOutputScalarType(modifierLabelmap.GetScalarType())
626 modifierLabelmap.DeepCopy(thresh.GetOutput())
628 logging.error(
"apply: Failed to threshold source volume!")
632 self.
scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
653 def updatePreviewDisplayPipelines(self):
655 layoutManager = slicer.app.layoutManager()
659 sliceWidgetPipelinesToKeep = []
660 for sliceViewName
in sliceViewNames:
662 sliceWidget = layoutManager.sliceWidget(sliceViewName)
664 background_id = sliceWidget.sliceLogic().GetSliceCompositeNode().GetBackgroundVolumeID()
665 foreground_id = sliceWidget.sliceLogic().GetSliceCompositeNode().GetForegroundVolumeID()
666 volume_in_slice_view = bool(background_id
or foreground_id)
667 if not sliceWidget.isVisible()
or not volume_in_slice_view
or not self.
scriptedEffect.segmentationDisplayableInView(sliceWidget.mrmlSliceNode()):
674 segmentationDisplayableManager = sliceWidget.sliceView().displayableManagerByClassName(
"vtkMRMLSegmentationsDisplayableManager2D")
675 existingSegmentRendererTag = segmentationDisplayableManager.GetCustomSegmentRendererTag(
677 if existingSegmentRendererTag != 0:
678 if (
not pipeline)
or (pipeline.customRendererTag != existingSegmentRendererTag):
682 sliceWidgetPipelinesToKeep.append(sliceWidget)
690 logging.error(
"updatePreviewDisplayPipelines: Failed to get renderer!")
695 pipeline.customRendererTag = segmentationDisplayableManager.AddCustomSegmentRenderer(
700 for sliceWidget, pipeline
in previewPipelines:
701 if sliceWidget
in sliceWidgetPipelinesToKeep:
704 segmentationDisplayableManager = sliceWidget.sliceView().displayableManagerByClassName(
"vtkMRMLSegmentationsDisplayableManager2D")
705 segmentationDisplayableManager.RemoveCustomSegmentRenderer(pipeline.customRendererTag)
756 def processInteractionEvents(self, callerInteractor, eventId, viewWidget):
773 if masterImageData
is None:
777 if viewWidget.className() !=
"qMRMLSliceWidget":
780 anyModifierKeyPressed = callerInteractor.GetShiftKey()
or callerInteractor.GetControlKey()
or callerInteractor.GetAltKey()
783 if eventId == vtk.vtkCommand.LeftButtonPressEvent
and not anyModifierKeyPressed:
789 xy = callerInteractor.GetEventPosition()
790 ras = self.
xyToRas(xy, viewWidget)
792 if eventId == vtk.vtkCommand.LeftButtonPressEvent
and not anyModifierKeyPressed:
797 elif eventId == vtk.vtkCommand.LeftButtonReleaseEvent:
801 elif eventId == vtk.vtkCommand.MouseMoveEvent:
844 def getSourceVolumeLayerLogic(self, sliceWidget):
845 sourceVolumeNode = self.
scriptedEffect.parameterSetNode().GetSourceVolumeNode()
846 sliceLogic = sliceWidget.sliceLogic()
848 backgroundLogic = sliceLogic.GetBackgroundLayer()
849 backgroundVolumeNode = backgroundLogic.GetVolumeNode()
850 if sourceVolumeNode == backgroundVolumeNode:
851 return backgroundLogic
853 foregroundLogic = sliceLogic.GetForegroundLayer()
854 foregroundVolumeNode = foregroundLogic.GetVolumeNode()
855 if sourceVolumeNode == foregroundVolumeNode:
856 return foregroundLogic
858 foregroundOpacity = 0.0
859 if foregroundVolumeNode:
860 compositeNode = sliceLogic.GetSliceCompositeNode()
861 foregroundOpacity = compositeNode.GetForegroundOpacity()
863 if foregroundOpacity > 0.5:
864 return foregroundLogic
866 return backgroundLogic
868 def updateHistogram(self):
881 brushBounds = brushPolydata.GetBounds()
882 brushExtent = [0, -1, 0, -1, 0, -1]
884 brushExtent[2 * i] = vtk.vtkMath.Floor(brushBounds[2 * i])
885 brushExtent[2 * i + 1] = vtk.vtkMath.Ceil(brushBounds[2 * i + 1])
886 if brushExtent[0] > brushExtent[1]
or brushExtent[2] > brushExtent[3]
or brushExtent[4] > brushExtent[5]:
891 self.
reslice.SetInputConnection(layerLogic.GetReslice().GetInputConnection(0, 0))
892 self.
reslice.SetResliceTransform(layerLogic.GetReslice().GetResliceTransform())
893 self.
reslice.SetInterpolationMode(layerLogic.GetReslice().GetInterpolationMode())
894 self.
reslice.SetOutputExtent(brushExtent)
896 maxNumberOfBins = 1000
898 scalarRange = masterImageData.GetScalarRange()
899 scalarType = masterImageData.GetScalarType()
900 if scalarType == vtk.VTK_FLOAT
or scalarType == vtk.VTK_DOUBLE:
901 numberOfBins = maxNumberOfBins
903 numberOfBins = int(scalarRange[1] - scalarRange[0]) + 1
904 numberOfBins = min(numberOfBins, maxNumberOfBins)
905 binSpacing = (scalarRange[1] - scalarRange[0] + 1) / numberOfBins
907 self.
imageAccumulate.SetComponentExtent(0, numberOfBins - 1, 0, 0, 0, 0)
908 self.
imageAccumulate.SetComponentSpacing(binSpacing, binSpacing, binSpacing)
909 self.
imageAccumulate.SetComponentOrigin(scalarRange[0], scalarRange[0], scalarRange[0])
914 tableSize = self.
imageAccumulate.GetOutput().GetPointData().GetScalars().GetNumberOfTuples()
915 for i
in range(tableSize):
916 value = self.
imageAccumulate.GetOutput().GetPointData().GetScalars().GetTuple1(i)
930 lower = min(startX, endX)
931 average = (startX + endX) / 2.0
932 upper = max(startX, endX)
950 minimumThreshold = lower
951 maximumThreshold = upper
953 histogramSetModeLower = self.
scriptedEffect.parameter(HISTOGRAM_SET_LOWER_PARAMETER_NAME)
954 if histogramSetModeLower == HISTOGRAM_SET_MINIMUM:
955 minimumThreshold = scalarRange[0]
956 elif histogramSetModeLower == HISTOGRAM_SET_LOWER:
957 minimumThreshold = lower
958 elif histogramSetModeLower == HISTOGRAM_SET_AVERAGE:
959 minimumThreshold = average
961 histogramSetModeUpper = self.
scriptedEffect.parameter(HISTOGRAM_SET_UPPER_PARAMETER_NAME)
962 if histogramSetModeUpper == HISTOGRAM_SET_AVERAGE:
963 maximumThreshold = average
964 elif histogramSetModeUpper == HISTOGRAM_SET_UPPER:
965 maximumThreshold = upper
966 elif histogramSetModeUpper == HISTOGRAM_SET_MAXIMUM:
967 maximumThreshold = scalarRange[1]
969 self.
scriptedEffect.setParameter(
"MinimumThreshold", minimumThreshold)
970 self.
scriptedEffect.setParameter(
"MaximumThreshold", maximumThreshold)
972 def updateHistogramBackground(self):
976 if masterImageData
is None:
979 scalarRange = masterImageData.GetScalarRange()
984 low = max(scalarRange[0] + epsilon, low)
985 upper = min(scalarRange[1] - epsilon, upper)