def xyzToIjk(self, xyz, viewWidget, image):
   import vtkSegmentationCorePython as vtkSegmentationCore
   xyzVector = qt.QVector3D(xyz[0], xyz[1], xyz[2])
   ijkVector = self.scriptedEffect.xyzToIjk(xyzVector, viewWidget, image)
   return [int(ijkVector.x()), int(ijkVector.y()), int(ijkVector.z())]
 def xyzToRas(self, xyz, viewWidget):
   xyzVector = qt.QVector3D(xyz[0], xyz[1], xyz[2])
   rasVector = self.scriptedEffect.xyzToRas(xyzVector, viewWidget)
   return [rasVector.x(), rasVector.y(), rasVector.z()]
 def rasToXy(self, ras, viewWidget):
   rasVector = qt.QVector3D(ras[0], ras[1], ras[2])
   xyPoint = self.scriptedEffect.rasToXy(rasVector, viewWidget)
   return [xyPoint.x(), xyPoint.y()]
    def process(self):
        import time
        startTime = time.time()
        logging.info('Processing started')

        slicer.util.showStatusMessage("Segment editor setup")
        slicer.app.processEvents()
        """
    Find segment editor widgets.
    Use a dedicated class to store widget references once only.
    Not reasonable to dig through the UI on every run.
    """
        if not self.segmentEditorWidgets:
            self.segmentEditorWidgets = SegmentEditorWidgets()
            self.segmentEditorWidgets.findWidgets()

        # Create a new segmentation if none is specified.
        if not self.outputSegmentation:
            segmentation = slicer.mrmlScene.AddNewNodeByClass(
                "vtkMRMLSegmentationNode")
            self.outputSegmentation = segmentation
        else:
            # Prefer a local reference for readability
            segmentation = self.outputSegmentation

        # Local direct reference to slicer.modules.SegmentEditorWidget.editor
        seWidgetEditor = self.segmentEditorWidgets.widgetEditor

        # Get volume node
        sliceWidget = slicer.app.layoutManager().sliceWidget(
            self.inputSliceNode.GetName())
        volumeNode = sliceWidget.sliceLogic().GetBackgroundLayer(
        ).GetVolumeNode()

        # Set segment editor controls
        seWidgetEditor.setSegmentationNode(segmentation)
        seWidgetEditor.setMasterVolumeNode(volumeNode)
        """
    This geometry update does the speed-up magic ! No need to crop the master volume.
    We don't strictly need it right here because it is the first master volume of the segmentation. It's however required below each time the master volume node is changed.
    https://discourse.slicer.org/t/resampled-segmentation-limited-by-a-bounding-box-not-the-whole-volume/18772/3
    """
        segmentation.SetReferenceImageGeometryParameterFromVolumeNode(
            volumeNode)

        # Go to Segment Editor.
        mainWindow = slicer.util.mainWindow()
        mainWindow.moduleSelector().selectModule('SegmentEditor')

        # Show the input curve. Colour of control points change on selection, helps to wait.
        self.inputCurveNode.SetDisplayVisibility(True)
        # Reset segment editor masking widgets. Values set by previous work must not interfere here.
        self.segmentEditorWidgets.resetMaskingWidgets()

        #---------------------- Draw tube with VTK---------------------
        # https://discourse.slicer.org/t/converting-markupscurve-to-markupsfiducial/20246/3
        tube = vtk.vtkTubeFilter()
        tube.SetInputData(self.inputCurveNode.GetCurveWorld())
        tube.SetRadius(self.tubeDiameter / 2)
        tube.SetNumberOfSides(30)
        tube.CappingOn()
        tube.Update()
        segmentation.AddSegmentFromClosedSurfaceRepresentation(
            tube.GetOutput(), "TubeMask")
        # Select it so that Split Volume can work on this specific segment only.
        seWidgetEditor.setCurrentSegmentID("TubeMask")

        #---------------------- Split volume ---------------------
        slicer.util.showStatusMessage("Split volume")
        slicer.app.processEvents()
        seWidgetEditor.setActiveEffectByName("Split volume")
        svEffect = seWidgetEditor.activeEffect()
        svEffect.setParameter("FillValue", -1000)
        # Work on the TubeMask segment only.
        svEffect.setParameter("ApplyToAllVisibleSegments", 0)
        svEffect.self().onApply()
        seWidgetEditor.setActiveEffectByName(None)

        # Get output split volume
        allScalarVolumeNodes = slicer.mrmlScene.GetNodesByClass(
            "vtkMRMLScalarVolumeNode")
        outputSplitVolumeNode = allScalarVolumeNodes.GetItemAsObject(
            allScalarVolumeNodes.GetNumberOfItems() - 1)
        # Remove no longer needed drawn tube segment
        segment = segmentation.GetSegmentation().GetSegment("TubeMask")
        segmentation.GetSegmentation().RemoveSegment(segment)
        # Replace master volume of segmentation
        seWidgetEditor.setMasterVolumeNode(outputSplitVolumeNode)
        segmentation.SetReferenceImageGeometryParameterFromVolumeNode(
            outputSplitVolumeNode)
        """
    Split Volume creates a folder that contains the segmentation node,
    and the split volume(s) it creates.
    Here, we need to get rid of the split volume. There is no reason to keep
    around the created folder, that takes owneship of the segmentation node.
    So we'll later move the segmentation node to the Scene node and remove the
    residual empty folder.
    """
        shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(
            slicer.mrmlScene)
        shSplitVolumeId = shNode.GetItemByDataNode(outputSplitVolumeNode)
        shSplitVolumeParentId = shNode.GetItemParent(shSplitVolumeId)
        shSegmentationId = shNode.GetItemByDataNode(segmentation)
        shSceneId = shNode.GetSceneItemID()

        #---------------------- Manage segment --------------------
        # Remove a segment node and keep its color
        segment = None
        segmentColor = []
        """
    Control the segment ID.
    It will be the same in all segmentations.
    We can reach it precisely.
    """
        segmentID = "Segment_" + self.inputCurveNode.GetID()
        segment = segmentation.GetSegmentation().GetSegment(segmentID)
        if segment:
            segmentColor = segment.GetColor()
            segmentation.GetSegmentation().RemoveSegment(segment)

        # Add a new segment, with controlled ID and known color.
        object = segmentation.GetSegmentation().AddEmptySegment(segmentID)
        segment = segmentation.GetSegmentation().GetSegment(object)
        # Visually identify the segment by the input fiducial name
        segmentName = "Segment_" + self.inputCurveNode.GetName()
        segment.SetName(segmentName)
        if len(segmentColor):
            segment.SetColor(segmentColor)
        # Select new segment
        seWidgetEditor.setCurrentSegmentID(segmentID)

        #---------------------- Flood filling ---------------------
        # Set parameters
        seWidgetEditor.setActiveEffectByName("Flood filling")
        ffEffect = seWidgetEditor.activeEffect()
        ffEffect.setParameter("IntensityTolerance", self.intensityTolerance)
        ffEffect.setParameter("NeighborhoodSizeMm", self.neighbourhoodSize)
        # +++ If an alien ROI is set, segmentation may fail and take an infinite time.
        ffEffect.parameterSetNode().SetNodeReferenceID("FloodFilling.ROI",
                                                       None)
        ffEffect.updateGUIFromMRML()

        # Get input curve control points
        curveControlPoints = vtk.vtkPoints()
        self.inputCurveNode.GetControlPointPositionsWorld(curveControlPoints)
        numberOfCurveControlPoints = curveControlPoints.GetNumberOfPoints()

        # Apply flood filling at curve control points. Ignore first and last point as the resulting segment would be a big lump. The voxels of split volume at -1000 would be included in the segment.
        for i in range(1, numberOfCurveControlPoints - 1):
            # Show progress in status bar. Helpful to wait.
            t = time.time()
            msg = f'Flood filling : {t-startTime:.2f} seconds - '
            self.showStatusMessage(
                (msg, str(i + 1), "/", str(numberOfCurveControlPoints)))

            rasPoint = curveControlPoints.GetPoint(i)
            slicer.vtkMRMLSliceNode.JumpSlice(
                sliceWidget.sliceLogic().GetSliceNode(), *rasPoint)
            point3D = qt.QVector3D(rasPoint[0], rasPoint[1], rasPoint[2])
            point2D = ffEffect.rasToXy(point3D, sliceWidget)
            qIjkPoint = ffEffect.xyToIjk(
                point2D, sliceWidget,
                ffEffect.self().getClippedMasterImageData())
            ffEffect.self().floodFillFromPoint(
                (int(qIjkPoint.x()), int(qIjkPoint.y()), int(qIjkPoint.z())))

        # Switch off active effect
        seWidgetEditor.setActiveEffect(None)
        # Replace master volume of segmentation
        seWidgetEditor.setMasterVolumeNode(volumeNode)
        segmentation.SetReferenceImageGeometryParameterFromVolumeNode(
            volumeNode)
        # Remove no longer needed split volume.
        slicer.mrmlScene.RemoveNode(outputSplitVolumeNode)

        # Remove folder created by Split Volume.
        # First, reparent the segmentation item to scene item.
        shNode.SetItemParent(shSegmentationId, shSceneId)
        """
    Remove an empty folder directly. Keep it if there are volumes from other
    work.
    """
        if shNode.GetNumberOfItemChildren(shSplitVolumeParentId) == 0:
            if shNode.GetItemLevel(shSplitVolumeParentId) == "Folder":
                shNode.RemoveItem(shSplitVolumeParentId)

        if not self.extractCenterlines:
            stopTime = time.time()
            message = f'Processing completed in {stopTime-startTime:.2f} seconds'
            logging.info(message)
            slicer.util.showStatusMessage(message, 5000)
            return

        #---------------------- Extract centerlines ---------------------
        slicer.util.showStatusMessage("Extract centerline setup")
        slicer.app.processEvents()
        mainWindow.moduleSelector().selectModule('ExtractCenterline')
        if not self.extractCenterlineWidgets:
            self.extractCenterlineWidgets = ExtractCenterlineWidgets()
            self.extractCenterlineWidgets.findWidgets()

        inputSurfaceComboBox = self.extractCenterlineWidgets.inputSurfaceComboBox
        inputSegmentSelectorWidget = self.extractCenterlineWidgets.inputSegmentSelectorWidget
        endPointsMarkupsSelector = self.extractCenterlineWidgets.endPointsMarkupsSelector
        outputCenterlineModelSelector = self.extractCenterlineWidgets.outputCenterlineModelSelector
        outputCenterlineCurveSelector = self.extractCenterlineWidgets.outputCenterlineCurveSelector
        preprocessInputSurfaceModelCheckBox = self.extractCenterlineWidgets.preprocessInputSurfaceModelCheckBox
        applyButton = self.extractCenterlineWidgets.applyButton

        # Set input segmentation
        inputSurfaceComboBox.setCurrentNode(segmentation)
        inputSegmentSelectorWidget.setCurrentSegmentID(segmentID)
        # Create 2 fiducial endpoints, at start and end of input curve. We call it output because it is not user input.
        outputFiducialNode = self.outputFiducialNode
        if not outputFiducialNode:
            outputFiducialNode = slicer.mrmlScene.AddNewNodeByClass(
                "vtkMRMLMarkupsFiducialNode")
            # Visually identify the segment by the input fiducial name
            outputFiducialNode.SetName("Endpoints_" +
                                       self.inputCurveNode.GetName())
            firstInputCurveControlPoint = self.inputCurveNode.GetNthControlPointPositionVector(
                0)
            outputFiducialNode.AddControlPointWorld(
                firstInputCurveControlPoint)
            endPointsMarkupsSelector.setCurrentNode(outputFiducialNode)
            lastInputCurveControlPoint = self.inputCurveNode.GetNthControlPointPositionVector(
                curveControlPoints.GetNumberOfPoints() - 1)
            outputFiducialNode.AddControlPointWorld(lastInputCurveControlPoint)
            endPointsMarkupsSelector.setCurrentNode(outputFiducialNode)
            self.outputFiducialNode = outputFiducialNode
        # Account for rename. Control points are not remaned though.
        outputFiducialNode.SetName("Endpoints_" +
                                   self.inputCurveNode.GetName())

        # Output centerline model. A single node throughout.
        centerlineModel = self.outputCenterlineModel
        if not centerlineModel:
            centerlineModel = slicer.mrmlScene.AddNewNodeByClass(
                "vtkMRMLModelNode")
            # Visually identify the segment by the input fiducial name
            centerlineModel.SetName("Centerline_model_" +
                                    self.inputCurveNode.GetName())
            self.outputCenterlineModel = centerlineModel
        # Account for rename
        centerlineModel.SetName("Centerline_model_" +
                                self.inputCurveNode.GetName())
        outputCenterlineModelSelector.setCurrentNode(centerlineModel)

        # Output centerline curve. A single node throughout.
        centerlineCurve = self.outputCenterlineCurve
        if not centerlineCurve:
            centerlineCurve = slicer.mrmlScene.AddNewNodeByClass(
                "vtkMRMLMarkupsCurveNode")
            # Visually identify the segment by the input fiducial name
            centerlineCurve.SetName("Centerline_curve_" +
                                    self.inputCurveNode.GetName())
            self.outputCenterlineCurve = centerlineCurve
        # Account for rename
        centerlineCurve.SetName("Centerline_curve_" +
                                self.inputCurveNode.GetName())

        outputCenterlineCurveSelector.setCurrentNode(centerlineCurve)
        """
    Don't preprocess input surface. Decimation error may crash Slicer. Quadric method for decimation is slower but more reliable.
    """
        preprocessInputSurfaceModelCheckBox.setChecked(False)
        # Apply
        applyButton.click()
        # Hide the input curve to show the centerlines
        self.inputCurveNode.SetDisplayVisibility(False)
        # Close network pane; we don't use this here.
        self.extractCenterlineWidgets.outputNetworkGroupBox.collapsed = True

        stopTime = time.time()
        message = f'Processing completed in {stopTime-startTime:.2f} seconds'
        logging.info(message)
        slicer.util.showStatusMessage(message, 5000)
  def process(self):
    import time
    startTime = time.time()
    logging.info('Processing started')
    
    slicer.util.showStatusMessage("Segment editor setup")
    slicer.app.processEvents()
    """
    Find segment editor widgets.
    Use a dedicated class to store widget references once only.
    Not reasonable to dig through the UI on every run.
    """
    if not self.segmentEditorWidgets:
        self.segmentEditorWidgets = SegmentEditorWidgets()
        self.segmentEditorWidgets.findWidgets()
    # Create a new segmentation if none is specified.
    if not self.outputSegmentation:
        segmentation=slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
        self.outputSegmentation = segmentation
    else:
        # Prefer a local reference for readability
        segmentation = self.outputSegmentation
        
    # Local direct reference to slicer.modules.SegmentEditorWidget.editor
    seWidgetEditor=self.segmentEditorWidgets.widgetEditor

    # Get volume node
    sliceWidget = slicer.app.layoutManager().sliceWidget(self.inputSliceNode.GetName())
    volumeNode = sliceWidget.sliceLogic().GetBackgroundLayer().GetVolumeNode()
    
    # Set segment editor controls
    seWidgetEditor.setSegmentationNode(segmentation)
    seWidgetEditor.setMasterVolumeNode(volumeNode)
    
    # Go to Segment Editor.
    mainWindow = slicer.util.mainWindow()
    mainWindow.moduleSelector().selectModule('SegmentEditor')
    
    #---------------------- Manage segment --------------------
    # Remove a segment node and keep its color
    segment = None
    segmentColor = []
    """
    Control the segment ID.
    It will be the same in all segmentations.
    We can reach it precisely.
    """
    segmentID = "Segment_" + self.inputFiducialNode.GetID()
    segment = segmentation.GetSegmentation().GetSegment(segmentID)
    if segment:
        segmentColor = segment.GetColor()
        segmentation.GetSegmentation().RemoveSegment(segment)
    
    # Add a new segment, with controlled ID and known color.
    object = segmentation.GetSegmentation().AddEmptySegment(segmentID)
    segment = segmentation.GetSegmentation().GetSegment(object)
    # Visually identify the segment by the input fiducial name
    segmentName = "Segment_" + self.inputFiducialNode.GetName()
    segment.SetName(segmentName)
    if len(segmentColor):
        segment.SetColor(segmentColor)
    # Select new segment
    seWidgetEditor.setCurrentSegmentID(segmentID)
    
    #---------------------- Flood filling --------------------
    # Each fiducial point will be a user click.
    # Set parameters
    seWidgetEditor.setActiveEffectByName("Flood filling")
    ffEffect = seWidgetEditor.activeEffect()
    ffEffect.setParameter("IntensityTolerance", self.intensityTolerance)
    ffEffect.setParameter("NeighborhoodSizeMm", self.neighbourhoodSize)
    ffEffect.parameterSetNode().SetNodeReferenceID("FloodFilling.ROI", self.inputROINode.GetID() if self.inputROINode else None)
    ffEffect.updateGUIFromMRML()
    # Reset segment editor masking widgets. Values set by previous work must not interfere here.
    self.segmentEditorWidgets.resetMaskingWidgets()
    
    # Apply flood filling at each fiducial point.
    points=vtk.vtkPoints()
    self.inputFiducialNode.GetControlPointPositionsWorld(points)
    numberOfFiducialControlPoints = points.GetNumberOfPoints()
    for i in range(numberOfFiducialControlPoints):
        # Show progress in status bar. Helpful to wait.
        t = time.time()
        msg = f'Flood filling : {t-startTime:.2f} seconds - '
        self.showStatusMessage((msg, str(i + 1), "/", str(numberOfFiducialControlPoints)))
        
        rasPoint = points.GetPoint(i)
        slicer.vtkMRMLSliceNode.JumpSlice(sliceWidget.sliceLogic().GetSliceNode(), *rasPoint)
        point3D = qt.QVector3D(rasPoint[0], rasPoint[1], rasPoint[2])
        point2D = ffEffect.rasToXy(point3D, sliceWidget)
        qIjkPoint = ffEffect.xyToIjk(point2D, sliceWidget, ffEffect.self().getClippedMasterImageData())
        ffEffect.self().floodFillFromPoint((int(qIjkPoint.x()), int(qIjkPoint.y()), int(qIjkPoint.z())))
    
    # Switch off active effect
    seWidgetEditor.setActiveEffect(None)
    # Show segment
    show3DctkMenuButton = self.segmentEditorWidgets.show3DctkMenuButton
    # Don't use click() here, smoothing options make a mess.
    show3DctkMenuButton.setChecked(True)
    # Hide ROI
    if self.inputROINode:
        self.inputROINode.SetDisplayVisibility(False)
    
    if not self.extractCenterlines:
        stopTime = time.time()
        message = f'Processing completed in {stopTime-startTime:.2f} seconds'
        logging.info(message)
        slicer.util.showStatusMessage(message, 5000)
        return
    
    #---------------------- Extract centerlines ---------------------
    slicer.util.showStatusMessage("Extract centerline setup")
    slicer.app.processEvents()
    mainWindow.moduleSelector().selectModule('ExtractCenterline')
    if not self.extractCenterlineWidgets:
        self.extractCenterlineWidgets = ExtractCenterlineWidgets()
        self.extractCenterlineWidgets.findWidgets()
    
    inputSurfaceComboBox = self.extractCenterlineWidgets.inputSurfaceComboBox
    inputSegmentSelectorWidget = self.extractCenterlineWidgets.inputSegmentSelectorWidget
    endPointsMarkupsSelector = self.extractCenterlineWidgets.endPointsMarkupsSelector
    outputCenterlineModelSelector = self.extractCenterlineWidgets.outputCenterlineModelSelector
    outputCenterlineCurveSelector = self.extractCenterlineWidgets.outputCenterlineCurveSelector
    preprocessInputSurfaceModelCheckBox = self.extractCenterlineWidgets.preprocessInputSurfaceModelCheckBox
    applyButton = self.extractCenterlineWidgets.applyButton
    
    # Set input segmentation and endpoints
    inputSurfaceComboBox.setCurrentNode(segmentation)
    inputSegmentSelectorWidget.setCurrentSegmentID(segmentID)
    endPointsMarkupsSelector.setCurrentNode(self.inputFiducialNode)
    
    # Output centerline model. A single node throughout.
    centerlineModel = self.outputCenterlineModel
    if not centerlineModel:
        centerlineModel = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLModelNode")
        # Visually identify the segment by the input fiducial name
        centerlineModel.SetName("Centerline_model_" + self.inputFiducialNode.GetName())
        self.outputCenterlineModel = centerlineModel
    # Account for rename
    centerlineModel.SetName("Centerline_model_" + self.inputFiducialNode.GetName())
    outputCenterlineModelSelector.setCurrentNode(centerlineModel)
    
    # Output centerline curve. A single node throughout.
    centerlineCurve = self.outputCenterlineCurve
    if not centerlineCurve:
        centerlineCurve = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsCurveNode")
        # Visually identify the segment by the input fiducial name
        centerlineCurve.SetName("Centerline_curve_" + self.inputFiducialNode.GetName())
        self.outputCenterlineCurve = centerlineCurve
    # Account for rename
    centerlineCurve.SetName("Centerline_curve_" + self.inputFiducialNode.GetName())
    outputCenterlineCurveSelector.setCurrentNode(centerlineCurve)
    
    """
    Don't preprocess input surface. Decimation error may crash Slicer. Quadric method for decimation is slower but more reliable.
    """
    preprocessInputSurfaceModelCheckBox.setChecked(False)
    # Apply
    applyButton.click()
    # Close network pane; we don't use this here.
    self.extractCenterlineWidgets.outputNetworkGroupBox.collapsed = True
    
    stopTime = time.time()
    message = f'Processing completed in {stopTime-startTime:.2f} seconds'
    logging.info(message)
    slicer.util.showStatusMessage(message, 5000)
 def xyzToIjk(self, xyz, viewWidget, image, parentTransformNode=None):
     xyzVector = qt.QVector3D(xyz[0], xyz[1], xyz[2])
     ijkVector = self.scriptedEffect.xyzToIjk(xyzVector, viewWidget, image, parentTransformNode)
     return [int(ijkVector.x()), int(ijkVector.y()), int(ijkVector.z())]