def TestSection_01_GenerateInputData(self):

    self.inputSegmentationNode = slicer.vtkMRMLSegmentationNode()
    slicer.mrmlScene.AddNode(self.inputSegmentationNode)

    # Create new segments
    import random
    for segmentName in ['first', 'second', 'third']:

      sphereSegment = vtkSegmentationCore.vtkSegment()
      sphereSegment.SetName(segmentName)
      sphereSegment.SetColor(random.uniform(0.0,1.0), random.uniform(0.0,1.0), random.uniform(0.0,1.0))

      sphere = vtk.vtkSphereSource()
      sphere.SetCenter(random.uniform(0,100),random.uniform(0,100),random.uniform(0,100))
      sphere.SetRadius(random.uniform(20,30))
      sphere.Update()
      spherePolyData = sphere.GetOutput()
      sphereSegment.AddRepresentation(
        vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName(),
        spherePolyData)

      self.inputSegmentationNode.GetSegmentation().AddSegment(sphereSegment)

    self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 3)
    def TestSection_01_GenerateInputData(self):
        logging.info('Test section 1: GenerateInputData')
        self.inputSegmentationNode = slicer.mrmlScene.AddNewNodeByClass(
            'vtkMRMLSegmentationNode')

        # Create new segments
        import random
        for segmentName in ['first', 'second', 'third']:
            sphereSegment = vtkSegmentationCore.vtkSegment()
            sphereSegment.SetName(segmentName)
            sphereSegment.SetColor(random.uniform(0.0, 1.0),
                                   random.uniform(0.0, 1.0),
                                   random.uniform(0.0, 1.0))

            sphere = vtk.vtkSphereSource()
            sphere.SetCenter(random.uniform(0, 100), random.uniform(0, 100),
                             random.uniform(0, 100))
            sphere.SetRadius(random.uniform(20, 30))
            sphere.Update()
            spherePolyData = sphere.GetOutput()
            sphereSegment.AddRepresentation(
                vtkSegmentationCore.vtkSegmentationConverter.
                GetSegmentationClosedSurfaceRepresentationName(),
                spherePolyData)

            self.inputSegmentationNode.GetSegmentation().AddSegment(
                sphereSegment)

        self.assertEqual(
            self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(),
            3)

        self.inputSegmentationNode.CreateDefaultDisplayNodes()
        displayNode = self.inputSegmentationNode.GetDisplayNode()
        self.assertIsNotNone(displayNode)
Example #3
0
  def test_SegmentStatisticsBasic(self):
    """
    This tests some aspects of the label statistics
    """

    self.delayDisplay("Starting test_SegmentStatisticsBasic")

    import vtkSegmentationCorePython as vtkSegmentationCore
    import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
    import SampleData
    from SegmentStatistics import SegmentStatisticsLogic

    self.delayDisplay("Load master volume")

    sampleDataLogic = SampleData.SampleDataLogic()
    masterVolumeNode = sampleDataLogic.downloadMRBrainTumor1()

    self.delayDisplay("Create segmentation containing a few spheres")

    segmentationNode = slicer.vtkMRMLSegmentationNode()
    slicer.mrmlScene.AddNode(segmentationNode)
    segmentationNode.CreateDefaultDisplayNodes()
    segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)

    # Geometry for each segment is defined by: radius, posX, posY, posZ
    segmentGeometries = [[10, -6,30,28], [20, 0,65,32], [15, 1, -14, 30], [12, 0, 28, -7], [5, 0,30,64], [12, 31, 33, 27], [17, -42, 30, 27]]
    for segmentGeometry in segmentGeometries:
      sphereSource = vtk.vtkSphereSource()
      sphereSource.SetRadius(segmentGeometry[0])
      sphereSource.SetCenter(segmentGeometry[1], segmentGeometry[2], segmentGeometry[3])
      sphereSource.Update()
      segment = vtkSegmentationCore.vtkSegment()
      segment.SetName(segmentationNode.GetSegmentation().GenerateUniqueSegmentID("Test"))
      segment.AddRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName(), sphereSource.GetOutput())
      segmentationNode.GetSegmentation().AddSegment(segment)

    self.delayDisplay("Compute statistics")

    segStatLogic = SegmentStatisticsLogic()
    segStatLogic.computeStatistics(segmentationNode, masterVolumeNode)

    self.delayDisplay("Check a few numerical results")
    self.assertEqual( segStatLogic.statistics["Test_2","LM voxel count"], 9807)
    self.assertEqual( segStatLogic.statistics["Test_4","GS voxel count"], 380)

    self.delayDisplay("Export results to table")
    resultsTableNode = slicer.vtkMRMLTableNode()
    slicer.mrmlScene.AddNode(resultsTableNode)
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)

    self.delayDisplay("Export results to string")
    logging.info(segStatLogic.exportToString())

    outputFilename = slicer.app.temporaryPath + '/SegmentStatisticsTestOutput.csv'
    self.delayDisplay("Export results to CSV file: "+outputFilename)
    segStatLogic.exportToCSVFile(outputFilename)

    self.delayDisplay('test_SegmentStatisticsBasic passed!')
Example #4
0
 def _createAndObserveSegment(self):
     import vtkSegmentationCorePython as vtkSegmentationCore
     segment = vtkSegmentationCore.vtkSegment()
     segment.SetName(self._masterVolume.GetName())
     # TODO need to think about the reference more in detail. After loading the volume nodes don't occupy the same address
     self.mrmlNode.SetReferenceImageGeometryParameterFromVolumeNode(
         self._masterVolume)
     self.mrmlNode.GetSegmentation().AddSegment(segment)
     self.mrmlNode.AddObserver(
         vtkSegmentationCore.vtkSegmentation.SegmentModified,
         self._onSegmentModified)
Example #5
0
  def TestSection_1_AddRemoveSegment(self):
    # Add/remove segment from segmentation (check display properties, color table, etc.)
    logging.info('Test section 1: 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 = displayNode.GetSegmentColor('Body_Contour')
    self.assertTrue(int(bodyColor[0]*100) == 33 and int(bodyColor[1]*100) == 66 and bodyColor[2] == 0.0)
    tumorColor = displayNode.GetSegmentColor('Tumor_Contour')
    self.assertTrue(tumorColor[0] == 1.0 and tumorColor[1] == 0.0 and tumorColor[2] == 0.0)

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

    self.sphereSegment = vtkSegmentationCore.vtkSegment()
    self.sphereSegment.SetName(self.sphereSegmentName)
    self.sphereSegment.SetDefaultColor(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)
    sphereColor = displayNode.GetSegmentColor(self.sphereSegmentName)
    self.assertTrue(sphereColor[0] == 0.0 and sphereColor[1] == 0.0 and sphereColor[2] == 1.0)

    # 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()
    self.assertEqual(imageStat.GetVoxelCount(), 1000)
    imageStatResult = imageStat.GetOutput()
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 795)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 194)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 5)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 6)

    # Remove segment from segmentation
    self.inputSegmentationNode.GetSegmentation().RemoveSegment(self.sphereSegmentName)
    self.assertEqual(self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(), 2)
    sphereColor = displayNode.GetSegmentColor(self.sphereSegmentName)
    self.assertTrue(sphereColor[0] == 0.5 and sphereColor[1] == 0.5 and sphereColor[2] == 0.5)
  def test_Autoscroll(self):
    """
    Basic automated test of the segmentation method:
    - Create segmentation by placing sphere-shaped seeds
    - Run segmentation
    - Verify results using segment statistics
    The test can be executed from SelfTests module (test name: SegmentEditorAutoscroll)
    """

    self.delayDisplay("Starting test_Autoscroll")

    import vtkSegmentationCorePython as vtkSegmentationCore
    import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
    import SampleData
    from SegmentStatistics import SegmentStatisticsLogic

    ##################################
    self.delayDisplay("Load master volume")

    import SampleData
    sampleDataLogic = SampleData.SampleDataLogic()
    masterVolumeNode = sampleDataLogic.downloadMRBrainTumor1()

    ##################################
    self.delayDisplay("Create segmentation with an empty segment")

    segmentationNode = slicer.vtkMRMLSegmentationNode()
    slicer.mrmlScene.AddNode(segmentationNode)
    segmentationNode.CreateDefaultDisplayNodes()
    segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)
    segment = vtkSegmentationCore.vtkSegment()
    segmentationNode.GetSegmentation().AddSegment(segment)

    ##################################
    self.delayDisplay("Create segment editor")

    segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
    segmentEditorWidget.show()
    segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
    segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()
    slicer.mrmlScene.AddNode(segmentEditorNode)
    segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
    segmentEditorWidget.setSegmentationNode(segmentationNode)
    segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)

    ##################################
    self.delayDisplay("Run autoscroll for 10 seconds")
    segmentEditorWidget.setActiveEffectByName("Autoscroll")
    effect = segmentEditorWidget.activeEffect()

    qt.QTimer.singleShot(10000, effect.self().onApply)
    effect.self().onApply()
    self.delayDisplay('test_Autoscroll passed')
  def test_create_report(self):

    self.delayDisplay('Starting %s' % inspect.stack()[0][3])

    qrWidget = slicer.modules.QuantitativeReportingWidget

    with DICOMUtils.TemporaryDICOMDatabase(self.dicomDatabaseDir) as db:
      self.assertTrue(db.isOpen)
      self.assertEqual(slicer.dicomDatabase, db)

      self.loadTestVolume()
      success, err = qrWidget.saveReport()
      self.assertFalse(success)

      self.delayDisplay('Add segments')

      qrWidget = slicer.modules.QuantitativeReportingWidget
      segmentation = qrWidget.segmentEditorWidget.segmentationNode.GetSegmentation()

      segmentGeometries = {
        'Tumor': [[2, 30, 30, -127.7], [2, 40, 40, -127.7], [2, 50, 50, -127.7], [2, 40, 80, -127.7]],
        'Air': [[2, 60, 100, -127.7], [2, 80, 30, -127.7]]
      }

      for segmentName, segmentGeometry in segmentGeometries.iteritems():
        appender = vtk.vtkAppendPolyData()

        for sphere in segmentGeometry:
          sphereSource = vtk.vtkSphereSource()
          sphereSource.SetRadius(sphere[0])
          sphereSource.SetCenter(sphere[1], sphere[2], sphere[3])
          appender.AddInputConnection(sphereSource.GetOutputPort())

        segment = vtkSegmentationCore.vtkSegment()
        segment.SetName(segmentation.GenerateUniqueSegmentID(segmentName))

        appender.Update()
        representationName = vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName()
        segment.AddRepresentation(representationName, appender.GetOutput())
        segmentation.AddSegment(segment)

      self.delayDisplay('Save report')

      success, err = qrWidget.saveReport()
      self.assertTrue(success)

      self.delayDisplay('Test passed!')
  def test_create_report(self):

    self.delayDisplay('Starting %s' % inspect.stack()[0][3])

    qrWidget = slicer.modules.QuantitativeReportingWidget

    with DICOMUtils.TemporaryDICOMDatabase(self.dicomDatabaseDir) as db:
      self.assertTrue(db.isOpen)
      self.assertEqual(slicer.dicomDatabase, db)

      self.loadTestVolume()
      success, err = qrWidget.saveReport()
      self.assertFalse(success)

      self.delayDisplay('Add segments')

      qrWidget = slicer.modules.QuantitativeReportingWidget
      segmentation = qrWidget.segmentEditorWidget.segmentationNode.GetSegmentation()

      segmentGeometries = {
        'Tumor': [[2, 30, 30, -127.7], [2, 40, 40, -127.7], [2, 50, 50, -127.7], [2, 40, 80, -127.7]],
        'Air': [[2, 60, 100, -127.7], [2, 80, 30, -127.7]]
      }

      for segmentName, segmentGeometry in six.iteritems(segmentGeometries):
        appender = vtk.vtkAppendPolyData()

        for sphere in segmentGeometry:
          sphereSource = vtk.vtkSphereSource()
          sphereSource.SetRadius(sphere[0])
          sphereSource.SetCenter(sphere[1], sphere[2], sphere[3])
          appender.AddInputConnection(sphereSource.GetOutputPort())

        segment = vtkSegmentationCore.vtkSegment()
        segment.SetName(segmentation.GenerateUniqueSegmentID(segmentName))

        appender.Update()
        representationName = vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName()
        segment.AddRepresentation(representationName, appender.GetOutput())
        segmentation.AddSegment(segment)

      self.delayDisplay('Save report')

      success, err = qrWidget.saveReport()
      self.assertTrue(success)

      self.delayDisplay('Test passed!')
    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()
Example #10
0
  def TestSection_2_qMRMLSegmentationGeometryWidget(self):
    logging.info('Test section 2: qMRMLSegmentationGeometryWidget')

    import vtkSegmentationCore
    binaryLabelmapReprName = vtkSegmentationCore.vtkSegmentationConverter.GetBinaryLabelmapRepresentationName()
    closedSurfaceReprName = vtkSegmentationCore.vtkSegmentationConverter.GetClosedSurfaceRepresentationName()

    # Use MRHead and Tinypatient for testing
    import SampleData
    sampleDataLogic = SampleData.SampleDataLogic()
    mrVolumeNode = sampleDataLogic.downloadMRHead()
    [tinyVolumeNode, tinySegmentationNode] = sampleDataLogic.downloadSample('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.vtkMRMLSegmentationNode()
    segmentationNode.GetSegmentation().SetMasterRepresentationName(binaryLabelmapReprName)
    geometryStr = vtkSegmentationCore.vtkSegmentationConverter.SerializeImageGeometry(mrOrientedImageData)
    segmentationNode.GetSegmentation().SetConversionParameter(
      vtkSegmentationCore.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), geometryStr)
    slicer.mrmlScene.AddNode(segmentationNode)
    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 = vtkSegmentationCore.vtkOrientedImageData()
    segmentOrientedImageData.DeepCopy(threshold.GetOutput())
    mrImageToWorldMatrix = vtk.vtkMatrix4x4()
    mrOrientedImageData.GetImageToWorldMatrix(mrImageToWorldMatrix)
    segmentOrientedImageData.SetImageToWorldMatrix(mrImageToWorldMatrix)
    segment = vtkSegmentationCore.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 = vtkSegmentationCore.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]]))
    vtkSegmentationCore.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), (224.3439, 223.7890, -135.25),
        [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]]))
    vtkSegmentationCore.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]]))
    vtkSegmentationCore.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]]))
    vtkSegmentationCore.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]]))
    vtkSegmentationCore.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), (-167.156, -217.711, -135.75),
        [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]]))
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentOrientedImageData, geometryImageData, geometryImageData, False, True)
    self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5223846)
    slicer.util.delayDisplay('Segmentation source cases - OK')

    # Model source with no transform
    outputModelHierarchy = slicer.vtkMRMLModelHierarchyNode()
    slicer.mrmlScene.AddNode(outputModelHierarchy)
    success = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.ExportVisibleSegmentsToModelHierarchy(
      tinySegmentationNode, outputModelHierarchy )
    self.assertTrue(success)
    modelNode = slicer.util.getNode('Body_Contour')
    geometryWidget.setSourceNode(modelNode)
    geometryWidget.geometryImageData(geometryImageData)
    self.assertTrue(self.compareOutputGeometry(geometryImageData,
        (1,1,1), (-167.156, -217.711, -135.75),
        [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]]))
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentOrientedImageData, geometryImageData, geometryImageData, False, True)
    self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5223846)

    # 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), (-167.156, -58.6623, -249.228),
        [[0.0, 0.0, 1.0], [-0.7071, -0.7071, 0.0], [0.7071, -0.7071, 0.0]]))
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentOrientedImageData, geometryImageData, geometryImageData, False, True)
    self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5221241)

    # ROI source
    roiNode = slicer.vtkMRMLAnnotationROINode()
    roiNode.SetName('SourceROI')
    slicer.mrmlScene.AddNode(roiNode)
    roiNode.UnRegister(None)
    xyz = [0]*3
    center = [0]*3
    slicer.vtkMRMLSliceLogic.GetVolumeRASBox(tinyVolumeNode, xyz, center)
    radius = map(lambda x: x/2.0, xyz)
    roiNode.SetXYZ(center)
    roiNode.SetRadiusXYZ(radius)
    geometryWidget.setSourceNode(roiNode)
    geometryWidget.geometryImageData(geometryImageData)
    self.assertTrue(self.compareOutputGeometry(geometryImageData,
        (1,1,1), (-216.156, -217.711, -135.75),
        [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]]))
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentOrientedImageData, geometryImageData, geometryImageData, False, True)
    self.assertEqual(self.getForegroundVoxelCount(geometryImageData), 5223846)
    slicer.util.delayDisplay('Model and ROI source cases - OK')

    slicer.util.delayDisplay('Segmentation geometry widget test passed')
Example #11
0
  def test_SegmentStatisticsPlugins(self):
    """
    This tests some aspects of the segment statistics plugins
    """

    self.delayDisplay("Starting test_SegmentStatisticsPlugins")

    import vtkSegmentationCorePython as vtkSegmentationCore
    import SampleData
    from SegmentStatistics import SegmentStatisticsLogic

    self.delayDisplay("Load master volume")

    masterVolumeNode = SampleData.downloadSample('MRBrainTumor1')

    self.delayDisplay("Create segmentation containing a few spheres")

    segmentationNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode')
    segmentationNode.CreateDefaultDisplayNodes()
    segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)

    # Geometry for each segment is defined by: radius, posX, posY, posZ
    segmentGeometries = [[10, -6,30,28], [20, 0,65,32], [15, 1, -14, 30], [12, 0, 28, -7], [5, 0,30,64],
                         [12, 31, 33, 27], [17, -42, 30, 27]]
    for segmentGeometry in segmentGeometries:
      sphereSource = vtk.vtkSphereSource()
      sphereSource.SetRadius(segmentGeometry[0])
      sphereSource.SetCenter(segmentGeometry[1], segmentGeometry[2], segmentGeometry[3])
      sphereSource.Update()
      segment = vtkSegmentationCore.vtkSegment()
      uniqueSegmentID = segmentationNode.GetSegmentation().GenerateUniqueSegmentID("Test")
      segmentationNode.AddSegmentFromClosedSurfaceRepresentation(sphereSource.GetOutput(), uniqueSegmentID)

    # test calculating only measurements for selected segments
    self.delayDisplay("Test calculating only measurements for individual segments")
    segStatLogic = SegmentStatisticsLogic()
    segStatLogic.getParameterNode().SetParameter("Segmentation", segmentationNode.GetID())
    segStatLogic.getParameterNode().SetParameter("ScalarVolume", masterVolumeNode.GetID())
    segStatLogic.updateStatisticsForSegment('Test_2')
    resultsTableNode = slicer.vtkMRMLTableNode()
    slicer.mrmlScene.AddNode(resultsTableNode)
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)
    self.assertEqual( segStatLogic.getStatistics()["Test_2","LabelmapSegmentStatisticsPlugin.voxel_count"], 9807)
    with self.assertRaises(KeyError): segStatLogic.getStatistics()["Test_4","ScalarVolumeSegmentStatisticsPlugin.voxel count"]
    # assert there are no result for this segment
    segStatLogic.updateStatisticsForSegment('Test_4')
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)
    self.assertEqual( segStatLogic.getStatistics()["Test_2","LabelmapSegmentStatisticsPlugin.voxel_count"], 9807)
    self.assertEqual( segStatLogic.getStatistics()["Test_4","LabelmapSegmentStatisticsPlugin.voxel_count"], 380)
    with self.assertRaises(KeyError): segStatLogic.getStatistics()["Test_5","ScalarVolumeSegmentStatisticsPlugin.voxel count"]
    # assert there are no result for this segment

    # calculate measurements for all segments
    segStatLogic.computeStatistics()
    self.assertEqual( segStatLogic.getStatistics()["Test","LabelmapSegmentStatisticsPlugin.voxel_count"], 2948)
    self.assertEqual( segStatLogic.getStatistics()["Test_1","LabelmapSegmentStatisticsPlugin.voxel_count"], 23281)

    # test updating measurements for segments one by one
    self.delayDisplay("Update some segments in the segmentation")
    segmentGeometriesNew = [[5, -6,30,28], [21, 0,65,32]]
    # We add/remove representations, so we temporarily block segment modifications
    # to make sure display managers don't try to access data while it is in an
    # inconsistent state.
    wasModified = segmentationNode.StartModify()
    for i in range(len(segmentGeometriesNew)):
      segmentGeometry  = segmentGeometriesNew[i]
      sphereSource = vtk.vtkSphereSource()
      sphereSource.SetRadius(segmentGeometry[0])
      sphereSource.SetCenter(segmentGeometry[1], segmentGeometry[2], segmentGeometry[3])
      sphereSource.Update()
      segment = segmentationNode.GetSegmentation().GetNthSegment(i)
      segment.RemoveAllRepresentations()
      closedSurfaceName = vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName()
      segment.AddRepresentation(closedSurfaceName,
                                sphereSource.GetOutput())
    segmentationNode.EndModify(wasModified)
    self.assertEqual( segStatLogic.getStatistics()["Test","LabelmapSegmentStatisticsPlugin.voxel_count"], 2948)
    self.assertEqual( segStatLogic.getStatistics()["Test_1","LabelmapSegmentStatisticsPlugin.voxel_count"], 23281)
    segStatLogic.updateStatisticsForSegment('Test_1')
    self.assertEqual( segStatLogic.getStatistics()["Test","LabelmapSegmentStatisticsPlugin.voxel_count"], 2948)
    self.assertTrue( segStatLogic.getStatistics()["Test_1","LabelmapSegmentStatisticsPlugin.voxel_count"]!=23281)
    segStatLogic.updateStatisticsForSegment('Test')
    self.assertTrue( segStatLogic.getStatistics()["Test","LabelmapSegmentStatisticsPlugin.voxel_count"]!=2948)
    self.assertTrue( segStatLogic.getStatistics()["Test_1","LabelmapSegmentStatisticsPlugin.voxel_count"]!=23281)

    # test enabling/disabling of individual measurements
    self.delayDisplay("Test disabling of individual measurements")
    segStatLogic = SegmentStatisticsLogic()
    segStatLogic.getParameterNode().SetParameter("Segmentation", segmentationNode.GetID())
    segStatLogic.getParameterNode().SetParameter("ScalarVolume", masterVolumeNode.GetID())
    segStatLogic.getParameterNode().SetParameter("LabelmapSegmentStatisticsPlugin.voxel_count.enabled",str(False))
    segStatLogic.getParameterNode().SetParameter("LabelmapSegmentStatisticsPlugin.volume_cm3.enabled",str(False))
    segStatLogic.computeStatistics()
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)
    columnHeaders = [resultsTableNode.GetColumnName(i) for i in range(resultsTableNode.GetNumberOfColumns())]
    self.assertFalse('Number of voxels [voxels] (1)' in columnHeaders)
    self.assertTrue('Volume [mm3] (1)' in columnHeaders)
    self.assertFalse('Volume [cm3] (3)' in columnHeaders)

    self.delayDisplay("Test re-enabling of individual measurements")
    segStatLogic.getParameterNode().SetParameter("LabelmapSegmentStatisticsPlugin.voxel_count.enabled",str(True))
    segStatLogic.getParameterNode().SetParameter("LabelmapSegmentStatisticsPlugin.volume_cm3.enabled",str(True))
    segStatLogic.computeStatistics()
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)
    columnHeaders = [resultsTableNode.GetColumnName(i) for i in range(resultsTableNode.GetNumberOfColumns())]
    self.assertTrue('Number of voxels [voxels] (1)' in columnHeaders)
    self.assertTrue('Volume [mm3] (1)' in columnHeaders)
    self.assertTrue('Volume [cm3] (1)' in columnHeaders)

    # test enabling/disabling of individual plugins
    self.delayDisplay("Test disabling of plugin")
    segStatLogic.getParameterNode().SetParameter("LabelmapSegmentStatisticsPlugin.enabled",str(False))
    segStatLogic.computeStatistics()
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)
    columnHeaders = [resultsTableNode.GetColumnName(i) for i in range(resultsTableNode.GetNumberOfColumns())]
    self.assertFalse('Number of voxels [voxels] (3)' in columnHeaders)
    self.assertFalse('Volume [mm3] (3)' in columnHeaders)
    self.assertTrue('Volume [mm3] (2)' in columnHeaders)

    self.delayDisplay("Test re-enabling of plugin")
    segStatLogic.getParameterNode().SetParameter("LabelmapSegmentStatisticsPlugin.enabled",str(True))
    segStatLogic.computeStatistics()
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)
    columnHeaders = [resultsTableNode.GetColumnName(i) for i in range(resultsTableNode.GetNumberOfColumns())]
    self.assertTrue('Number of voxels [voxels] (2)' in columnHeaders)
    self.assertTrue('Volume [mm3] (3)' in columnHeaders)

    # test unregistering/registering of plugins
    self.delayDisplay("Test of removing all registered plugins")
    SegmentStatisticsLogic.registeredPlugins = [] # remove all registered plugins
    segStatLogic = SegmentStatisticsLogic()
    segStatLogic.getParameterNode().SetParameter("Segmentation", segmentationNode.GetID())
    segStatLogic.getParameterNode().SetParameter("ScalarVolume", masterVolumeNode.GetID())
    segStatLogic.computeStatistics()
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)
    columnHeaders = [resultsTableNode.GetColumnName(i) for i in range(resultsTableNode.GetNumberOfColumns())]
    self.assertEqual(len(columnHeaders),1) # only header element should be "Segment"
    self.assertEqual(columnHeaders[0],"Segment") # only header element should be "Segment"

    self.delayDisplay("Test registering plugins")
    SegmentStatisticsLogic.registerPlugin(LabelmapSegmentStatisticsPlugin())
    SegmentStatisticsLogic.registerPlugin(ScalarVolumeSegmentStatisticsPlugin())
    SegmentStatisticsLogic.registerPlugin(ClosedSurfaceSegmentStatisticsPlugin())
    segStatLogic = SegmentStatisticsLogic()
    segStatLogic.getParameterNode().SetParameter("Segmentation", segmentationNode.GetID())
    segStatLogic.getParameterNode().SetParameter("ScalarVolume", masterVolumeNode.GetID())
    segStatLogic.computeStatistics()
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)
    columnHeaders = [resultsTableNode.GetColumnName(i) for i in range(resultsTableNode.GetNumberOfColumns())]
    self.assertTrue('Number of voxels [voxels] (1)' in columnHeaders)
    self.assertTrue('Number of voxels [voxels] (2)' in columnHeaders)
    self.assertTrue('Surface area [mm2]' in columnHeaders)

    self.delayDisplay('test_SegmentStatisticsPlugins passed!')
    def test_SplitVolume1(self):
        """
    Basic automated test of the segmentation method:
    - Create segmentation by placing sphere-shaped seeds
    - Run segmentation
    - Verify results using segment statistics
    The test can be executed from SelfTests module (test name: SegmentEditorSplitVolume)
    """

        self.delayDisplay("Starting test_SplitVolume1")

        import vtkSegmentationCorePython as vtkSegmentationCore
        import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
        import SampleData
        from SegmentStatistics import SegmentStatisticsLogic

        ##################################
        self.delayDisplay("Load master volume")

        import SampleData
        sampleDataLogic = SampleData.SampleDataLogic()
        masterVolumeNode = sampleDataLogic.downloadMRBrainTumor1()

        ##################################
        self.delayDisplay("Create segmentation containing a few spheres")

        segmentationNode = slicer.vtkMRMLSegmentationNode()
        slicer.mrmlScene.AddNode(segmentationNode)
        segmentationNode.CreateDefaultDisplayNodes()
        segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(
            masterVolumeNode)

        # Segments are defined by a list of: name and a list of sphere [radius, posX, posY, posZ]
        segmentGeometries = [['Tumor', [[10, -6, 30, 28]]],
                             [
                                 'Background',
                                 [[10, 0, 65, 22], [15, 1, -14, 30],
                                  [12, 0, 28, -7], [5, 0, 30, 54],
                                  [12, 31, 33, 27], [17, -42, 30, 27],
                                  [6, -2, -17, 71]]
                             ], ['Air', [[10, 76, 73, 0], [15, -70, 74, 0]]]]
        for segmentGeometry in segmentGeometries:
            segmentName = segmentGeometry[0]
            appender = vtk.vtkAppendPolyData()
            for sphere in segmentGeometry[1]:
                sphereSource = vtk.vtkSphereSource()
                sphereSource.SetRadius(sphere[0])
                sphereSource.SetCenter(sphere[1], sphere[2], sphere[3])
                appender.AddInputConnection(sphereSource.GetOutputPort())
            segment = vtkSegmentationCore.vtkSegment()
            segment.SetName(
                segmentationNode.GetSegmentation().GenerateUniqueSegmentID(
                    segmentName))
            appender.Update()
            segment.AddRepresentation(
                vtkSegmentationCore.vtkSegmentationConverter.
                GetSegmentationClosedSurfaceRepresentationName(),
                appender.GetOutput())
            segmentationNode.GetSegmentation().AddSegment(segment)

        ##################################
        self.delayDisplay("Create segment editor")

        segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
        segmentEditorWidget.show()
        segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
        segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()
        slicer.mrmlScene.AddNode(segmentEditorNode)
        segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
        segmentEditorWidget.setSegmentationNode(segmentationNode)
        segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)

        ##################################
        self.delayDisplay("Run segmentation")
        segmentEditorWidget.setActiveEffectByName("SplitVolume")
        effect = segmentEditorWidget.activeEffect()
        effect.self().onApply()

        ##################################
        self.delayDisplay("Make segmentation results nicely visible in 3D")
        segmentationDisplayNode = segmentationNode.GetDisplayNode()
        segmentationDisplayNode.SetSegmentVisibility("Air", False)
        segmentationDisplayNode.SetSegmentOpacity3D("Background", 0.5)

        ##################################
        self.delayDisplay("Check extent of cropped volumes")
        expectedBounds = [[
            100.31225000000003, 119.99975000000003, 101.24974999999999,
            119.99974999999999, -78.4, -58.8
        ],
                          [
                              19.68725000000002, 119.99975000000003,
                              16.874749999999977, 119.99974999999999, -78.4,
                              16.80000000000001
                          ],
                          [
                              -49.687749999999994, 119.99975000000003,
                              90.93724999999999, 119.99974999999999, -78.4,
                              -47.6
                          ]]

        for segmentIndex in range(
                segmentationNode.GetSegmentation().GetNumberOfSegments()):
            segmentID = segmentationNode.GetSegmentation().GetNthSegmentID(
                segmentIndex)
            outputVolumeName = masterVolumeNode.GetName() + '_' + segmentID
            outputVolume = slicer.util.getNode(outputVolumeName)
            bounds = [0, 0, 0, 0, 0, 0]
            outputVolume.GetBounds(bounds)
            self.assertEqual(bounds, expectedBounds)

        ##################################
        self.delayDisplay("Compute statistics")

        segStatLogic = SegmentStatisticsLogic()
        segStatLogic.computeStatistics(segmentationNode, masterVolumeNode)

        # Export results to table (just to see all results)
        resultsTableNode = slicer.vtkMRMLTableNode()
        slicer.mrmlScene.AddNode(resultsTableNode)
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)

        self.delayDisplay("Check a few numerical results")
        self.assertEqual(
            round(segStatLogic.statistics["Tumor", "LM volume cc"]), 16)
        self.assertEqual(
            round(segStatLogic.statistics["Background", "LM volume cc"]), 3010)

        self.delayDisplay('test_SplitVolume1 passed')
Example #13
0
    def preview(self):
        # Get master volume image data
        import vtkSegmentationCorePython as vtkSegmentationCore
        masterImageData = self.scriptedEffect.masterVolumeImageData()

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

        previewNode = self.getPreviewNode()
        if not previewNode or not self.mergedLabelmapGeometryImage \
          or (self.clippedMasterImageDataRequired and not self.clippedMasterImageData):
            self.reset()
            # Compute merged labelmap extent (effective extent slightly expanded)
            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))
                return
            if not self.mergedLabelmapGeometryImage:
                self.mergedLabelmapGeometryImage = vtkSegmentationCore.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)

            masterImageExtent = masterImageData.GetExtent()
            labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent(
            )
            margin = [17, 17, 17]
            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])
            ]
            self.mergedLabelmapGeometryImage.SetExtent(labelsExpandedExtent)

            previewNode = slicer.mrmlScene.CreateNodeByClass(
                'vtkMRMLSegmentationNode')
            previewNode.UnRegister(None)
            previewNode = slicer.mrmlScene.AddNode(previewNode)
            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)

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

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

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

        outputLabelmap = vtkSegmentationCore.vtkOrientedImageData()

        self.computePreviewLabelmap(mergedImage, outputLabelmap)

        # Write output segmentation results in segments
        for index in xrange(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 = vtkSegmentationCore.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)
            slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(
                newSegmentLabelmap, previewNode, segmentID)

        self.updateGUIFromMRML()
  def test_ScriptedSegmentEditorEffectModuleTemplate1(self):
    """
    Basic automated test of the segmentation method:
    - Create segmentation by placing sphere-shaped seeds
    - Run segmentation
    - Verify results using segment statistics
    The test can be executed from SelfTests module (test name: SegmentEditorScriptedSegmentEditorEffectModuleTemplate)
    """

    self.delayDisplay("Starting test_ScriptedSegmentEditorEffectModuleTemplate1")

    import vtkSegmentationCorePython as vtkSegmentationCore
    import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
    import SampleData
    from SegmentStatistics import SegmentStatisticsLogic

    ##################################
    self.delayDisplay("Load master volume")

    import SampleData
    sampleDataLogic = SampleData.SampleDataLogic()
    masterVolumeNode = sampleDataLogic.downloadMRBrainTumor1()

    ##################################
    self.delayDisplay("Create segmentation containing a few spheres")

    segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
    segmentationNode.CreateDefaultDisplayNodes()
    segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)

    # Segments are defined by a list of: name and a list of sphere [radius, posX, posY, posZ]
    segmentGeometries = [
      ['Tumor', [[10, -6,30,28]]],
      ['Background', [[10, 0,65,22], [15, 1, -14, 30], [12, 0, 28, -7], [5, 0,30,54], [12, 31, 33, 27], [17, -42, 30, 27], [6, -2,-17,71]]],
      ['Air', [[10, 76,73,0], [15, -70,74,0]]] ]
    for segmentGeometry in segmentGeometries:
      segmentName = segmentGeometry[0]
      appender = vtk.vtkAppendPolyData()
      for sphere in segmentGeometry[1]:
        sphereSource = vtk.vtkSphereSource()
        sphereSource.SetRadius(sphere[0])
        sphereSource.SetCenter(sphere[1], sphere[2], sphere[3])
        appender.AddInputConnection(sphereSource.GetOutputPort())
      segment = vtkSegmentationCore.vtkSegment()
      segment.SetName(segmentationNode.GetSegmentation().GenerateUniqueSegmentID(segmentName))
      appender.Update()
      segment.AddRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName(), appender.GetOutput())
      segmentationNode.GetSegmentation().AddSegment(segment)

    ##################################
    self.delayDisplay("Create segment editor")

    segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
    segmentEditorWidget.show()
    segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
    segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()
    slicer.mrmlScene.AddNode(segmentEditorNode)
    segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
    segmentEditorWidget.setSegmentationNode(segmentationNode)
    segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)

    ##################################
    self.delayDisplay("Run segmentation")
    segmentEditorWidget.setActiveEffectByName("ScriptedSegmentEditorEffectModuleTemplate")
    effect = segmentEditorWidget.activeEffect()
    effect.setParameter("ObjectScaleMm", 3.0)
    effect.self().onApply()

    ##################################
    self.delayDisplay("Make segmentation results nicely visible in 3D")
    segmentationDisplayNode = segmentationNode.GetDisplayNode()
    segmentationDisplayNode.SetSegmentVisibility("Air", False)
    segmentationDisplayNode.SetSegmentOpacity3D("Background",0.5)

    ##################################
    self.delayDisplay("Compute statistics")

    segStatLogic = SegmentStatisticsLogic()
    segStatLogic.computeStatistics(segmentationNode, masterVolumeNode)

    # Export results to table (just to see all results)
    resultsTableNode = slicer.vtkMRMLTableNode()
    slicer.mrmlScene.AddNode(resultsTableNode)
    segStatLogic.exportToTable(resultsTableNode)
    segStatLogic.showTable(resultsTableNode)

    self.delayDisplay("Check a few numerical results")
    self.assertEqual( round(segStatLogic.statistics["Tumor","LM volume cc"]), 16)
    self.assertEqual( round(segStatLogic.statistics["Background","LM volume cc"]), 3010)

    self.delayDisplay('test_ScriptedSegmentEditorEffectModuleTemplate1 passed')
    def test_SegmentStatisticsPlugins(self):
        """
    This tests some aspects of the segment statistics plugins
    """

        self.delayDisplay("Starting test_SegmentStatisticsPlugins")

        import vtkSegmentationCorePython as vtkSegmentationCore
        import SampleData
        from SegmentStatistics import SegmentStatisticsLogic

        self.delayDisplay("Load master volume")

        masterVolumeNode = SampleData.downloadSample('MRBrainTumor1')

        self.delayDisplay("Create segmentation containing a few spheres")

        segmentationNode = slicer.mrmlScene.AddNewNodeByClass(
            'vtkMRMLSegmentationNode')
        segmentationNode.CreateDefaultDisplayNodes()
        segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(
            masterVolumeNode)

        # Geometry for each segment is defined by: radius, posX, posY, posZ
        segmentGeometries = [[10, -6, 30, 28], [20, 0, 65, 32],
                             [15, 1, -14, 30], [12, 0, 28, -7], [5, 0, 30, 64],
                             [12, 31, 33, 27], [17, -42, 30, 27]]
        for segmentGeometry in segmentGeometries:
            sphereSource = vtk.vtkSphereSource()
            sphereSource.SetRadius(segmentGeometry[0])
            sphereSource.SetCenter(segmentGeometry[1], segmentGeometry[2],
                                   segmentGeometry[3])
            sphereSource.Update()
            segment = vtkSegmentationCore.vtkSegment()
            uniqueSegmentID = segmentationNode.GetSegmentation(
            ).GenerateUniqueSegmentID("Test")
            segmentationNode.AddSegmentFromClosedSurfaceRepresentation(
                sphereSource.GetOutput(), uniqueSegmentID)

        # test calculating only measurements for selected segments
        self.delayDisplay(
            "Test calculating only measurements for individual segments")
        segStatLogic = SegmentStatisticsLogic()
        segStatLogic.getParameterNode().SetParameter("Segmentation",
                                                     segmentationNode.GetID())
        segStatLogic.getParameterNode().SetParameter("ScalarVolume",
                                                     masterVolumeNode.GetID())
        segStatLogic.updateStatisticsForSegment('Test_2')
        resultsTableNode = slicer.vtkMRMLTableNode()
        slicer.mrmlScene.AddNode(resultsTableNode)
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)
        self.assertEqual(
            segStatLogic.getStatistics()[
                "Test_2", "LabelmapSegmentStatisticsPlugin.voxel_count"], 9807)
        with self.assertRaises(KeyError):
            segStatLogic.getStatistics()[
                "Test_4", "ScalarVolumeSegmentStatisticsPlugin.voxel count"]
        # assert there are no result for this segment
        segStatLogic.updateStatisticsForSegment('Test_4')
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)
        self.assertEqual(
            segStatLogic.getStatistics()[
                "Test_2", "LabelmapSegmentStatisticsPlugin.voxel_count"], 9807)
        self.assertEqual(
            segStatLogic.getStatistics()[
                "Test_4", "LabelmapSegmentStatisticsPlugin.voxel_count"], 380)
        with self.assertRaises(KeyError):
            segStatLogic.getStatistics()[
                "Test_5", "ScalarVolumeSegmentStatisticsPlugin.voxel count"]
        # assert there are no result for this segment

        # calculate measurements for all segments
        segStatLogic.computeStatistics()
        self.assertEqual(
            segStatLogic.getStatistics()[
                "Test", "LabelmapSegmentStatisticsPlugin.voxel_count"], 2948)
        self.assertEqual(
            segStatLogic.getStatistics()
            ["Test_1", "LabelmapSegmentStatisticsPlugin.voxel_count"], 23281)

        # test updating measurements for segments one by one
        self.delayDisplay("Update some segments in the segmentation")
        segmentGeometriesNew = [[5, -6, 30, 28], [21, 0, 65, 32]]
        # We add/remove representations, so we temporarily block segment modifications
        # to make sure display managers don't try to access data while it is in an
        # inconsistent state.
        wasModified = segmentationNode.StartModify()
        for i in range(len(segmentGeometriesNew)):
            segmentGeometry = segmentGeometriesNew[i]
            sphereSource = vtk.vtkSphereSource()
            sphereSource.SetRadius(segmentGeometry[0])
            sphereSource.SetCenter(segmentGeometry[1], segmentGeometry[2],
                                   segmentGeometry[3])
            sphereSource.Update()
            segment = segmentationNode.GetSegmentation().GetNthSegment(i)
            segment.RemoveAllRepresentations()
            closedSurfaceName = vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName(
            )
            segment.AddRepresentation(closedSurfaceName,
                                      sphereSource.GetOutput())
        segmentationNode.EndModify(wasModified)
        self.assertEqual(
            segStatLogic.getStatistics()[
                "Test", "LabelmapSegmentStatisticsPlugin.voxel_count"], 2948)
        self.assertEqual(
            segStatLogic.getStatistics()
            ["Test_1", "LabelmapSegmentStatisticsPlugin.voxel_count"], 23281)
        segStatLogic.updateStatisticsForSegment('Test_1')
        self.assertEqual(
            segStatLogic.getStatistics()[
                "Test", "LabelmapSegmentStatisticsPlugin.voxel_count"], 2948)
        self.assertTrue(segStatLogic.getStatistics()[
            "Test_1", "LabelmapSegmentStatisticsPlugin.voxel_count"] != 23281)
        segStatLogic.updateStatisticsForSegment('Test')
        self.assertTrue(segStatLogic.getStatistics()[
            "Test", "LabelmapSegmentStatisticsPlugin.voxel_count"] != 2948)
        self.assertTrue(segStatLogic.getStatistics()[
            "Test_1", "LabelmapSegmentStatisticsPlugin.voxel_count"] != 23281)

        # test enabling/disabling of individual measurements
        self.delayDisplay("Test disabling of individual measurements")
        segStatLogic = SegmentStatisticsLogic()
        segStatLogic.getParameterNode().SetParameter("Segmentation",
                                                     segmentationNode.GetID())
        segStatLogic.getParameterNode().SetParameter("ScalarVolume",
                                                     masterVolumeNode.GetID())
        segStatLogic.getParameterNode().SetParameter(
            "LabelmapSegmentStatisticsPlugin.voxel_count.enabled", str(False))
        segStatLogic.getParameterNode().SetParameter(
            "LabelmapSegmentStatisticsPlugin.volume_cm3.enabled", str(False))
        segStatLogic.computeStatistics()
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)
        columnHeaders = [
            resultsTableNode.GetColumnName(i)
            for i in range(resultsTableNode.GetNumberOfColumns())
        ]
        self.assertFalse('Number of voxels [voxels] (1)' in columnHeaders)
        self.assertTrue('Volume [mm3] (1)' in columnHeaders)
        self.assertFalse('Volume [cm3] (3)' in columnHeaders)

        self.delayDisplay("Test re-enabling of individual measurements")
        segStatLogic.getParameterNode().SetParameter(
            "LabelmapSegmentStatisticsPlugin.voxel_count.enabled", str(True))
        segStatLogic.getParameterNode().SetParameter(
            "LabelmapSegmentStatisticsPlugin.volume_cm3.enabled", str(True))
        segStatLogic.computeStatistics()
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)
        columnHeaders = [
            resultsTableNode.GetColumnName(i)
            for i in range(resultsTableNode.GetNumberOfColumns())
        ]
        self.assertTrue('Number of voxels [voxels] (1)' in columnHeaders)
        self.assertTrue('Volume [mm3] (1)' in columnHeaders)
        self.assertTrue('Volume [cm3] (1)' in columnHeaders)

        # test enabling/disabling of individual plugins
        self.delayDisplay("Test disabling of plugin")
        segStatLogic.getParameterNode().SetParameter(
            "LabelmapSegmentStatisticsPlugin.enabled", str(False))
        segStatLogic.computeStatistics()
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)
        columnHeaders = [
            resultsTableNode.GetColumnName(i)
            for i in range(resultsTableNode.GetNumberOfColumns())
        ]
        self.assertFalse('Number of voxels [voxels] (3)' in columnHeaders)
        self.assertFalse('Volume [mm3] (3)' in columnHeaders)
        self.assertTrue('Volume [mm3] (2)' in columnHeaders)

        self.delayDisplay("Test re-enabling of plugin")
        segStatLogic.getParameterNode().SetParameter(
            "LabelmapSegmentStatisticsPlugin.enabled", str(True))
        segStatLogic.computeStatistics()
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)
        columnHeaders = [
            resultsTableNode.GetColumnName(i)
            for i in range(resultsTableNode.GetNumberOfColumns())
        ]
        self.assertTrue('Number of voxels [voxels] (2)' in columnHeaders)
        self.assertTrue('Volume [mm3] (3)' in columnHeaders)

        # test unregistering/registering of plugins
        self.delayDisplay("Test of removing all registered plugins")
        SegmentStatisticsLogic.registeredPlugins = [
        ]  # remove all registered plugins
        segStatLogic = SegmentStatisticsLogic()
        segStatLogic.getParameterNode().SetParameter("Segmentation",
                                                     segmentationNode.GetID())
        segStatLogic.getParameterNode().SetParameter("ScalarVolume",
                                                     masterVolumeNode.GetID())
        segStatLogic.computeStatistics()
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)
        columnHeaders = [
            resultsTableNode.GetColumnName(i)
            for i in range(resultsTableNode.GetNumberOfColumns())
        ]
        self.assertEqual(len(columnHeaders),
                         1)  # only header element should be "Segment"
        self.assertEqual(columnHeaders[0],
                         "Segment")  # only header element should be "Segment"

        self.delayDisplay("Test registering plugins")
        SegmentStatisticsLogic.registerPlugin(
            LabelmapSegmentStatisticsPlugin())
        SegmentStatisticsLogic.registerPlugin(
            ScalarVolumeSegmentStatisticsPlugin())
        SegmentStatisticsLogic.registerPlugin(
            ClosedSurfaceSegmentStatisticsPlugin())
        segStatLogic = SegmentStatisticsLogic()
        segStatLogic.getParameterNode().SetParameter("Segmentation",
                                                     segmentationNode.GetID())
        segStatLogic.getParameterNode().SetParameter("ScalarVolume",
                                                     masterVolumeNode.GetID())
        segStatLogic.computeStatistics()
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)
        columnHeaders = [
            resultsTableNode.GetColumnName(i)
            for i in range(resultsTableNode.GetNumberOfColumns())
        ]
        self.assertTrue('Number of voxels [voxels] (1)' in columnHeaders)
        self.assertTrue('Number of voxels [voxels] (2)' in columnHeaders)
        self.assertTrue('Surface area [mm2]' in columnHeaders)

        self.delayDisplay('test_SegmentStatisticsPlugins passed!')
    def test_WrapSolidify1(self):
        """
    Basic automated test of the segmentation method:
    - Create segmentation by placing sphere-shaped seeds
    - Run segmentation
    - Verify results using segment statistics
    The test can be executed from SelfTests module (test name: SegmentEditorWrapSolidify)
    """

        self.delayDisplay("Starting test_WrapSolidify1")

        import vtkSegmentationCorePython as vtkSegmentationCore
        import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
        import SampleData
        from SegmentStatistics import SegmentStatisticsLogic

        ##################################
        self.delayDisplay("Load master volume")

        masterVolumeNode = SampleData.downloadSample('MRBrainTumor1')

        ##################################
        self.delayDisplay("Create segmentation containing a two spheres")

        segmentationNode = slicer.mrmlScene.AddNewNodeByClass(
            'vtkMRMLSegmentationNode')
        segmentationNode.CreateDefaultDisplayNodes()
        segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(
            masterVolumeNode)

        features = ["none", "carveCavities", "createShell", "both"]
        spheres = [[20, 5, 5, 5], [20, -5, -5, -5]]
        appender = vtk.vtkAppendPolyData()
        for sphere in spheres:
            sphereSource = vtk.vtkSphereSource()
            sphereSource.SetRadius(sphere[0])
            sphereSource.SetCenter(sphere[1], sphere[2], sphere[3])
            appender.AddInputConnection(sphereSource.GetOutputPort())

        for m in features:
            segmentName = str(m)
            segment = vtkSegmentationCore.vtkSegment()
            segment.SetName(
                segmentationNode.GetSegmentation().GenerateUniqueSegmentID(
                    segmentName))
            appender.Update()
            segment.AddRepresentation(
                vtkSegmentationCore.vtkSegmentationConverter.
                GetSegmentationClosedSurfaceRepresentationName(),
                appender.GetOutput())
            segmentationNode.GetSegmentation().AddSegment(segment)

        ##################################
        self.delayDisplay("Create segment editor")

        segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
        segmentEditorWidget.show()
        segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
        segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()
        slicer.mrmlScene.AddNode(segmentEditorNode)
        segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
        segmentEditorWidget.setSegmentationNode(segmentationNode)
        segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)

        ##################################
        self.delayDisplay("Run WrapSolidify Effect")
        segmentEditorWidget.setActiveEffectByName("Wrap Solidify")
        effect = segmentEditorWidget.activeEffect()

        for t in ["SEGMENTATION", "MODEL"]:
            effect.setParameter("outputType", t)

            self.delayDisplay(
                "Creating Output Type %s, activated feature none" % (t))
            segmentEditorWidget.setCurrentSegmentID(
                segmentationNode.GetSegmentation().GetSegmentIdBySegmentName(
                    'none'))
            effect.setParameter("carveCavities", False)
            effect.setParameter("createShell", False)
            effect.self().onApply()

            self.delayDisplay(
                "Creating Output Type %s, activated feature carveCavities" %
                (t))
            effect.setParameter("carveCavities", True)
            effect.setParameter("createShell", False)
            segmentEditorWidget.setCurrentSegmentID(
                segmentationNode.GetSegmentation().GetSegmentIdBySegmentName(
                    'carveCavities'))
            effect.self().onApply()

            self.delayDisplay(
                "Creating Output Type %s, activated feature createShell" % (t))
            effect.setParameter("carveCavities", False)
            effect.setParameter("createShell", True)
            segmentEditorWidget.setCurrentSegmentID(
                segmentationNode.GetSegmentation().GetSegmentIdBySegmentName(
                    'createShell'))
            effect.self().onApply()

            self.delayDisplay(
                "Creating Output Type %s, activated feature both" % (t))
            effect.setParameter("carveCavities", True)
            effect.setParameter("createShell", True)
            segmentEditorWidget.setCurrentSegmentID(
                segmentationNode.GetSegmentation().GetSegmentIdBySegmentName(
                    'both'))
            effect.self().onApply()

        ##################################
        self.delayDisplay("Creating Segments from Models")
        for m in features:
            model = slicer.util.getNode(m)
            segmentName = "MODEL_%s" % m
            segment = vtkSegmentationCore.vtkSegment()
            segment.SetName(
                segmentationNode.GetSegmentation().GenerateUniqueSegmentID(
                    segmentName))
            segment.SetColor(model.GetDisplayNode().GetColor())
            segment.AddRepresentation(
                vtkSegmentationCore.vtkSegmentationConverter.
                GetSegmentationClosedSurfaceRepresentationName(),
                model.GetPolyData())
            segmentationNode.GetSegmentation().AddSegment(segment)

        ##################################
        self.delayDisplay("Compute statistics")
        segStatLogic = SegmentStatisticsLogic()
        segStatLogic.getParameterNode().SetParameter("Segmentation",
                                                     segmentationNode.GetID())
        segStatLogic.getParameterNode().SetParameter("ScalarVolume",
                                                     masterVolumeNode.GetID())
        segStatLogic.computeStatistics()
        statistics = segStatLogic.getStatistics()

        ##################################
        self.delayDisplay("Check a few numerical results")

        # logging.info(round(statistics["none",'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']))
        # logging.info(round(statistics["MODEL_none",'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']))
        # logging.info(round(statistics["carveCavities",'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']))
        # logging.info(round(statistics["MODEL_carveCavities",'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']))
        # logging.info(round(statistics["createShell",'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']))
        # logging.info(round(statistics["MODEL_createShell",'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']))
        # logging.info(round(statistics["both",'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']))
        # logging.info(round(statistics["MODEL_both",'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']))

        self.assertEqual(
            round(
                statistics["none",
                           'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']),
            46605)
        self.assertEqual(
            round(
                statistics["MODEL_none",
                           'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']),
            46320)

        self.assertEqual(
            round(
                statistics["carveCavities",
                           'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']),
            46605)
        self.assertEqual(
            round(
                statistics["MODEL_carveCavities",
                           'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']),
            46321)

        self.assertEqual(
            round(
                statistics["createShell",
                           'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']),
            9257)
        self.assertEqual(
            round(
                statistics["MODEL_createShell",
                           'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']),
            9230)

        self.assertEqual(
            round(
                statistics["both",
                           'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']),
            9254)
        self.assertEqual(
            round(
                statistics["MODEL_both",
                           'ScalarVolumeSegmentStatisticsPlugin.volume_mm3']),
            9245)

        self.delayDisplay('test_WrapSolidify1 passed')
  def TestSection_2_qMRMLSegmentationGeometryWidget(self):
    logging.info('Test section 2: qMRMLSegmentationGeometryWidget')

    import vtkSegmentationCore
    binaryLabelmapReprName = vtkSegmentationCore.vtkSegmentationConverter.GetBinaryLabelmapRepresentationName()
    closedSurfaceReprName = vtkSegmentationCore.vtkSegmentationConverter.GetClosedSurfaceRepresentationName()

    # Use MRHead and Tinypatient for testing
    import SampleData
    sampleDataLogic = SampleData.SampleDataLogic()
    mrVolumeNode = sampleDataLogic.downloadMRHead()
    [tinyVolumeNode, tinySegmentationNode] = sampleDataLogic.downloadSample('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.vtkMRMLSegmentationNode()
    segmentationNode.GetSegmentation().SetMasterRepresentationName(binaryLabelmapReprName)
    geometryStr = vtkSegmentationCore.vtkSegmentationConverter.SerializeImageGeometry(mrOrientedImageData)
    segmentationNode.GetSegmentation().SetConversionParameter(
      vtkSegmentationCore.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), geometryStr)
    slicer.mrmlScene.AddNode(segmentationNode)
    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 = vtkSegmentationCore.vtkOrientedImageData()
    segmentOrientedImageData.DeepCopy(threshold.GetOutput())
    mrImageToWorldMatrix = vtk.vtkMatrix4x4()
    mrOrientedImageData.GetImageToWorldMatrix(mrImageToWorldMatrix)
    segmentOrientedImageData.SetImageToWorldMatrix(mrImageToWorldMatrix)
    segment = vtkSegmentationCore.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 = vtkSegmentationCore.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),
        [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]))
    slicer.util.delayDisplay('Volume source with no transforms - OK')

    # 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), (224.3439, 223.7890, -135.25),
        [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]))
    slicer.util.delayDisplay('Transformed volume source - OK')

    # 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),
        [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]))
    slicer.util.delayDisplay('Volume source with isotropic spacing - OK')

    # 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),
        [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]))
    slicer.util.delayDisplay('Volume source with oversampling - 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),
        [[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]))
    slicer.util.delayDisplay('Segmentation source with binary labelmap master - OK')

    # 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)
    # Manually resample and check if the output is non-empty
    # Note: This is done for all poly data based geometry calculations below
    #TODO: Come up with proper geometry baselines for these too
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentOrientedImageData, geometryImageData, geometryImageData, False, True)
    self.assertTrue(self.imageDataContainsData(geometryImageData))
    slicer.util.delayDisplay('Segmentation source with closed surface master - OK')

    # Model source with no transform
    outputModelHierarchy = slicer.vtkMRMLModelHierarchyNode()
    slicer.mrmlScene.AddNode(outputModelHierarchy)
    success = vtkSlicerSegmentationsModuleLogic.vtkSlicerSegmentationsModuleLogic.ExportVisibleSegmentsToModelHierarchy(
      tinySegmentationNode, outputModelHierarchy )
    self.assertTrue(success)
    modelNode = slicer.util.getNode('Body_Contour')
    geometryWidget.setSourceNode(modelNode)
    geometryWidget.geometryImageData(geometryImageData)
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentOrientedImageData, geometryImageData, geometryImageData, False, True)
    self.assertTrue(self.imageDataContainsData(geometryImageData))
    slicer.util.delayDisplay('Model source with no transform - OK')

    # 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)
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentOrientedImageData, geometryImageData, geometryImageData, False, True)
    self.assertTrue(self.imageDataContainsData(geometryImageData))
    slicer.util.delayDisplay('Transformed model source - OK')

    # ROI source
    roiNode = slicer.vtkMRMLAnnotationROINode()
    roiNode.SetName('SourceROI')
    slicer.mrmlScene.AddNode(roiNode)
    roiNode.UnRegister(None)
    xyz = [0]*3
    center = [0]*3
    slicer.vtkMRMLSliceLogic.GetVolumeRASBox(tinyVolumeNode, xyz, center)
    radius = map(lambda x: x/2.0, xyz)
    roiNode.SetXYZ(center)
    roiNode.SetRadiusXYZ(radius)
    geometryWidget.setSourceNode(roiNode)
    geometryWidget.geometryImageData(geometryImageData)
    vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
      segmentOrientedImageData, geometryImageData, geometryImageData, False, True)
    self.assertTrue(self.imageDataContainsData(geometryImageData))
    slicer.util.delayDisplay('ROI source - OK')

    slicer.util.delayDisplay('Segmentation geometry widget test passed')
Example #18
0
    def TestSection_1_AddRemoveSegment(self):
        # Add/remove segment from segmentation (check display properties, color table, etc.)
        logging.info('Test section 1: Add/remove segment')

        # Get baseline values
        displayNode = self.inputSegmentationNode.GetDisplayNode()
        self.assertIsNotNone(displayNode)
        colorTableNode = displayNode.GetColorNode()
        self.assertIsNotNone(colorTableNode)
        self.assertEqual(colorTableNode.GetNumberOfColors(), 3)
        # If segments are not found then the returned color is the pre-defined invalid color
        bodyColor = displayNode.GetSegmentColor('Body_Contour')
        self.assertTrue(
            int(bodyColor[0] * 100) == 33 and int(bodyColor[1] * 100) == 66
            and bodyColor[2] == 0.0)
        tumorColor = displayNode.GetSegmentColor('Tumor_Contour')
        self.assertTrue(tumorColor[0] == 1.0 and tumorColor[1] == 0.0
                        and tumorColor[2] == 0.0)

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

        self.sphereSegment = vtkSegmentationCore.vtkSegment()
        self.sphereSegment.SetName(self.sphereSegmentName)
        self.sphereSegment.SetDefaultColor(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)
        self.assertEqual(colorTableNode.GetNumberOfColors(), 4)
        sphereColor = displayNode.GetSegmentColor(self.sphereSegmentName)
        self.assertTrue(sphereColor[0] == 0.0 and sphereColor[1] == 0.0
                        and sphereColor[2] == 1.0)

        # Check merged labelmap
        imageStat = vtk.vtkImageAccumulate()
        imageStat.SetInputData(self.inputSegmentationNode.GetImageData())
        imageStat.SetComponentExtent(0, 4, 0, 0, 0, 0)
        imageStat.SetComponentOrigin(0, 0, 0)
        imageStat.SetComponentSpacing(1, 1, 1)
        imageStat.Update()
        self.assertEqual(imageStat.GetVoxelCount(), 1000)
        imageStatResult = imageStat.GetOutput()
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 795)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 194)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 5)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 6)

        # Remove segment from segmentation
        self.inputSegmentationNode.GetSegmentation().RemoveSegment(
            self.sphereSegmentName)
        self.assertEqual(
            self.inputSegmentationNode.GetSegmentation().GetNumberOfSegments(),
            2)
        self.assertEqual(colorTableNode.GetNumberOfColors(), 4)
        sphereColorArray = [0] * 4
        colorTableNode.GetColor(3, sphereColorArray)
        self.assertEqual(int(sphereColorArray[0] * 100), 50)
        self.assertEqual(int(sphereColorArray[1] * 100), 50)
        self.assertEqual(int(sphereColorArray[2] * 100), 50)
        sphereColor = displayNode.GetSegmentColor(self.sphereSegmentName)
        self.assertTrue(sphereColor[0] == 0.5 and sphereColor[1] == 0.5
                        and sphereColor[2] == 0.5)
Example #19
0
  def test_SegmentEditorHollow1(self):
    """
    Basic automated test of the segmentation method:
    - Create segmentation by placing sphere-shaped seeds
    - Run segmentation
    - Verify results using segment statistics
    The test can be executed from SelfTests module (test name: SegmentEditorHollow)
    """

    self.delayDisplay("Starting test_SegmentEditorHollow1")

    import vtkSegmentationCorePython as vtkSegmentationCore
    import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
    import SampleData
    from SegmentStatistics import SegmentStatisticsLogic

    ##################################
    self.delayDisplay("Load master volume")

    import SampleData
    sampleDataLogic = SampleData.SampleDataLogic()
    masterVolumeNode = sampleDataLogic.downloadMRBrainTumor1()

    ##################################
    self.delayDisplay("Create segmentation containing a few spheres")

    segmentationNode = slicer.vtkMRMLSegmentationNode()
    slicer.mrmlScene.AddNode(segmentationNode)
    segmentationNode.CreateDefaultDisplayNodes()
    segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(masterVolumeNode)

    # Segments are defined by a list of: name and a list of sphere [radius, posX, posY, posZ]
    segmentGeometries = [
      ['Tumor', [[10, -6,30,28]]],
      ['Background', [[10, 0,65,22], [15, 1, -14, 30], [12, 0, 28, -7], [5, 0,30,54], [12, 31, 33, 27], [17, -42, 30, 27], [6, -2,-17,71]]],
      ['Air', [[10, 76,73,0], [15, -70,74,0]]] ]
    for segmentGeometry in segmentGeometries:
      segmentName = segmentGeometry[0]
      appender = vtk.vtkAppendPolyData()
      for sphere in segmentGeometry[1]:
        sphereSource = vtk.vtkSphereSource()
        sphereSource.SetRadius(sphere[0])
        sphereSource.SetCenter(sphere[1], sphere[2], sphere[3])
        appender.AddInputConnection(sphereSource.GetOutputPort())
      segment = vtkSegmentationCore.vtkSegment()
      segment.SetName(segmentationNode.GetSegmentation().GenerateUniqueSegmentID(segmentName))
      appender.Update()
      segment.AddRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName(), appender.GetOutput())
      segmentationNode.GetSegmentation().AddSegment(segment)

    ##################################
    self.delayDisplay("Create segment editor")

    segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
    segmentEditorWidget.show()
    segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
    segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()
    slicer.mrmlScene.AddNode(segmentEditorNode)
    segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
    segmentEditorWidget.setSegmentationNode(segmentationNode)
    segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)

    ##################################
    self.delayDisplay("Run segmentation")
    segmentEditorWidget.setActiveEffectByName("Hollow")
    effect = segmentEditorWidget.activeEffect()
    effect.setParameter("ShellThicknessMm", 3.0)
    effect.setParameter("ShellMode", "MEDIAL_SURFACE")
    
    effect.self().onApply()

    ##################################
    self.delayDisplay("Make segmentation results nicely visible in 3D")
    segmentationDisplayNode = segmentationNode.GetDisplayNode()
    segmentationDisplayNode.SetSegmentVisibility("Air", False)
    segmentationDisplayNode.SetSegmentOpacity3D("Background",0.5)

    self.delayDisplay('test_SegmentEditorHollow1 passed')
  def TestSection_1_AddRemoveSegment(self):
    # Add/remove segment from segmentation (check display properties, color table, etc.)
    logging.info('Test section 1: 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('Body_Contour').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('Tumor_Contour').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(50)
    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()
    self.assertEqual(imageStat.GetVoxelCount(), 1000)
    imageStatResult = imageStat.GetOutput()
    for i in range(4):
      logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0)))
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 795)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 194)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 4)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(3,0,0,0), 7)

    # 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()
    self.assertEqual(imageStat.GetVoxelCount(), 1000)
    imageStatResult = imageStat.GetOutput()
    for i in range(4):
      logging.info("Volume {0}: {1}".format(i, imageStatResult.GetScalarComponentAsDouble(i,0,0,0)))
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(0,0,0,0), 795)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(1,0,0,0), 194)
    self.assertEqual(imageStatResult.GetScalarComponentAsDouble(2,0,0,0), 6)
    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 test_SegmentStatisticsBasic(self):
        """
    This tests some aspects of the label statistics
    """

        self.delayDisplay("Starting test_SegmentStatisticsBasic")

        import vtkSegmentationCorePython as vtkSegmentationCore
        import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
        import SampleData
        from SegmentStatistics import SegmentStatisticsLogic

        self.delayDisplay("Load master volume")

        sampleDataLogic = SampleData.SampleDataLogic()
        masterVolumeNode = sampleDataLogic.downloadMRBrainTumor1()

        self.delayDisplay("Create segmentation containing a few spheres")

        segmentationNode = slicer.vtkMRMLSegmentationNode()
        slicer.mrmlScene.AddNode(segmentationNode)
        segmentationNode.CreateDefaultDisplayNodes()
        segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(
            masterVolumeNode)

        # Geometry for each segment is defined by: radius, posX, posY, posZ
        segmentGeometries = [[10, -6, 30, 28], [20, 0, 65, 32],
                             [15, 1, -14, 30], [12, 0, 28, -7], [5, 0, 30, 64],
                             [12, 31, 33, 27], [17, -42, 30, 27]]
        for segmentGeometry in segmentGeometries:
            sphereSource = vtk.vtkSphereSource()
            sphereSource.SetRadius(segmentGeometry[0])
            sphereSource.SetCenter(segmentGeometry[1], segmentGeometry[2],
                                   segmentGeometry[3])
            sphereSource.Update()
            segment = vtkSegmentationCore.vtkSegment()
            segment.SetName(
                segmentationNode.GetSegmentation().GenerateUniqueSegmentID(
                    "Test"))
            segment.AddRepresentation(
                vtkSegmentationCore.vtkSegmentationConverter.
                GetSegmentationClosedSurfaceRepresentationName(),
                sphereSource.GetOutput())
            segmentationNode.GetSegmentation().AddSegment(segment)

        self.delayDisplay("Compute statistics")

        segStatLogic = SegmentStatisticsLogic()
        segStatLogic.computeStatistics(segmentationNode, masterVolumeNode)

        self.delayDisplay("Check a few numerical results")
        self.assertEqual(segStatLogic.statistics["Test_2", "LM voxel count"],
                         9807)
        self.assertEqual(segStatLogic.statistics["Test_4", "GS voxel count"],
                         380)

        self.delayDisplay("Export results to table")
        resultsTableNode = slicer.vtkMRMLTableNode()
        slicer.mrmlScene.AddNode(resultsTableNode)
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)

        self.delayDisplay("Export results to string")
        logging.info(segStatLogic.exportToString())

        outputFilename = slicer.app.temporaryPath + '/SegmentStatisticsTestOutput.csv'
        self.delayDisplay("Export results to CSV file: " + outputFilename)
        segStatLogic.exportToCSVFile(outputFilename)

        self.delayDisplay('test_SegmentStatisticsBasic passed!')
    def test_Engrave1(self):
        """
    Basic automated test of the segmentation method:
    - Create segmentation by placing fiducials around tumor
    - Apply
    - Verify results using segment statistics
    The test can be executed from SelfTests module (test name: SegmentEditorEngrave)
    """

        self.delayDisplay("Starting test_Engrave1")

        ##################################
        self.delayDisplay("Load master volume")

        import SampleData
        sampleDataLogic = SampleData.SampleDataLogic()
        masterVolumeNode = sampleDataLogic.downloadMRBrainTumor1()

        ##################################
        self.delayDisplay("Create tumor segmentation")

        segmentationNode = slicer.vtkMRMLSegmentationNode()
        slicer.mrmlScene.AddNode(segmentationNode)
        segmentationNode.CreateDefaultDisplayNodes()
        segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(
            masterVolumeNode)

        segmentName = "Tumor"
        import vtkSegmentationCorePython as vtkSegmentationCore

        segment = vtkSegmentationCore.vtkSegment()
        segment.SetName(
            segmentationNode.GetSegmentation().GenerateUniqueSegmentID(
                segmentName))
        segmentationNode.GetSegmentation().AddSegment(segment)

        ##################################
        self.delayDisplay("Create segment editor")

        segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
        segmentEditorWidget.show()
        segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
        segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()
        slicer.mrmlScene.AddNode(segmentEditorNode)
        segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
        segmentEditorWidget.setSegmentationNode(segmentationNode)
        segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)

        ##################################
        self.delayDisplay("Run segmentation")

        segmentEditorWidget.setActiveEffectByName("ENgrave")
        effect = segmentEditorWidget.activeEffect()

        # effect.self().fiducialPlacementToggle.placeButton().click()

        # points =[[2.589283578714074, 44.60536690073953, 27.299999999999997], [8.515228351086698, 35.22262101114956, 27.299999999999997],
        #          [13.700430026912741, 25.099132025013006, 27.299999999999997], [5.799170330415919, 19.17318725264039, 27.299999999999997],
        #          [2.589283578714074, 9.296612632019361, 27.299999999999997], [-10.250263428093263, 12.25958501820567, 27.299999999999997],
        #          [-16.17620820046588, 18.185529790578286, 27.299999999999997], [-20.373752414229813, 27.568275680168263, 27.299999999999997],
        #          [-15.929293834950343, 38.679422128366916, 27.299999999999997], [-11.484835255670887, 44.11153816970849, 27.299999999999997],
        #          [6.539913426962492, 33.49422045254088, 31.499999999999993], [1.354711751136449, 42.383137611099805, 31.499999999999993],
        #          [-8.768777235000101, 44.35845253522401, 31.499999999999993], [-14.200893276341674, 36.70410720424271, 31.499999999999993],
        #          [-18.398437490105607, 27.07444694913721, 31.499999999999993], [-12.719407083248512, 16.704043597485132, 31.499999999999993],
        #          [-7.534205407422476, 11.765756287174618, 31.499999999999993], [0.12013992355882408, 12.25958501820567, 31.499999999999993],
        #          [5.799170330415919, 16.21021486645408, 31.499999999999993], [8.268313985571176, 21.642330907795646, 31.499999999999993],
        #          [13.947344392428263, 26.827532583621682, 31.499999999999993], [-3.0897468281430065, 32.50656299047878, 45.49999999999998],
        #          [2.589283578714074, 27.32136131465274, 45.49999999999998], [-5.3119761177827485, 21.642330907795646, 45.49999999999998],
        #          [-8.02803413845352, 27.32136131465274, 45.49999999999998], [-14.694722007372718, 30.778162431870093, 38.499999999999986],
        #          [-8.02803413845352, 12.01267065269014, 38.499999999999986], [-3.583575559174065, 39.66707959042902, 11.900000000000007],
        #          [3.576941040776184, 31.765819893932196, 11.900000000000007], [0.12013992355882408, 20.901587811249065, 11.900000000000007],
        #          [-9.26260596603116, 28.555933142230366, 11.900000000000007], [6.046084695931441, 38.432507762851394, 17.500000000000007],
        #          [-17.163865662527982, 33.7411348180564, 17.500000000000007], [-14.200893276341674, 21.889245273311168, 17.500000000000007]]

        # for p in points:
        #   effect.self().segmentMarkupNode.AddControlPoint(p)

        # effect.self().onApply()

        # ##################################
        # self.delayDisplay("Make segmentation results nicely visible in 3D")
        # segmentationDisplayNode = segmentationNode.GetDisplayNode()
        # segmentationDisplayNode.SetSegmentVisibility(segmentName, True)
        # slicer.util.findChild(segmentEditorWidget, "Show3DButton").checked = True
        # segmentationDisplayNode.SetSegmentOpacity3D("Background",0.5)

        # ##################################
        # self.delayDisplay("Compute statistics")

        # from SegmentStatistics import SegmentStatisticsLogic

        # segStatLogic = SegmentStatisticsLogic()

        # segStatLogic.getParameterNode().SetParameter("Segmentation", segmentationNode.GetID())
        # segStatLogic.getParameterNode().SetParameter("ScalarVolume", masterVolumeNode.GetID())
        # segStatLogic.getParameterNode().SetParameter("visibleSegmentsOnly", "False")

        # segStatLogic.computeStatistics()

        # # Export results to table (just to see all results)
        # resultsTableNode = slicer.vtkMRMLTableNode()
        # slicer.mrmlScene.AddNode(resultsTableNode)
        # segStatLogic.exportToTable(resultsTableNode)
        # segStatLogic.showTable(resultsTableNode)

        # self.delayDisplay("Check a few numerical results")

        # stats = segStatLogic.getStatistics()
        # self.assertEqual( round(stats['Tumor', 'LabelmapSegmentStatisticsPlugin.volume_mm3']), 19498.0)

        self.delayDisplay('test_Engrave1 passed')
  def preview(self):
    # Get master volume image data
    import vtkSegmentationCorePython as vtkSegmentationCore
    masterImageData = self.scriptedEffect.masterVolumeImageData()

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

    previewNode = self.getPreviewNode()
    if (not previewNode or not self.mergedLabelmapGeometryImage
      or (self.clippedMasterImageDataRequired and not self.clippedMasterImageData)):

      self.reset()
      # Compute merged labelmap extent (effective extent slightly expanded)
      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))
        return
      if not self.mergedLabelmapGeometryImage:
        self.mergedLabelmapGeometryImage = vtkSegmentationCore.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)

      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 = vtkSegmentationCore.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 = vtkSegmentationCore.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 = vtkSegmentationCore.vtkOrientedImageData()
    segmentationNode.GenerateMergedLabelmapForAllSegments(mergedImage,
      vtkSegmentationCore.vtkSegmentation.EXTENT_UNION_OF_EFFECTIVE_SEGMENTS, self.mergedLabelmapGeometryImage, self.selectedSegmentIds)

    outputLabelmap = vtkSegmentationCore.vtkOrientedImageData()

    self.computePreviewLabelmap(mergedImage, outputLabelmap)

    # Write output segmentation results in segments
    for index in xrange(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 = vtkSegmentationCore.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)
      slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(newSegmentLabelmap, previewNode, segmentID)

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

    self.updateGUIFromMRML()
Example #24
0
    def preview(self):
        # Get source 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(
                    f"Auto-complete operation skipped: at least {self.minimumNumberOfSegments} visible segments are required"
                )
                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.sourceVolumeImageData()
            masterImageExtent = masterImageData.GetExtent()
            labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent(
            )
            # Margin size is relative to combined seed region size, but minimum of 3 voxels
            print(f"self.extentGrowthRatio = {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(
                ).GetSourceVolumeIntensityMask()
                maskSegmentID = self.scriptedEffect.parameterSetNode(
                ).GetMaskSegmentID() if self.scriptedEffect.parameterSetNode(
                ).GetMaskSegmentID() else ""
                intensityRange = self.scriptedEffect.parameterSetNode(
                ).GetSourceVolumeIntensityMaskRange(
                ) if intensityBasedMasking else None
                success = segmentationNode.GenerateEditMask(
                    self.clippedMaskImageData,
                    self.scriptedEffect.parameterSetNode().GetMaskMode(),
                    self.clippedMasterImageData,  # reference geometry
                    "",  # edited segment ID
                    maskSegmentID,
                    self.clippedMasterImageData
                    if intensityBasedMasking else None,
                    intensityRange)
                if not success:
                    logging.error("Failed to create edit mask")
                    self.clippedMaskImageData = None

        previewNode.SetName(segmentationNode.GetName() + " preview")
        previewNode.RemoveClosedSurfaceRepresentation(
        )  # Force the closed surface representation to update
        # TODO: This will no longer be required when we can use the segment editor to set multiple segments
        # as the closed surfaces will be converted as necessary by the segmentation logic.

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

        outputLabelmap = slicer.vtkOrientedImageData()
        self.computePreviewLabelmap(mergedImage, outputLabelmap)

        if previewNode.GetSegmentation().GetNumberOfSegments(
        ) != self.selectedSegmentIds.GetNumberOfValues():
            # first update (or number of segments changed), need a full reinitialization
            previewNode.GetSegmentation().RemoveAllSegments()

        for index in range(self.selectedSegmentIds.GetNumberOfValues()):
            segmentID = self.selectedSegmentIds.GetValue(index)

            previewSegment = previewNode.GetSegmentation().GetSegment(
                segmentID)
            if not previewSegment:
                inputSegment = segmentationNode.GetSegmentation().GetSegment(
                    segmentID)

                previewSegment = vtkSegmentationCore.vtkSegment()
                previewSegment.SetName(inputSegment.GetName())
                previewSegment.SetColor(inputSegment.GetColor())
                previewNode.GetSegmentation().AddSegment(
                    previewSegment, segmentID)

            labelValue = index + 1  # n-th segment label value = n + 1 (background label value is 0)
            previewSegment.AddRepresentation(
                vtkSegmentationCore.vtkSegmentationConverter.
                GetSegmentationBinaryLabelmapRepresentationName(),
                outputLabelmap)
            previewSegment.SetLabelValue(labelValue)

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

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

        self.updateGUIFromMRML()
  def preview(self):
    # Get master volume image data
    import vtkSegmentationCorePython as vtkSegmentationCore
    masterImageData = self.scriptedEffect.masterVolumeImageData()

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

    previewNode = self.getPreviewNode()
    if not previewNode or not self.mergedLabelmapGeometryImage \
      or (self.clippedMasterImageDataRequired and not self.clippedMasterImageData):
      self.reset()
      # Compute merged labelmap extent (effective extent slightly expanded)
      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))
        return
      if not self.mergedLabelmapGeometryImage:
        self.mergedLabelmapGeometryImage = vtkSegmentationCore.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)

      masterImageExtent = masterImageData.GetExtent()
      labelsEffectiveExtent = self.mergedLabelmapGeometryImage.GetExtent()
      margin = [17, 17, 17]
      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]) ]
      self.mergedLabelmapGeometryImage.SetExtent(labelsExpandedExtent)

      previewNode = slicer.mrmlScene.CreateNodeByClass('vtkMRMLSegmentationNode')
      previewNode.UnRegister(None)
      previewNode = slicer.mrmlScene.AddNode(previewNode)
      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)

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

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

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

    outputLabelmap = vtkSegmentationCore.vtkOrientedImageData()

    self.computePreviewLabelmap(mergedImage, outputLabelmap)

    # Write output segmentation results in segments
    for index in xrange(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 = vtkSegmentationCore.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)
      slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(newSegmentLabelmap, previewNode, segmentID)

    self.updateGUIFromMRML()
    def TestSection_1_AddRemoveSegment(self):
        # Add/remove segment from segmentation (check display properties, color table, etc.)
        logging.info('Test section 1: 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(
            'Body_Contour').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(
            'Tumor_Contour').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(50)
        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()
        self.assertEqual(imageStat.GetVoxelCount(), 1000)
        imageStatResult = imageStat.GetOutput()
        for i in range(4):
            logging.info("Volume {0}: {1}".format(
                i, imageStatResult.GetScalarComponentAsDouble(i, 0, 0, 0)))
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 795)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 194)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 4)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(3, 0, 0, 0), 7)

        # 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()
        self.assertEqual(imageStat.GetVoxelCount(), 1000)
        imageStatResult = imageStat.GetOutput()
        for i in range(4):
            logging.info("Volume {0}: {1}".format(
                i, imageStatResult.GetScalarComponentAsDouble(i, 0, 0, 0)))
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(0, 0, 0, 0), 795)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(1, 0, 0, 0), 194)
        self.assertEqual(
            imageStatResult.GetScalarComponentAsDouble(2, 0, 0, 0), 6)
        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)
Example #27
0
    def test_NvidiaAIAA1(self):
        """
        Basic automated test of the segmentation method:
        - Create segmentation by placing sphere-shaped seeds
        - Run segmentation
        - Verify results using segment statistics
        The test can be executed from SelfTests module (test name: SegmentEditorNvidiaAIAA)
        """

        self.delayDisplay("Starting test_NvidiaAIAA1")

        import vtkSegmentationCorePython as vtkSegmentationCore
        import vtkSlicerSegmentationsModuleLogicPython as vtkSlicerSegmentationsModuleLogic
        import SampleData
        from SegmentStatistics import SegmentStatisticsLogic

        ##################################
        self.delayDisplay("Load master volume")

        masterVolumeNode = SampleData.downloadSample('MRBrainTumor1')

        ##################################
        self.delayDisplay("Create segmentation containing a few spheres")

        segmentationNode = slicer.mrmlScene.AddNewNodeByClass(
            'vtkMRMLSegmentationNode')
        segmentationNode.CreateDefaultDisplayNodes()
        segmentationNode.SetReferenceImageGeometryParameterFromVolumeNode(
            masterVolumeNode)

        # Segments are defined by a list of: name and a list of sphere [radius, posX, posY, posZ]
        segmentGeometries = [['Tumor', [[10, -6, 30, 28]]],
                             [
                                 'Background',
                                 [[10, 0, 65, 22], [15, 1, -14, 30],
                                  [12, 0, 28, -7], [5, 0, 30, 54],
                                  [12, 31, 33, 27], [17, -42, 30, 27],
                                  [6, -2, -17, 71]]
                             ], ['Air', [[10, 76, 73, 0], [15, -70, 74, 0]]]]
        for segmentGeometry in segmentGeometries:
            segmentName = segmentGeometry[0]
            appender = vtk.vtkAppendPolyData()
            for sphere in segmentGeometry[1]:
                sphereSource = vtk.vtkSphereSource()
                sphereSource.SetRadius(sphere[0])
                sphereSource.SetCenter(sphere[1], sphere[2], sphere[3])
                appender.AddInputConnection(sphereSource.GetOutputPort())
            segment = vtkSegmentationCore.vtkSegment()
            segment.SetName(
                segmentationNode.GetSegmentation().GenerateUniqueSegmentID(
                    segmentName))
            appender.Update()
            segment.AddRepresentation(
                vtkSegmentationCore.vtkSegmentationConverter.
                GetSegmentationClosedSurfaceRepresentationName(),
                appender.GetOutput())
            segmentationNode.GetSegmentation().AddSegment(segment)

        ##################################
        self.delayDisplay("Create segment editor")

        segmentEditorWidget = slicer.qMRMLSegmentEditorWidget()
        segmentEditorWidget.show()
        segmentEditorWidget.setMRMLScene(slicer.mrmlScene)
        segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()
        slicer.mrmlScene.AddNode(segmentEditorNode)
        segmentEditorWidget.setMRMLSegmentEditorNode(segmentEditorNode)
        segmentEditorWidget.setSegmentationNode(segmentationNode)
        segmentEditorWidget.setMasterVolumeNode(masterVolumeNode)

        ##################################
        self.delayDisplay("Run segmentation")
        segmentEditorWidget.setActiveEffectByName("NvidiaAIAA")
        effect = segmentEditorWidget.activeEffect()
        effect.setParameter("ObjectScaleMm", 3.0)
        effect.self().onApply()

        ##################################
        self.delayDisplay("Make segmentation results nicely visible in 3D")
        segmentationDisplayNode = segmentationNode.GetDisplayNode()
        segmentationDisplayNode.SetSegmentVisibility("Air", False)
        segmentationDisplayNode.SetSegmentOpacity3D("Background", 0.5)

        ##################################
        self.delayDisplay("Compute statistics")

        segStatLogic = SegmentStatisticsLogic()
        segStatLogic.computeStatistics(segmentationNode, masterVolumeNode)

        # Export results to table (just to see all results)
        resultsTableNode = slicer.vtkMRMLTableNode()
        slicer.mrmlScene.AddNode(resultsTableNode)
        segStatLogic.exportToTable(resultsTableNode)
        segStatLogic.showTable(resultsTableNode)

        self.delayDisplay("Check a few numerical results")
        self.assertEqual(
            round(segStatLogic.statistics["Tumor", "LM volume cc"]), 16)
        self.assertEqual(
            round(segStatLogic.statistics["Background", "LM volume cc"]), 3010)

        self.delayDisplay('test_NvidiaAIAA1 passed')
Example #28
0
 def _addInitialSegment(self):
     import vtkSegmentationCorePython as vtkSegmentationCore
     segment = vtkSegmentationCore.vtkSegment()
     segment.SetName("Prostate Segmentation")
     self._segmentationNode.GetSegmentation().AddSegment(segment)
    def TestSection_03_qMRMLSegmentationGeometryWidget(self):
        logging.info('Test section 2: qMRMLSegmentationGeometryWidget')

        import vtkSegmentationCore
        binaryLabelmapReprName = vtkSegmentationCore.vtkSegmentationConverter.GetBinaryLabelmapRepresentationName(
        )
        closedSurfaceReprName = vtkSegmentationCore.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 = vtkSegmentationCore.vtkSegmentationConverter.SerializeImageGeometry(
            mrOrientedImageData)
        segmentationNode.GetSegmentation().SetConversionParameter(
            vtkSegmentationCore.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 = vtkSegmentationCore.vtkOrientedImageData()
        segmentOrientedImageData.DeepCopy(threshold.GetOutput())
        mrImageToWorldMatrix = vtk.vtkMatrix4x4()
        mrOrientedImageData.GetImageToWorldMatrix(mrImageToWorldMatrix)
        segmentOrientedImageData.SetImageToWorldMatrix(mrImageToWorldMatrix)
        segment = vtkSegmentationCore.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 = vtkSegmentationCore.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]]))
        vtkSegmentationCore.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), (224.3439, 223.7890, -135.25),
                [[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]]))
        vtkSegmentationCore.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]]))
        vtkSegmentationCore.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]]))
        vtkSegmentationCore.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]]))
        vtkSegmentationCore.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), (-167.156, -217.711, -135.75),
                [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]]))
        vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData),
                         5223846)
        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), (-167.156, -217.711, -135.75),
                [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]]))
        vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData),
                         5223846)

        # 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), (-167.156, -58.6623, -249.228),
                [[0.0, 0.0, 1.0], [-0.7071, -0.7071, 0.0],
                 [0.7071, -0.7071, 0.0]]))
        vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData),
                         5221241)

        # ROI source
        roiNode = slicer.vtkMRMLAnnotationROINode()
        roiNode.SetName('SourceROI')
        slicer.mrmlScene.AddNode(roiNode)
        roiNode.UnRegister(None)
        xyz = [0] * 3
        center = [0] * 3
        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), (-216.156, -217.711, -135.75),
                [[0.0, 0.0, 1.0], [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]]))
        vtkSegmentationCore.vtkOrientedImageDataResample.ResampleOrientedImageToReferenceOrientedImage(
            segmentOrientedImageData, geometryImageData, geometryImageData,
            False, True)
        self.assertEqual(self.getForegroundVoxelCount(geometryImageData),
                         5223846)
        slicer.util.delayDisplay('Model and ROI source cases - OK')

        slicer.util.delayDisplay('Segmentation geometry widget test passed')
Example #30
0
    def load(self, loadable):
        """ Load the DICOM SEG object
    """
        logging.debug('DICOM SEG load()')
        try:
            uid = loadable.uid
            logging.debug('in load(): uid = ', uid)
        except AttributeError:
            return False

        self.tempDir = os.path.join(slicer.app.temporaryPath, "QIICR", "SEG",
                                    self.currentDateTime, loadable.uid)
        try:
            os.makedirs(self.tempDir)
        except OSError:
            pass

        # produces output label map files, one per segment, and information files with
        # the terminology information for each segment
        segFileName = slicer.dicomDatabase.fileForInstance(uid)
        if segFileName is None:
            logging.error(
                'Failed to get the filename from the DICOM database for ' +
                uid)
            self.cleanup()
            return False

        parameters = {
            "inputSEGFileName": segFileName,
            "outputDirName": self.tempDir,
        }

        try:
            segimage2itkimage = slicer.modules.segimage2itkimage
        except AttributeError:
            logging.error(
                'Unable to find CLI module segimage2itkimage, unable to load DICOM Segmentation object'
            )
            self.cleanup()
            return False

        cliNode = None
        cliNode = slicer.cli.run(segimage2itkimage,
                                 cliNode,
                                 parameters,
                                 wait_for_completion=True)
        if cliNode.GetStatusString() != 'Completed':
            logging.error(
                'SEG2NRRD did not complete successfully, unable to load DICOM Segmentation'
            )
            self.cleanup()
            return False

        numberOfSegments = len(glob.glob(os.path.join(self.tempDir, '*.nrrd')))

        # resize the color table to include the segments plus 0 for the background

        seriesName = self.referencedSeriesName(loadable)
        segmentLabelNodes = []
        metaFileName = os.path.join(self.tempDir, "meta.json")

        # Load terminology in the metafile into context
        terminologiesLogic = slicer.modules.terminologies.logic()
        terminologiesLogic.LoadTerminologyFromSegmentDescriptorFile(
            loadable.name, metaFileName)
        terminologiesLogic.LoadAnatomicContextFromSegmentDescriptorFile(
            loadable.name, metaFileName)

        with open(metaFileName) as metaFile:
            data = json.load(metaFile)
            logging.debug('number of segmentation files = ' +
                          str(numberOfSegments))
            if numberOfSegments != len(data["segmentAttributes"]):
                logging.error(
                    'Loading failed: Inconsistent number of segments in the descriptor file and on disk'
                )
                return
            for segmentAttributes in data["segmentAttributes"]:
                # TODO: only handles the first item of lists
                for segment in segmentAttributes:
                    # load each of the segments' segmentations
                    # Initialize color and terminology from .info file
                    # See SEG2NRRD.cxx and EncodeSEG.cxx for how it's written.
                    # Format of the .info file (no leading spaces, labelNum, RGBColor, SegmentedPropertyCategory and
                    # SegmentedPropertyCategory are required):
                    # labelNum;RGB:R,G,B;SegmentedPropertyCategory:code,scheme,meaning;SegmentedPropertyType:code,scheme,meaning;SegmentedPropertyTypeModifier:code,scheme,meaning;AnatomicRegion:code,scheme,meaning;AnatomicRegionModifier:code,scheme,meaning
                    # R, G, B are 0-255 in file, but mapped to 0-1 for use in color node
                    # set defaults in case of missing fields, modifiers are optional

                    try:
                        rgb255 = segment["recommendedDisplayRGBValue"]
                        rgb = map(lambda c: float(c) / 255., rgb255)
                    except KeyError:
                        rgb = (0., 0., 0.)

                    segmentId = segment["LabelID"]

                    defaults = ['T-D0050', 'Tissue', 'SRT']
                    categoryCode, categoryCodingScheme, categoryCodeMeaning = \
                      self.getValuesFromCodeSequence(segment, "SegmentedPropertyCategoryCodeSequence", defaults)

                    typeCode, typeCodingScheme, typeCodeMeaning = \
                      self.getValuesFromCodeSequence(segment, "SegmentedPropertyTypeCodeSequence", defaults)

                    typeModCode, typeModCodingScheme, typeModCodeMeaning = \
                      self.getValuesFromCodeSequence(segment, "SegmentedPropertyTypeModifierCodeSequence")

                    anatomicRegionDefaults = ['T-D0010', 'SRT', 'Entire Body']
                    regionCode, regionCodingScheme, regionCodeMeaning = \
                      self.getValuesFromCodeSequence(segment, "AnatomicRegionCodeSequence", anatomicRegionDefaults)

                    regionModCode, regionModCodingScheme, regionModCodeMeaning = \
                      self.getValuesFromCodeSequence(segment, "AnatomicRegionModifierCodeSequence")

                    dummyTerminologyWidget = slicer.qSlicerTerminologyNavigatorWidget(
                    )  # Still cannot call static methods from python
                    segmentTerminologyTag = dummyTerminologyWidget.serializeTerminologyEntry(
                        loadable.name, categoryCode, categoryCodingScheme,
                        categoryCodeMeaning, typeCode, typeCodingScheme,
                        typeCodeMeaning, typeModCode, typeModCodingScheme,
                        typeModCodeMeaning, loadable.name, regionCode,
                        regionCodingScheme, regionCodeMeaning, regionModCode,
                        regionModCodingScheme, regionModCodeMeaning)
                    # end of processing a line of terminology

                    # TODO: Create logic class that both CLI and this plugin uses so that we don't need to have temporary NRRD files and labelmap nodes
                    # if not hasattr(slicer.modules, 'segmentations'):

                    # load the segmentation volume file and name it for the reference series and segment color
                    labelFileName = os.path.join(self.tempDir,
                                                 str(segmentId) + ".nrrd")
                    segmentName = seriesName + "-" + typeCodeMeaning + "-label"
                    (success, labelNode) = slicer.util.loadLabelVolume(
                        labelFileName,
                        properties={'name': segmentName},
                        returnNode=True)
                    if not success:
                        raise ValueError(
                            "{} could not be loaded into Slicer!".format(
                                labelFileName))
                    segmentLabelNodes.append(labelNode)

                    # Set terminology properties as attributes to the label node (which is a temporary node)
                    #TODO: This is a quick solution, maybe there is a better one
                    labelNode.SetAttribute("Terminology",
                                           segmentTerminologyTag)
                    labelNode.SetAttribute("ColorR", str(rgb[0]))
                    labelNode.SetAttribute("ColorG", str(rgb[1]))
                    labelNode.SetAttribute("ColorB", str(rgb[2]))

                    # create Subject hierarchy nodes for the loaded series
                    self.addSeriesInSubjectHierarchy(loadable, labelNode)

            metaFile.close()

        self.cleanup()

        import vtkSegmentationCorePython as vtkSegmentationCore

        segmentationNode = slicer.vtkMRMLSegmentationNode()
        segmentationNode.SetName(seriesName)
        slicer.mrmlScene.AddNode(segmentationNode)

        segmentationDisplayNode = slicer.vtkMRMLSegmentationDisplayNode()
        slicer.mrmlScene.AddNode(segmentationDisplayNode)
        segmentationNode.SetAndObserveDisplayNodeID(
            segmentationDisplayNode.GetID())

        segmentation = vtkSegmentationCore.vtkSegmentation()
        segmentation.SetMasterRepresentationName(
            vtkSegmentationCore.vtkSegmentationConverter.
            GetSegmentationBinaryLabelmapRepresentationName())

        segmentationNode.SetAndObserveSegmentation(segmentation)
        self.addSeriesInSubjectHierarchy(loadable, segmentationNode)

        for segmentLabelNode in segmentLabelNodes:
            segment = vtkSegmentationCore.vtkSegment()
            segment.SetName(segmentLabelNode.GetName())

            segmentColor = [
                float(segmentLabelNode.GetAttribute("ColorR")),
                float(segmentLabelNode.GetAttribute("ColorG")),
                float(segmentLabelNode.GetAttribute("ColorB"))
            ]
            segment.SetColor(segmentColor)

            segment.SetTag(
                vtkSegmentationCore.vtkSegment.GetTerminologyEntryTagName(),
                segmentLabelNode.GetAttribute("Terminology"))

            #TODO: when the logic class is created, this will need to be changed
            orientedImage = slicer.vtkSlicerSegmentationsModuleLogic.CreateOrientedImageDataFromVolumeNode(
                segmentLabelNode)
            segment.AddRepresentation(
                vtkSegmentationCore.vtkSegmentationConverter.
                GetSegmentationBinaryLabelmapRepresentationName(),
                orientedImage)
            segmentation.AddSegment(segment)

            segmentDisplayNode = segmentLabelNode.GetDisplayNode()
            if segmentDisplayNode is not None:
                slicer.mrmlScene.RemoveNode(segmentDisplayNode)
            slicer.mrmlScene.RemoveNode(segmentLabelNode)

        segmentation.CreateRepresentation(
            vtkSegmentationCore.vtkSegmentationConverter.
            GetSegmentationClosedSurfaceRepresentationName(), True)

        return True