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)
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 _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)
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()
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')
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')
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')
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)
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()
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)
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')
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')
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