def computeStatistics(self, segmentID):
        import vtkSegmentationCorePython as vtkSegmentationCore
        requestedKeys = self.getRequestedKeys()

        segmentationNode = slicer.mrmlScene.GetNodeByID(
            self.getParameterNode().GetParameter("Segmentation"))

        if len(requestedKeys) == 0:
            return {}

        containsLabelmapRepresentation = segmentationNode.GetSegmentation(
        ).ContainsRepresentation(
            vtkSegmentationCore.vtkSegmentationConverter.
            GetSegmentationBinaryLabelmapRepresentationName())
        if not containsLabelmapRepresentation:
            return {}

        segmentLabelmap = slicer.vtkOrientedImageData()
        segmentationNode.GetBinaryLabelmapRepresentation(
            segmentID, segmentLabelmap)
        if (not segmentLabelmap or not segmentLabelmap.GetPointData()
                or not segmentLabelmap.GetPointData().GetScalars()):
            # No input label data
            return {}

        # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value
        labelValue = 1
        backgroundValue = 0
        thresh = vtk.vtkImageThreshold()
        thresh.SetInputData(segmentLabelmap)
        thresh.ThresholdByLower(0)
        thresh.SetInValue(backgroundValue)
        thresh.SetOutValue(labelValue)
        thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
        thresh.Update()

        #  Use binary labelmap as a stencil
        stencil = vtk.vtkImageToImageStencil()
        stencil.SetInputData(thresh.GetOutput())
        stencil.ThresholdByUpper(labelValue)
        stencil.Update()

        stat = vtk.vtkImageAccumulate()
        stat.SetInputData(thresh.GetOutput())
        stat.SetStencilData(stencil.GetOutput())
        stat.Update()

        # Add data to statistics list
        cubicMMPerVoxel = reduce(lambda x, y: x * y,
                                 segmentLabelmap.GetSpacing())
        ccPerCubicMM = 0.001
        stats = {}
        if "voxel_count" in requestedKeys:
            stats["voxel_count"] = stat.GetVoxelCount()
        if "volume_mm3" in requestedKeys:
            stats["volume_mm3"] = stat.GetVoxelCount() * cubicMMPerVoxel
        if "volume_cm3" in requestedKeys:
            stats["volume_cm3"] = stat.GetVoxelCount(
            ) * cubicMMPerVoxel * ccPerCubicMM
        return stats
예제 #2
0
    def generateMergedLabelmapInReferenceGeometry(self, segmentationNode,
                                                  referenceVolumeNode):
        if segmentationNode is None:
            logging.error("Invalid segmentation node")
            return None
        if referenceVolumeNode is None:
            logging.error("Invalid reference volume node")
            return None

        # Get reference geometry in the segmentation node's coordinate system
        referenceGeometry_Reference = slicer.vtkOrientedImageData(
        )  # reference geometry in reference node coordinate system
        referenceGeometry_Segmentation = slicer.vtkOrientedImageData()
        mergedLabelmap_Reference = slicer.vtkOrientedImageData()
        referenceGeometryToSegmentationTransform = vtk.vtkGeneralTransform()

        # Set reference image geometry
        referenceGeometry_Reference.SetExtent(
            referenceVolumeNode.GetImageData().GetExtent())
        ijkToRasMatrix = vtk.vtkMatrix4x4()
        referenceVolumeNode.GetIJKToRASMatrix(ijkToRasMatrix)
        referenceGeometry_Reference.SetGeometryFromImageToWorldMatrix(
            ijkToRasMatrix)

        # Transform it to the segmentation node coordinate system
        referenceGeometry_Segmentation = slicer.vtkOrientedImageData()
        referenceGeometry_Segmentation.DeepCopy(referenceGeometry_Reference)

        # Get transform between reference volume and segmentation node
        if (referenceVolumeNode.GetParentTransformNode() !=
                segmentationNode.GetParentTransformNode()):
            slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(
                referenceVolumeNode.GetParentTransformNode(),
                segmentationNode.GetParentTransformNode(),
                referenceGeometryToSegmentationTransform)
            slicer.vtkOrientedImageDataResample.TransformOrientedImage(
                referenceGeometry_Segmentation,
                referenceGeometryToSegmentationTransform, True)

        # Generate shared labelmap for the exported segments in segmentation coordinates
        sharedImage_Segmentation = slicer.vtkOrientedImageData()
        if (not segmentationNode.GenerateMergedLabelmapForAllSegments(
                sharedImage_Segmentation, 0, None)):
            logging.error(
                "ExportSegmentsToLabelmapNode: Failed to generate shared labelmap"
            )
            return None

        # Transform shared labelmap to reference geometry coordinate system
        segmentationToReferenceGeometryTransform = referenceGeometryToSegmentationTransform.GetInverse(
        )
        segmentationToReferenceGeometryTransform.Update()
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            sharedImage_Segmentation, referenceGeometry_Reference,
            mergedLabelmap_Reference, False, False,
            segmentationToReferenceGeometryTransform)

        return mergedLabelmap_Reference
예제 #3
0
  def TestSection_MarginEffects(self):
    logging.info("Running test on margin effect")

    slicer.modules.segmenteditor.widgetRepresentation().self().editor.effectByName("Margin")

    self.segmentation.RemoveAllSegments()
    segment1Id = self.segmentation.AddEmptySegment("Segment_1")
    segment1 = self.segmentation.GetSegment(segment1Id)
    segment1.SetLabelValue(1)

    segment2Id = self.segmentation.AddEmptySegment("Segment_2")
    segment2 = self.segmentation.GetSegment(segment2Id)
    segment2.SetLabelValue(2)

    binaryLabelmapRepresentationName = slicer.vtkSegmentationConverter.GetBinaryLabelmapRepresentationName()
    dataTypes = [
      vtk.VTK_CHAR,
      vtk.VTK_SIGNED_CHAR,
      vtk.VTK_UNSIGNED_CHAR,
      vtk.VTK_SHORT,
      vtk.VTK_UNSIGNED_SHORT,
      vtk.VTK_INT,
      vtk.VTK_UNSIGNED_INT,
      vtk.VTK_LONG,
      vtk.VTK_UNSIGNED_LONG,
      vtk.VTK_FLOAT,
      vtk.VTK_DOUBLE,
      #vtk.VTK_LONG_LONG, # These types are unsupported in ITK
      #vtk.VTK_UNSIGNED_LONG_LONG,
    ]
    logging.info("Testing shared labelmaps")
    for dataType in dataTypes:
      initialLabelmap = slicer.vtkOrientedImageData()
      initialLabelmap.SetImageToWorldMatrix(self.ijkToRas)
      initialLabelmap.SetExtent(0, 10, 0, 10, 0, 10)
      segment1.AddRepresentation(binaryLabelmapRepresentationName, initialLabelmap)
      segment2.AddRepresentation(binaryLabelmapRepresentationName, initialLabelmap)

      self.runMarginEffect(segment1, segment2, dataType, self.segmentEditorNode.OverwriteAllSegments)
      self.assertEqual(self.segmentation.GetNumberOfLayers(), 1)
      self.runMarginEffect(segment1, segment2, dataType, self.segmentEditorNode.OverwriteNone)
      self.assertEqual(self.segmentation.GetNumberOfLayers(), 2)

    logging.info("Testing separate labelmaps")
    for dataType in dataTypes:
      segment1Labelmap = slicer.vtkOrientedImageData()
      segment1Labelmap.SetImageToWorldMatrix(self.ijkToRas)
      segment1Labelmap.SetExtent(0, 10, 0, 10, 0, 10)
      segment1.AddRepresentation(binaryLabelmapRepresentationName, segment1Labelmap)

      segment2Labelmap = slicer.vtkOrientedImageData()
      segment2Labelmap.DeepCopy(segment1Labelmap)
      segment2.AddRepresentation(binaryLabelmapRepresentationName, segment2Labelmap)

      self.runMarginEffect(segment1, segment2, dataType, self.segmentEditorNode.OverwriteAllSegments)
      self.assertEqual(self.segmentation.GetNumberOfLayers(), 2)
      self.runMarginEffect(segment1, segment2, dataType, self.segmentEditorNode.OverwriteNone)
      self.assertEqual(self.segmentation.GetNumberOfLayers(), 2)
예제 #4
0
    def paintApply(self, viewWidget):

        # Current limitation: smoothing brush is not implemented for joint smoothing
        smoothingMethod = self.scriptedEffect.parameter("SmoothingMethod")
        if smoothingMethod == JOINT_TAUBIN:
            self.scriptedEffect.clearBrushes()
            self.scriptedEffect.forceRender(viewWidget)
            slicer.util.messageBox(
                "Smoothing brush is not available for 'joint smoothing' method."
            )
            return

        modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
        maskImage = slicer.vtkOrientedImageData()
        maskImage.DeepCopy(modifierLabelmap)
        maskExtent = self.scriptedEffect.paintBrushesIntoLabelmap(
            maskImage, viewWidget)
        self.scriptedEffect.clearBrushes()
        self.scriptedEffect.forceRender(viewWidget)
        if maskExtent[0] > maskExtent[1] or maskExtent[2] > maskExtent[
                3] or maskExtent[4] > maskExtent[5]:
            return

        self.scriptedEffect.saveStateForUndo()
        self.onApply(maskImage, maskExtent)
  def onApply(self):
    self.delayedAutoUpdateTimer.stop()
    self.observeSegmentation(False)

    import vtkSegmentationCorePython as vtkSegmentationCore
    segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
    segmentationDisplayNode = segmentationNode.GetDisplayNode()
    previewNode = self.getPreviewNode()

    self.scriptedEffect.saveStateForUndo()

    previewContainsClosedSurfaceRepresentation = previewNode.GetSegmentation().ContainsRepresentation(
      slicer.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())

    # Move segments from preview into current segmentation
    segmentIDs = vtk.vtkStringArray()
    previewNode.GetSegmentation().GetSegmentIDs(segmentIDs)
    for index in range(segmentIDs.GetNumberOfValues()):
      segmentID = segmentIDs.GetValue(index)
      previewSegmentLabelmap = slicer.vtkOrientedImageData()
      previewNode.GetBinaryLabelmapRepresentation(segmentID, previewSegmentLabelmap)
      self.scriptedEffect.modifySegmentByLabelmap(segmentationNode, segmentID, previewSegmentLabelmap,
        slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
      if segmentationDisplayNode is not None and self.isBackgroundLabelmap(previewSegmentLabelmap):
        # Automatically hide result segments that are background (all eight corners are non-zero)
        segmentationDisplayNode.SetSegmentVisibility(segmentID, False)
      previewNode.GetSegmentation().RemoveSegment(segmentID) # delete now to limit memory usage

    if previewContainsClosedSurfaceRepresentation:
      segmentationNode.CreateClosedSurfaceRepresentation()

    self.reset()
  def modifySelectedSegmentByLabelmap(self, smoothedImage, selectedSegmentLabelmap, modifierLabelmap, maskImage, maskExtent):
    if maskImage:
      smoothedClippedSelectedSegmentLabelmap = slicer.vtkOrientedImageData()
      smoothedClippedSelectedSegmentLabelmap.ShallowCopy(smoothedImage)
      smoothedClippedSelectedSegmentLabelmap.CopyDirections(modifierLabelmap)

      # fill smoothed selected segment outside the painted region to 1 so that in the end the image is not modified by OPERATION_MINIMUM
      fillValue = 1.0
      slicer.vtkOrientedImageDataResample.ApplyImageMask(smoothedClippedSelectedSegmentLabelmap, maskImage, fillValue, False)
      # set original segment labelmap outside painted region, solid 1 inside painted region
      slicer.vtkOrientedImageDataResample.ModifyImage(maskImage, selectedSegmentLabelmap,
                                                      slicer.vtkOrientedImageDataResample.OPERATION_MAXIMUM)
      slicer.vtkOrientedImageDataResample.ModifyImage(maskImage, smoothedClippedSelectedSegmentLabelmap,
                                                      slicer.vtkOrientedImageDataResample.OPERATION_MINIMUM)

      updateExtent = [0, -1, 0, -1, 0, -1]
      modifierExtent = modifierLabelmap.GetExtent()
      for i in range(3):
        updateExtent[2 * i] = min(maskExtent[2 * i], modifierExtent[2 * i])
        updateExtent[2 * i + 1] = max(maskExtent[2 * i + 1], modifierExtent[2 * i + 1])

      self.scriptedEffect.modifySelectedSegmentByLabelmap(maskImage,
                                                          slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet,
                                                          updateExtent)
    else:
      modifierLabelmap.DeepCopy(smoothedImage)
      self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
예제 #7
0
    def runGrowCut(self, masterImageData, seedLabelmap, outputLabelmap):

        self.clippedMaskImageData = slicer.vtkOrientedImageData()
        intensityBasedMasking = self.scriptedEffect.parameterSetNode(
        ).GetMasterVolumeIntensityMask()
        segmentationNode = self.scriptedEffect.parameterSetNode(
        ).GetSegmentationNode()
        success = segmentationNode.GenerateEditMask(
            self.clippedMaskImageData,
            self.scriptedEffect.parameterSetNode().GetMaskMode(),
            masterImageData,  # reference geometry
            "",  # edited segment ID
            self.scriptedEffect.parameterSetNode().GetMaskSegmentID() if
            self.scriptedEffect.parameterSetNode().GetMaskSegmentID() else "",
            masterImageData if intensityBasedMasking else None,
            self.scriptedEffect.parameterSetNode(
            ).GetMasterVolumeIntensityMaskRange()
            if intensityBasedMasking else None)

        import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
        self.growCutFilter = vtkSlicerSegmentationsModuleLogic.vtkImageGrowCutSegment(
        )
        self.growCutFilter.SetIntensityVolume(masterImageData)
        self.growCutFilter.SetSeedLabelVolume(seedLabelmap)
        self.growCutFilter.SetMaskVolume(self.clippedMaskImageData)
        self.growCutFilter.Update()
        outputLabelmap.ShallowCopy(self.growCutFilter.GetOutput())
  def effectiveExtentChanged(self):
    if self.mergedLabelmapGeometryImage is None:
      return True
    if self.selectedSegmentIds is None:
      return True

    import vtkSegmentationCorePython as vtkSegmentationCore

    segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()

    # The effective extent for the current input segments
    effectiveGeometryImage = slicer.vtkOrientedImageData()
    effectiveGeometryString = segmentationNode.GetSegmentation().DetermineCommonLabelmapGeometry(
      vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.selectedSegmentIds)
    if effectiveGeometryString is None:
        return True
    vtkSegmentationCore.vtkSegmentationConverter.DeserializeImageGeometry(effectiveGeometryString, effectiveGeometryImage)

    masterImageData = self.scriptedEffect.masterVolumeImageData()
    masterImageExtent = masterImageData.GetExtent()

    # The effective extent of the selected segments
    effectiveLabelExtent = effectiveGeometryImage.GetExtent()
    # Current extent used for auto-complete preview
    currentLabelExtent = self.mergedLabelmapGeometryImage.GetExtent()

    # Determine if the current merged labelmap extent has less than a 3 voxel margin around the effective segment extent (limited by the master image extent)
    return ((masterImageExtent[0] != currentLabelExtent[0] and currentLabelExtent[0] > effectiveLabelExtent[0] - self.minimumExtentMargin) or
            (masterImageExtent[1] != currentLabelExtent[1] and currentLabelExtent[1] < effectiveLabelExtent[1] + self.minimumExtentMargin) or
            (masterImageExtent[2] != currentLabelExtent[2] and currentLabelExtent[2] > effectiveLabelExtent[2] - self.minimumExtentMargin) or
            (masterImageExtent[3] != currentLabelExtent[3] and currentLabelExtent[3] < effectiveLabelExtent[3] + self.minimumExtentMargin) or
            (masterImageExtent[4] != currentLabelExtent[4] and currentLabelExtent[4] > effectiveLabelExtent[4] - self.minimumExtentMargin) or
            (masterImageExtent[5] != currentLabelExtent[5] and currentLabelExtent[5] < effectiveLabelExtent[5] + self.minimumExtentMargin))
예제 #9
0
    def _updateInputPd(self):

        segment = self.segmentationNode.GetSegmentation().GetSegment(
            self.segmentId)
        self._inputPd = vtk.vtkPolyData()

        # Get input polydata and input spacing
        if self.segmentationNode.GetSegmentation().GetMasterRepresentationName(
        ) == slicer.vtkSegmentationConverter(
        ).GetSegmentationBinaryLabelmapRepresentationName():
            # Master representation is binary labelmap
            # Reconvert to closed surface using chosen chosen smoothing factor
            originalSurfaceSmoothing = float(
                self.segmentationNode.GetSegmentation().GetConversionParameter(
                    slicer.vtkBinaryLabelmapToClosedSurfaceConversionRule(
                    ).GetSmoothingFactorParameterName()))
            if abs(originalSurfaceSmoothing - self.smoothingFactor) > 0.001:
                self.segmentationNode.GetSegmentation().SetConversionParameter(
                    slicer.vtkBinaryLabelmapToClosedSurfaceConversionRule(
                    ).GetSmoothingFactorParameterName(),
                    str(self.smoothingFactor))
                # Force re-conversion
                self.segmentationNode.RemoveClosedSurfaceRepresentation()
            self.segmentationNode.CreateClosedSurfaceRepresentation()
            self.segmentationNode.GetClosedSurfaceRepresentation(
                self.segmentId, self._inputPd)
            if self._inputPd.GetNumberOfPoints() == 0:
                raise ValueError(
                    "Input segment closed surface representation is empty")
            # Get input spacing
            inputLabelmap = slicer.vtkOrientedImageData()
            self.segmentationNode.GetBinaryLabelmapRepresentation(
                self.segmentId, inputLabelmap)
            extent = inputLabelmap.GetExtent()
            if extent[0] > extent[1] or extent[2] > extent[3] or extent[
                    4] > extent[5]:
                raise ValueError(
                    "Input segment labelmap representation is empty")
            self._inputSpacing = math.sqrt(
                np.sum(np.array(inputLabelmap.GetSpacing())**2))
        else:
            # Representation is already closed surface
            self.segmentationNode.CreateClosedSurfaceRepresentation()
            self.segmentationNode.GetClosedSurfaceRepresentation(
                self.segmentId, self._inputPd)
            # set spacing to have an approxmately 250^3 volume
            # this size is not too large for average computing hardware yet
            # it is sufficiently detailed for many applications
            preferredVolumeSizeInVoxels = 250 * 250 * 250
            bounds = np.zeros(6)
            self._inputPd.GetBounds(bounds)
            volumeSizeInMm3 = (bounds[1] - bounds[0]) * (
                bounds[3] - bounds[2]) * (bounds[5] - bounds[4])
            self._inputSpacing = pow(
                volumeSizeInMm3 / preferredVolumeSizeInVoxels, 1 / 3.)
 def clipImage(self, inputImage, maskExtent, margin):
   clipper = vtk.vtkImageClip()
   clipper.SetOutputWholeExtent(maskExtent[0] - margin[0], maskExtent[1] + margin[0],
                                maskExtent[2] - margin[1], maskExtent[3] + margin[1],
                                maskExtent[4] - margin[2], maskExtent[5] + margin[2])
   clipper.SetInputData(inputImage)
   clipper.SetClipData(True)
   clipper.Update()
   clippedImage = slicer.vtkOrientedImageData()
   clippedImage.ShallowCopy(clipper.GetOutput())
   clippedImage.CopyDirections(inputImage)
   return clippedImage
    def sitkImageToVtkOrientedImage(self, img):
        imgNode = sitkUtils.PushVolumeToSlicer(img)
        vtkImage = imgNode.GetImageData()

        vtkOrientedImage = slicer.vtkOrientedImageData()
        vtkOrientedImage.DeepCopy(vtkImage)
        dir = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
        imgNode.GetIJKToRASDirections(dir)
        vtkOrientedImage.SetDirections([dir[0], dir[1], dir[2]])
        vtkOrientedImage.SetOrigin(imgNode.GetOrigin())
        vtkOrientedImage.SetSpacing(imgNode.GetSpacing())

        slicer.mrmlScene.RemoveNode(imgNode)
        return vtkOrientedImage
  def checkSegmentVoxelCount(self, segmentIndex, expectedVoxelCount):
    segment = self.segmentation.GetNthSegment(segmentIndex)
    self.assertIsNotNone(segment)

    labelmap = slicer.vtkOrientedImageData()
    segmentID = self.segmentation.GetNthSegmentID(segmentIndex)
    self.segmentationNode.GetBinaryLabelmapRepresentation(segmentID, labelmap)

    imageStat = vtk.vtkImageAccumulate()
    imageStat.SetInputData(labelmap)
    imageStat.SetComponentExtent(0,4,0,0,0,0)
    imageStat.SetComponentOrigin(0,0,0)
    imageStat.SetComponentSpacing(1,1,1)
    imageStat.IgnoreZeroOn()
    imageStat.Update()

    self.assertEqual(imageStat.GetVoxelCount(), expectedVoxelCount)
예제 #13
0
    def getInvertedBinaryLabelmap(self, modifierLabelmap):
        fillValue = 1
        eraseValue = 0
        inverter = vtk.vtkImageThreshold()
        inverter.SetInputData(modifierLabelmap)
        inverter.SetInValue(fillValue)
        inverter.SetOutValue(eraseValue)
        inverter.ReplaceInOn()
        inverter.ThresholdByLower(0)
        inverter.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
        inverter.Update()

        invertedModifierLabelmap = slicer.vtkOrientedImageData()
        invertedModifierLabelmap.ShallowCopy(inverter.GetOutput())
        imageToWorldMatrix = vtk.vtkMatrix4x4()
        modifierLabelmap.GetImageToWorldMatrix(imageToWorldMatrix)
        invertedModifierLabelmap.SetGeometryFromImageToWorldMatrix(
            imageToWorldMatrix)
        return invertedModifierLabelmap
예제 #14
0
    def cropOrientedImage(masterImageData, roiNode):
        """Clip master image data with annotation ROI and return result in a new vtkOrientedImageData"""
        # This is a utility function, also used in FloodFilling effect.
        # Probably we should apply relative transform between ROI and master image data node

        worldToImageMatrix = vtk.vtkMatrix4x4()
        masterImageData.GetWorldToImageMatrix(worldToImageMatrix)

        bounds = [0, 0, 0, 0, 0, 0]
        roiNode.GetRASBounds(bounds)
        corner1RAS = [bounds[0], bounds[2], bounds[4], 1]
        corner1IJK = [0, 0, 0, 0]
        worldToImageMatrix.MultiplyPoint(corner1RAS, corner1IJK)

        corner2RAS = [bounds[1], bounds[3], bounds[5], 1]
        corner2IJK = [0, 0, 0, 0]
        worldToImageMatrix.MultiplyPoint(corner2RAS, corner2IJK)

        extent = [0, -1, 0, -1, 0, -1]
        for i in range(3):
            lowerPoint = min(corner1IJK[i], corner2IJK[i])
            upperPoint = max(corner1IJK[i], corner2IJK[i])
            extent[2 * i] = int(math.floor(lowerPoint))
            extent[2 * i + 1] = int(math.ceil(upperPoint))

        imageToWorldMatrix = vtk.vtkMatrix4x4()
        masterImageData.GetImageToWorldMatrix(imageToWorldMatrix)
        clippedMasterImageData = slicer.vtkOrientedImageData()
        padder = vtk.vtkImageConstantPad()
        padder.SetInputData(masterImageData)
        padder.SetOutputWholeExtent(extent)
        padder.Update()
        clippedMasterImageData.ShallowCopy(padder.GetOutput())
        clippedMasterImageData.SetImageToWorldMatrix(imageToWorldMatrix)

        return clippedMasterImageData
    def processInteractionEvents(self, callerInteractor, eventId, viewWidget):
        import vtkSegmentationCorePython as vtkSegmentationCore

        abortEvent = False

        # Only allow in modes where segment selection is needed
        if not self.currentOperationRequiresSegmentSelection():
            return False

        # Only allow for slice views
        if viewWidget.className() != "qMRMLSliceWidget":
            return abortEvent

        if eventId != vtk.vtkCommand.LeftButtonPressEvent:
            return abortEvent

        abortEvent = True

        # Generate merged labelmap of all visible segments
        segmentationNode = self.scriptedEffect.parameterSetNode(
        ).GetSegmentationNode()
        visibleSegmentIds = vtk.vtkStringArray()
        segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(
            visibleSegmentIds)
        if visibleSegmentIds.GetNumberOfValues() == 0:
            logging.info(
                "Smoothing operation skipped: there are no visible segments")
            return abortEvent

        self.scriptedEffect.saveStateForUndo()

        # This can be a long operation - indicate it to the user
        qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)

        operationName = self.scriptedEffect.parameter("Operation")

        if operationName == ADD_SELECTED_ISLAND:
            inputLabelImage = slicer.vtkOrientedImageData()
            if not segmentationNode.GenerateMergedLabelmapForAllSegments(
                    inputLabelImage, vtkSegmentationCore.vtkSegmentation.
                    EXTENT_UNION_OF_SEGMENTS_PADDED, None, visibleSegmentIds):
                logging.error(
                    'Failed to apply smoothing: cannot get list of visible segments'
                )
                qt.QApplication.restoreOverrideCursor()
                return abortEvent
        else:
            selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap(
            )
            # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value
            labelValue = 1
            backgroundValue = 0
            thresh = vtk.vtkImageThreshold()
            thresh.SetInputData(selectedSegmentLabelmap)
            thresh.ThresholdByLower(0)
            thresh.SetInValue(backgroundValue)
            thresh.SetOutValue(labelValue)
            thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
            thresh.Update()
            # Create oriented image data from output
            import vtkSegmentationCorePython as vtkSegmentationCore
            inputLabelImage = slicer.vtkOrientedImageData()
            inputLabelImage.ShallowCopy(thresh.GetOutput())
            selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
            selectedSegmentLabelmap.GetImageToWorldMatrix(
                selectedSegmentLabelmapImageToWorldMatrix)
            inputLabelImage.SetImageToWorldMatrix(
                selectedSegmentLabelmapImageToWorldMatrix)

        xy = callerInteractor.GetEventPosition()
        ijk = self.xyToIjk(xy, viewWidget, inputLabelImage,
                           segmentationNode.GetParentTransformNode())
        pixelValue = inputLabelImage.GetScalarComponentAsFloat(
            ijk[0], ijk[1], ijk[2], 0)

        try:
            floodFillingFilter = vtk.vtkImageThresholdConnectivity()
            floodFillingFilter.SetInputData(inputLabelImage)
            seedPoints = vtk.vtkPoints()
            origin = inputLabelImage.GetOrigin()
            spacing = inputLabelImage.GetSpacing()
            seedPoints.InsertNextPoint(origin[0] + ijk[0] * spacing[0],
                                       origin[1] + ijk[1] * spacing[1],
                                       origin[2] + ijk[2] * spacing[2])
            floodFillingFilter.SetSeedPoints(seedPoints)
            floodFillingFilter.ThresholdBetween(pixelValue, pixelValue)

            if operationName == ADD_SELECTED_ISLAND:
                floodFillingFilter.SetInValue(1)
                floodFillingFilter.SetOutValue(0)
                floodFillingFilter.Update()
                modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap(
                )
                modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())
                self.scriptedEffect.modifySelectedSegmentByLabelmap(
                    modifierLabelmap, slicer.
                    qSlicerSegmentEditorAbstractEffect.ModificationModeAdd)

            elif pixelValue != 0:  # if clicked on empty part then there is nothing to remove or keep

                if operationName == KEEP_SELECTED_ISLAND:
                    floodFillingFilter.SetInValue(1)
                    floodFillingFilter.SetOutValue(0)
                else:  # operationName == REMOVE_SELECTED_ISLAND:
                    floodFillingFilter.SetInValue(1)
                    floodFillingFilter.SetOutValue(0)

                floodFillingFilter.Update()
                modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap(
                )
                modifierLabelmap.DeepCopy(floodFillingFilter.GetOutput())

                if operationName == KEEP_SELECTED_ISLAND:
                    self.scriptedEffect.modifySelectedSegmentByLabelmap(
                        modifierLabelmap, slicer.
                        qSlicerSegmentEditorAbstractEffect.ModificationModeSet)
                else:  # operationName == REMOVE_SELECTED_ISLAND:
                    self.scriptedEffect.modifySelectedSegmentByLabelmap(
                        modifierLabelmap,
                        slicer.qSlicerSegmentEditorAbstractEffect.
                        ModificationModeRemove)

        except IndexError:
            logging.error('apply: Failed to threshold master volume!')
        finally:
            qt.QApplication.restoreOverrideCursor()

        return abortEvent
    def splitSegments(self, minimumSize=0, maxNumberOfSegments=0, split=True):
        """
    minimumSize: if 0 then it means that all islands are kept, regardless of size
    maxNumberOfSegments: if 0 then it means that all islands are kept, regardless of how many
    """
        # This can be a long operation - indicate it to the user
        qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)

        self.scriptedEffect.saveStateForUndo()

        # Get modifier labelmap
        selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()

        castIn = vtk.vtkImageCast()
        castIn.SetInputData(selectedSegmentLabelmap)
        castIn.SetOutputScalarTypeToUnsignedInt()

        # Identify the islands in the inverted volume and
        # find the pixel that corresponds to the background
        islandMath = vtkITK.vtkITKIslandMath()
        islandMath.SetInputConnection(castIn.GetOutputPort())
        islandMath.SetFullyConnected(False)
        islandMath.SetMinimumSize(minimumSize)
        islandMath.Update()

        islandImage = slicer.vtkOrientedImageData()
        islandImage.ShallowCopy(islandMath.GetOutput())
        selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
        selectedSegmentLabelmap.GetImageToWorldMatrix(
            selectedSegmentLabelmapImageToWorldMatrix)
        islandImage.SetImageToWorldMatrix(
            selectedSegmentLabelmapImageToWorldMatrix)

        islandCount = islandMath.GetNumberOfIslands()
        islandOrigCount = islandMath.GetOriginalNumberOfIslands()
        ignoredIslands = islandOrigCount - islandCount
        logging.info("%d islands created (%d ignored)" %
                     (islandCount, ignoredIslands))

        baseSegmentName = "Label"
        selectedSegmentID = self.scriptedEffect.parameterSetNode(
        ).GetSelectedSegmentID()
        segmentationNode = self.scriptedEffect.parameterSetNode(
        ).GetSegmentationNode()
        with slicer.util.NodeModify(segmentationNode):
            segmentation = segmentationNode.GetSegmentation()
            selectedSegment = segmentation.GetSegment(selectedSegmentID)
            selectedSegmentName = selectedSegment.GetName()
            if selectedSegmentName is not None and selectedSegmentName != "":
                baseSegmentName = selectedSegmentName

            labelValues = vtk.vtkIntArray()
            slicer.vtkSlicerSegmentationsModuleLogic.GetAllLabelValues(
                labelValues, islandImage)

            # Erase segment from in original labelmap.
            # Individuall islands will be added back later.
            threshold = vtk.vtkImageThreshold()
            threshold.SetInputData(selectedSegmentLabelmap)
            threshold.ThresholdBetween(0, 0)
            threshold.SetInValue(0)
            threshold.SetOutValue(0)
            threshold.Update()
            emptyLabelmap = slicer.vtkOrientedImageData()
            emptyLabelmap.ShallowCopy(threshold.GetOutput())
            emptyLabelmap.CopyDirections(selectedSegmentLabelmap)
            self.scriptedEffect.modifySegmentByLabelmap(
                segmentationNode, selectedSegmentID, emptyLabelmap,
                slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)

            for i in range(labelValues.GetNumberOfTuples()):
                if (maxNumberOfSegments > 0 and i >= maxNumberOfSegments):
                    # We only care about the segments up to maxNumberOfSegments.
                    # If we do not want to split segments, we only care about the first.
                    break

                labelValue = int(labelValues.GetTuple1(i))
                segment = selectedSegment
                segmentID = selectedSegmentID
                if i != 0 and split:
                    segment = slicer.vtkSegment()
                    name = baseSegmentName + "_" + str(i + 1)
                    segment.SetName(name)
                    segment.AddRepresentation(
                        slicer.vtkSegmentationConverter.
                        GetSegmentationBinaryLabelmapRepresentationName(),
                        selectedSegment.GetRepresentation(
                            slicer.vtkSegmentationConverter.
                            GetSegmentationBinaryLabelmapRepresentationName()))
                    segmentation.AddSegment(segment)
                    segmentID = segmentation.GetSegmentIdBySegment(segment)
                    segment.SetLabelValue(
                        segmentation.GetUniqueLabelValueForSharedLabelmap(
                            selectedSegmentID))

                threshold = vtk.vtkImageThreshold()
                threshold.SetInputData(islandMath.GetOutput())
                threshold.ThresholdBetween(labelValue, labelValue)
                threshold.SetInValue(1)
                threshold.SetOutValue(0)
                threshold.Update()

                modificationMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd
                if i == 0:
                    modificationMode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet

                # Create oriented image data from output
                modifierImage = slicer.vtkOrientedImageData()
                modifierImage.DeepCopy(threshold.GetOutput())
                selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
                selectedSegmentLabelmap.GetImageToWorldMatrix(
                    selectedSegmentLabelmapImageToWorldMatrix)
                modifierImage.SetGeometryFromImageToWorldMatrix(
                    selectedSegmentLabelmapImageToWorldMatrix)
                # We could use a single slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode
                # method call to import all the resulting segments at once but that would put all the imported segments
                # in a new layer. By using modifySegmentByLabelmap, the number of layers will not increase.
                self.scriptedEffect.modifySegmentByLabelmap(
                    segmentationNode, segmentID, modifierImage,
                    modificationMode)

        qt.QApplication.restoreOverrideCursor()
  def onApply(self):
    import vtkSegmentationCorePython as vtkSegmentationCore

    self.scriptedEffect.saveStateForUndo()

    import vtkSegmentationCorePython as vtkSegmentationCore

    # Get modifier labelmap and parameters

    operation = self.scriptedEffect.parameter("Operation")
    bypassMasking =  (self.scriptedEffect.integerParameter("BypassMasking") != 0)

    selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()

    segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
    segmentation = segmentationNode.GetSegmentation()

    if operation in self.operationsRequireModifierSegment:

      # Get modifier segment
      modifierSegmentID = self.modifierSegmentID()
      if not modifierSegmentID:
        logging.error("Operation {0} requires a selected modifier segment".format(operation))
        return
      modifierSegment = segmentation.GetSegment(modifierSegmentID)
      modifierSegmentLabelmap = slicer.vtkOrientedImageData()
      segmentationNode.GetBinaryLabelmapRepresentation(modifierSegmentID, modifierSegmentLabelmap)

      # Get common geometry
      commonGeometryString = segmentationNode.GetSegmentation().DetermineCommonLabelmapGeometry(
        vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_SEGMENTS, None)
      if not commonGeometryString:
        logging.info("Logical operation skipped: all segments are empty")
        return
      commonGeometryImage = slicer.vtkOrientedImageData()
      vtkSegmentationCore.vtkSegmentationConverter.DeserializeImageGeometry(commonGeometryString, commonGeometryImage, False)

      # Make sure modifier segment has correct geometry
      # (if modifier segment has been just copied over from another segment then its geometry may be different)
      if not vtkSegmentationCore.vtkOrientedImageDataResample.DoGeometriesMatch(commonGeometryImage, modifierSegmentLabelmap):
        modifierSegmentLabelmap_CommonGeometry = slicer.vtkOrientedImageData()
        vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
          modifierSegmentLabelmap, commonGeometryImage, modifierSegmentLabelmap_CommonGeometry,
          False, # nearest neighbor interpolation,
          True # make sure resampled modifier segment is not cropped
          )
        modifierSegmentLabelmap = modifierSegmentLabelmap_CommonGeometry

      if operation == LOGICAL_COPY:
          self.scriptedEffect.modifySelectedSegmentByLabelmap(
            modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking)
      elif operation == LOGICAL_UNION:
          self.scriptedEffect.modifySelectedSegmentByLabelmap(
            modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd, bypassMasking)
      elif operation == LOGICAL_SUBTRACT:
          self.scriptedEffect.modifySelectedSegmentByLabelmap(
            modifierSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove, bypassMasking)
      elif operation == LOGICAL_INTERSECT:
        selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
        intersectionLabelmap = slicer.vtkOrientedImageData()
        vtkSegmentationCore.vtkOrientedImageDataResample.MergeImage(
          selectedSegmentLabelmap, modifierSegmentLabelmap, intersectionLabelmap,
          vtkSegmentationCore.vtkOrientedImageDataResample.OPERATION_MINIMUM, selectedSegmentLabelmap.GetExtent())
        selectedSegmentLabelmapExtent = selectedSegmentLabelmap.GetExtent()
        modifierSegmentLabelmapExtent = modifierSegmentLabelmap.GetExtent()
        commonExtent = [max(selectedSegmentLabelmapExtent[0], modifierSegmentLabelmapExtent[0]),
          min(selectedSegmentLabelmapExtent[1], modifierSegmentLabelmapExtent[1]),
          max(selectedSegmentLabelmapExtent[2], modifierSegmentLabelmapExtent[2]),
          min(selectedSegmentLabelmapExtent[3], modifierSegmentLabelmapExtent[3]),
          max(selectedSegmentLabelmapExtent[4], modifierSegmentLabelmapExtent[4]),
          min(selectedSegmentLabelmapExtent[5], modifierSegmentLabelmapExtent[5])]
        self.scriptedEffect.modifySelectedSegmentByLabelmap(
          intersectionLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, commonExtent, bypassMasking)

    elif operation == LOGICAL_INVERT:
      selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
      invertedSelectedSegmentLabelmap = self.getInvertedBinaryLabelmap(selectedSegmentLabelmap)
      self.scriptedEffect.modifySelectedSegmentByLabelmap(
        invertedSelectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking)

    elif operation == LOGICAL_CLEAR or operation == LOGICAL_FILL:
      selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()
      vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(selectedSegmentLabelmap, 1 if operation == LOGICAL_FILL else 0, selectedSegmentLabelmap.GetExtent())
      self.scriptedEffect.modifySelectedSegmentByLabelmap(
        selectedSegmentLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet, bypassMasking)

    else:
      logging.error("Unknown operation: {0}".format(operation))
예제 #18
0
    def TestSection_ImportExportSegment2(self):
        # Testing sequential add of individual segments to a segmentation through ImportLabelmapToSegmentationNode
        logging.info('Test section: Import/export segment 2')

        # Export body segment to volume node
        bodySegment = self.inputSegmentationNode.GetSegmentation().GetSegment(self.bodySegmentName)
        bodyLabelmapNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLabelMapVolumeNode', 'BodyLabelmap')
        result = slicer.vtkSlicerSegmentationsModuleLogic.ExportSegmentToRepresentationNode(bodySegment, bodyLabelmapNode)
        self.assertTrue(result)
        bodyImageData = bodyLabelmapNode.GetImageData()
        self.assertIsNotNone(bodyImageData)
        imageStat = vtk.vtkImageAccumulate()
        imageStat.SetInputData(bodyImageData)
        imageStat.Update()
        self.assertEqual(imageStat.GetVoxelCount(), 792)
        self.assertEqual(imageStat.GetMin()[0], 0)
        self.assertEqual(imageStat.GetMax()[0], 1)

        # Export tumor segment to volume node
        tumorSegment = self.inputSegmentationNode.GetSegmentation().GetSegment(self.tumorSegmentName)
        tumorLabelmapNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLabelMapVolumeNode', 'TumorLabelmap')
        result = slicer.vtkSlicerSegmentationsModuleLogic.ExportSegmentToRepresentationNode(tumorSegment, tumorLabelmapNode)
        self.assertTrue(result)
        tumorImageData = tumorLabelmapNode.GetImageData()
        self.assertIsNotNone(tumorImageData)
        imageStat = vtk.vtkImageAccumulate()
        imageStat.SetInputData(tumorImageData)
        imageStat.Update()
        self.assertEqual(imageStat.GetVoxelCount(), 12)
        self.assertEqual(imageStat.GetMin()[0], 0)
        self.assertEqual(imageStat.GetMax()[0], 1)

        # Import single-label labelmap to segmentation
        singleLabelImportSegmentationNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode', 'SingleLabelImport')
        singleLabelImportSegmentationNode.GetSegmentation().SetMasterRepresentationName(self.binaryLabelmapReprName)

        bodySegmentID = singleLabelImportSegmentationNode.GetSegmentation().AddEmptySegment('BodyLabelmap')
        bodySegmentIDArray = vtk.vtkStringArray()
        bodySegmentIDArray.SetNumberOfValues(1)
        bodySegmentIDArray.SetValue(0, bodySegmentID)
        result = slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode(bodyLabelmapNode, singleLabelImportSegmentationNode, bodySegmentIDArray)

        self.assertTrue(result)
        self.assertEqual(singleLabelImportSegmentationNode.GetSegmentation().GetNumberOfSegments(), 1)

        tumorSegmentID = singleLabelImportSegmentationNode.GetSegmentation().AddEmptySegment('TumorLabelmap')
        tumorSegmentIDArray = vtk.vtkStringArray()
        tumorSegmentIDArray.SetNumberOfValues(1)
        tumorSegmentIDArray.SetValue(0, tumorSegmentID)
        result = slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode(tumorLabelmapNode, singleLabelImportSegmentationNode, tumorSegmentIDArray)
        self.assertTrue(result)
        self.assertEqual(singleLabelImportSegmentationNode.GetSegmentation().GetNumberOfSegments(), 2)

        bodyLabelmap = slicer.vtkOrientedImageData()
        singleLabelImportSegmentationNode.GetBinaryLabelmapRepresentation(bodySegmentID, bodyLabelmap)
        imageStat = vtk.vtkImageAccumulate()
        imageStat.SetInputData(bodyLabelmap)
        imageStat.Update()
        self.assertEqual(imageStat.GetVoxelCount(), 792)
        self.assertEqual(imageStat.GetMin()[0], 0)
        self.assertEqual(imageStat.GetMax()[0], 1)

        tumorLabelmap = slicer.vtkOrientedImageData()
        singleLabelImportSegmentationNode.GetBinaryLabelmapRepresentation(tumorSegmentID, tumorLabelmap)
        self.assertIsNotNone(tumorLabelmap)
        imageStat = vtk.vtkImageAccumulate()
        imageStat.SetInputData(tumorLabelmap)
        imageStat.Update()
        self.assertEqual(imageStat.GetVoxelCount(), 12)
        self.assertEqual(imageStat.GetMin()[0], 0)
        self.assertEqual(imageStat.GetMax()[0], 1)

        # Clean up temporary nodes
        slicer.mrmlScene.RemoveNode(bodyLabelmapNode)
        slicer.mrmlScene.RemoveNode(tumorLabelmapNode)
        slicer.mrmlScene.RemoveNode(singleLabelImportSegmentationNode)
    def preview(self):
        # Get master volume image data
        import vtkSegmentationCorePython as vtkSegmentationCore

        # Get segmentation
        segmentationNode = self.scriptedEffect.parameterSetNode(
        ).GetSegmentationNode()

        previewNode = self.getPreviewNode()
        previewOpacity = self.getPreviewOpacity()
        previewShow3D = self.getPreviewShow3D()

        # If the selectedSegmentIds have been specified, then they shouldn't be overwritten here
        currentSelectedSegmentIds = self.selectedSegmentIds

        if self.effectiveExtentChanged():
            self.reset()

            # Restore the selectedSegmentIds
            self.selectedSegmentIds = currentSelectedSegmentIds
            if self.selectedSegmentIds is None:
                self.selectedSegmentIds = vtk.vtkStringArray()
                segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(
                    self.selectedSegmentIds)
            if self.selectedSegmentIds.GetNumberOfValues(
            ) < self.minimumNumberOfSegments:
                logging.error(
                    "Auto-complete operation skipped: at least {0} visible segments are required"
                    .format(self.minimumNumberOfSegments))
                self.selectedSegmentIds = None
                return

            # Compute merged labelmap extent (effective extent slightly expanded)
            if not self.mergedLabelmapGeometryImage:
                self.mergedLabelmapGeometryImage = slicer.vtkOrientedImageData(
                )
            commonGeometryString = segmentationNode.GetSegmentation(
            ).DetermineCommonLabelmapGeometry(
                vtkSegmentationCore.vtkSegmentation.
                EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.selectedSegmentIds)
            if not commonGeometryString:
                logging.info(
                    "Auto-complete operation skipped: all visible segments are empty"
                )
                return
            vtkSegmentationCore.vtkSegmentationConverter.DeserializeImageGeometry(
                commonGeometryString, self.mergedLabelmapGeometryImage)

            masterImageData = self.scriptedEffect.masterVolumeImageData()
            masterImageExtent = masterImageData.GetExtent()
            labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent(
            )
            # Margin size is relative to combined seed region size, but minimum of 3 voxels
            print("self.extentGrowthRatio = {0}".format(
                self.extentGrowthRatio))
            margin = [
                int(
                    max(
                        3,
                        self.extentGrowthRatio * (labelsEffectiveExtent[1] -
                                                  labelsEffectiveExtent[0]))),
                int(
                    max(
                        3,
                        self.extentGrowthRatio * (labelsEffectiveExtent[3] -
                                                  labelsEffectiveExtent[2]))),
                int(
                    max(
                        3,
                        self.extentGrowthRatio *
                        (labelsEffectiveExtent[5] - labelsEffectiveExtent[4])))
            ]
            labelsExpandedExtent = [
                max(masterImageExtent[0],
                    labelsEffectiveExtent[0] - margin[0]),
                min(masterImageExtent[1],
                    labelsEffectiveExtent[1] + margin[0]),
                max(masterImageExtent[2],
                    labelsEffectiveExtent[2] - margin[1]),
                min(masterImageExtent[3],
                    labelsEffectiveExtent[3] + margin[1]),
                max(masterImageExtent[4],
                    labelsEffectiveExtent[4] - margin[2]),
                min(masterImageExtent[5], labelsEffectiveExtent[5] + margin[2])
            ]
            print("masterImageExtent = " + repr(masterImageExtent))
            print("labelsEffectiveExtent = " + repr(labelsEffectiveExtent))
            print("labelsExpandedExtent = " + repr(labelsExpandedExtent))
            self.mergedLabelmapGeometryImage.SetExtent(labelsExpandedExtent)

            # Create and setup preview node
            previewNode = slicer.mrmlScene.AddNewNodeByClass(
                "vtkMRMLSegmentationNode")
            previewNode.CreateDefaultDisplayNodes()
            previewNode.GetDisplayNode().SetVisibility2DOutline(False)
            if segmentationNode.GetParentTransformNode():
                previewNode.SetAndObserveTransformNodeID(
                    segmentationNode.GetParentTransformNode().GetID())
            self.scriptedEffect.parameterSetNode().SetNodeReferenceID(
                ResultPreviewNodeReferenceRole, previewNode.GetID())
            self.scriptedEffect.setCommonParameter(
                "SegmentationResultPreviewOwnerEffect",
                self.scriptedEffect.name)
            self.setPreviewOpacity(0.6)

            # Disable smoothing for closed surface generation to make it fast
            previewNode.GetSegmentation().SetConversionParameter(
                slicer.vtkBinaryLabelmapToClosedSurfaceConversionRule.
                GetSmoothingFactorParameterName(), "-0.5")

            inputContainsClosedSurfaceRepresentation = segmentationNode.GetSegmentation(
            ).ContainsRepresentation(
                slicer.vtkSegmentationConverter.
                GetSegmentationClosedSurfaceRepresentationName())

            self.setPreviewShow3D(inputContainsClosedSurfaceRepresentation)

            if self.clippedMasterImageDataRequired:
                self.clippedMasterImageData = slicer.vtkOrientedImageData()
                masterImageClipper = vtk.vtkImageConstantPad()
                masterImageClipper.SetInputData(masterImageData)
                masterImageClipper.SetOutputWholeExtent(
                    self.mergedLabelmapGeometryImage.GetExtent())
                masterImageClipper.Update()
                self.clippedMasterImageData.ShallowCopy(
                    masterImageClipper.GetOutput())
                self.clippedMasterImageData.CopyDirections(
                    self.mergedLabelmapGeometryImage)

            self.clippedMaskImageData = None
            if self.clippedMaskImageDataRequired:
                self.clippedMaskImageData = slicer.vtkOrientedImageData()
                intensityBasedMasking = self.scriptedEffect.parameterSetNode(
                ).GetMasterVolumeIntensityMask()
                success = segmentationNode.GenerateEditMask(
                    self.clippedMaskImageData,
                    self.scriptedEffect.parameterSetNode().GetMaskMode(),
                    self.clippedMasterImageData,  # reference geometry
                    "",  # edited segment ID
                    self.scriptedEffect.parameterSetNode().GetMaskSegmentID()
                    if
                    self.scriptedEffect.parameterSetNode().GetMaskSegmentID()
                    else "",
                    self.clippedMasterImageData
                    if intensityBasedMasking else None,
                    self.scriptedEffect.parameterSetNode(
                    ).GetMasterVolumeIntensityMaskRange()
                    if intensityBasedMasking else None)
                if not success:
                    logging.error("Failed to create edit mask")
                    self.clippedMaskImageData = None

        previewNode.SetName(segmentationNode.GetName() + " preview")

        mergedImage = slicer.vtkOrientedImageData()
        segmentationNode.GenerateMergedLabelmapForAllSegments(
            mergedImage, vtkSegmentationCore.vtkSegmentation.
            EXTENT_UNION_OF_EFFECTIVE_SEGMENTS,
            self.mergedLabelmapGeometryImage, self.selectedSegmentIds)

        outputLabelmap = slicer.vtkOrientedImageData()

        self.computePreviewLabelmap(mergedImage, outputLabelmap)

        # Write output segmentation results in segments
        for index in range(self.selectedSegmentIds.GetNumberOfValues()):
            segmentID = self.selectedSegmentIds.GetValue(index)
            segment = segmentationNode.GetSegmentation().GetSegment(segmentID)
            # Disable save with scene?

            # Get only the label of the current segment from the output image
            thresh = vtk.vtkImageThreshold()
            thresh.ReplaceInOn()
            thresh.ReplaceOutOn()
            thresh.SetInValue(1)
            thresh.SetOutValue(0)
            labelValue = index + 1  # n-th segment label value = n + 1 (background label value is 0)
            thresh.ThresholdBetween(labelValue, labelValue)
            thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
            thresh.SetInputData(outputLabelmap)
            thresh.Update()

            # Write label to segment
            newSegmentLabelmap = slicer.vtkOrientedImageData()
            newSegmentLabelmap.ShallowCopy(thresh.GetOutput())
            newSegmentLabelmap.CopyDirections(mergedImage)
            newSegment = previewNode.GetSegmentation().GetSegment(segmentID)
            if not newSegment:
                newSegment = vtkSegmentationCore.vtkSegment()
                newSegment.SetName(segment.GetName())
                color = segmentationNode.GetSegmentation().GetSegment(
                    segmentID).GetColor()
                newSegment.SetColor(color)
                previewNode.GetSegmentation().AddSegment(newSegment, segmentID)
            self.scriptedEffect.modifySegmentByLabelmap(
                previewNode, segmentID, newSegmentLabelmap,
                slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)

            # Automatically hide result segments that are background (all eight corners are non-zero)
            previewNode.GetDisplayNode().SetSegmentVisibility3D(
                segmentID, not self.isBackgroundLabelmap(newSegmentLabelmap))

        # If the preview was reset, we need to restore the visibility options
        self.setPreviewOpacity(previewOpacity)
        self.setPreviewShow3D(previewShow3D)

        self.updateGUIFromMRML()
예제 #20
0
    def TestSection_MarginEffects(self):
        logging.info("Running test on margin effect")

        slicer.modules.segmenteditor.widgetRepresentation().self(
        ).editor.effectByName("Margin")

        self.segmentation.RemoveAllSegments()
        segment1Id = self.segmentation.AddEmptySegment("Segment_1")
        segment1 = self.segmentation.GetSegment(segment1Id)
        segment1.SetLabelValue(1)

        segment2Id = self.segmentation.AddEmptySegment("Segment_2")
        segment2 = self.segmentation.GetSegment(segment2Id)
        segment2.SetLabelValue(2)

        binaryLabelmapRepresentationName = slicer.vtkSegmentationConverter.GetBinaryLabelmapRepresentationName(
        )
        dataTypes = [
            vtk.VTK_CHAR,
            vtk.VTK_SIGNED_CHAR,
            vtk.VTK_UNSIGNED_CHAR,
            vtk.VTK_SHORT,
            vtk.VTK_UNSIGNED_SHORT,
            vtk.VTK_INT,
            vtk.VTK_UNSIGNED_INT,
            #vtk.VTK_LONG, # On linux, VTK_LONG has the same size as VTK_LONG_LONG. This causes issues in vtkImageThreshold.
            #vtk.VTK_UNSIGNED_LONG, See https://github.com/Slicer/Slicer/issues/5427
            #vtk.VTK_FLOAT, # Since float can't represent all int, we jump straight to double.
            vtk.VTK_DOUBLE,
            #vtk.VTK_LONG_LONG, # These types are unsupported in ITK
            #vtk.VTK_UNSIGNED_LONG_LONG,
        ]
        logging.info("Testing shared labelmaps")
        for dataType in dataTypes:
            initialLabelmap = slicer.vtkOrientedImageData()
            initialLabelmap.SetImageToWorldMatrix(self.ijkToRas)
            initialLabelmap.SetExtent(0, 10, 0, 10, 0, 10)
            initialLabelmap.AllocateScalars(dataType, 1)
            initialLabelmap.GetPointData().GetScalars().Fill(0)
            segment1.AddRepresentation(binaryLabelmapRepresentationName,
                                       initialLabelmap)
            segment2.AddRepresentation(binaryLabelmapRepresentationName,
                                       initialLabelmap)

            self.runMarginEffect(segment1, segment2, dataType,
                                 self.segmentEditorNode.OverwriteAllSegments)
            self.assertEqual(self.segmentation.GetNumberOfLayers(), 1)
            self.runMarginEffect(segment1, segment2, dataType,
                                 self.segmentEditorNode.OverwriteNone)
            self.assertEqual(self.segmentation.GetNumberOfLayers(), 2)

        logging.info("Testing separate labelmaps")
        for dataType in dataTypes:
            segment1Labelmap = slicer.vtkOrientedImageData()
            segment1Labelmap.SetImageToWorldMatrix(self.ijkToRas)
            segment1Labelmap.SetExtent(0, 10, 0, 10, 0, 10)
            segment1Labelmap.AllocateScalars(dataType, 1)
            segment1Labelmap.GetPointData().GetScalars().Fill(0)
            segment1.AddRepresentation(binaryLabelmapRepresentationName,
                                       segment1Labelmap)

            segment2Labelmap = slicer.vtkOrientedImageData()
            segment2Labelmap.DeepCopy(segment1Labelmap)
            segment2.AddRepresentation(binaryLabelmapRepresentationName,
                                       segment2Labelmap)

            self.runMarginEffect(segment1, segment2, dataType,
                                 self.segmentEditorNode.OverwriteAllSegments)
            self.assertEqual(self.segmentation.GetNumberOfLayers(), 2)
            self.runMarginEffect(segment1, segment2, dataType,
                                 self.segmentEditorNode.OverwriteNone)
            self.assertEqual(self.segmentation.GetNumberOfLayers(), 2)
예제 #21
0
    def smoothMultipleSegments(self, maskImage=None, maskExtent=None):
        import vtkSegmentationCorePython as vtkSegmentationCore

        self.showStatusMessage(f'Joint smoothing ...')
        # Generate merged labelmap of all visible segments
        segmentationNode = self.scriptedEffect.parameterSetNode(
        ).GetSegmentationNode()
        visibleSegmentIds = vtk.vtkStringArray()
        segmentationNode.GetDisplayNode().GetVisibleSegmentIDs(
            visibleSegmentIds)
        if visibleSegmentIds.GetNumberOfValues() == 0:
            logging.info(
                "Smoothing operation skipped: there are no visible segments")
            return

        mergedImage = slicer.vtkOrientedImageData()
        if not segmentationNode.GenerateMergedLabelmapForAllSegments(
                mergedImage, vtkSegmentationCore.vtkSegmentation.
                EXTENT_UNION_OF_SEGMENTS_PADDED, None, visibleSegmentIds):
            logging.error(
                'Failed to apply smoothing: cannot get list of visible segments'
            )
            return

        segmentLabelValues = []  # list of [segmentId, labelValue]
        for i in range(visibleSegmentIds.GetNumberOfValues()):
            segmentId = visibleSegmentIds.GetValue(i)
            segmentLabelValues.append([segmentId, i + 1])

        # Perform smoothing in voxel space
        ici = vtk.vtkImageChangeInformation()
        ici.SetInputData(mergedImage)
        ici.SetOutputSpacing(1, 1, 1)
        ici.SetOutputOrigin(0, 0, 0)

        # Convert labelmap to combined polydata
        # vtkDiscreteFlyingEdges3D cannot be used here, as in the output of that filter,
        # each labeled region is completely disconnected from neighboring regions, and
        # for joint smoothing it is essential for the points to move together.
        convertToPolyData = vtk.vtkDiscreteMarchingCubes()
        convertToPolyData.SetInputConnection(ici.GetOutputPort())
        convertToPolyData.SetNumberOfContours(len(segmentLabelValues))

        contourIndex = 0
        for segmentId, labelValue in segmentLabelValues:
            convertToPolyData.SetValue(contourIndex, labelValue)
            contourIndex += 1

        # Low-pass filtering using Taubin's method
        smoothingFactor = self.scriptedEffect.doubleParameter(
            "JointTaubinSmoothingFactor")
        smoothingIterations = 100  # according to VTK documentation 10-20 iterations could be enough but we use a higher value to reduce chance of shrinking
        passBand = pow(
            10.0, -4.0 * smoothingFactor
        )  # gives a nice range of 1-0.0001 from a user input of 0-1
        smoother = vtk.vtkWindowedSincPolyDataFilter()
        smoother.SetInputConnection(convertToPolyData.GetOutputPort())
        smoother.SetNumberOfIterations(smoothingIterations)
        smoother.BoundarySmoothingOff()
        smoother.FeatureEdgeSmoothingOff()
        smoother.SetFeatureAngle(90.0)
        smoother.SetPassBand(passBand)
        smoother.NonManifoldSmoothingOn()
        smoother.NormalizeCoordinatesOn()

        # Extract a label
        threshold = vtk.vtkThreshold()
        threshold.SetInputConnection(smoother.GetOutputPort())

        # Convert to polydata
        geometryFilter = vtk.vtkGeometryFilter()
        geometryFilter.SetInputConnection(threshold.GetOutputPort())

        # Convert polydata to stencil
        polyDataToImageStencil = vtk.vtkPolyDataToImageStencil()
        polyDataToImageStencil.SetInputConnection(
            geometryFilter.GetOutputPort())
        polyDataToImageStencil.SetOutputSpacing(1, 1, 1)
        polyDataToImageStencil.SetOutputOrigin(0, 0, 0)
        polyDataToImageStencil.SetOutputWholeExtent(mergedImage.GetExtent())

        # Convert stencil to image
        stencil = vtk.vtkImageStencil()
        emptyBinaryLabelMap = vtk.vtkImageData()
        emptyBinaryLabelMap.SetExtent(mergedImage.GetExtent())
        emptyBinaryLabelMap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
        vtkSegmentationCore.vtkOrientedImageDataResample.FillImage(
            emptyBinaryLabelMap, 0)
        stencil.SetInputData(emptyBinaryLabelMap)
        stencil.SetStencilConnection(polyDataToImageStencil.GetOutputPort())
        stencil.ReverseStencilOn()
        stencil.SetBackgroundValue(
            1
        )  # General foreground value is 1 (background value because of reverse stencil)

        imageToWorldMatrix = vtk.vtkMatrix4x4()
        mergedImage.GetImageToWorldMatrix(imageToWorldMatrix)

        # TODO: Temporarily setting the overwrite mode to OverwriteVisibleSegments is an approach that should be change once additional
        # layer control options have been implemented. Users may wish to keep segments on separate layers, and not allow them to be separated/merged automatically.
        # This effect could leverage those options once they have been implemented.
        oldOverwriteMode = self.scriptedEffect.parameterSetNode(
        ).GetOverwriteMode()
        self.scriptedEffect.parameterSetNode().SetOverwriteMode(
            slicer.vtkMRMLSegmentEditorNode.OverwriteVisibleSegments)
        for segmentId, labelValue in segmentLabelValues:
            threshold.ThresholdBetween(labelValue, labelValue)
            stencil.Update()
            smoothedBinaryLabelMap = slicer.vtkOrientedImageData()
            smoothedBinaryLabelMap.ShallowCopy(stencil.GetOutput())
            smoothedBinaryLabelMap.SetImageToWorldMatrix(imageToWorldMatrix)
            self.scriptedEffect.modifySegmentByLabelmap(
                segmentationNode, segmentId, smoothedBinaryLabelMap,
                slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet,
                False)
        self.scriptedEffect.parameterSetNode().SetOverwriteMode(
            oldOverwriteMode)
예제 #22
0
    def TestSection_03_qMRMLSegmentationGeometryWidget(self):
        logging.info('Test section 2: qMRMLSegmentationGeometryWidget')

        binaryLabelmapReprName = slicer.vtkSegmentationConverter.GetBinaryLabelmapRepresentationName(
        )
        closedSurfaceReprName = slicer.vtkSegmentationConverter.GetClosedSurfaceRepresentationName(
        )

        # Use MRHead and Tinypatient for testing
        import SampleData
        mrVolumeNode = SampleData.downloadSample("MRHead")
        [tinyVolumeNode,
         tinySegmentationNode] = SampleData.downloadSamples('TinyPatient')

        # Convert MRHead to oriented image data
        import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
        mrOrientedImageData = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.CreateOrientedImageDataFromVolumeNode(
            mrVolumeNode)
        mrOrientedImageData.UnRegister(None)

        # Create segmentation node with binary labelmap master and one segment with MRHead geometry
        segmentationNode = slicer.mrmlScene.AddNewNodeByClass(
            'vtkMRMLSegmentationNode')
        segmentationNode.GetSegmentation().SetMasterRepresentationName(
            binaryLabelmapReprName)
        geometryStr = slicer.vtkSegmentationConverter.SerializeImageGeometry(
            mrOrientedImageData)
        segmentationNode.GetSegmentation().SetConversionParameter(
            slicer.vtkSegmentationConverter.
            GetReferenceImageGeometryParameterName(), geometryStr)

        threshold = vtk.vtkImageThreshold()
        threshold.SetInputData(mrOrientedImageData)
        threshold.ThresholdByUpper(16.0)
        threshold.SetInValue(1)
        threshold.SetOutValue(0)
        threshold.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
        threshold.Update()
        segmentOrientedImageData = slicer.vtkOrientedImageData()
        segmentOrientedImageData.DeepCopy(threshold.GetOutput())
        mrImageToWorldMatrix = vtk.vtkMatrix4x4()
        mrOrientedImageData.GetImageToWorldMatrix(mrImageToWorldMatrix)
        segmentOrientedImageData.SetImageToWorldMatrix(mrImageToWorldMatrix)
        segment = slicer.vtkSegment()
        segment.SetName('Brain')
        segment.SetColor(0.0, 0.0, 1.0)
        segment.AddRepresentation(binaryLabelmapReprName,
                                  segmentOrientedImageData)
        segmentationNode.GetSegmentation().AddSegment(segment)

        # Create geometry widget
        geometryWidget = slicer.qMRMLSegmentationGeometryWidget()
        geometryWidget.setSegmentationNode(segmentationNode)
        geometryWidget.editEnabled = True
        geometryImageData = slicer.vtkOrientedImageData(
        )  # To contain the output later

        # Volume source with no transforms
        geometryWidget.setSourceNode(tinyVolumeNode)
        geometryWidget.geometryImageData(geometryImageData)
        self.assertTrue(
            self.compareOutputGeometry(
                geometryImageData, (49, 49, 23), (248.8439, 248.2890, -123.75),
                [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]]))
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 92)

        # Transformed volume source
        translationTransformMatrix = vtk.vtkMatrix4x4()
        translationTransformMatrix.SetElement(0, 3, 24.5)
        translationTransformMatrix.SetElement(1, 3, 24.5)
        translationTransformMatrix.SetElement(2, 3, 11.5)
        translationTransformNode = slicer.vtkMRMLLinearTransformNode()
        translationTransformNode.SetName('TestTranslation')
        slicer.mrmlScene.AddNode(translationTransformNode)
        translationTransformNode.SetMatrixTransformToParent(
            translationTransformMatrix)

        tinyVolumeNode.SetAndObserveTransformNodeID(
            translationTransformNode.GetID())
        geometryWidget.geometryImageData(geometryImageData)
        self.assertTrue(
            self.compareOutputGeometry(
                geometryImageData, (49, 49, 23), (273.3439, 272.7890, -112.25),
                [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]]))
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 94)

        # Volume source with isotropic spacing
        tinyVolumeNode.SetAndObserveTransformNodeID(None)
        geometryWidget.setIsotropicSpacing(True)
        geometryWidget.geometryImageData(geometryImageData)
        self.assertTrue(
            self.compareOutputGeometry(
                geometryImageData, (23, 23, 23), (248.8439, 248.2890, -123.75),
                [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]]))
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 414)

        # Volume source with oversampling
        geometryWidget.setIsotropicSpacing(False)
        geometryWidget.setOversamplingFactor(2.0)
        geometryWidget.geometryImageData(geometryImageData)
        self.assertTrue(
            self.compareOutputGeometry(
                geometryImageData, (24.5, 24.5, 11.5),
                (261.0939, 260.5390, -129.5),
                [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]]))
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 751)
        slicer.util.delayDisplay('Volume source cases - OK')

        # Segmentation source with binary labelmap master
        geometryWidget.setOversamplingFactor(1.0)
        geometryWidget.setSourceNode(tinySegmentationNode)
        geometryWidget.geometryImageData(geometryImageData)
        self.assertTrue(
            self.compareOutputGeometry(
                geometryImageData, (49, 49, 23), (248.8439, 248.2890, -123.75),
                [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]]))
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 92)

        # Segmentation source with closed surface master
        tinySegmentationNode.GetSegmentation().SetConversionParameter(
            'Smoothing factor', '0.0')
        self.assertTrue(
            tinySegmentationNode.GetSegmentation().CreateRepresentation(
                closedSurfaceReprName))
        tinySegmentationNode.GetSegmentation().SetMasterRepresentationName(
            closedSurfaceReprName)
        tinySegmentationNode.Modified(
        )  # Trigger re-calculation of geometry (only generic Modified event is observed)
        geometryWidget.geometryImageData(geometryImageData)
        self.assertTrue(
            self.compareOutputGeometry(
                geometryImageData,
                (1, 1, 1),
                (-86.645, 133.929,
                 116.786),  # current origin of the segmentation is kept
                [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]]))
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData),
                         5223040)
        slicer.util.delayDisplay('Segmentation source cases - OK')

        # Model source with no transform
        shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(
            slicer.mrmlScene)
        outputFolderId = shNode.CreateFolderItem(shNode.GetSceneItemID(),
                                                 'ModelsFolder')
        success = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.ExportVisibleSegmentsToModels(
            tinySegmentationNode, outputFolderId)
        self.assertTrue(success)
        modelNode = slicer.util.getNode('Body_Contour')
        geometryWidget.setSourceNode(modelNode)
        geometryWidget.geometryImageData(geometryImageData)
        self.assertTrue(
            self.compareOutputGeometry(
                geometryImageData,
                (1, 1, 1),
                (-86.645, 133.929,
                 116.786),  # current origin of the segmentation is kept
                [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]]))
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData),
                         5223040)

        # Transformed model source
        rotationTransform = vtk.vtkTransform()
        rotationTransform.RotateX(45)
        rotationTransformMatrix = vtk.vtkMatrix4x4()
        rotationTransform.GetMatrix(rotationTransformMatrix)
        rotationTransformNode = slicer.vtkMRMLLinearTransformNode()
        rotationTransformNode.SetName('TestRotation')
        slicer.mrmlScene.AddNode(rotationTransformNode)
        rotationTransformNode.SetMatrixTransformToParent(
            rotationTransformMatrix)

        modelNode.SetAndObserveTransformNodeID(rotationTransformNode.GetID())
        modelNode.Modified()
        geometryWidget.geometryImageData(geometryImageData)
        self.assertTrue(
            self.compareOutputGeometry(
                geometryImageData, (1, 1, 1), (-86.645, 177.282, -12.122),
                [[0.0, 0.0, 1.0], [-0.7071, -0.7071, 0.0],
                 [0.7071, -0.7071, 0.0]]))
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData),
                         5229164)

        # ROI source
        roiNode = slicer.mrmlScene.AddNewNodeByClass(
            "vtkMRMLAnnotationROINode", 'SourceROI')
        xyz = [0.0, 0.0, 0.0]
        center = [0.0, 0.0, 0.0]
        slicer.vtkMRMLSliceLogic.GetVolumeRASBox(tinyVolumeNode, xyz, center)
        radius = [x / 2.0 for x in xyz]
        roiNode.SetXYZ(center)
        roiNode.SetRadiusXYZ(radius)
        geometryWidget.setSourceNode(roiNode)
        geometryWidget.geometryImageData(geometryImageData)
        self.assertTrue(
            self.compareOutputGeometry(
                geometryImageData, (1, 1, 1), (0.0, 0.0, 0.0),
                [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]))
        slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData),
                         5224232)
        slicer.util.delayDisplay('Model and ROI source cases - OK')

        slicer.util.delayDisplay('Segmentation geometry widget test passed')
예제 #23
0
    def splitSegments(self, minimumSize=0, maxNumberOfSegments=0, split=True):
        """
    minimumSize: if 0 then it means that all islands are kept, regardless of size
    maxNumberOfSegments: if 0 then it means that all islands are kept, regardless of how many
    """
        # This can be a long operation - indicate it to the user
        qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)

        self.scriptedEffect.saveStateForUndo()

        # Get modifier labelmap
        selectedSegmentLabelmap = self.scriptedEffect.selectedSegmentLabelmap()

        castIn = vtk.vtkImageCast()
        castIn.SetInputData(selectedSegmentLabelmap)
        castIn.SetOutputScalarTypeToUnsignedInt()

        # Identify the islands in the inverted volume and
        # find the pixel that corresponds to the background
        islandMath = vtkITK.vtkITKIslandMath()
        islandMath.SetInputConnection(castIn.GetOutputPort())
        islandMath.SetFullyConnected(False)
        islandMath.SetMinimumSize(minimumSize)
        islandMath.Update()

        # Create a separate image for the first (largest) island
        labelValue = 1
        backgroundValue = 0
        thresh = vtk.vtkImageThreshold()
        if split:
            thresh.ThresholdBetween(1, 1)
        else:
            if maxNumberOfSegments != 0:
                thresh.ThresholdBetween(1, maxNumberOfSegments)
            else:
                thresh.ThresholdByUpper(1)

        thresh.SetInputData(islandMath.GetOutput())
        thresh.SetOutValue(backgroundValue)
        thresh.SetInValue(labelValue)
        thresh.SetOutputScalarType(selectedSegmentLabelmap.GetScalarType())
        thresh.Update()
        # Create oriented image data from output
        import vtkSegmentationCorePython as vtkSegmentationCore
        largestIslandImage = slicer.vtkOrientedImageData()
        largestIslandImage.ShallowCopy(thresh.GetOutput())
        selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
        selectedSegmentLabelmap.GetImageToWorldMatrix(
            selectedSegmentLabelmapImageToWorldMatrix)
        largestIslandImage.SetImageToWorldMatrix(
            selectedSegmentLabelmapImageToWorldMatrix)

        if split and (maxNumberOfSegments != 1):

            thresh2 = vtk.vtkImageThreshold()
            # 0 is background, 1 is largest island; we need label 2 and higher
            if maxNumberOfSegments != 0:
                thresh2.ThresholdBetween(2, maxNumberOfSegments)
            else:
                thresh2.ThresholdByUpper(2)
            thresh2.SetInputData(islandMath.GetOutput())
            thresh2.SetOutValue(backgroundValue)
            thresh2.ReplaceInOff()
            thresh2.Update()

            islandCount = islandMath.GetNumberOfIslands()
            islandOrigCount = islandMath.GetOriginalNumberOfIslands()
            ignoredIslands = islandOrigCount - islandCount
            logging.info("%d islands created (%d ignored)" %
                         (islandCount, ignoredIslands))

            # Create oriented image data from output
            import vtkSegmentationCorePython as vtkSegmentationCore
            multiLabelImage = slicer.vtkOrientedImageData()
            multiLabelImage.DeepCopy(thresh2.GetOutput())
            selectedSegmentLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
            selectedSegmentLabelmap.GetImageToWorldMatrix(
                selectedSegmentLabelmapImageToWorldMatrix)
            multiLabelImage.SetGeometryFromImageToWorldMatrix(
                selectedSegmentLabelmapImageToWorldMatrix)

            # Import multi-label labelmap to segmentation
            segmentationNode = self.scriptedEffect.parameterSetNode(
            ).GetSegmentationNode()
            selectedSegmentID = self.scriptedEffect.parameterSetNode(
            ).GetSelectedSegmentID()
            selectedSegmentIndex = segmentationNode.GetSegmentation(
            ).GetSegmentIndex(selectedSegmentID)
            insertBeforeSegmentID = segmentationNode.GetSegmentation(
            ).GetNthSegmentID(selectedSegmentIndex + 1)
            selectedSegmentName = segmentationNode.GetSegmentation(
            ).GetSegment(selectedSegmentID).GetName()
            slicer.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode(
                multiLabelImage, segmentationNode, selectedSegmentName + " -",
                insertBeforeSegmentID)

        self.scriptedEffect.modifySelectedSegmentByLabelmap(
            largestIslandImage,
            slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeSet)

        qt.QApplication.restoreOverrideCursor()
    def updateSegmentationMask(self,
                               extreme_points,
                               in_file,
                               modelInfo,
                               overwriteCurrentSegment=False):
        start = time.time()
        logging.debug('Update Segmentation Mask from: {}'.format(in_file))
        if in_file is None or os.path.exists(in_file) is False:
            return False

        segmentationNode = self.scriptedEffect.parameterSetNode(
        ).GetSegmentationNode()
        segmentation = segmentationNode.GetSegmentation()
        currentSegment = self.currentSegment()

        labelImage = sitk.ReadImage(in_file)
        labelmapVolumeNode = sitkUtils.PushVolumeToSlicer(
            labelImage, None, className='vtkMRMLLabelMapVolumeNode')

        numberOfExistingSegments = segmentation.GetNumberOfSegments()
        slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(
            labelmapVolumeNode, segmentationNode)
        slicer.mrmlScene.RemoveNode(labelmapVolumeNode)

        modelLabels = modelInfo['labels']
        numberOfAddedSegments = segmentation.GetNumberOfSegments(
        ) - numberOfExistingSegments
        logging.debug('Adding {} segments'.format(numberOfAddedSegments))
        addedSegmentIds = [
            segmentation.GetNthSegmentID(numberOfExistingSegments + i)
            for i in range(numberOfAddedSegments)
        ]
        for i, segmentId in enumerate(addedSegmentIds):
            segment = segmentation.GetSegment(segmentId)
            if i == 0 and overwriteCurrentSegment and currentSegment:
                logging.debug(
                    'Update current segment with id: {} => {}'.format(
                        segmentId, segment.GetName()))
                # Copy labelmap representation to the current segment then remove the imported segment
                labelmap = slicer.vtkOrientedImageData()
                segmentationNode.GetBinaryLabelmapRepresentation(
                    segmentId, labelmap)
                self.scriptedEffect.modifySelectedSegmentByLabelmap(
                    labelmap, slicer.qSlicerSegmentEditorAbstractEffect.
                    ModificationModeSet)
                segmentationNode.RemoveSegment(segmentId)
            else:
                logging.debug(
                    'Setting new segmentation with id: {} => {}'.format(
                        segmentId, segment.GetName()))
                if i < len(modelLabels):
                    segment.SetName(modelLabels[i])
                else:
                    # we did not get enough labels (for exampe annotation_mri_prostate_cg_and_pz model returns a labelmap with
                    # 2 labels but in the model infor only 1 label is provided)
                    segment.SetName("unknown {}".format(i))

        # Save extreme points into first segment
        if extreme_points:
            logging.debug('Extreme Points: {}'.format(extreme_points))
            if overwriteCurrentSegment and currentSegment:
                segment = currentSegment
            else:
                segment = segmentation.GetNthSegment(numberOfExistingSegments)
            if segment:
                segment.SetTag("AIAA.DExtr3DExtremePoints",
                               json.dumps(extreme_points))

        os.unlink(in_file)
        logging.info(
            "Time consumed by updateSegmentationMask: {0:3.1f}".format(
                time.time() - start))
        return True
    def updateSegmentationMask(self,
                               extreme_points,
                               in_file,
                               modelInfo,
                               overwriteCurrentSegment=False):
        print('updateSegmentationMask()')
        start = time.time()
        logging.debug('Update Segmentation Mask from: {}'.format(in_file))
        if in_file is None or os.path.exists(in_file) is False:
            return False
        else:
            segmentationNode = self.scriptedEffect.parameterSetNode(
            ).GetSegmentationNode()
            segmentation = segmentationNode.GetSegmentation()
            currentSegment = self.currentSegment()
            labelImage = sitk.ReadImage(in_file)
            labelmapVolumeNode = sitkUtils.PushVolumeToSlicer(
                labelImage, None, className='vtkMRMLLabelMapVolumeNode')
            numberOfExistingSegments = segmentation.GetNumberOfSegments()
            slicer.modules.segmentations.logic(
            ).ImportLabelmapToSegmentationNode(labelmapVolumeNode,
                                               segmentationNode)
            slicer.mrmlScene.RemoveNode(labelmapVolumeNode)
            modelLabels = modelInfo['labels']
            numberOfAddedSegments = segmentation.GetNumberOfSegments(
            ) - numberOfExistingSegments
            logging.debug('Adding {} segments'.format(numberOfAddedSegments))
            addedSegmentIds = [
                segmentation.GetNthSegmentID(numberOfExistingSegments + i)
                for i in range(numberOfAddedSegments)
            ]
            for i, segmentId in enumerate(addedSegmentIds):
                segment = segmentation.GetSegment(segmentId)
                if i == 0:
                    if overwriteCurrentSegment:
                        if currentSegment:
                            logging.debug(
                                'Update current segment with id: {} => {}'.
                                format(segmentId, segment.GetName()))
                            labelmap = slicer.vtkOrientedImageData()
                            segmentationNode.GetBinaryLabelmapRepresentation(
                                segmentId, labelmap)
                            self.scriptedEffect.modifySelectedSegmentByLabelmap(
                                labelmap,
                                slicer.qSlicerSegmentEditorAbstractEffect.
                                ModificationModeSet)
                            segmentationNode.RemoveSegment(segmentId)
                    logging.debug(
                        'Setting new segmentation with id: {} => {}'.format(
                            segmentId, segment.GetName()))
                    if i < len(modelLabels):
                        segment.SetName(modelLabels[i])
                    else:
                        segment.SetName('unknown {}'.format(i))

            if extreme_points:
                logging.debug('Extreme Points: {}'.format(extreme_points))
                if overwriteCurrentSegment:
                    if currentSegment:
                        segment = currentSegment
                else:
                    segment = segmentation.GetNthSegment(
                        numberOfExistingSegments)
                if segment:
                    segment.SetTag('AIAA.DExtr3DExtremePoints',
                                   json.dumps(extreme_points))
            os.unlink(in_file)
            logging.info(
                'Time consumed by updateSegmentationMask: {0:3.1f}'.format(
                    time.time() - start))
            return True
예제 #26
0
def getBinaryLabelmapRepresentation(segmentationNode, segmentID: str):
  segmentLabelmap = slicer.vtkOrientedImageData()
  segmentationNode.GetBinaryLabelmapRepresentation(segmentID, segmentLabelmap)
  return segmentLabelmap
    def applySegmentation(self):

        if not self.segmentEditorWidget.activeEffect():
            # no region growing was done
            return

        import time
        startTime = time.time()

        self.showStatusMessage('Finalize region growing...')
        # Ensure closed surface representation is not present (would slow down computations)
        self.outputSegmentation.RemoveClosedSurfaceRepresentation()

        effect = self.segmentEditorWidget.activeEffect()
        effect.self().onApply()

        segmentEditorNode = self.segmentEditorWidget.mrmlSegmentEditorNode()

        # disable intensity masking, otherwise vessels do not fill
        segmentEditorNode.SetMasterVolumeIntensityMask(False)

        # Prevent confirmation popup for editing a hidden segment
        previousConfirmEditHiddenSegmentSetting = slicer.app.settings().value(
            "Segmentations/ConfirmEditHiddenSegment")
        slicer.app.settings().setValue(
            "Segmentations/ConfirmEditHiddenSegment", qt.QMessageBox.No)

        segmentIds = [
            self.rightLungSegmentId, self.leftLungSegmentId,
            self.tracheaSegmentId
        ]

        # fill holes
        for i, segmentId in enumerate(segmentIds):
            self.showStatusMessage(
                f'Filling holes ({i+1}/{len(segmentIds)})...')
            segmentEditorNode.SetSelectedSegmentID(segmentId)
            self.segmentEditorWidget.setActiveEffectByName("Smoothing")
            effect = self.segmentEditorWidget.activeEffect()
            effect.setParameter("SmoothingMethod", "MORPHOLOGICAL_CLOSING")
            effect.setParameter("KernelSizeMm", "12")
            effect.self().onApply()

        # switch to full-resolution segmentation (this is quick, there is no need for progress message)
        self.outputSegmentation.SetReferenceImageGeometryParameterFromVolumeNode(
            self.inputVolume)
        referenceGeometryString = self.outputSegmentation.GetSegmentation(
        ).GetConversionParameter(slicer.vtkSegmentationConverter.
                                 GetReferenceImageGeometryParameterName())
        referenceGeometryImageData = slicer.vtkOrientedImageData()
        slicer.vtkSegmentationConverter.DeserializeImageGeometry(
            referenceGeometryString, referenceGeometryImageData, False)
        wasModified = self.outputSegmentation.StartModify()
        for i, segmentId in enumerate(segmentIds):
            currentSegment = self.outputSegmentation.GetSegmentation(
            ).GetSegment(segmentId)
            # Get master labelmap from segment
            currentLabelmap = currentSegment.GetRepresentation(
                "Binary labelmap")
            # Resample
            if not slicer.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
                    currentLabelmap, referenceGeometryImageData,
                    currentLabelmap, False, True):
                raise ValueError(
                    "Failed to resample segment " << currentSegment.GetName())
        self.segmentEditorWidget.setMasterVolumeNode(self.inputVolume)
        # Trigger display update
        self.outputSegmentation.Modified()
        self.outputSegmentation.EndModify(wasModified)

        # Final smoothing
        for i, segmentId in enumerate(segmentIds):
            if self.detailedAirways and segmentId == self.tracheaSegmentId:
                print('Not smooth airways ...')
                # do not smooth the airways
            else:
                self.showStatusMessage(
                    f'Final smoothing ({i+1}/{len(segmentIds)})...')
                segmentEditorNode.SetSelectedSegmentID(segmentId)
                self.segmentEditorWidget.setActiveEffectByName("Smoothing")
                effect = self.segmentEditorWidget.activeEffect()
                effect.setParameter("SmoothingMethod", "GAUSSIAN")
                effect.setParameter("KernelSizeMm", "2")
                effect.self().onApply()

        self.outputSegmentation.GetDisplayNode().SetOpacity3D(0.5)
        self.outputSegmentation.GetDisplayNode().SetVisibility(True)
        self.outputSegmentation.CreateClosedSurfaceRepresentation()

        # Restore confirmation popup setting for editing a hidden segment
        slicer.app.settings().setValue(
            "Segmentations/ConfirmEditHiddenSegment",
            previousConfirmEditHiddenSegmentSetting)

        slicer.mrmlScene.RemoveNode(self.rightLungFiducials)
        slicer.mrmlScene.RemoveNode(self.leftLungFiducials)
        slicer.mrmlScene.RemoveNode(self.tracheaFiducials)

        self.removeTemporaryObjects()
        self.segmentationStarted = False
        self.segmentationFinished = True

        stopTime = time.time()
        logging.info(
            'ApplySegmentation completed in {0:.2f} seconds'.format(stopTime -
                                                                    startTime))
예제 #28
0
  def apply(self, ijkPoints):
    kernelSizePixel = self.getKernelSizePixel()
    if kernelSizePixel[0]<=0 and kernelSizePixel[1]<=0 and kernelSizePixel[2]<=0:
      return

    qt.QApplication.setOverrideCursor(qt.Qt.WaitCursor)

    # Get parameter set node
    parameterSetNode = self.scriptedEffect.parameterSetNode()

    # Get parameters
    minimumThreshold = self.scriptedEffect.doubleParameter("MinimumThreshold")
    maximumThreshold = self.scriptedEffect.doubleParameter("MaximumThreshold")

    # Get modifier labelmap
    modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()

    # Get master volume image data
    masterImageData = self.scriptedEffect.masterVolumeImageData()

    # Set intensity range
    oldMasterVolumeIntensityMask = parameterSetNode.GetMasterVolumeIntensityMask()
    parameterSetNode.MasterVolumeIntensityMaskOn()
    oldIntensityMaskRange = parameterSetNode.GetMasterVolumeIntensityMaskRange()
    intensityRange = [265.00, 1009.00]
    if oldMasterVolumeIntensityMask:
      intensityRange = [max(oldIntensityMaskRange[0], minimumThreshold), min(oldIntensityMaskRange[1], maximumThreshold)]
    parameterSetNode.SetMasterVolumeIntensityMaskRange(intensityRange)

    roiNode = lumbarSeed ##self.roiSelector.currentNode()
    clippedMasterImageData = masterImageData
    if roiNode is not None:
      worldToImageMatrix = vtk.vtkMatrix4x4()
      masterImageData.GetWorldToImageMatrix(worldToImageMatrix)

      bounds = [0,0,0,0,0,0]
      roiNode.GetRASBounds(bounds)
      corner1RAS = [bounds[0], bounds[2], bounds[4], 1]
      corner1IJK = [0, 0, 0, 0]
      worldToImageMatrix.MultiplyPoint(corner1RAS, corner1IJK)

      corner2RAS = [bounds[1], bounds[3], bounds[5], 1]
      corner2IJK = [0, 0, 0, 0]
      worldToImageMatrix.MultiplyPoint(corner2RAS, corner2IJK)

      extent = [0, -1, 0, -1, 0, -1]
      for i in range(3):
          lowerPoint = min(corner1IJK[i], corner2IJK[i])
          upperPoint = max(corner1IJK[i], corner2IJK[i])
          extent[2*i] = int(math.floor(lowerPoint))
          extent[2*i+1] = int(math.ceil(upperPoint))

      imageToWorldMatrix = vtk.vtkMatrix4x4()
      masterImageData.GetImageToWorldMatrix(imageToWorldMatrix)
      clippedMasterImageData = slicer.vtkOrientedImageData()
      self.padder = vtk.vtkImageConstantPad()
      self.padder.SetInputData(masterImageData)
      self.padder.SetOutputWholeExtent(extent)
      self.padder.Update()
      clippedMasterImageData.ShallowCopy(self.padder.GetOutput())
      clippedMasterImageData.SetImageToWorldMatrix(imageToWorldMatrix)

    # Pipeline
    self.thresh = vtk.vtkImageThreshold()
    self.thresh.SetInValue(LABEL_VALUE)
    self.thresh.SetOutValue(BACKGROUND_VALUE)
    self.thresh.SetInputData(clippedMasterImageData)
    self.thresh.ThresholdBetween(minimumThreshold, maximumThreshold)
    self.thresh.SetOutputScalarTypeToUnsignedChar()
    self.thresh.Update()

    self.erode = vtk.vtkImageDilateErode3D()
    self.erode.SetInputConnection(self.thresh.GetOutputPort())
    self.erode.SetDilateValue(BACKGROUND_VALUE)
    self.erode.SetErodeValue(LABEL_VALUE)
    self.erode.SetKernelSize(
      kernelSizePixel[0],
      kernelSizePixel[1],
      kernelSizePixel[2])

    self.erodeCast = vtk.vtkImageCast()
    self.erodeCast.SetInputConnection(self.erode.GetOutputPort())
    self.erodeCast.SetOutputScalarTypeToUnsignedInt()
    self.erodeCast.Update()

    # Remove small islands
    self.islandMath = vtkITK.vtkITKIslandMath()
    self.islandMath.SetInputConnection(self.erodeCast.GetOutputPort())
    self.islandMath.SetFullyConnected(False)
    self.islandMath.SetMinimumSize(125)  # remove regions smaller than 5x5x5 voxels

    self.islandThreshold = vtk.vtkImageThreshold()
    self.islandThreshold.SetInputConnection(self.islandMath.GetOutputPort())
    self.islandThreshold.ThresholdByLower(BACKGROUND_VALUE)
    self.islandThreshold.SetInValue(BACKGROUND_VALUE)
    self.islandThreshold.SetOutValue(LABEL_VALUE)
    self.islandThreshold.SetOutputScalarTypeToUnsignedChar()
    self.islandThreshold.Update()

    # Points may be outside the region after it is eroded.
    # Snap the points to LABEL_VALUE voxels,
    snappedIJKPoints = self.snapIJKPointsToLabel(ijkPoints, self.islandThreshold.GetOutput())
    if snappedIJKPoints.GetNumberOfPoints() == 0:
      qt.QApplication.restoreOverrideCursor()
      return

    # Convert points to real data coordinates. Required for vtkImageThresholdConnectivity.
    seedPoints = vtk.vtkPoints()
    origin = masterImageData.GetOrigin()
    spacing = masterImageData.GetSpacing()
    for i in range(snappedIJKPoints.GetNumberOfPoints()):
      ijkPoint = snappedIJKPoints.GetPoint(i)
      seedPoints.InsertNextPoint(
        origin[0]+ijkPoint[0]*spacing[0],
        origin[1]+ijkPoint[1]*spacing[1],
        origin[2]+ijkPoint[2]*spacing[2])

    segmentationAlgorithm = self.scriptedEffect.parameter(SEGMENTATION_ALGORITHM_PARAMETER_NAME)
    if segmentationAlgorithm == SEGMENTATION_ALGORITHM_MASKING:
      self.runMasking(seedPoints, self.islandThreshold.GetOutput(), modifierLabelmap)

    else:
      self.floodFillingFilterIsland = vtk.vtkImageThresholdConnectivity()
      self.floodFillingFilterIsland.SetInputConnection(self.islandThreshold.GetOutputPort())
      self.floodFillingFilterIsland.SetInValue(SELECTED_ISLAND_VALUE)
      self.floodFillingFilterIsland.ReplaceInOn()
      self.floodFillingFilterIsland.ReplaceOutOff()
      self.floodFillingFilterIsland.ThresholdBetween(265.00, 1009.00)
      self.floodFillingFilterIsland.SetSeedPoints(seedPoints)
      self.floodFillingFilterIsland.Update()

      self.maskCast = vtk.vtkImageCast()
      self.maskCast.SetInputData(self.thresh.GetOutput())
      self.maskCast.SetOutputScalarTypeToUnsignedChar()
      self.maskCast.Update()

      self.imageMask = vtk.vtkImageMask()
      self.imageMask.SetInputConnection(self.floodFillingFilterIsland.GetOutputPort())
      self.imageMask.SetMaskedOutputValue(OUTSIDE_THRESHOLD_VALUE)
      self.imageMask.SetMaskInputData(self.maskCast.GetOutput())
      self.imageMask.Update()

      imageMaskOutput = slicer.vtkOrientedImageData()
      imageMaskOutput.ShallowCopy(self.imageMask.GetOutput())
      imageMaskOutput.CopyDirections(clippedMasterImageData)

      imageToWorldMatrix = vtk.vtkMatrix4x4()
      imageMaskOutput.GetImageToWorldMatrix(imageToWorldMatrix)

      segmentOutputLabelmap = slicer.vtkOrientedImageData()
      if segmentationAlgorithm == SEGMENTATION_ALGORITHM_GROWCUT:
        self.runGrowCut(clippedMasterImageData, imageMaskOutput, segmentOutputLabelmap)
      elif segmentationAlgorithm == SEGMENTATION_ALGORITHM_WATERSHED:
        self.runWatershed(clippedMasterImageData, imageMaskOutput, segmentOutputLabelmap)
      else:
        logging.error("Unknown segmentation algorithm: \"" + segmentationAlgorithm + "\"")

      segmentOutputLabelmap.SetImageToWorldMatrix(imageToWorldMatrix)

      self.selectedSegmentThreshold = vtk.vtkImageThreshold()
      self.selectedSegmentThreshold.SetInputData(segmentOutputLabelmap)
      self.selectedSegmentThreshold.ThresholdBetween(SELECTED_ISLAND_VALUE, SELECTED_ISLAND_VALUE)
      self.selectedSegmentThreshold.SetInValue(LABEL_VALUE)
      self.selectedSegmentThreshold.SetOutValue(BACKGROUND_VALUE)
      self.selectedSegmentThreshold.SetOutputScalarType(modifierLabelmap.GetScalarType())
      self.selectedSegmentThreshold.Update()
      modifierLabelmap.ShallowCopy(self.selectedSegmentThreshold.GetOutput())

    self.scriptedEffect.saveStateForUndo()
    self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd)

    parameterSetNode.SetMasterVolumeIntensityMask(oldMasterVolumeIntensityMask)
    parameterSetNode.SetMasterVolumeIntensityMaskRange(oldIntensityMaskRange)

    qt.QApplication.restoreOverrideCursor()
예제 #29
0
    def computeStatistics(self, segmentID):
        import vtkSegmentationCorePython as vtkSegmentationCore
        requestedKeys = self.getRequestedKeys()

        segmentationNode = slicer.mrmlScene.GetNodeByID(
            self.getParameterNode().GetParameter("Segmentation"))

        if len(requestedKeys) == 0:
            return {}

        containsLabelmapRepresentation = segmentationNode.GetSegmentation(
        ).ContainsRepresentation(
            vtkSegmentationCore.vtkSegmentationConverter.
            GetSegmentationBinaryLabelmapRepresentationName())
        if not containsLabelmapRepresentation:
            return {}

        segmentLabelmap = slicer.vtkOrientedImageData()
        segmentationNode.GetBinaryLabelmapRepresentation(
            segmentID, segmentLabelmap)
        if (not segmentLabelmap or not segmentLabelmap.GetPointData()
                or not segmentLabelmap.GetPointData().GetScalars()):
            # No input label data
            return {}

        # We need to know exactly the value of the segment voxels, apply threshold to make force the selected label value
        labelValue = 1
        backgroundValue = 0
        thresh = vtk.vtkImageThreshold()
        thresh.SetInputData(segmentLabelmap)
        thresh.ThresholdByLower(0)
        thresh.SetInValue(backgroundValue)
        thresh.SetOutValue(labelValue)
        thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
        thresh.Update()

        #  Use binary labelmap as a stencil
        stencil = vtk.vtkImageToImageStencil()
        stencil.SetInputData(thresh.GetOutput())
        stencil.ThresholdByUpper(labelValue)
        stencil.Update()

        stat = vtk.vtkImageAccumulate()
        stat.SetInputData(thresh.GetOutput())
        stat.SetStencilData(stencil.GetOutput())
        stat.Update()

        # Add data to statistics list
        cubicMMPerVoxel = reduce(lambda x, y: x * y,
                                 segmentLabelmap.GetSpacing())
        ccPerCubicMM = 0.001
        stats = {}
        if "voxel_count" in requestedKeys:
            stats["voxel_count"] = stat.GetVoxelCount()
        if "volume_mm3" in requestedKeys:
            stats["volume_mm3"] = stat.GetVoxelCount() * cubicMMPerVoxel
        if "volume_cm3" in requestedKeys:
            stats["volume_cm3"] = stat.GetVoxelCount(
            ) * cubicMMPerVoxel * ccPerCubicMM

        calculateShapeStats = False
        for shapeKey in self.shapeKeys:
            if shapeKey in requestedKeys:
                calculateShapeStats = True
                break

        if calculateShapeStats:
            directions = vtk.vtkMatrix4x4()
            segmentLabelmap.GetDirectionMatrix(directions)

            # Remove oriented bounding box from requested keys and replace with individual keys
            requestedOptions = requestedKeys
            statFilterOptions = self.shapeKeys
            calculateOBB = ("obb_diameter_mm" in requestedKeys
                            or "obb_origin_ras" in requestedKeys
                            or "obb_direction_ras_x" in requestedKeys
                            or "obb_direction_ras_y" in requestedKeys
                            or "obb_direction_ras_z" in requestedKeys)

            if calculateOBB:
                temp = statFilterOptions
                statFilterOptions = []
                for option in temp:
                    if not option in self.obbKeys:
                        statFilterOptions.append(option)
                statFilterOptions.append("oriented_bounding_box")

                temp = requestedOptions
                requestedOptions = []
                for option in temp:
                    if not option in self.obbKeys:
                        requestedOptions.append(option)
                requestedOptions.append("oriented_bounding_box")

            shapeStat = vtkITK.vtkITKLabelShapeStatistics()
            shapeStat.SetInputData(thresh.GetOutput())
            shapeStat.SetDirections(directions)
            for shapeKey in statFilterOptions:
                shapeStat.SetComputeShapeStatistic(
                    self.keyToShapeStatisticNames[shapeKey], shapeKey
                    in requestedOptions)
            shapeStat.Update()

            # If segmentation node is transformed, apply that transform to get RAS coordinates
            transformSegmentToRas = vtk.vtkGeneralTransform()
            slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(
                segmentationNode.GetParentTransformNode(), None,
                transformSegmentToRas)

            statTable = shapeStat.GetOutput()
            if "centroid_ras" in requestedKeys:
                centroidRAS = [0, 0, 0]
                centroidArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["centroid_ras"])
                centroid = centroidArray.GetTuple(0)
                transformSegmentToRas.TransformPoint(centroid, centroidRAS)
                stats["centroid_ras"] = centroidRAS

            if "roundness" in requestedKeys:
                roundnessArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["roundness"])
                roundness = roundnessArray.GetTuple(0)[0]
                stats["roundness"] = roundness

            if "flatness" in requestedKeys:
                flatnessArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["flatness"])
                flatness = flatnessArray.GetTuple(0)[0]
                stats["flatness"] = flatness

            if "feret_diameter_mm" in requestedKeys:
                feretDiameterArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["feret_diameter_mm"])
                feretDiameter = feretDiameterArray.GetTuple(0)[0]
                stats["feret_diameter_mm"] = feretDiameter

            if "surface_area_mm2" in requestedKeys:
                perimeterArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["surface_area_mm2"])
                perimeter = perimeterArray.GetTuple(0)[0]
                stats["surface_area_mm2"] = perimeter

            if "obb_origin_ras" in requestedKeys:
                obbOriginRAS = [0, 0, 0]
                obbOriginArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["obb_origin_ras"])
                obbOrigin = obbOriginArray.GetTuple(0)
                transformSegmentToRas.TransformPoint(obbOrigin, obbOriginRAS)
                stats["obb_origin_ras"] = obbOriginRAS

            if "obb_diameter_mm" in requestedKeys:
                obbDiameterArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["obb_diameter_mm"])
                obbDiameterMM = list(obbDiameterArray.GetTuple(0))
                stats["obb_diameter_mm"] = obbDiameterMM

            if "obb_direction_ras_x" in requestedKeys:
                obbOriginArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["obb_origin_ras"])
                obbOrigin = obbOriginArray.GetTuple(0)
                obbDirectionXArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["obb_direction_ras_x"])
                obbDirectionX = list(obbDirectionXArray.GetTuple(0))
                transformSegmentToRas.TransformVectorAtPoint(
                    obbOrigin, obbDirectionX, obbDirectionX)
                stats["obb_direction_ras_x"] = obbDirectionX

            if "obb_direction_ras_y" in requestedKeys:
                obbOriginArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["obb_origin_ras"])
                obbOrigin = obbOriginArray.GetTuple(0)
                obbDirectionYArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["obb_direction_ras_y"])
                obbDirectionY = list(obbDirectionYArray.GetTuple(0))
                transformSegmentToRas.TransformVectorAtPoint(
                    obbOrigin, obbDirectionY, obbDirectionY)
                stats["obb_direction_ras_y"] = obbDirectionY

            if "obb_direction_ras_z" in requestedKeys:
                obbOriginArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["obb_origin_ras"])
                obbOrigin = obbOriginArray.GetTuple(0)
                obbDirectionZArray = statTable.GetColumnByName(
                    self.keyToShapeStatisticNames["obb_direction_ras_z"])
                obbDirectionZ = list(obbDirectionZArray.GetTuple(0))
                transformSegmentToRas.TransformVectorAtPoint(
                    obbOrigin, obbDirectionZ, obbDirectionZ)
                stats["obb_direction_ras_z"] = obbDirectionZ

        return stats
예제 #30
0
  def createMeshFromSegmentationCleaver(self, inputSegmentation, outputMeshNode, additionalParameters = None, removeBackgroundMesh = False, paddingRatio = 0.10):

    if additionalParameters is None:
      additionalParameters="--scale 0.2 --multiplier 2 --grading 5"

    self.abortRequested = False
    tempDir = self.createTempDirectory()
    self.addLog('Mesh generation using Cleaver is started in working directory: '+tempDir)

    inputParamsCleaver = []

    # Write inputs
    qt.QDir().mkpath(tempDir)

    # Create temporary labelmap node. It will be used both for storing reference geometry
    # and resulting merged labelmap.
    labelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLabelMapVolumeNode')
    parentTransformNode  = inputSegmentation.GetParentTransformNode()
    labelmapVolumeNode.SetAndObserveTransformNodeID(parentTransformNode.GetID() if parentTransformNode else None)

    # Create binary labelmap representation using default parameters
    if not inputSegmentation.GetSegmentation().CreateRepresentation(slicer.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName()):
      self.addLog('Failed to create binary labelmap representation')
      return

    # Set reference geometry in labelmapVolumeNode
    referenceGeometry_Segmentation = slicer.vtkOrientedImageData()
    inputSegmentation.GetSegmentation().SetImageGeometryFromCommonLabelmapGeometry(referenceGeometry_Segmentation, None,
      slicer.vtkSegmentation.EXTENT_REFERENCE_GEOMETRY)
    slicer.modules.segmentations.logic().CopyOrientedImageDataToVolumeNode(referenceGeometry_Segmentation, labelmapVolumeNode)

    # Add margin
    extent = labelmapVolumeNode.GetImageData().GetExtent()
    paddedExtent = [0, -1, 0, -1, 0, -1]
    for axisIndex in range(3):
      paddingSizeVoxels = int((extent[axisIndex * 2 + 1] - extent[axisIndex * 2]) * paddingRatio)
      paddedExtent[axisIndex * 2] = extent[axisIndex * 2] - paddingSizeVoxels
      paddedExtent[axisIndex * 2 + 1] = extent[axisIndex * 2 + 1] + paddingSizeVoxels
    labelmapVolumeNode.GetImageData().SetExtent(paddedExtent)
    labelmapVolumeNode.ShiftImageDataExtentToZeroStart()

    # Get merged labelmap
    segmentIdList = vtk.vtkStringArray()
    slicer.modules.segmentations.logic().ExportSegmentsToLabelmapNode(inputSegmentation, segmentIdList, labelmapVolumeNode, labelmapVolumeNode)

    inputLabelmapVolumeFilePath = os.path.join(tempDir, "inputLabelmap.nrrd")
    slicer.util.saveNode(labelmapVolumeNode, inputLabelmapVolumeFilePath, {"useCompression": False})
    inputParamsCleaver.extend(["--input_files", inputLabelmapVolumeFilePath])
    inputParamsCleaver.append("--segmentation")

    # Keep IJK to RAS matrix, we'll need it later
    unscaledIjkToRasMatrix = vtk.vtkMatrix4x4()
    labelmapVolumeNode.GetIJKToRASDirectionMatrix(unscaledIjkToRasMatrix)
    origin = labelmapVolumeNode.GetOrigin()
    for i in range(3):
      unscaledIjkToRasMatrix.SetElement(i,3, origin[i])

    # Keep color node, we'll need it later
    colorTableNode = labelmapVolumeNode.GetDisplayNode().GetColorNode()
    # Background color is transparent by default which is not ideal for 3D display
    colorTableNode.SetColor(0,0.6,0.6,0.6,1.0)

    slicer.mrmlScene.RemoveNode(labelmapVolumeNode)
    slicer.mrmlScene.RemoveNode(colorTableNode)

    # Set up output format

    inputParamsCleaver.extend(["--output_path", tempDir+"/"])
    inputParamsCleaver.extend(["--output_format", "vtkUSG"]) # VTK unstructed grid
    inputParamsCleaver.append("--fix_tet_windup") # prevent inside-out tets
    inputParamsCleaver.append("--strip_exterior") # remove temporary elements that are added to make the volume cubic

    inputParamsCleaver.append("--verbose")

    # Quality
    inputParamsCleaver.extend(slicer.util.toVTKString(additionalParameters).split(' '))

    # Run Cleaver
    ep = self.startMesher(inputParamsCleaver, self.getCleaverPath())
    self.logProcessOutput(ep, self.cleaverFilename)

    # Read results
    if not self.abortRequested:
      outputVolumetricMeshPath = os.path.join(tempDir, "output.vtk")
      outputReader = vtk.vtkUnstructuredGridReader()
      outputReader.SetFileName(outputVolumetricMeshPath)
      outputReader.ReadAllScalarsOn()
      outputReader.ReadAllVectorsOn()
      outputReader.ReadAllNormalsOn()
      outputReader.ReadAllTensorsOn()
      outputReader.ReadAllColorScalarsOn()
      outputReader.ReadAllTCoordsOn()
      outputReader.ReadAllFieldsOn()
      outputReader.Update()

      # Cleaver returns the mesh in voxel coordinates, need to transform to RAS space
      transformer = vtk.vtkTransformFilter()
      transformer.SetInputData(outputReader.GetOutput())
      ijkToRasTransform = vtk.vtkTransform()
      ijkToRasTransform.SetMatrix(unscaledIjkToRasMatrix)
      transformer.SetTransform(ijkToRasTransform)

      if removeBackgroundMesh:
        transformer.Update()
        mesh = transformer.GetOutput()
        cellData = mesh.GetCellData()
        cellData.SetActiveScalars("labels")
        backgroundMeshRemover = vtk.vtkThreshold()
        backgroundMeshRemover.SetInputData(mesh)
        backgroundMeshRemover.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject.FIELD_ASSOCIATION_CELLS, vtk.vtkDataSetAttributes.SCALARS)
        backgroundMeshRemover.ThresholdByUpper(1)
        outputMeshNode.SetUnstructuredGridConnection(backgroundMeshRemover.GetOutputPort())
      else:
        outputMeshNode.SetUnstructuredGridConnection(transformer.GetOutputPort())

      outputMeshDisplayNode = outputMeshNode.GetDisplayNode()
      if not outputMeshDisplayNode:
        # Initial setup of display node
        outputMeshNode.CreateDefaultDisplayNodes()

        outputMeshDisplayNode = outputMeshNode.GetDisplayNode()
        outputMeshDisplayNode.SetEdgeVisibility(True)
        outputMeshDisplayNode.SetClipping(True)

        colorTableNode = slicer.mrmlScene.AddNode(colorTableNode)
        outputMeshDisplayNode.SetAndObserveColorNodeID(colorTableNode.GetID())

        outputMeshDisplayNode.ScalarVisibilityOn()
        outputMeshDisplayNode.SetActiveScalarName('labels')
        outputMeshDisplayNode.SetActiveAttributeLocation(vtk.vtkAssignAttribute.CELL_DATA)
        outputMeshDisplayNode.SetSliceIntersectionVisibility(True)
        outputMeshDisplayNode.SetSliceIntersectionOpacity(0.5)
        outputMeshDisplayNode.SetScalarRangeFlag(slicer.vtkMRMLDisplayNode.UseColorNodeScalarRange)
      else:
        currentColorNode = outputMeshDisplayNode.GetColorNode()
        if currentColorNode is not None and currentColorNode.GetType() == currentColorNode.User and currentColorNode.IsA("vtkMRMLColorTableNode"):
          # current color table node can be overwritten
          currentColorNode.Copy(colorTableNode)
        else:
          colorTableNode = slicer.mrmlScene.AddNode(colorTableNode)
          outputMeshDisplayNode.SetAndObserveColorNodeID(colorTableNode.GetID())

      # Flip clipping setting twice, this workaround forces update of the display pipeline
      # when switching between surface and volumetric mesh
      outputMeshDisplayNode.SetClipping(not outputMeshDisplayNode.GetClipping())
      outputMeshDisplayNode.SetClipping(not outputMeshDisplayNode.GetClipping())

    # Clean up
    if self.deleteTemporaryFiles:
      import shutil
      shutil.rmtree(tempDir)

    self.addLog("Model generation is completed")