def resetIslandSegments(self, islandSizes):
    self.segmentation.RemoveAllSegments()

    totalSize = 0
    voxelSizeSum = 0
    for size in islandSizes:
      totalSize += size + 1
      voxelSizeSum += size

    mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData()
    mergedLabelmapExtent = [0, totalSize-1, 0, 0, 0, 0]
    self.setupIslandLabelmap(mergedLabelmap, mergedLabelmapExtent, 0)

    emptySegment = slicer.vtkSegment()
    emptySegment.SetName("Segment_1")
    emptySegment.AddRepresentation(self.binaryLabelmapReprName, mergedLabelmap)
    self.segmentation.AddSegment(emptySegment)
    self.segmentEditorNode.SetSelectedSegmentID("Segment_1")

    startExtent = 0
    for size in islandSizes:
      islandLabelmap = vtkSegmentationCore.vtkOrientedImageData()
      islandExtent = [startExtent, startExtent+size-1, 0, 0, 0, 0]
      self.setupIslandLabelmap(islandLabelmap, islandExtent)
      self.paintEffect.modifySelectedSegmentByLabelmap(islandLabelmap, self.paintEffect.ModificationModeAdd)
      startExtent += size + 1
    self.checkSegmentVoxelCount(0, voxelSizeSum)


    layerCount = self.segmentation.GetNumberOfLayers()
    self.assertEqual(layerCount, 1)
Esempio n. 2
0
  def TestSection_TestSharedLabelmapMultipleLayerEditing(self):

    self.segmentation.RemoveAllSegments()
    self.segmentation.AddEmptySegment("Segment_1")
    self.segmentation.AddEmptySegment("Segment_2")

    mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData()
    mergedLabelmap.SetExtent(0, 10, 0, 10, 0, 10)
    mergedLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)

    threshold = vtk.vtkImageThreshold()
    threshold.SetInputData(mergedLabelmap)
    threshold.ThresholdBetween(vtk.VTK_UNSIGNED_CHAR_MIN, vtk.VTK_UNSIGNED_CHAR_MAX)
    threshold.SetInValue(1)
    threshold.SetOutValue(0)
    threshold.Update()
    mergedLabelmap.ShallowCopy(threshold.GetOutput())

    self.segmentEditorNode.SetSelectedSegmentID("Segment_1")
    self.paintEffect.modifySelectedSegmentByLabelmap(mergedLabelmap, self.paintEffect.ModificationModeAdd)
    self.segmentEditorNode.SetSelectedSegmentID("Segment_2")
    self.paintEffect.modifySelectedSegmentByLabelmap(mergedLabelmap, self.paintEffect.ModificationModeAdd)

    layerCount = self.segmentation.GetNumberOfLayers()
    self.assertEqual(layerCount, 1)

    self.segmentEditorNode.SetOverwriteMode(self.segmentEditorNode.OverwriteNone)
    self.segmentEditorNode.SetSelectedSegmentID("Segment_1")
    self.paintEffect.modifySelectedSegmentByLabelmap(mergedLabelmap, self.paintEffect.ModificationModeAdd)
    layerCount = self.segmentation.GetNumberOfLayers()
    self.assertEqual(layerCount, 2)

    logging.info('Multiple layer editing successful')
  def onApply(self):
    try:
      # Get master volume image data
      import vtkSegmentationCore
      masterImageData = vtkSegmentationCore.vtkOrientedImageData()
      self.scriptedEffect.masterVolumeImageData(masterImageData)
      # Get edited labelmap
      editedLabelmap = self.scriptedEffect.parameterSetNode().GetEditedLabelmap()
      # Get parameters
      min = self.scriptedEffect.doubleParameter("MinimumThreshold")
      max = self.scriptedEffect.doubleParameter("MaximumThreshold")

      # Save state for undo
      #TODO:
      #self.undoRedo.saveState()

      # Perform thresholding
      thresh = vtk.vtkImageThreshold()
      thresh.SetInputData(masterImageData)
      thresh.ThresholdBetween(min, max)
      thresh.SetInValue(1)
      thresh.SetOutValue(0)
      thresh.SetOutputScalarType(editedLabelmap.GetScalarType())
      thresh.Update()
      editedLabelmap.DeepCopy(thresh.GetOutput())
    except IndexError:
      logging.error('apply: Failed to threshold master volume!')
      pass

    # Notify editor about changes.
    # This needs to be called so that the changes are written back to the edited segment
    self.scriptedEffect.apply()
    
    # De-select effect
    self.scriptedEffect.selectEffect("")
    def TestSection_SharedLabelmapMultipleLayerEditing(self):

        self.segmentation.RemoveAllSegments()
        self.segmentation.AddEmptySegment("Segment_1")
        self.segmentation.AddEmptySegment("Segment_2")

        mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData()
        mergedLabelmap.SetExtent(0, 10, 0, 10, 0, 10)
        mergedLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
        mergedLabelmap.GetPointData().GetScalars().Fill(1)

        oldOverwriteMode = self.segmentEditorNode.GetOverwriteMode()

        self.segmentEditorNode.SetOverwriteMode(
            self.segmentEditorNode.OverwriteAllSegments)
        self.segmentEditorNode.SetSelectedSegmentID("Segment_1")
        self.paintEffect.modifySelectedSegmentByLabelmap(
            mergedLabelmap, self.paintEffect.ModificationModeAdd)
        self.segmentEditorNode.SetSelectedSegmentID("Segment_2")
        self.paintEffect.modifySelectedSegmentByLabelmap(
            mergedLabelmap, self.paintEffect.ModificationModeAdd)

        layerCount = self.segmentation.GetNumberOfLayers()
        self.assertEqual(layerCount, 1)

        self.segmentEditorNode.SetOverwriteMode(
            self.segmentEditorNode.OverwriteNone)
        self.segmentEditorNode.SetSelectedSegmentID("Segment_1")
        self.paintEffect.modifySelectedSegmentByLabelmap(
            mergedLabelmap, self.paintEffect.ModificationModeAdd)
        layerCount = self.segmentation.GetNumberOfLayers()
        self.assertEqual(layerCount, 2)

        self.segmentEditorNode.SetOverwriteMode(oldOverwriteMode)
        logging.info('Multiple layer editing successful')
  def onApply(self):
    #TODO:
    #self.logic.undoRedo = self.undoRedo
    
    # Get parameters
    fullyConnected = self.scriptedEffect.integerParameter("FullyConnected")
    minimumSize = self.scriptedEffect.integerParameter("MinimumSize")

    # Get edited labelmap
    editedLabelmap = self.scriptedEffect.parameterSetNode().GetEditedLabelmap()

    castIn = vtk.vtkImageCast()
    castIn.SetInputData(editedLabelmap)
    castIn.SetOutputScalarTypeToUnsignedLong()

    # 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(fullyConnected)
    islandMath.SetMinimumSize(minimumSize)

    # Note that island operation happens in unsigned long space
    # but the segment editor works in unsigned char
    castOut = vtk.vtkImageCast()
    castOut.SetInputConnection(islandMath.GetOutputPort())
    castOut.SetOutputScalarTypeToUnsignedChar()
    castOut.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 vtkSegmentationCore
    multiLabelImage = vtkSegmentationCore.vtkOrientedImageData()
    multiLabelImage.DeepCopy(castOut.GetOutput())
    editedLabelmapImageToWorldMatrix = vtk.vtkMatrix4x4()
    editedLabelmap.GetImageToWorldMatrix(editedLabelmapImageToWorldMatrix)
    multiLabelImage.SetGeometryFromImageToWorldMatrix(editedLabelmapImageToWorldMatrix)
    
    # Import multi-label labelmap to segmentation
    import vtkSlicerSegmentationsModuleMRML
    segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
    selectedSegmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
    selectedSegmentName = segmentationNode.GetSegmentation().GetSegment(selectedSegmentID).GetName()
    import vtkSlicerSegmentationsModuleLogic
    vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.ImportLabelmapToSegmentationNode( \
      multiLabelImage, segmentationNode, selectedSegmentName )

    # Set labelmap visibility to outline for the new segments
    displayNode = segmentationNode.GetDisplayNode()
    for index in xrange(1,islandCount+1):
      segmentID = selectedSegmentName + "_" + str(index)
      displayNode.SetSegmentVisibility2DFill(segmentID, False)
      displayNode.SetSegmentVisibility2DOutline(segmentID, True)
 def masterVolumeNodeChanged(self):
   # Set scalar range of master volume image data to threshold slider
   import vtkSegmentationCore
   masterImageData = vtkSegmentationCore.vtkOrientedImageData()
   success = self.scriptedEffect.masterVolumeImageData(masterImageData)
   if success:
     lo, hi = masterImageData.GetScalarRange()
     self.thresholdSlider.minimum, self.thresholdSlider.maximum = lo, hi
     self.thresholdSlider.singleStep = (hi - lo) / 1000.
  def TestSection_MergeLabelmapWithDifferentGeometries(self):
    # Merge labelmap when segments containing labelmaps with different geometries (both same directions, different directions)
    logging.info('Test section: Merge labelmap with different geometries')

    self.assertIsNotNone(self.sphereSegment)
    self.sphereSegment.RemoveRepresentation(self.binaryLabelmapReprName)
    self.assertIsNone(self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName))

    # Create new segmentation with sphere segment
    self.secondSegmentationNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode', 'Second')
    self.secondSegmentationNode.GetSegmentation().SetMasterRepresentationName(self.binaryLabelmapReprName)

    self.secondSegmentationNode.GetSegmentation().AddSegment(self.sphereSegment)

    # Check automatically converted labelmap. It is supposed to have the default geometry
    # (which is different than the one in the input segmentation)
    sphereLabelmap = self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName)
    self.assertIsNotNone(sphereLabelmap)
    sphereLabelmapSpacing = sphereLabelmap.GetSpacing()
    self.assertAlmostEqual(sphereLabelmapSpacing[0], 0.629257364931788, 8)
    self.assertAlmostEqual(sphereLabelmapSpacing[1], 0.629257364931788, 8)
    self.assertAlmostEqual(sphereLabelmapSpacing[2], 0.629257364931788, 8)

    # Create binary labelmap in segmentation that will create the merged labelmap from
    # different geometries so that labelmap is not removed from sphere segment when adding
    self.inputSegmentationNode.GetSegmentation().CreateRepresentation(self.binaryLabelmapReprName)

    # Copy segment to input segmentation
    self.inputSegmentationNode.GetSegmentation().CopySegmentFromSegmentation(self.secondSegmentationNode.GetSegmentation(), self.sphereSegmentName)
    self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3)

    # Check merged labelmap
    # Reference geometry has the tiny patient spacing, and it is oversampled to have similar
    # voxel size as the sphere labelmap with the uniform 0.629mm spacing
    mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData()
    self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0)
    mergedLabelmapSpacing = mergedLabelmap.GetSpacing()
    self.assertAlmostEqual(mergedLabelmapSpacing[0], 0.80327868852459, 8)
    self.assertAlmostEqual(mergedLabelmapSpacing[1], 0.80327868852459, 8)
    self.assertAlmostEqual(mergedLabelmapSpacing[2], 0.377049180327869, 8)

    imageStat = vtk.vtkImageAccumulate()
    imageStat.SetInputData(mergedLabelmap)
    imageStat.SetComponentExtent(0,5,0,0,0,0)
    imageStat.SetComponentOrigin(0,0,0)
    imageStat.SetComponentSpacing(1,1,1)
    imageStat.Update()
    imageStatResult = imageStat.GetOutput()
    for i in range(5):
      logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0)))
    self.assertEqual(imageStat.GetVoxelCount(), 226981000)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 178838889)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 39705288)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 890883)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 7545940)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(4,0,0,0), 0)  # Built from color table and color four is removed in previous test section
Esempio n. 8
0
    def TestSection_MergeLabelmapWithDifferentGeometries(self):
        # Merge labelmap when segments containing labelmaps with different geometries (both same directions, different directions)
        logging.info('Test section: Merge labelmap with different geometries')

        self.assertIsNotNone(self.sphereSegment)
        self.sphereSegment.RemoveRepresentation(self.binaryLabelmapReprName)
        self.assertIsNone(self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName))

        # Create new segmentation with sphere segment
        self.secondSegmentationNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode', 'Second')
        self.secondSegmentationNode.GetSegmentation().SetMasterRepresentationName(self.binaryLabelmapReprName)

        self.secondSegmentationNode.GetSegmentation().AddSegment(self.sphereSegment)

        # Check automatically converted labelmap. It is supposed to have the default geometry
        # (which is different than the one in the input segmentation)
        sphereLabelmap = self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName)
        self.assertIsNotNone(sphereLabelmap)
        sphereLabelmapSpacing = sphereLabelmap.GetSpacing()
        self.assertAlmostEqual(sphereLabelmapSpacing[0], 0.629257364931788, 8)
        self.assertAlmostEqual(sphereLabelmapSpacing[1], 0.629257364931788, 8)
        self.assertAlmostEqual(sphereLabelmapSpacing[2], 0.629257364931788, 8)

        # Create binary labelmap in segmentation that will create the merged labelmap from
        # different geometries so that labelmap is not removed from sphere segment when adding
        self.inputSegmentationNode.GetSegmentation().CreateRepresentation(self.binaryLabelmapReprName)

        # Copy segment to input segmentation
        self.inputSegmentationNode.GetSegmentation().CopySegmentFromSegmentation(self.secondSegmentationNode.GetSegmentation(), self.sphereSegmentName)
        self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3)

        # Check merged labelmap
        # Reference geometry has the tiny patient spacing, and it is oversampled to have similar
        # voxel size as the sphere labelmap with the uniform 0.629mm spacing
        mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData()
        self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0)
        mergedLabelmapSpacing = mergedLabelmap.GetSpacing()
        self.assertAlmostEqual(mergedLabelmapSpacing[0], 0.80327868852459, 8)
        self.assertAlmostEqual(mergedLabelmapSpacing[1], 0.80327868852459, 8)
        self.assertAlmostEqual(mergedLabelmapSpacing[2], 0.377049180327869, 8)

        imageStat = vtk.vtkImageAccumulate()
        imageStat.SetInputData(mergedLabelmap)
        imageStat.SetComponentExtent(0, 5, 0, 0, 0, 0)
        imageStat.SetComponentOrigin(0, 0, 0)
        imageStat.SetComponentSpacing(1, 1, 1)
        imageStat.Update()
        imageStatResult = imageStat.GetOutput()
        for i in range(5):
            logging.info(f"Volume {i}: {imageStatResult.GetScalarComponentAsDouble(i,0,0,0)}")
        self.assertEqual(imageStat.GetVoxelCount(), 226981000)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 178838889)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 39705288)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 890883)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 7545940)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(4, 0, 0, 0), 0)  # Built from color table and color four is removed in previous test section
  def preview(self,xy):
    # Calculate the current level trace view if the mouse is inside the volume extent
    
    # Get master volume image data
    import vtkSegmentationCore
    masterImageData = vtkSegmentationCore.vtkOrientedImageData()
    self.effect.scriptedEffect.masterVolumeImageData(masterImageData)

    self.xyPoints.Reset()
    ijk = self.effect.xyToIjk(xy, self.sliceWidget, masterImageData)
    dimensions = masterImageData.GetDimensions()
    
    for index in xrange(3):
      # TracingFilter crashes if it receives a seed point at the edge of the image,
      # so only accept the point if it is inside the image and is at least one pixel away from the edge
      if ijk[index] < 1 or ijk[index] >= dimensions[index]-1:
        return
    self.tracingFilter.SetInputData(masterImageData)
    self.tracingFilter.SetSeed(ijk)

    # Select the plane corresponding to current slice orientation
    # for the input volume
    sliceNode = self.effect.scriptedEffect.viewNode(self.sliceWidget)
    offset = max(sliceNode.GetDimensions())
    i0,j0,k0 = self.effect.xyToIjk((0,0), self.sliceWidget, masterImageData)
    i1,j1,k1 = self.effect.xyToIjk((offset,offset), self.sliceWidget, masterImageData)
    if i0 == i1:
      self.tracingFilter.SetPlaneToJK()
    if j0 == j1:
      self.tracingFilter.SetPlaneToIK()
    if k0 == k1:
      self.tracingFilter.SetPlaneToIJ()

    self.tracingFilter.Update()
    polyData = self.tracingFilter.GetOutput()

    # Get master volume IJK to slice XY transform
    xyToRas = sliceNode.GetXYToRAS()
    rasToIjk = vtk.vtkMatrix4x4()
    masterImageData.GetImageToWorldMatrix(rasToIjk)
    rasToIjk.Invert()
    xyToIjk = vtk.vtkGeneralTransform()
    xyToIjk.PostMultiply()
    xyToIjk.Concatenate(xyToRas)
    xyToIjk.Concatenate(rasToIjk)
    ijkToXy = xyToIjk.GetInverse()
    ijkToXy.TransformPoints(polyData.GetPoints(), self.xyPoints)

    self.polyData.DeepCopy(polyData)
    self.polyData.GetPoints().DeepCopy(self.xyPoints)
    self.sliceWidget.sliceView().scheduleRender()
  def onApply(self):
    # Get master volume image data
    import vtkSegmentationCore
    masterImageData = vtkSegmentationCore.vtkOrientedImageData()
    self.scriptedEffect.masterVolumeImageData(masterImageData)

    # Check validity of master volume: whether scalar type is supported
    if masterImageData.GetScalarType() != vtk.VTK_SHORT:
      logging.warning("GrowCut is attempted with image type '{0}', which is not directly supported".format(masterImageData.GetScalarTypeAsString()))
      if not slicer.util.confirmOkCancelDisplay("Current image type is '{0}', which is not supported by GrowCut. "
          "The image scalars will be temporarily casted to short.\n\nIf the segmentation result is not satisfacory, "
          "then it may mean the scalar range of the master image is out of the range covered by short."
          .format(masterImageData.GetScalarTypeAsString()), windowTitle='Segment editor'):
        logging.warning('GrowCut is cancelled by the user')
        return

    slicer.util.showStatusMessage("Running GrowCut...", 2000)
    #TODO:
    #self.logic.undoRedo = self.undoRedo
    self.growCut()
    slicer.util.showStatusMessage("GrowCut Finished", 2000)
  def apply(self, segmentMarkupNode, segmentModel, text, textDepth, mode):

    self.updateModel(segmentMarkupNode, segmentModel, text, textDepth)

    import vtkSegmentationCore

    if not segmentMarkupNode:
      raise AttributeError(f"{self.__class__.__name__}: segment markup node not set.")
    if not segmentModel:
      raise AttributeError(f"{self.__class__.__name__}: segment model not set.")

    if segmentMarkupNode and segmentModel.GetPolyData().GetNumberOfCells() > 0:
      segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()
      if not segmentationNode:
        raise AttributeError(f"{self.__class__.__name__}: Segmentation node not set.")

      modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
      if not modifierLabelmap:
        raise AttributeError("{}: ModifierLabelmap not set. This can happen for various reasons:\n"
                             "No master volume set for segmentation,\n"
                             "No existing segments for segmentation, or\n"
                             "No referenceImageGeometry is specified in the segmentation".format(self.__class__.__name__))

      WorldToModifierLabelmapIjkTransform = vtk.vtkTransform()

      WorldToModifierLabelmapIjkTransformer = vtk.vtkTransformPolyDataFilter()
      WorldToModifierLabelmapIjkTransformer.SetTransform(WorldToModifierLabelmapIjkTransform)
      WorldToModifierLabelmapIjkTransformer.SetInputConnection(segmentModel.GetPolyDataConnection())

      segmentationToSegmentationIjkTransformMatrix = vtk.vtkMatrix4x4()
      modifierLabelmap.GetImageToWorldMatrix(segmentationToSegmentationIjkTransformMatrix)
      segmentationToSegmentationIjkTransformMatrix.Invert()
      WorldToModifierLabelmapIjkTransform.Concatenate(segmentationToSegmentationIjkTransformMatrix)

      worldToSegmentationTransformMatrix = vtk.vtkMatrix4x4()
      slicer.vtkMRMLTransformNode.GetMatrixTransformBetweenNodes(None, segmentationNode.GetParentTransformNode(),
                                                                 worldToSegmentationTransformMatrix)
      WorldToModifierLabelmapIjkTransform.Concatenate(worldToSegmentationTransformMatrix)
      WorldToModifierLabelmapIjkTransformer.Update()

      polyToStencil = vtk.vtkPolyDataToImageStencil()
      polyToStencil.SetOutputSpacing(1.0, 1.0, 1.0)
      polyToStencil.SetInputConnection(WorldToModifierLabelmapIjkTransformer.GetOutputPort())
      boundsIjk = WorldToModifierLabelmapIjkTransformer.GetOutput().GetBounds()
      modifierLabelmapExtent = self.scriptedEffect.modifierLabelmap().GetExtent()
      polyToStencil.SetOutputWholeExtent(modifierLabelmapExtent[0], modifierLabelmapExtent[1],
                                         modifierLabelmapExtent[2], modifierLabelmapExtent[3],
                                         int(round(boundsIjk[4])), int(round(boundsIjk[5])))
      polyToStencil.Update()

      stencilData = polyToStencil.GetOutput()
      stencilExtent = [0, -1, 0, -1, 0, -1]
      stencilData.SetExtent(stencilExtent)

      stencilToImage = vtk.vtkImageStencilToImage()
      stencilToImage.SetInputConnection(polyToStencil.GetOutputPort())
      stencilToImage.SetInsideValue(1.0)
      stencilToImage.SetOutsideValue(0.0)
      stencilToImage.SetOutputScalarType(modifierLabelmap.GetScalarType())

      stencilPositioner = vtk.vtkImageChangeInformation()
      stencilPositioner.SetInputConnection(stencilToImage.GetOutputPort())
      stencilPositioner.SetOutputSpacing(modifierLabelmap.GetSpacing())
      stencilPositioner.SetOutputOrigin(modifierLabelmap.GetOrigin())

      stencilPositioner.Update()
      orientedStencilPositionerOutput = vtkSegmentationCore.vtkOrientedImageData()
      orientedStencilPositionerOutput.ShallowCopy(stencilToImage.GetOutput())
      imageToWorld = vtk.vtkMatrix4x4()
      modifierLabelmap.GetImageToWorldMatrix(imageToWorld)
      orientedStencilPositionerOutput.SetImageToWorldMatrix(imageToWorld)

      vtkSegmentationCore.vtkOrientedImageDataResample.ModifyImage(
        modifierLabelmap, orientedStencilPositionerOutput,
        vtkSegmentationCore.vtkOrientedImageDataResample.OPERATION_MAXIMUM)

      modeName = self.scriptedEffect.parameter("Mode")
      if modeName == "EMBOSS":
        mode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeAdd
      elif modeName == "ENGRAVE":
        mode = slicer.qSlicerSegmentEditorAbstractEffect.ModificationModeRemove
      else:
        logging.error("Invalid mode: "+modeName+" (valid modes: EMBOSS, ENGRAVE)")
      self.scriptedEffect.modifySelectedSegmentByLabelmap(modifierLabelmap, mode)

      # get plane parameters as space-separated string list
      planeParameters = []
      if segmentMarkupNode.GetIsPlaneValid():
        planeParameters.extend(segmentMarkupNode.GetOriginWorld())
        xAxis = [1, 0, 0]
        yAxis = [0, 1, 0]
        zAxis = [0, 0, 1]
        segmentMarkupNode.GetAxesWorld(xAxis, yAxis, zAxis)
        planeParameters.extend(xAxis)
        planeParameters.extend(yAxis)
        planeParameters.extend(zAxis)
        planeParameters.extend(segmentMarkupNode.GetPlaneBounds())
      planeParametersString = ' '.join(map(str, planeParameters))

      segmentID = self.scriptedEffect.parameterSetNode().GetSelectedSegmentID()
      segment = segmentationNode.GetSegmentation().GetSegment(segmentID)
      segment.SetTag("EngraveEffectPlaneParameters", planeParametersString)
Esempio n. 12
0
    def cutSurfaceWithModel(self, segmentMarkupNode, segmentModel):
        import vtkSegmentationCore

        if not segmentMarkupNode:
            raise AttributeError("{}: segment markup node not set.".format(
                self.__class__.__name__))
        if not segmentModel:
            raise AttributeError("{}: segment model not set.".format(
                self.__class__.__name__))

        if segmentMarkupNode and segmentModel.GetPolyData().GetNumberOfCells(
        ) > 0:
            segmentationNode = self.scriptedEffect.parameterSetNode(
            ).GetSegmentationNode()
            if not segmentationNode:
                raise AttributeError("{}: Segmentation node not set.".format(
                    self.__class__.__name__))

            modifierLabelmap = self.scriptedEffect.defaultModifierLabelmap()
            if not modifierLabelmap:
                raise AttributeError(
                    "{}: ModifierLabelmap not set. This can happen for various reasons:\n"
                    "No master volume set for segmentation,\n"
                    "No existing segments for segmentation, or\n"
                    "No referenceImageGeometry is specified in the segmentation"
                    .format(self.__class__.__name__))

            WorldToModifierLabelmapIjkTransform = vtk.vtkTransform()

            WorldToModifierLabelmapIjkTransformer = vtk.vtkTransformPolyDataFilter(
            )
            WorldToModifierLabelmapIjkTransformer.SetTransform(
                WorldToModifierLabelmapIjkTransform)
            WorldToModifierLabelmapIjkTransformer.SetInputConnection(
                segmentModel.GetPolyDataConnection())

            segmentationToSegmentationIjkTransformMatrix = vtk.vtkMatrix4x4()
            modifierLabelmap.GetImageToWorldMatrix(
                segmentationToSegmentationIjkTransformMatrix)
            segmentationToSegmentationIjkTransformMatrix.Invert()
            WorldToModifierLabelmapIjkTransform.Concatenate(
                segmentationToSegmentationIjkTransformMatrix)

            worldToSegmentationTransformMatrix = vtk.vtkMatrix4x4()
            slicer.vtkMRMLTransformNode.GetMatrixTransformBetweenNodes(
                None, segmentationNode.GetParentTransformNode(),
                worldToSegmentationTransformMatrix)
            WorldToModifierLabelmapIjkTransform.Concatenate(
                worldToSegmentationTransformMatrix)
            WorldToModifierLabelmapIjkTransformer.Update()

            polyToStencil = vtk.vtkPolyDataToImageStencil()
            polyToStencil.SetOutputSpacing(1.0, 1.0, 1.0)
            polyToStencil.SetInputConnection(
                WorldToModifierLabelmapIjkTransformer.GetOutputPort())
            boundsIjk = WorldToModifierLabelmapIjkTransformer.GetOutput(
            ).GetBounds()
            modifierLabelmapExtent = self.scriptedEffect.modifierLabelmap(
            ).GetExtent()
            polyToStencil.SetOutputWholeExtent(modifierLabelmapExtent[0],
                                               modifierLabelmapExtent[1],
                                               modifierLabelmapExtent[2],
                                               modifierLabelmapExtent[3],
                                               int(round(boundsIjk[4])),
                                               int(round(boundsIjk[5])))
            polyToStencil.Update()

            stencilData = polyToStencil.GetOutput()
            stencilExtent = [0, -1, 0, -1, 0, -1]
            stencilData.SetExtent(stencilExtent)

            stencilToImage = vtk.vtkImageStencilToImage()
            stencilToImage.SetInputConnection(polyToStencil.GetOutputPort())
            stencilToImage.SetInsideValue(1.0)
            stencilToImage.SetOutsideValue(0.0)
            stencilToImage.SetOutputScalarType(
                modifierLabelmap.GetScalarType())

            stencilPositioner = vtk.vtkImageChangeInformation()
            stencilPositioner.SetInputConnection(
                stencilToImage.GetOutputPort())
            stencilPositioner.SetOutputSpacing(modifierLabelmap.GetSpacing())
            stencilPositioner.SetOutputOrigin(modifierLabelmap.GetOrigin())

            stencilPositioner.Update()
            orientedStencilPositionerOutput = vtkSegmentationCore.vtkOrientedImageData(
            )
            orientedStencilPositionerOutput.ShallowCopy(
                stencilToImage.GetOutput())
            imageToWorld = vtk.vtkMatrix4x4()
            modifierLabelmap.GetImageToWorldMatrix(imageToWorld)
            orientedStencilPositionerOutput.SetImageToWorldMatrix(imageToWorld)

            vtkSegmentationCore.vtkOrientedImageDataResample.ModifyImage(
                modifierLabelmap, orientedStencilPositionerOutput,
                vtkSegmentationCore.vtkOrientedImageDataResample.
                OPERATION_MAXIMUM)

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

            # get fiducial positions as space-separated list
            import numpy
            n = segmentMarkupNode.GetNumberOfFiducials()
            fPos = []
            for i in xrange(n):
                coord = [0.0, 0.0, 0.0]
                segmentMarkupNode.GetNthFiducialPosition(i, coord)
                fPos.extend(coord)
            fPosString = ' '.join(map(str, fPos))

            segmentID = self.scriptedEffect.parameterSetNode(
            ).GetSelectedSegmentID()
            segment = segmentationNode.GetSegmentation().GetSegment(segmentID)
            segment.SetTag("DrawTubeEffectMarkupPositions", fPosString)
Esempio n. 13
0
    def TestSection_AddRemoveSegment(self):
        # Add/remove segment from segmentation (check display properties, color table, etc.)
        logging.info('Test section: Add/remove segment')

        # Get baseline values
        displayNode = self.inputSegmentationNode.GetDisplayNode()
        self.assertIsNotNone(displayNode)
        # If segments are not found then the returned color is the pre-defined invalid color
        bodyColor = self.inputSegmentationNode.GetSegmentation().GetSegment(self.bodySegmentName).GetColor()
        logging.info(f"bodyColor: {bodyColor}")
        self.assertEqual(int(bodyColor[0] * 100), 33)
        self.assertEqual(int(bodyColor[1] * 100), 66)
        self.assertEqual(int(bodyColor[2] * 100), 0)
        tumorColor = self.inputSegmentationNode.GetSegmentation().GetSegment(self.tumorSegmentName).GetColor()
        logging.info(f"tumorColor: {tumorColor}")
        self.assertEqual(int(tumorColor[0] * 100), 100)
        self.assertEqual(int(tumorColor[1] * 100), 0)
        self.assertEqual(int(tumorColor[2] * 100), 0)

        # Create new segment
        sphere = vtk.vtkSphereSource()
        sphere.SetCenter(0, 50, 0)
        sphere.SetRadius(80)
        sphere.Update()
        spherePolyData = vtk.vtkPolyData()
        spherePolyData.DeepCopy(sphere.GetOutput())

        self.sphereSegment = vtkSegmentationCore.vtkSegment()
        self.sphereSegment.SetName(self.sphereSegmentName)
        self.sphereSegment.SetColor(0.0, 0.0, 1.0)
        self.sphereSegment.AddRepresentation(self.closedSurfaceReprName, spherePolyData)

        # Add segment to segmentation
        self.inputSegmentationNode.GetSegmentation().AddSegment(self.sphereSegment)
        self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3)

        # Check merged labelmap
        mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData()
        self.inputSegmentationNode.GetSegmentation().CreateRepresentation(self.binaryLabelmapReprName)
        self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0)
        imageStat = vtk.vtkImageAccumulate()
        imageStat.SetInputData(mergedLabelmap)
        imageStat.SetComponentExtent(0, 4, 0, 0, 0, 0)
        imageStat.SetComponentOrigin(0, 0, 0)
        imageStat.SetComponentSpacing(1, 1, 1)
        imageStat.Update()
        imageStatResult = imageStat.GetOutput()
        for i in range(4):
            logging.info(f"Volume {i}: {imageStatResult.GetScalarComponentAsDouble(i,0,0,0)}")
        self.assertEqual(imageStat.GetVoxelCount(), 1000)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 786)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 170)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 4)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 40)

        # Check if segment reorder is taken into account in merged labelmap generation
        # Change segment order
        sphereSegmentId = self.inputSegmentationNode.GetSegmentation().GetSegmentIdBySegment(self.sphereSegment)
        self.inputSegmentationNode.GetSegmentation().SetSegmentIndex(sphereSegmentId, 1)
        # Re-generate merged labelmap
        self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0)
        imageStat.SetInputData(mergedLabelmap)
        imageStat.Update()
        imageStatResult = imageStat.GetOutput()
        for i in range(4):
            logging.info(f"Volume {i}: {imageStatResult.GetScalarComponentAsDouble(i,0,0,0)}")
        self.assertEqual(imageStat.GetVoxelCount(), 1000)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 786)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 170)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 39)
        self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 5)

        # Remove segment from segmentation
        self.inputSegmentationNode.GetSegmentation().RemoveSegment(self.sphereSegmentName)
        self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 2)
  def exportAsDICOMSEG(self, exportablesCollection):
    """Export the given node to a segmentation object and load it in the
    DICOM database
    
    This function was copied and modified from the EditUtil.py function of the same name in Slicer.
    """

    import logging
    
    if hasattr(slicer.modules, 'segmentations'):
    
      exportable = exportablesCollection.GetItemAsObject(0)
      subjectHierarchyNode = slicer.mrmlScene.GetNodeByID(exportable.GetNodeID())

      instanceUIDs = subjectHierarchyNode.GetAttribute("DICOM.ReferencedInstanceUIDs").split()

      if instanceUIDs == "":
          raise Exception("Editor master node does not have DICOM information")

      # get the list of source DICOM files
      inputDICOMImageFileNames = ""
      for instanceUID in instanceUIDs:
        inputDICOMImageFileNames += slicer.dicomDatabase.fileForInstance(instanceUID) + ","
      inputDICOMImageFileNames = inputDICOMImageFileNames[:-1] # strip last comma

      # save the per-structure volumes in the temp directory
      inputSegmentationsFileNames = ""

      import random # TODO: better way to generate temp file names?
      import vtkITK
      writer = vtkITK.vtkITKImageWriter()
      rasToIJKMatrix = vtk.vtkMatrix4x4()

      import vtkSegmentationCore
      import vtkSlicerSegmentationsModuleLogic
      logic = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic()

      segmentationTransform = vtk.vtkMatrix4x4()
      segmentationNode = subjectHierarchyNode.GetAssociatedNode()

      mergedSegmentationImageData = segmentationNode.GetImageData()
      mergedSegmentationLabelmapNode = slicer.vtkMRMLLabelMapVolumeNode()

      segmentationNode.GetRASToIJKMatrix(rasToIJKMatrix)
      mergedSegmentationLabelmapNode.SetRASToIJKMatrix(rasToIJKMatrix)
      mergedSegmentationLabelmapNode.SetAndObserveImageData(mergedSegmentationImageData)
      mergedSegmentationOrientedImageData = logic.CreateOrientedImageDataFromVolumeNode(mergedSegmentationLabelmapNode)

      segmentation = segmentationNode.GetSegmentation()

      segmentIDs = vtk.vtkStringArray()
      segmentation.GetSegmentIDs(segmentIDs)
      segmentationName = segmentationNode.GetName()

      for i in range(0, segmentIDs.GetNumberOfValues()):
        segmentID = segmentIDs.GetValue(i)
        segment = segmentation.GetSegment(segmentID)

        segmentName = segment.GetName()
        structureName = segmentName[len(segmentationName)+1:-1*len('-label')]

        structureFileName = structureName + str(random.randint(0,vtk.VTK_INT_MAX)) + ".nrrd"
        filePath = os.path.join(slicer.app.temporaryPath, structureFileName)
        writer.SetFileName(filePath)

        segmentImageData = segment.GetRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationBinaryLabelmapRepresentationName())
        paddedImageData = vtkSegmentationCore.vtkOrientedImageData()
        vtkSegmentationCore.vtkOrientedImageDataResample.PadImageToContainImage(segmentImageData, mergedSegmentationOrientedImageData, paddedImageData)

        labelmapImageData = slicer.vtkMRMLLabelMapVolumeNode()
        logic.CreateLabelmapVolumeFromOrientedImageData(paddedImageData, labelmapImageData)

        writer.SetInputDataObject(labelmapImageData.GetImageData())

        labelmapImageData.GetRASToIJKMatrix(rasToIJKMatrix)
        writer.SetRasToIJKMatrix(rasToIJKMatrix)
        logging.debug("Saving to %s..." % filePath)
        writer.Write()
        inputSegmentationsFileNames += filePath + ","
      inputSegmentationsFileNames = inputSegmentationsFileNames[:-1] # strip last comma

      # save the per-structure volumes label attributes
      colorNode = segmentationNode.GetNodeReference('colorNodeID')

      terminologyName = colorNode.GetAttribute("TerminologyName")
      colorLogic = slicer.modules.colors.logic()
      if not terminologyName or not colorLogic:
        raise Exception("No terminology or color logic - cannot export")

      inputLabelAttributesFileNames = ""

      for i in range(0, segmentIDs.GetNumberOfValues()):
        segmentID = segmentIDs.GetValue(i)
        segment = segmentation.GetSegment(segmentID)

        segmentName = segment.GetName()
        structureName = segmentName[len(segmentationName)+1:-1*len('-label')]
        labelIndex = colorNode.GetColorIndexByName( structureName )

        rgbColor = [0,]*4
        colorNode.GetColor(labelIndex, rgbColor)
        rgbColor = map(lambda e: e*255., rgbColor)

        # get the attributes and conver to format CodeValue,CodeMeaning,CodingSchemeDesignator
        # or empty strings if not defined
        propertyCategoryWithColons = colorLogic.GetSegmentedPropertyCategory(labelIndex, terminologyName)
        if propertyCategoryWithColons == '':
          logging.debug ('ERROR: no segmented property category found for label ',str(labelIndex))
          # Try setting a default as this section is required
          propertyCategory = "C94970,NCIt,Reference Region"
        else:
          propertyCategory = propertyCategoryWithColons.replace(':',',')

        propertyTypeWithColons = colorLogic.GetSegmentedPropertyType(labelIndex, terminologyName)
        propertyType = propertyTypeWithColons.replace(':',',')

        propertyTypeModifierWithColons = colorLogic.GetSegmentedPropertyTypeModifier(labelIndex, terminologyName)
        propertyTypeModifier = propertyTypeModifierWithColons.replace(':',',')

        anatomicRegionWithColons = colorLogic.GetAnatomicRegion(labelIndex, terminologyName)
        anatomicRegion = anatomicRegionWithColons.replace(':',',')

        anatomicRegionModifierWithColons = colorLogic.GetAnatomicRegionModifier(labelIndex, terminologyName)
        anatomicRegionModifier = anatomicRegionModifierWithColons.replace(':',',')

        structureFileName = structureName + str(random.randint(0,vtk.VTK_INT_MAX)) + ".info"
        filePath = os.path.join(slicer.app.temporaryPath, structureFileName)

        # EncodeSEG is expecting a file of format:
        # labelNum;SegmentedPropertyCategory:codeValue,codeScheme,codeMeaning;SegmentedPropertyType:v,m,s etc
        attributes = "%d" % labelIndex
        attributes += ";SegmentedPropertyCategory:"+propertyCategory
        if propertyType != "":
          attributes += ";SegmentedPropertyType:" + propertyType
        if propertyTypeModifier != "":
          attributes += ";SegmentedPropertyTypeModifier:" + propertyTypeModifier
        if anatomicRegion != "":
          attributes += ";AnatomicRegion:" + anatomicRegion
        if anatomicRegionModifier != "":
          attributes += ";AnatomicRegionModifer:" + anatomicRegionModifier
        attributes += ";SegmentAlgorithmType:AUTOMATIC"
        attributes += ";SegmentAlgorithmName:SlicerSelfTest"
        attributes += ";RecommendedDisplayRGBValue:%g,%g,%g" % tuple(rgbColor[:-1])
        fp = open(filePath, "w")
        fp.write(attributes)
        fp.close()
        logging.debug ("filePath: %s", filePath)
        logging.debug ("attributes: %s", attributes)
        inputLabelAttributesFileNames += filePath + ","
      inputLabelAttributesFileNames = inputLabelAttributesFileNames[:-1] # strip last comma'''

      try:
        user = os.environ['USER']
      except KeyError:
        user = "******"
      segFileName = "editor_export.SEG" + str(random.randint(0,vtk.VTK_INT_MAX)) + ".dcm"
      segFilePath = os.path.join(slicer.app.temporaryPath, segFileName)
      # TODO: define a way to set parameters like description
      # TODO: determine a good series number automatically by looking in the database
      parameters = {
        "inputDICOMImageFileNames": inputDICOMImageFileNames,
        "inputSegmentationsFileNames": inputSegmentationsFileNames,
        "inputLabelAttributesFileNames": inputLabelAttributesFileNames,
        "readerId": user,
        "sessionId": "1",
        "timePointId": "1",
        "seriesDescription": "SlicerEditorSEGExport",
        "seriesNumber": "100",
        "instanceNumber": "1",
        "bodyPart": "HEAD",
        "algorithmDescriptionFileName": "Editor",
        "outputSEGFileName": segFilePath,
        "skipEmptySlices": False,
        "compress": False,
        }

      encodeSEG = slicer.modules.encodeseg
      cliNode = None

      cliNode = slicer.cli.run(encodeSEG, cliNode, parameters, delete_temporary_files=False)
      waitCount = 0
      while cliNode.IsBusy() and waitCount < 20:
        slicer.util.delayDisplay( "Running SEG Encoding... %d" % waitCount, 1000 )
        waitCount += 1

      if cliNode.GetStatusString() != 'Completed':
        raise Exception("encodeSEG CLI did not complete cleanly")

      logging.info("Added segmentation to DICOM database (%s)", segFilePath)
      slicer.dicomDatabase.insert(segFilePath)
Esempio n. 15
0
    def TestSection_MaskingSettings(self):

        self.segmentation.RemoveAllSegments()
        segment1Id = self.segmentation.AddEmptySegment("Segment_1")
        segment2Id = self.segmentation.AddEmptySegment("Segment_2")
        segment3Id = self.segmentation.AddEmptySegment("Segment_3")
        segment4Id = self.segmentation.AddEmptySegment("Segment_4")

        oldOverwriteMode = self.segmentEditorNode.GetOverwriteMode()

        #-------------------
        # Test applying threshold with no masking
        self.segmentEditorNode.SetSelectedSegmentID(segment1Id)
        self.thresholdEffect.setParameter("MinimumThreshold", "-17")
        self.thresholdEffect.setParameter("MaximumThreshold", "848")
        self.thresholdEffect.self().onApply()
        self.checkSegmentVoxelCount(0, 204)  # Segment_1
        self.checkSegmentVoxelCount(1, 0)  # Segment_2

        #-------------------
        # Add paint to segment 2. No overwrite
        paintModifierLabelmap = vtkSegmentationCore.vtkOrientedImageData()
        paintModifierLabelmap.SetImageToWorldMatrix(self.ijkToRas)
        paintModifierLabelmap.SetExtent(2, 5, 2, 5, 2, 5)
        paintModifierLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
        paintModifierLabelmap.GetPointData().GetScalars().Fill(1)

        self.segmentEditorNode.SetOverwriteMode(
            self.segmentEditorNode.OverwriteNone)
        self.segmentEditorNode.SetSelectedSegmentID(segment2Id)
        self.paintEffect.modifySelectedSegmentByLabelmap(
            paintModifierLabelmap, self.paintEffect.ModificationModeAdd)

        self.checkSegmentVoxelCount(0, 204)  # Segment_1
        self.checkSegmentVoxelCount(1, 64)  # Segment_2

        #-------------------
        # Test erasing with no masking
        eraseModifierLabelmap = vtkSegmentationCore.vtkOrientedImageData()
        eraseModifierLabelmap.SetImageToWorldMatrix(self.ijkToRas)
        eraseModifierLabelmap.SetExtent(2, 5, 2, 5, 2, 5)
        eraseModifierLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
        eraseModifierLabelmap.GetPointData().GetScalars().Fill(1)

        self.segmentEditorNode.SetSelectedSegmentID(segment1Id)
        self.eraseEffect.modifySelectedSegmentByLabelmap(
            eraseModifierLabelmap, self.paintEffect.ModificationModeRemove)
        self.checkSegmentVoxelCount(0, 177)  # Segment_1
        self.checkSegmentVoxelCount(1, 64)  # Segment_2

        #-------------------
        # Test erasing with masking on empty segment
        self.segmentEditorNode.SetSelectedSegmentID(segment1Id)
        self.thresholdEffect.self().onApply()  # Reset Segment_1
        self.checkSegmentVoxelCount(0, 204)  # Segment_1
        self.segmentEditorNode.SetMaskMode(
            slicer.vtkMRMLSegmentationNode.EditAllowedInsideSingleSegment)
        self.segmentEditorNode.SetMaskSegmentID(segment2Id)
        self.eraseEffect.modifySelectedSegmentByLabelmap(
            eraseModifierLabelmap, self.paintEffect.ModificationModeRemove)
        self.checkSegmentVoxelCount(
            0, 177
        )  # We expect to be able to erase the current segment regardless of masking
        self.checkSegmentVoxelCount(1, 64)  # Segment_2

        #-------------------
        # Test erasing with masking on the same segment
        self.segmentEditorNode.SetSelectedSegmentID(segment1Id)
        self.thresholdEffect.self().onApply()  # Reset Segment_1
        self.checkSegmentVoxelCount(0, 204)  # Segment_1
        self.segmentEditorNode.SetMaskSegmentID(segment1Id)
        self.eraseEffect.modifySelectedSegmentByLabelmap(
            eraseModifierLabelmap, self.paintEffect.ModificationModeRemove)
        self.checkSegmentVoxelCount(0, 177)  # Segment_1
        self.checkSegmentVoxelCount(1, 64)  # Segment_2

        #-------------------
        # Test erasing all segments
        self.segmentEditorNode.SetMaskMode(
            slicer.vtkMRMLSegmentationNode.EditAllowedEverywhere)
        self.thresholdEffect.self().onApply()  # Reset Segment_1
        self.checkSegmentVoxelCount(0, 204)  # Segment_1
        self.segmentEditorNode.SetSelectedSegmentID(segment1Id)
        self.eraseEffect.modifySelectedSegmentByLabelmap(
            eraseModifierLabelmap, self.paintEffect.ModificationModeRemoveAll)
        self.checkSegmentVoxelCount(0, 177)  # Segment_1
        self.checkSegmentVoxelCount(1, 0)  # Segment_2

        #-------------------
        # Test adding back segments
        self.thresholdEffect.self().onApply()  # Reset Segment_1
        self.checkSegmentVoxelCount(0, 204)  # Segment_1
        self.segmentEditorNode.SetMaskMode(
            slicer.vtkMRMLSegmentationNode.EditAllowedInsideSingleSegment)
        self.segmentEditorNode.SetMaskSegmentID(segment2Id)
        self.eraseEffect.modifySelectedSegmentByLabelmap(
            eraseModifierLabelmap, self.paintEffect.ModificationModeRemove)
        self.checkSegmentVoxelCount(0, 177)  # Segment_1
        self.checkSegmentVoxelCount(1, 27)  # Segment_2

        #-------------------
        # Test threshold effect segment mask
        self.segmentEditorNode.SetMaskSegmentID(segment2Id)  # Erase Segment_2
        self.segmentEditorNode.SetSelectedSegmentID(segment2Id)
        self.eraseEffect.modifySelectedSegmentByLabelmap(
            eraseModifierLabelmap, self.paintEffect.ModificationModeRemove)
        self.segmentEditorNode.SetMaskSegmentID(segment1Id)
        self.segmentEditorNode.SetSelectedSegmentID(segment2Id)
        self.thresholdEffect.self().onApply(
        )  # Threshold Segment_2 within Segment_1
        self.checkSegmentVoxelCount(0, 177)  # Segment_1
        self.checkSegmentVoxelCount(1, 177)  # Segment_2

        #-------------------
        # Test intensity masking with segment mask
        self.segmentEditorNode.MasterVolumeIntensityMaskOn()
        self.segmentEditorNode.SetMasterVolumeIntensityMaskRange(-17, 848)
        self.thresholdEffect.setParameter("MinimumThreshold", "-99999")
        self.thresholdEffect.setParameter("MaximumThreshold", "99999")
        self.segmentEditorNode.SetSelectedSegmentID(segment3Id)
        self.thresholdEffect.self().onApply()  # Threshold Segment_3
        self.checkSegmentVoxelCount(2, 177)  # Segment_3

        #-------------------
        # Test intensity masking with islands
        self.segmentEditorNode.SetMaskMode(
            slicer.vtkMRMLSegmentationNode.EditAllowedEverywhere)
        self.segmentEditorNode.MasterVolumeIntensityMaskOff()
        self.segmentEditorNode.SetSelectedSegmentID(segment4Id)

        island1ModifierLabelmap = vtkSegmentationCore.vtkOrientedImageData()
        island1ModifierLabelmap.SetImageToWorldMatrix(self.ijkToRas)
        island1ModifierLabelmap.SetExtent(2, 5, 2, 5, 2, 5)
        island1ModifierLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
        island1ModifierLabelmap.GetPointData().GetScalars().Fill(1)
        self.paintEffect.modifySelectedSegmentByLabelmap(
            island1ModifierLabelmap, self.paintEffect.ModificationModeAdd)

        island2ModifierLabelmap = vtkSegmentationCore.vtkOrientedImageData()
        island2ModifierLabelmap.SetImageToWorldMatrix(self.ijkToRas)
        island2ModifierLabelmap.SetExtent(7, 9, 7, 9, 7, 9)
        island2ModifierLabelmap.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 1)
        island2ModifierLabelmap.GetPointData().GetScalars().Fill(1)
        self.paintEffect.modifySelectedSegmentByLabelmap(
            island2ModifierLabelmap, self.paintEffect.ModificationModeAdd)
        self.checkSegmentVoxelCount(3, 91)  # Segment_4

        # Test that no masking works as expected
        minimumSize = 3
        self.islandEffect.setParameter('MinimumSize', minimumSize)
        self.islandEffect.setParameter('Operation', 'KEEP_LARGEST_ISLAND')
        self.islandEffect.self().onApply()
        self.checkSegmentVoxelCount(3, 64)  # Segment_4

        # Reset Segment_4 islands
        self.paintEffect.modifySelectedSegmentByLabelmap(
            island1ModifierLabelmap, self.paintEffect.ModificationModeAdd)
        self.paintEffect.modifySelectedSegmentByLabelmap(
            island2ModifierLabelmap, self.paintEffect.ModificationModeAdd)

        # Test intensity masking
        self.segmentEditorNode.MasterVolumeIntensityMaskOn()
        self.segmentEditorNode.SetMasterVolumeIntensityMaskRange(-17, 848)
        self.islandEffect.self().onApply()
        self.checkSegmentVoxelCount(3, 87)  # Segment_4

        # Restore old overwrite setting
        self.segmentEditorNode.SetOverwriteMode(oldOverwriteMode)
        self.segmentEditorNode.MasterVolumeIntensityMaskOff()
Esempio n. 16
0
  def TestSection_AddRemoveSegment(self):
    # Add/remove segment from segmentation (check display properties, color table, etc.)
    logging.info('Test section: Add/remove segment')

    # Get baseline values
    displayNode = self.inputSegmentationNode.GetDisplayNode()
    self.assertIsNotNone(displayNode)
    # If segments are not found then the returned color is the pre-defined invalid color
    bodyColor = self.inputSegmentationNode.GetSegmentation().GetSegment(self.bodySegmentName).GetColor()
    logging.info("bodyColor: {0}".format(bodyColor))
    self.assertEqual(int(bodyColor[0]*100), 33)
    self.assertEqual(int(bodyColor[1]*100), 66)
    self.assertEqual(int(bodyColor[2]*100), 0)
    tumorColor = self.inputSegmentationNode.GetSegmentation().GetSegment(self.tumorSegmentName).GetColor()
    logging.info("tumorColor: {0}".format(tumorColor))
    self.assertEqual(int(tumorColor[0]*100), 100)
    self.assertEqual(int(tumorColor[1]*100), 0)
    self.assertEqual(int(tumorColor[2]*100), 0)

    # Create new segment
    sphere = vtk.vtkSphereSource()
    sphere.SetCenter(0,50,0)
    sphere.SetRadius(80)
    sphere.Update()
    spherePolyData = vtk.vtkPolyData()
    spherePolyData.DeepCopy(sphere.GetOutput())

    self.sphereSegment = vtkSegmentationCore.vtkSegment()
    self.sphereSegment.SetName(self.sphereSegmentName)
    self.sphereSegment.SetColor(0.0,0.0,1.0)
    self.sphereSegment.AddRepresentation(self.closedSurfaceReprName, spherePolyData)

    # Add segment to segmentation
    self.inputSegmentationNode.GetSegmentation().AddSegment(self.sphereSegment)
    self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3)

    # Check merged labelmap
    mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData()
    self.inputSegmentationNode.GetSegmentation().CreateRepresentation(self.binaryLabelmapReprName)
    self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0)
    imageStat = vtk.vtkImageAccumulate()
    imageStat.SetInputData(mergedLabelmap)
    imageStat.SetComponentExtent(0,4,0,0,0,0)
    imageStat.SetComponentOrigin(0,0,0)
    imageStat.SetComponentSpacing(1,1,1)
    imageStat.Update()
    imageStatResult = imageStat.GetOutput()
    for i in range(4):
      logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0)))
    self.assertEqual(imageStat.GetVoxelCount(), 1000)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 786)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 170)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 4)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 40)

    # Check if segment reorder is taken into account in merged labelmap generation
    # Change segment order
    sphereSegmentId = self.inputSegmentationNode.GetSegmentation().GetSegmentIdBySegment(self.sphereSegment)
    self.inputSegmentationNode.GetSegmentation().SetSegmentIndex(sphereSegmentId, 1)
    # Re-generate merged labelmap
    self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(mergedLabelmap, 0)
    imageStat.SetInputData(mergedLabelmap)
    imageStat.Update()
    imageStatResult = imageStat.GetOutput()
    for i in range(4):
      logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0)))
    self.assertEqual(imageStat.GetVoxelCount(), 1000)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 786)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 170)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 39)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 5)

    # Remove segment from segmentation
    self.inputSegmentationNode.GetSegmentation().RemoveSegment(self.sphereSegmentName)
    self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 2)
    def TestSection_MergeLabelmapWithDifferentGeometries(self):
        # Merge labelmap when segments containing labelmaps with different geometries (both same directions, different directions)
        logging.info('Test section: Merge labelmap with different geometries')

        self.assertIsNotNone(self.sphereSegment)
        self.sphereSegment.RemoveRepresentation(self.binaryLabelmapReprName)
        self.assertIsNone(
            self.sphereSegment.GetRepresentation(self.binaryLabelmapReprName))

        # Create new segmentation with sphere segment
        self.secondSegmentationNode = slicer.vtkMRMLSegmentationNode()
        self.secondSegmentationNode.SetName('Second')
        self.secondSegmentationNode.GetSegmentation(
        ).SetMasterRepresentationName(self.binaryLabelmapReprName)
        slicer.mrmlScene.AddNode(self.secondSegmentationNode)

        self.secondSegmentationNode.GetSegmentation().AddSegment(
            self.sphereSegment)

        # Check automatically converted labelmap. It is supposed to have the default geometry
        # (which is different than the one in the input segmentation)
        sphereLabelmap = self.sphereSegment.GetRepresentation(
            self.binaryLabelmapReprName)
        self.assertIsNotNone(sphereLabelmap)
        sphereLabelmapSpacing = sphereLabelmap.GetSpacing()
        self.assertTrue(sphereLabelmapSpacing[0] == 1.0
                        and sphereLabelmapSpacing[1] == 1.0
                        and sphereLabelmapSpacing[2] == 1.0)

        # Create binary labelmap in segmentation that will create the merged labelmap from
        # different geometries so that labelmap is not removed from sphere segment when adding
        self.inputSegmentationNode.GetSegmentation().CreateRepresentation(
            self.binaryLabelmapReprName)

        # Copy segment to input segmentation
        self.inputSegmentationNode.GetSegmentation(
        ).CopySegmentFromSegmentation(
            self.secondSegmentationNode.GetSegmentation(),
            self.sphereSegmentName)
        self.assertEqual(
            self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(),
            3)

        # Check merged labelmap
        # Reference geometry has the tiny patient spacing, and it is oversampled to have smimilar
        # voxel size as the sphere labelmap with the uniform 1mm spacing
        mergedLabelmap = vtkSegmentationCore.vtkOrientedImageData()
        self.inputSegmentationNode.GenerateMergedLabelmapForAllSegments(
            mergedLabelmap, 0)
        mergedLabelmapSpacing = mergedLabelmap.GetSpacing()
        self.assertAlmostEqual(mergedLabelmapSpacing[0], 1.2894736842, 8)
        self.assertAlmostEqual(mergedLabelmapSpacing[1], 1.2894736842, 8)
        self.assertAlmostEqual(mergedLabelmapSpacing[2], 0.6052631578, 8)

        imageStat = vtk.vtkImageAccumulate()
        imageStat.SetInputData(mergedLabelmap)
        imageStat.SetComponentExtent(0, 5, 0, 0, 0, 0)
        imageStat.SetComponentOrigin(0, 0, 0)
        imageStat.SetComponentSpacing(1, 1, 1)
        imageStat.Update()
        self.assertEqual(imageStat.GetVoxelCount(), 54872000)
        imageStatResult = imageStat.GetOutput()
        for i in range(5):
            logging.info("Volume {0}: {1}".format(
                i, imageStatResult.GetScalarComponentAsDouble(i, 0, 0, 0)))
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 43573723)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 10601312)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 251476)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 445489)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(4, 0, 0, 0), 0
        )  # Built from color table and color four is removed in previous test section
  def growCut(self):
    # Get master volume image data
    import vtkSegmentationCore
    masterImageData = vtkSegmentationCore.vtkOrientedImageData()
    self.scriptedEffect.masterVolumeImageData(masterImageData)
    # Get segmentation
    import vtkSlicerSegmentationsModuleMRML
    segmentationNode = self.scriptedEffect.parameterSetNode().GetSegmentationNode()

    # Cast master image if not short
    if masterImageData.GetScalarType() != vtk.VTK_SHORT:
      imageCast = vtk.vtkImageCast()
      imageCast.SetInputData(masterImageData)
      imageCast.SetOutputScalarTypeToShort()
      imageCast.ClampOverflowOn()
      imageCast.Update()
      masterImageDataShort = vtkSegmentationCore.vtkOrientedImageData()
      masterImageDataShort.DeepCopy(imageCast.GetOutput()) # Copy image data
      masterImageDataShort.CopyDirections(masterImageData) # Copy geometry
      masterImageData = masterImageDataShort

    # Generate merged labelmap as input to GrowCut
    mergedImage = vtk.vtkImageData()
    mergedImageToWorldMatrix = vtk.vtkMatrix4x4()
    segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage, mergedImageToWorldMatrix, masterImageData)

    # Make a zero-valued volume for the output
    outputLabelmap = vtkSegmentationCore.vtkOrientedImageData()
    thresh = vtk.vtkImageThreshold()
    thresh.ReplaceInOn()
    thresh.ReplaceOutOn()
    thresh.SetInValue(0)
    thresh.SetOutValue(0)
    thresh.SetOutputScalarType( vtk.VTK_SHORT )
    thresh.SetInputData( mergedImage )
    thresh.SetOutput( outputLabelmap )
    thresh.Update()
    outputLabelmap.DeepCopy( mergedImage ) #TODO: It was thresholded just above, why deep copy now?

    # Perform grow cut
    growCutFilter = vtkITK.vtkITKGrowCutSegmentationImageFilter()
    growCutFilter.SetInputData( 0, masterImageData )
    growCutFilter.SetInputData( 1, mergedImage )
    #TODO: This call sets an empty image for the optional "previous segmentation", and
    #      is apparently needed for the first segmentation too. Why?
    growCutFilter.SetInputConnection( 2, thresh.GetOutputPort() )

    #TODO: These are magic numbers inherited from EditorLib/GrowCut.py
    objectSize = 5.
    contrastNoiseRatio = 0.8
    priorStrength = 0.003
    segmented = 2
    conversion = 1000

    spacing = mergedImage.GetSpacing()
    voxelVolume = reduce(lambda x,y: x*y, spacing)
    voxelAmount = objectSize / voxelVolume
    voxelNumber = round(voxelAmount) * conversion

    cubeRoot = 1./3.
    oSize = int(round(pow(voxelNumber,cubeRoot)))

    growCutFilter.SetObjectSize( oSize )
    growCutFilter.SetContrastNoiseRatio( contrastNoiseRatio )
    growCutFilter.SetPriorSegmentConfidence( priorStrength )
    growCutFilter.Update()

    outputLabelmap.DeepCopy( growCutFilter.GetOutput() )

    # Write output segmentation results in segments
    import vtkSlicerSegmentationsModuleLogic
    segmentIDs = vtk.vtkStringArray()
    segmentationNode.GetSegmentation().GetSegmentIDs(segmentIDs)
    for index in xrange(segmentIDs.GetNumberOfValues()):
      segmentID = segmentIDs.GetValue(index)
      segment = segmentationNode.GetSegmentation().GetSegment(segmentID)

      # Get label corresponding to segment in merged labelmap (and so GrowCut output)
      colorIndexStr = vtk.mutable("")
      tagFound = segment.GetTag(vtkSlicerSegmentationsModuleMRML.vtkMRMLSegmentationDisplayNode.GetColorIndexTag(), colorIndexStr);
      if not tagFound:
        logging.error('Failed to apply GrowCut result on segment ' + segmentID)
        continue
      colorIndex = int(colorIndexStr.get())

      # 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)
      thresh.ThresholdBetween(colorIndex, colorIndex);
      thresh.SetOutputScalarType(vtk.VTK_UNSIGNED_CHAR)
      thresh.SetInputData(outputLabelmap)
      thresh.Update()

      # Write label to segment
      newSegmentLabelmap = vtkSegmentationCore.vtkOrientedImageData()
      newSegmentLabelmap.ShallowCopy(thresh.GetOutput())
      newSegmentLabelmap.SetGeometryFromImageToWorldMatrix(mergedImageToWorldMatrix)
      vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(newSegmentLabelmap, segmentationNode, segmentID)