23 def __init__(self, scriptedEffect):
24 AbstractScriptedSegmentEditorEffect.__init__(self, scriptedEffect)
25 scriptedEffect.name =
"Threshold"
26 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()
153 def setupOptionsFrame(self):
155 self.
thresholdSliderLabel.setToolTip(_(
"Set the range of the background values that should be labeled."))
169 " Threshold above/below: sets the range from the computed value to maximum/minimum."
170 " Set as lower/upper value: only modifies one side of the threshold range."))
194 " Useful for iterating through all available methods."))
199 " Useful for iterating through all available methods."))
205 qSize = qt.QSizePolicy()
206 qSize.setHorizontalPolicy(qt.QSizePolicy.Expanding)
209 autoThresholdFrame = qt.QGridLayout()
216 autoThresholdGroupBox = ctk.ctkCollapsibleGroupBox()
217 autoThresholdGroupBox.setTitle(_(
"Automatic threshold"))
218 autoThresholdGroupBox.setLayout(autoThresholdFrame)
219 autoThresholdGroupBox.collapsed =
True
222 histogramFrame = qt.QVBoxLayout()
224 histogramBrushFrame = qt.QHBoxLayout()
225 histogramFrame.addLayout(histogramBrushFrame)
261 histogramBrushFrame.addStretch()
310 histogramItemFrame = qt.QHBoxLayout()
311 histogramFrame.addLayout(histogramItemFrame)
316 lowerGroupBox = qt.QGroupBox(_(
"Lower"))
317 lowerHistogramLayout = qt.QHBoxLayout()
318 lowerHistogramLayout.setContentsMargins(0, 3, 0, 3)
319 lowerGroupBox.setLayout(lowerHistogramLayout)
320 histogramItemFrame.addWidget(lowerGroupBox)
349 upperGroupBox = qt.QGroupBox(_(
"Upper"))
350 upperHistogramLayout = qt.QHBoxLayout()
351 upperHistogramLayout.setContentsMargins(0, 3, 0, 3)
352 upperGroupBox.setLayout(upperHistogramLayout)
353 histogramItemFrame.addWidget(upperGroupBox)
379 histogramGroupBox = ctk.ctkCollapsibleGroupBox()
380 histogramGroupBox.setTitle(_(
"Local histogram"))
381 histogramGroupBox.setLayout(histogramFrame)
382 histogramGroupBox.collapsed =
True
386 self.
useForPaintButton.setToolTip(_(
"Use specified intensity range for masking and switch to Paint effect."))
390 self.
applyButton.objectName = self.__class__.__name__ +
"Apply"
391 self.
applyButton.setToolTip(_(
"Fill selected segment in regions that are in the specified intensity range."))
427 def updateGUIFromMRML(self):
443 histogramBrushType = self.
scriptedEffect.parameter(HISTOGRAM_BRUSH_TYPE_PARAMETER_NAME)
444 self.
boxROIButton.checked = histogramBrushType == HISTOGRAM_BRUSH_TYPE_BOX
445 self.
circleROIButton.checked = histogramBrushType == HISTOGRAM_BRUSH_TYPE_CIRCLE
446 self.
drawROIButton.checked = histogramBrushType == HISTOGRAM_BRUSH_TYPE_DRAW
447 self.
lineROIButton.checked = histogramBrushType == HISTOGRAM_BRUSH_TYPE_LINE
449 histogramSetModeLower = self.
scriptedEffect.parameter(HISTOGRAM_SET_LOWER_PARAMETER_NAME)
454 histogramSetModeUpper = self.
scriptedEffect.parameter(HISTOGRAM_SET_UPPER_PARAMETER_NAME)
461 def updateMRMLFromGUI(self):
462 with slicer.util.NodeModify(self.
scriptedEffect.parameterSetNode()):
468 self.
scriptedEffect.setParameter(
"AutoThresholdMethod", autoThresholdMethod)
472 self.
scriptedEffect.setParameter(
"AutoThresholdMode", autoThresholdMode)
474 histogramParameterChanged =
False
476 histogramBrushType = HISTOGRAM_BRUSH_TYPE_CIRCLE
478 histogramBrushType = HISTOGRAM_BRUSH_TYPE_BOX
480 histogramBrushType = HISTOGRAM_BRUSH_TYPE_CIRCLE
482 histogramBrushType = HISTOGRAM_BRUSH_TYPE_DRAW
484 histogramBrushType = HISTOGRAM_BRUSH_TYPE_LINE
486 if histogramBrushType != self.
scriptedEffect.parameter(HISTOGRAM_BRUSH_TYPE_PARAMETER_NAME):
487 self.
scriptedEffect.setParameter(HISTOGRAM_BRUSH_TYPE_PARAMETER_NAME, histogramBrushType)
488 histogramParameterChanged =
True
490 histogramSetModeLower = HISTOGRAM_SET_LOWER
492 histogramSetModeLower = HISTOGRAM_SET_MINIMUM
494 histogramSetModeLower = HISTOGRAM_SET_LOWER
496 histogramSetModeLower = HISTOGRAM_SET_AVERAGE
497 if histogramSetModeLower != self.
scriptedEffect.parameter(HISTOGRAM_SET_LOWER_PARAMETER_NAME):
498 self.
scriptedEffect.setParameter(HISTOGRAM_SET_LOWER_PARAMETER_NAME, histogramSetModeLower)
499 histogramParameterChanged =
True
501 histogramSetModeUpper = HISTOGRAM_SET_UPPER
503 histogramSetModeUpper = HISTOGRAM_SET_AVERAGE
505 histogramSetModeUpper = HISTOGRAM_SET_UPPER
507 histogramSetModeUpper = HISTOGRAM_SET_MAXIMUM
508 if histogramSetModeUpper != self.
scriptedEffect.parameter(HISTOGRAM_SET_UPPER_PARAMETER_NAME):
509 self.
scriptedEffect.setParameter(HISTOGRAM_SET_UPPER_PARAMETER_NAME, histogramSetModeUpper)
510 histogramParameterChanged =
True
512 if histogramParameterChanged:
548 def autoThreshold(self, autoThresholdMethod, autoThresholdMode):
549 if autoThresholdMethod == METHOD_HUANG:
551 elif autoThresholdMethod == METHOD_INTERMODES:
553 elif autoThresholdMethod == METHOD_ISO_DATA:
555 elif autoThresholdMethod == METHOD_KITTLER_ILLINGWORTH:
557 elif autoThresholdMethod == METHOD_LI:
559 elif autoThresholdMethod == METHOD_MAXIMUM_ENTROPY:
561 elif autoThresholdMethod == METHOD_MOMENTS:
563 elif autoThresholdMethod == METHOD_OTSU:
565 elif autoThresholdMethod == METHOD_RENYI_ENTROPY:
567 elif autoThresholdMethod == METHOD_SHANBHAG:
569 elif autoThresholdMethod == METHOD_TRIANGLE:
571 elif autoThresholdMethod == METHOD_YEN:
574 logging.error(f
"Unknown AutoThresholdMethod {autoThresholdMethod}")
582 sourceVolumeMin, sourceVolumeMax = masterImageData.GetScalarRange()
584 if autoThresholdMode == MODE_SET_UPPER:
585 self.
scriptedEffect.setParameter(
"MaximumThreshold", computedThreshold)
586 elif autoThresholdMode == MODE_SET_LOWER:
587 self.
scriptedEffect.setParameter(
"MinimumThreshold", computedThreshold)
588 elif autoThresholdMode == MODE_SET_MIN_UPPER:
589 self.
scriptedEffect.setParameter(
"MinimumThreshold", sourceVolumeMin)
590 self.
scriptedEffect.setParameter(
"MaximumThreshold", computedThreshold)
591 elif autoThresholdMode == MODE_SET_LOWER_MAX:
592 self.
scriptedEffect.setParameter(
"MinimumThreshold", computedThreshold)
593 self.
scriptedEffect.setParameter(
"MaximumThreshold", sourceVolumeMax)
595 logging.error(f
"Unknown AutoThresholdMode {autoThresholdMode}")
606 originalImageToWorldMatrix = vtk.vtkMatrix4x4()
607 modifierLabelmap.GetImageToWorldMatrix(originalImageToWorldMatrix)
615 thresh = vtk.vtkImageThreshold()
616 thresh.SetInputData(masterImageData)
617 thresh.ThresholdBetween(min, max)
619 thresh.SetOutValue(0)
620 thresh.SetOutputScalarType(modifierLabelmap.GetScalarType())
622 modifierLabelmap.DeepCopy(thresh.GetOutput())
624 logging.error(
"apply: Failed to threshold source volume!")
628 self.
scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
675 segmentationNode = self.
scriptedEffect.parameterSetNode().GetSegmentationNode()
676 if not segmentationNode:
679 displayNode = segmentationNode.GetDisplayNode()
680 if displayNode
is None:
681 logging.error(
"preview: Invalid segmentation display node!")
682 color = [0.5, 0.5, 0.5]
683 segmentID = self.
scriptedEffect.parameterSetNode().GetSelectedSegmentID()
689 r, g, b = segmentationNode.GetSegmentation().GetSegment(segmentID).GetColor()
694 pipeline.lookupTable.SetTableValue(1, r, g, b, opacity)
696 pipeline.thresholdFilter.SetInputConnection(layerLogic.GetReslice().GetOutputPort())
697 pipeline.thresholdFilter.ThresholdBetween(min, max)
698 pipeline.actor.VisibilityOn()
699 sliceWidget.sliceView().scheduleRender()
707 def processInteractionEvents(self, callerInteractor, eventId, viewWidget):
711 if masterImageData
is None:
715 if viewWidget.className() !=
"qMRMLSliceWidget":
718 anyModifierKeyPressed = callerInteractor.GetShiftKey()
or callerInteractor.GetControlKey()
or callerInteractor.GetAltKey()
721 if eventId == vtk.vtkCommand.LeftButtonPressEvent
and not anyModifierKeyPressed:
727 xy = callerInteractor.GetEventPosition()
728 ras = self.
xyToRas(xy, viewWidget)
730 if eventId == vtk.vtkCommand.LeftButtonPressEvent
and not anyModifierKeyPressed:
735 elif eventId == vtk.vtkCommand.LeftButtonReleaseEvent:
739 elif eventId == vtk.vtkCommand.MouseMoveEvent:
782 def getSourceVolumeLayerLogic(self, sliceWidget):
783 sourceVolumeNode = self.
scriptedEffect.parameterSetNode().GetSourceVolumeNode()
784 sliceLogic = sliceWidget.sliceLogic()
786 backgroundLogic = sliceLogic.GetBackgroundLayer()
787 backgroundVolumeNode = backgroundLogic.GetVolumeNode()
788 if sourceVolumeNode == backgroundVolumeNode:
789 return backgroundLogic
791 foregroundLogic = sliceLogic.GetForegroundLayer()
792 foregroundVolumeNode = foregroundLogic.GetVolumeNode()
793 if sourceVolumeNode == foregroundVolumeNode:
794 return foregroundLogic
796 logging.warning(
"Source volume is not set as either the foreground or background")
798 foregroundOpacity = 0.0
799 if foregroundVolumeNode:
800 compositeNode = sliceLogic.GetSliceCompositeNode()
801 foregroundOpacity = compositeNode.GetForegroundOpacity()
803 if foregroundOpacity > 0.5:
804 return foregroundLogic
806 return backgroundLogic
808 def updateHistogram(self):
821 brushBounds = brushPolydata.GetBounds()
822 brushExtent = [0, -1, 0, -1, 0, -1]
824 brushExtent[2 * i] = vtk.vtkMath.Floor(brushBounds[2 * i])
825 brushExtent[2 * i + 1] = vtk.vtkMath.Ceil(brushBounds[2 * i + 1])
826 if brushExtent[0] > brushExtent[1]
or brushExtent[2] > brushExtent[3]
or brushExtent[4] > brushExtent[5]:
831 self.
reslice.SetInputConnection(layerLogic.GetReslice().GetInputConnection(0, 0))
832 self.
reslice.SetResliceTransform(layerLogic.GetReslice().GetResliceTransform())
833 self.
reslice.SetInterpolationMode(layerLogic.GetReslice().GetInterpolationMode())
834 self.
reslice.SetOutputExtent(brushExtent)
836 maxNumberOfBins = 1000
838 scalarRange = masterImageData.GetScalarRange()
839 scalarType = masterImageData.GetScalarType()
840 if scalarType == vtk.VTK_FLOAT
or scalarType == vtk.VTK_DOUBLE:
841 numberOfBins = maxNumberOfBins
843 numberOfBins = int(scalarRange[1] - scalarRange[0]) + 1
844 if numberOfBins > maxNumberOfBins:
845 numberOfBins = maxNumberOfBins
846 binSpacing = (scalarRange[1] - scalarRange[0] + 1) / numberOfBins
848 self.
imageAccumulate.SetComponentExtent(0, numberOfBins - 1, 0, 0, 0, 0)
849 self.
imageAccumulate.SetComponentSpacing(binSpacing, binSpacing, binSpacing)
850 self.
imageAccumulate.SetComponentOrigin(scalarRange[0], scalarRange[0], scalarRange[0])
855 tableSize = self.
imageAccumulate.GetOutput().GetPointData().GetScalars().GetNumberOfTuples()
856 for i
in range(tableSize):
857 value = self.
imageAccumulate.GetOutput().GetPointData().GetScalars().GetTuple1(i)
871 lower = min(startX, endX)
872 average = (startX + endX) / 2.0
873 upper = max(startX, endX)
891 minimumThreshold = lower
892 maximumThreshold = upper
894 histogramSetModeLower = self.
scriptedEffect.parameter(HISTOGRAM_SET_LOWER_PARAMETER_NAME)
895 if histogramSetModeLower == HISTOGRAM_SET_MINIMUM:
896 minimumThreshold = scalarRange[0]
897 elif histogramSetModeLower == HISTOGRAM_SET_LOWER:
898 minimumThreshold = lower
899 elif histogramSetModeLower == HISTOGRAM_SET_AVERAGE:
900 minimumThreshold = average
902 histogramSetModeUpper = self.
scriptedEffect.parameter(HISTOGRAM_SET_UPPER_PARAMETER_NAME)
903 if histogramSetModeUpper == HISTOGRAM_SET_AVERAGE:
904 maximumThreshold = average
905 elif histogramSetModeUpper == HISTOGRAM_SET_UPPER:
906 maximumThreshold = upper
907 elif histogramSetModeUpper == HISTOGRAM_SET_MAXIMUM:
908 maximumThreshold = scalarRange[1]
910 self.
scriptedEffect.setParameter(
"MinimumThreshold", minimumThreshold)
911 self.
scriptedEffect.setParameter(
"MaximumThreshold", maximumThreshold)
913 def updateHistogramBackground(self):
917 if masterImageData
is None:
920 scalarRange = masterImageData.GetScalarRange()
925 low = max(scalarRange[0] + epsilon, low)
926 upper = min(scalarRange[1] - epsilon, upper)