def section_CliTableInputOutput(self): self.delayDisplay("Test table writing and reading by CLI module", self.delayMs) # Create input and output nodes inputTableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(inputTableNode) inputTableNode.AddColumn() inputTableNode.AddColumn() inputTableNode.AddColumn() inputTableNode.AddEmptyRow() inputTableNode.AddEmptyRow() inputTableNode.AddEmptyRow() for row in range(3): for col in range(3): inputTableNode.SetCellText(row, col, str( (row + 1) * (col + 1))) inputTableNode.SetCellText(0, 0, "source") outputTableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(outputTableNode) # Run CLI module self.delayDisplay("Run CLI module", self.delayMs) parameters = {} parameters["arg0"] = self.createDummyVolume().GetID() parameters["arg1"] = self.createDummyVolume().GetID() parameters["transform1"] = self.createDummyTransform().GetID() parameters["transform2"] = self.createDummyTransform().GetID() parameters["inputDT"] = inputTableNode.GetID() parameters["outputDT"] = outputTableNode.GetID() slicer.cli.run(slicer.modules.executionmodeltour, None, parameters, wait_for_completion=True) # Verify the output table content self.delayDisplay("Verify results", self.delayMs) # the ExecutionModelTour module copies the input table to the output exxcept the first two rows # of the first column, which is set to "Computed first" and "Computed second" strings for row in range(3): for col in range(3): if row == 0 and col == 0: self.assertTrue( outputTableNode.GetCellText(row, col) == "Computed first") elif row == 1 and col == 0: self.assertTrue( outputTableNode.GetCellText(row, col) == "Computed second") else: self.assertTrue( outputTableNode.GetCellText(row, col) == inputTableNode.GetCellText(row, col))
def getUserStatisticsTableNode(self): parameterNode = self.getParameterNode() if parameterNode is None: return tableNode = parameterNode.GetNodeReference(self.USER_STATISTICS_TABLE_REFERENCE_ROLE) if not tableNode is None: return tableNode if not self.getUserStatisticsEnabled(): return tableNodes = slicer.util.getNodesByClass("vtkMRMLTableNode") for node in tableNodes: if node.GetAttribute("UserStatistics.TableNode"): tableNode = node break if tableNode is None: logging.info("Create new table node") tableNode = slicer.vtkMRMLTableNode() tableNode.SetName("UserStatisticsTableNode") tableNode.SetAttribute("UserStatistics.TableNode", "") slicer.mrmlScene.AddNode(tableNode) self.setupTimerTableNode(tableNode) self.setUserStatisticsTableNode(tableNode) return tableNode
def exportToTable(self): """ Export statistics to table node """ colorNode = self.getColorNode() table = slicer.vtkMRMLTableNode() tableWasModified = table.StartModify() table.SetName(slicer.mrmlScene.GenerateUniqueName(self.nodeBaseName + ' statistics')) # Define table columns if colorNode: col = table.AddColumn() col.SetName("Type") for k in self.keys: col = table.AddColumn() col.SetName(k) for i in self.labelStats["Labels"]: rowIndex = table.AddEmptyRow() columnIndex = 0 if colorNode: table.SetCellText(rowIndex, columnIndex, colorNode.GetColorName(i)) columnIndex += 1 # Add other values for k in self.keys: table.SetCellText(rowIndex, columnIndex, str(self.labelStats[i, k])) columnIndex += 1 table.EndModify(tableWasModified) return table
def section_TableRole(self): self.delayDisplay("Table role", self.delayMs) # Create sample table node tableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(tableNode) tableNode.SetName(self.sampleTableName) # Add node to subject hierarchy from vtkSlicerSubjectHierarchyModuleMRML import vtkMRMLSubjectHierarchyNode studyNode = slicer.util.getNode( self.studyName + slicer.vtkMRMLSubjectHierarchyConstants.GetSubjectHierarchyNodeNamePostfix() ) self.assertTrue(studyNode != None) tableShNode = vtkMRMLSubjectHierarchyNode.CreateSubjectHierarchyNode( slicer.mrmlScene, studyNode, slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMLevelSeries(), self.sampleTableName, tableNode, ) self.assertTrue(tableShNode != None) self.assertTrue(tableShNode.GetParentNode() == studyNode) self.assertTrue(tableShNode.GetOwnerPluginName() == "Tables")
def createAndConfigureTable(self): table = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(table) table.SetAttribute("QuantitativeReporting", "Yes") table.SetAttribute("readonly", "Yes") table.SetUseColumnNameAsColumnHeader(True) return table
def mergeStatisticsTableNodes(self, tableNodes): if len(tableNodes) < 2: return currentRowIndexes = {} for tableNode in tableNodes: currentRowIndexes[tableNode] = 0 logging.info("Create new table node") newTableNode = slicer.vtkMRMLTableNode() newTableNode.SetName("UserStatisticsTableNode") newTableNode.SetAttribute("UserStatistics.TableNode", "") self.setupTimerTableNode(newTableNode) completed = False while not completed: remainingTableNodes = [] # Popupulate the list of rows starting at the top of each table and moving down for tableNode in tableNodes: currentIndex = currentRowIndexes[tableNode] if currentIndex < tableNode.GetNumberOfRows(): remainingTableNodes.append(tableNode) # Done! There are no more rows left in any of the tables if remainingTableNodes == []: completed = True else: # Find the "minimum" row by comparing the string format contents of all rows oldTableNode = None rowToAddString = None for tableNode in remainingTableNodes: rowString = self.serializeFromTable(currentRowIndexes[tableNode], tableNode, self.timerTableColumnNames) # This row is the new "minimum" row to be added to the table next if oldTableNode is None or rowString < rowToAddString: oldTableNode = tableNode rowToAddString = rowString # This row is the same as the current "minimum". It is a duplicate so discard it elif rowString == rowToAddString: currentRowIndexes[tableNode] += 1 # Add the "minimum" row and increment the index of the corresponding table node newTableRowIndex = newTableNode.AddEmptyRow() oldTableRowIndex = currentRowIndexes[oldTableNode] currentRowIndexes[oldTableNode] += 1 for name in self.timerTableColumnNames: oldTableColumnIndex = oldTableNode.GetColumnIndex(name) if oldTableColumnIndex < 0: continue newTableColumnIndex = newTableNode.GetColumnIndex(name) if newTableColumnIndex < 0: continue cellText = oldTableNode.GetCellText(oldTableRowIndex, oldTableColumnIndex) newTableNode.SetCellText(newTableRowIndex, newTableColumnIndex, cellText) slicer.mrmlScene.AddNode(newTableNode) for tableNode in tableNodes: slicer.mrmlScene.RemoveNode(tableNode)
def test_SegmentStatisticsBasic(self): """ This tests some aspects of the label statistics """ self.delayDisplay("Starting test_SegmentStatisticsBasic") import vtkSegmentationCorePython as vtkSegmentationCore 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() uniqueSegmentID = segmentationNode.GetSegmentation().GenerateUniqueSegmentID("Test") segmentationNode.AddSegmentFromClosedSurfaceRepresentation(sphereSource.GetOutput(), uniqueSegmentID) self.delayDisplay("Compute statistics") segStatLogic = SegmentStatisticsLogic() segStatLogic.getParameterNode().SetParameter("Segmentation", segmentationNode.GetID()) segStatLogic.getParameterNode().SetParameter("ScalarVolume", masterVolumeNode.GetID()) segStatLogic.computeStatistics() self.delayDisplay("Check a few numerical results") self.assertEqual( segStatLogic.getStatistics()["Test_2","LabelmapSegmentStatisticsPlugin.voxel_count"], 9807) self.assertEqual( segStatLogic.getStatistics()["Test_4","ScalarVolumeSegmentStatisticsPlugin.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 section_CliTableInputOutput(self): self.delayDisplay("Test table writing and reading by CLI module",self.delayMs) # Create input and output nodes inputTableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(inputTableNode) inputTableNode.AddColumn() inputTableNode.AddColumn() inputTableNode.AddColumn() inputTableNode.AddEmptyRow() inputTableNode.AddEmptyRow() inputTableNode.AddEmptyRow() for row in range(3): for col in range(3): inputTableNode.SetCellText(row,col,str((row+1)*(col+1))) inputTableNode.SetCellText(0,0,"source") outputTableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(outputTableNode) # Run CLI module self.delayDisplay("Run CLI module",self.delayMs) parameters = {} parameters["arg0"] = self.createDummyVolume().GetID() parameters["arg1"] = self.createDummyVolume().GetID() parameters["transform1"] = self.createDummyTransform().GetID() parameters["transform2"] = self.createDummyTransform().GetID() parameters["inputDT"] = inputTableNode.GetID() parameters["outputDT"] = outputTableNode.GetID() slicer.cli.run(slicer.modules.executionmodeltour, None, parameters, wait_for_completion=True) # Verify the output table content self.delayDisplay("Verify results",self.delayMs) # the ExecutionModelTour module copies the input table to the output exxcept the first two rows # of the first column, which is set to "Computed first" and "Computed second" strings for row in range(3): for col in range(3): if row==0 and col==0: self.assertTrue( outputTableNode.GetCellText(row, col) == "Computed first") elif row==1 and col==0: self.assertTrue( outputTableNode.GetCellText(row, col) == "Computed second") else: self.assertTrue( outputTableNode.GetCellText(row, col) == inputTableNode.GetCellText(row, col) )
def exportToTable(self, table=None, nonEmptyKeysOnly=True): if not table: table = slicer.vtkMRMLTableNode() table.SetName(slicer.mrmlScene.GenerateUniqueName(self.grayscaleNode.GetName() + ' statistics')) slicer.mrmlScene.AddNode(table) table.SetUseColumnNameAsColumnHeader(True) SegmentStatisticsLogic.exportToTable(self, table, nonEmptyKeysOnly) return table
def exportToTable(self, table=None, nonEmptyKeysOnly=True): if not table: table = slicer.vtkMRMLTableNode() table.SetName( slicer.mrmlScene.GenerateUniqueName( self.grayscaleNode.GetName() + ' statistics')) slicer.mrmlScene.AddNode(table) table.SetUseColumnNameAsColumnHeader(True) SegmentStatisticsLogic.exportToTable(self, table, nonEmptyKeysOnly) return table
def calculateStatistics(self): from SegmentStatistics import SegmentStatisticsLogic segStatLogic = SegmentStatisticsLogic() segStatLogic.getParameterNode().SetParameter( "Segmentation", self.segmentationEditorWidget.segmentationNodeID()) self.segmentationEditorWidget.segmentationNode( ).CreateDefaultDisplayNodes() segStatLogic.computeStatistics() resultsTableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(resultsTableNode) segStatLogic.exportToTable(resultsTableNode) segStatLogic.showTable(resultsTableNode)
def runCLIWithParameterFile(self, imageNode, maskNode, tableNode, parameterFilePath, callback=None): """ Run the actual algorithm using the provided customization file and provided image and region of interest(s) (ROIs) :param imageNode: Slicer Volume node representing the image from which features should be extracted :param labelNode: Slicer Labelmap node containing the ROIs as integer encoded volume (voxel value indicates ROI id) or a segmentation node containing the segments of the ROIs (will be converted to binary label maps) :param tableNode: Slicer Table node which will hold the calculated results :param parameterFilePath: String file path pointing to the parameter file used to customize the extraction :param callback: Function which is invoked when the CLI is done (can be used to unlock the GUI) """ if self.cliNode is not None: self.logger.warning('Already running an extraction!') return self.logger.info('Feature extraction started') self._parameterFile = parameterFilePath self._labelGenerators = [] if maskNode.IsA('vtkMRMLVolumeNode'): self._labelGenerators = chain( self._labelGenerators, self._getLabelGeneratorFromLabelMap(maskNode, imageNode)) elif maskNode.IsA('vtkMRMLSegmentationNode'): self._labelGenerators = chain( self._labelGenerators, self._getLabelGeneratorFromSegmentationNode( maskNode, imageNode)) else: self.logger.error('Invalid maskNode') return self._cli_output = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(self._cli_output) self.outTable = tableNode self._initOutputTable() self.callback = callback self._startCLI(firstRun=True)
def _initTableNode_(self): """ Initialize the vtkMRMLTableSQLiteStorageNode and add it to the scene """ self.tableStorageNode = slicer.vtkMRMLTableSQLiteStorageNode() slicer.mrmlScene.AddNode(self.tableStorageNode) self.tableStorageNode.SetFileName(self._dbFilePath_) self.tableStorageNode.SetTableName(self._dbTableName_) self.tableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(self.tableNode) self.tableNode.SetName("{}_table".format(self._dbTableName_)) self.tableNode.SetAndObserveStorageNodeID(self.tableStorageNode.GetID()) if os.path.isfile(self._dbFilePath_): # Read the previous data self.tableStorageNode.ReadData(self.tableNode) else: logging.info("The storage database has not been created yet")
def metadata2vtkTableNode(self, metafile): with open(metafile) as datafile: table = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(table) table.SetAttribute("QuantitativeReporting", "Yes") table.SetAttribute("readonly", "Yes") table.SetUseColumnNameAsColumnHeader(True) data = json.load(datafile) tableWasModified = table.StartModify() measurement = data["Measurements"][0] col = table.AddColumn() col.SetName("Segment") for measurementItem in measurement["measurementItems"]: col = table.AddColumn() if "derivationModifier" in measurementItem.keys(): col.SetName( SegmentStatisticsDICOMMeaningMapping.getKeyForValue( measurementItem["derivationModifier"] ["CodeMeaning"])) else: col.SetName( SegmentStatisticsDICOMMeaningMapping.getKeyForValue( measurementItem["quantity"]["CodeMeaning"] + " " + measurementItem["units"]["CodeValue"])) for measurement in data["Measurements"]: name = measurement["TrackingIdentifier"] value = measurement["ReferencedSegment"] rowIndex = table.AddEmptyRow() table.SetCellText(rowIndex, 0, name) for columnIndex, measurementItem in enumerate( measurement["measurementItems"]): table.SetCellText(rowIndex, columnIndex + 1, measurementItem["value"]) table.EndModify(tableWasModified) slicer.app.applicationLogic().GetSelectionNode( ).SetReferenceActiveTableID(table.GetID()) slicer.app.applicationLogic().PropagateTableSelection() return table
def onApplyButton(self): if self.debuggingCheckBox.checked: # Setup debug logging for the pyradiomics toolbox # PyRadiomics logs to stderr by default, which is picked up by slicer and added to the slicer log. radiomics.setVerbosity(logging.DEBUG) else: radiomics.setVerbosity(logging.WARNING) if not self.outputTableSelector.currentNode(): tableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(tableNode) self.outputTableSelector.setCurrentNode(tableNode) logic = SlicerRadiomicsLogic() featureClasses = self.getCheckedFeatureClasses() # Lock GUI self.applyButton.text = 'Working...' self.applyButton.setEnabled(False) slicer.app.processEvents() # Compute features kwargs = {} kwargs['binWidth'] = int(self.binWidthSliderWidget.value) kwargs['symmetricalGLCM'] = self.symmetricalGLCMCheckBox.checked # kwargs['label'] = int(self.labelSliderWidget.value) imageNode = self.inputVolumeSelector.currentNode() labelNode = self.inputMaskSelector.currentNode() segmentationNode = self.inputSegmentationSelector.currentNode() try: featuresDict = logic.run(imageNode, labelNode, segmentationNode, featureClasses, **kwargs) logic.exportToTable(featuresDict, self.outputTableSelector.currentNode()) except: self.logger.error("Feature calculation failed.") # Unlock GUI self.applyButton.setEnabled(True) self.applyButton.text = 'Apply' # Show results logic.showTable(self.outputTableSelector.currentNode())
def loadTestData(self, collection="MRHead", imageDataType='volume', uid="2.16.840.1.113662.4.4168496325.1025306170.548651188813145058"): if not len(slicer.dicomDatabase.filesForSeries(uid)): sampleData = TestDataLogic.downloadAndUnzipSampleData(collection) TestDataLogic.importIntoDICOMDatabase(sampleData[imageDataType]) self.loadSeries(uid) loadedVolumeNodes = slicer.util.getNodesByClass('vtkMRMLScalarVolumeNode') if not loadedVolumeNodes: logging.error("No volumes were loaded into Slicer. Canceling.") return masterNode = loadedVolumeNodes[-1] tableNode = slicer.vtkMRMLTableNode() tableNode.SetAttribute("QuantitativeReporting", "Yes") slicer.mrmlScene.AddNode(tableNode) self.measurementReportSelector.setCurrentNode(tableNode) self.segmentEditorWidget.editor.setMasterVolumeNode(masterNode) self.retrieveTestDataButton.enabled = False
def onApplyButton(self): self.applyButton.setText("Aguarde...") self.applyButton.setEnabled(False) self.progressBar.setVisible(True) slicer.app.processEvents() # Criar tabela para os dados logging.info('Criando a tabela') self.table = slicer.vtkMRMLTableNode() tableWasModified = self.table.StartModify() self.table.SetName("Export Table") self.table.SetUseColumnNameAsColumnHeader(True) col = self.table.AddColumn(); col.SetName("Volume") col = self.table.AddColumn(); col.SetName("ICort") col = self.table.AddColumn(); col.SetName("ITrab") col = self.table.AddColumn(); col.SetName("Ilow") col = self.table.AddColumn(); col.SetName("FVTO") for node in slicer.util.getNodesByClass('vtkMRMLScalarVolumeNode'): if node.GetClassName() == "vtkMRMLLabelMapVolumeNode": logging.info('Ignorando label: ' + node.GetName()) continue else: logging.info('Processando ' + node.GetName()) input = node label = slicer.util.getNode(node.GetName() + '-label') ROILabelValue = self.labelROISpin.value cortLabelValue = self.labelCortSpin.value self.run(input, label, ROILabelValue, cortLabelValue) # Adiciona a tabela a cena e exibe logging.info('Adicionar tabela e exibir') slicer.mrmlScene.AddNode(self.table) slicer.app.layoutManager().setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpTableView) slicer.app.applicationLogic().GetSelectionNode().SetReferenceActiveTableID(self.table.GetID()) slicer.app.applicationLogic().PropagateTableSelection() self.table.EndModify(tableWasModified) self.applyButton.setText("Iniciar") self.applyButton.setEnabled(True) return
def section_CreateTable(self): self.delayDisplay("Create table",self.delayMs) # Create sample table node tableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(tableNode) tableNode.SetName(self.sampleTableName) # Add a new column column = tableNode.AddColumn() self.assertTrue( column is not None ) column.InsertNextValue("some") column.InsertNextValue("data") column.InsertNextValue("in this") column.InsertNextValue("column") tableNode.Modified(); # Check table table = tableNode.GetTable() self.assertTrue( table is not None ) self.assertTrue( table.GetNumberOfRows() == 4 ) self.assertTrue( table.GetNumberOfColumns() == 1 )
def exportToTable(self): """ Export statistics to table node """ colorNode = self.getColorNode() table = slicer.vtkMRMLTableNode() tableWasModified = table.StartModify() table.SetName(slicer.mrmlScene.GenerateUniqueName(self.nodeBaseName+' statistics')) # Define table columns if colorNode: col=table.AddColumn() col.SetName("Type") for k in self.keys: col=table.AddColumn() col.SetName(k) for i in self.labelStats["Labels"]: rowIndex = table.AddEmptyRow() if colorNode: columnIndex = 0 table.SetCellText(rowIndex, columnIndex, colorNode.GetColorName(i)) columnIndex += 1 # Add other values for k in self.keys: table.SetCellText(rowIndex, columnIndex, str(self.labelStats[i,k])) columnIndex += 1 table.EndModify(tableWasModified) # Add table to the scene and show it slicer.mrmlScene.AddNode(table) slicer.app.layoutManager().setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpTableView) slicer.app.applicationLogic().GetSelectionNode().SetReferenceActiveTableID(table.GetID()) slicer.app.applicationLogic().PropagateTableSelection()
def metadata2vtkTableNode(self, metafile): with open(metafile) as datafile: table = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(table) table.SetAttribute("QuantitativeReporting", "Yes") table.SetAttribute("readonly", "Yes") table.SetUseColumnNameAsColumnHeader(True) data = json.load(datafile) tableWasModified = table.StartModify() measurement = data["Measurements"][0] col = table.AddColumn() col.SetName("Segment") for measurementItem in measurement["measurementItems"]: col = table.AddColumn() if "derivationModifier" in measurementItem.keys(): col.SetName(SegmentStatisticsDICOMMeaningMapping.getKeyForValue(measurementItem["derivationModifier"]["CodeMeaning"])) else: col.SetName(SegmentStatisticsDICOMMeaningMapping.getKeyForValue(measurementItem["quantity"]["CodeMeaning"] +" "+measurementItem["units"]["CodeValue"])) for measurement in data["Measurements"]: name = measurement["TrackingIdentifier"] value = measurement["ReferencedSegment"] rowIndex = table.AddEmptyRow() table.SetCellText(rowIndex, 0, name) for columnIndex, measurementItem in enumerate(measurement["measurementItems"]): table.SetCellText(rowIndex, columnIndex+1, measurementItem["value"]) table.EndModify(tableWasModified) slicer.app.applicationLogic().GetSelectionNode().SetReferenceActiveTableID(table.GetID()) slicer.app.applicationLogic().PropagateTableSelection() return table
def hausdorffDist(self): # Creation of a node for segments comparison self.segCompnode = slicer.vtkMRMLSegmentComparisonNode() slicer.mrmlScene.AddNode(self.segCompnode) slicer.modules.segmentcomparison.logic().SetMRMLScene(slicer.mrmlScene) # Loading of the segmentation node and the first segmentation self.segCompnode.SetAndObserveReferenceSegmentationNode(self.segment1) self.segCompnode.SetReferenceSegmentID(self.segment1.GetName()) # Loading of the segmentation node and the segmentation of comparison self.segCompnode.SetAndObserveCompareSegmentationNode(self.segment2) self.segCompnode.SetCompareSegmentID(self.segment2.GetName()) # Creation and configuration of a table node where the results will be shown self.tableH = slicer.vtkMRMLTableNode() self.tableH.SetName("Hausdorff Distance") slicer.mrmlScene.AddNode(self.tableH) self.segCompnode.SetAndObserveHausdorffTableNode(self.tableH) # Display Hausdorff Distance Table (3D Table View) slicer.app.layoutManager().setLayout( slicer.vtkMRMLLayoutNode.SlicerLayout3DTableView) slicer.app.applicationLogic().GetSelectionNode( ).SetReferenceActiveTableID(self.tableH.GetID()) slicer.app.applicationLogic().PropagateTableSelection() # Calculation of the Hausdorff Distance slicer.modules.segmentcomparison.logic().ComputeHausdorffDistances( self.segCompnode) # Save table in a CSV file storagenode = self.tableH.CreateDefaultStorageNode() storagenode.SetFileName("hausdorff.csv") storagenode.WriteData(self.tableH)
def diceCoeff(self): # Creation of a node for segments comparison self.segCompNode = slicer.vtkMRMLSegmentComparisonNode() slicer.mrmlScene.AddNode(self.segCompNode) slicer.modules.segmentcomparison.logic().SetMRMLScene(slicer.mrmlScene) # Loading of the segmentation node and the first segmentation self.segCompNode.SetAndObserveReferenceSegmentationNode(self.segment1) self.segCompNode.SetReferenceSegmentID(self.segment1.GetName()) # Loading of the segmentation node and the segmentation of comparison self.segCompNode.SetAndObserveCompareSegmentationNode(self.segment2) self.segCompNode.SetCompareSegmentID(self.segment2.GetName()) # Creation and configuration of a table node where the results will be shown self.tableD = slicer.vtkMRMLTableNode() self.tableD.SetName("Sorensen-Dice Coefficient") slicer.mrmlScene.AddNode(self.tableD) self.segCompNode.SetAndObserveDiceTableNode(self.tableD) # Display Dice Coefficient Table (3D Table View) slicer.app.layoutManager().setLayout( slicer.vtkMRMLLayoutNode.SlicerLayout3DTableView) slicer.app.applicationLogic().GetSelectionNode( ).SetReferenceActiveTableID(self.tableD.GetID()) slicer.app.applicationLogic().PropagateTableSelection() # Calculation of the Dice Index slicer.modules.segmentcomparison.logic().ComputeDiceStatistics( self.segCompNode) # Save table in a CSV file storagenode = self.tableD.CreateDefaultStorageNode() storagenode.SetFileName("dice.csv") storagenode.WriteData(self.tableD)
def test_SurfaceCut1(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: SegmentEditorSurfaceCut) """ self.delayDisplay("Starting test_SurfaceCut1") ################################## 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("Surface cut") 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.AddFiducialFromArray(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_SurfaceCut1 passed')
def setup(self): ScriptedLoadableModuleWidget.setup(self) self.logic = ExtensionStatsLogic() self.logic.setStatusCallback(self.setStatusText) self.queryInProgress = False # Instantiate and connect widgets ... # # Parameters Area # parametersCollapsibleButton = ctk.ctkCollapsibleButton() parametersCollapsibleButton.text = "Parameters" self.layout.addWidget(parametersCollapsibleButton) parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton) extensionNameBox = qt.QHBoxLayout() self.extensionNameEdit = qt.QLineEdit() self.extensionNameEdit.setText('') extensionNameBox.addWidget(self.extensionNameEdit) self.extensionNameAllButton = qt.QPushButton() self.extensionNameAllButton.text = "all" self.extensionNameAllButton.toolTip = "Get statistics for all extensions" extensionNameBox.addWidget(self.extensionNameAllButton) self.populateExtensionNameEdit() parametersFormLayout.addRow("Extension name: ", extensionNameBox) self.applyButton = qt.QPushButton("Get download statistics") self.applyButton.toolTip = "Get download statistics" parametersFormLayout.addRow(self.applyButton) self.statusText = qt.QLabel() parametersFormLayout.addRow("Status:", self.statusText) # Stats table self.statsTableWidget = slicer.qMRMLTableView() self.statsTableWidget.setMRMLScene(slicer.mrmlScene) parametersFormLayout.addRow("Statistics:", self.statsTableWidget) policy = qt.QSizePolicy() policy.setVerticalStretch(1) policy.setHorizontalPolicy(qt.QSizePolicy.Expanding) policy.setVerticalPolicy(qt.QSizePolicy.Expanding) self.statsTableWidget.setSizePolicy(policy) self.statsTableNode = slicer.vtkMRMLTableNode() self.statsTableNode.SetName('ExtensionStats') self.statsTableNode.SetUseColumnNameAsColumnHeader(True) self.statsTableNode.SetUseFirstColumnAsRowHeader(True) slicer.mrmlScene.AddNode(self.statsTableNode) self.statsTableWidget.setMRMLTableNode(self.statsTableNode) # Copy to clipboard button self.copyToClipboardButton = qt.QPushButton("Copy table to clipboard") parametersFormLayout.addRow('', self.copyToClipboardButton) # connections self.extensionNameAllButton.connect('clicked()', self.populateExtensionNameEdit) self.applyButton.connect('clicked(bool)', self.onApplyButton) self.copyToClipboardButton.connect('clicked()', self.copyTableToClipboard)
def onApplyButton(self): self.applyButton.setText("Aguarde...") self.applyButton.setEnabled(False) self.progressBar.setVisible(True) inputVolume = self.baseSelector.currentNode() cropLogic = slicer.modules.cropvolume.logic() volumesLogic = slicer.modules.volumes.logic() label = 1 perc = 0.75 hasROI = False logging.info('Processing started') # Criar tabela para os dados logging.info('Criando a tabela') table = slicer.vtkMRMLTableNode() tableWasModified = table.StartModify() table.SetName("Export Table") table.SetUseColumnNameAsColumnHeader(True) col = table.AddColumn() col.SetName("Volume") col = table.AddColumn() col.SetName("Qtde") col = table.AddColumn() col.SetName("Min-Max") col = table.AddColumn() col.SetName("Range") # Hierarquia para modelos 3D modelHNode = slicer.mrmlScene.CreateNodeByClass( 'vtkMRMLModelHierarchyNode') modelHNode.SetName('Models') modelHNode = slicer.mrmlScene.AddNode(modelHNode) # Atualiza tela slicer.app.processEvents() # Criando a ROI logging.info('Criar ROI a partir do Fiducial') for node in slicer.util.getNodesByClass('vtkMRMLAnnotationROINode'): ROI = node hasROI = True if not hasROI: ROI = self.createROI() # Calculando media do volume inicial logging.info('Calcular media volume inicial: ' + inputVolume.GetName()) arrayInputVolume = slicer.util.array(inputVolume.GetName()) meanInputVolume = arrayInputVolume.mean() # Realizar o crop no primeiro volume logging.info('Crop do volume inicial') outputVolume = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLScalarVolumeNode", inputVolume.GetName() + ' cropped') cropLogic.CropInterpolated(ROI, inputVolume, outputVolume, False, 1.0, 2, 0) # Calculo dos pontos mais intensos do primeiro volume logging.info("Calculando pontos mais intensos do volume inicial") arrayNode = slicer.util.array(outputVolume.GetName()) min = arrayNode.min() max = arrayNode.max() minValue = max - ((max - min) * perc) arrayValue = arrayNode[arrayNode >= minValue] meanValue = arrayValue.mean() countValue = len(arrayValue) # Atualiza tela slicer.app.processEvents() #segmentando o primeiro volume logging.info('Segmentando volume inicial') labelMap = volumesLogic.CreateAndAddLabelVolume( slicer.mrmlScene, outputVolume, outputVolume.GetName() + '-label') labelArray = slicer.util.array(labelMap.GetName()) labelArray[arrayNode > minValue] = label labelMap.GetImageData().Modified() inputLabelMap = labelMap # Apagando o volume cropped slicer.mrmlScene.RemoveNode(outputVolume) # Criar modelo 3D logging.info('Criar modelo 3D do volume inicial') parameters = {} parameters["InputVolume"] = labelMap.GetID() parameters["Name"] = labelMap.GetName() + '-model' parameters['ModelSceneFile'] = modelHNode.GetID() modelMaker = slicer.modules.modelmaker slicer.cli.run(modelMaker, None, parameters, True) # Atualiza tela slicer.app.processEvents() # Popular tabela com os dados do primeiro volume logging.info('Popular tabela') rowIndex = table.AddEmptyRow() table.SetCellText(rowIndex, 0, inputVolume.GetName()) table.SetCellText(rowIndex, 1, str(countValue)) table.SetCellText(rowIndex, 2, str(int(min)) + " - " + str(int(max))) table.SetCellText(rowIndex, 3, str(int(minValue)) + " - " + str(int(max))) # Atualiza tela slicer.app.processEvents() # Identificar todos os volumes e processar for node in slicer.util.getNodesByClass('vtkMRMLScalarVolumeNode'): logging.info('\nProcessando ' + node.GetName()) if node.GetName() == inputVolume.GetName(): logging.info('Ignorando volume: ' + node.GetName()) continue if node.GetClassName() == "vtkMRMLLabelMapVolumeNode": logging.info('Ignorando label: ' + node.GetName()) continue # Executar BrainsFit para corresgistro dos exames mais novos com o mais antigo logging.info('Registrando o volume') fixedVolume = inputVolume movingVolume = node registeredVolume = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLScalarVolumeNode", node.GetName() + ' Reg') brainsfitParameters = { 'fixedVolume': fixedVolume.GetID(), 'movingVolume': movingVolume.GetID(), 'outputVolume': registeredVolume.GetID(), 'useRigid': True } self.cliNode = slicer.cli.run(slicer.modules.brainsfit, None, brainsfitParameters) while (self.cliNode.IsBusy()): slicer.app.processEvents() # Normalizar volumes logging.info('Normalizando o volume') arrayRegisteredVolume = slicer.util.array( registeredVolume.GetName()) meanRegisteredVolume = arrayRegisteredVolume.mean() factor = meanRegisteredVolume / meanInputVolume arrayRegisteredVolume[:] = arrayRegisteredVolume / factor arrayRegisteredVolume[:] = numpy.around(arrayRegisteredVolume, 0) # Crop Interpolated logging.info('Crop do volume') outputVolume = slicer.mrmlScene.AddNewNodeByClass( "vtkMRMLScalarVolumeNode", registeredVolume.GetName() + ' cropped') cropLogic.CropInterpolated(ROI, registeredVolume, outputVolume, False, 1.0, 2, 0) # Calculo dos pontos mais intensos do volume corregistrado print("Calculando") arrayNode = slicer.util.array(outputVolume.GetName()) arrayNode[:] = numpy.around(arrayNode, 0) min = arrayNode.min() max = arrayNode.max() minValue = max - ((max - min) * perc) arrayValue = arrayNode[arrayNode >= minValue] meanValue = arrayValue.mean() countValue = len(arrayValue) # Atualiza tela slicer.app.processEvents() #segmentando o volume logging.info('Segmentando Volume') label = label + 1 labelMap = volumesLogic.CreateAndAddLabelVolume( slicer.mrmlScene, outputVolume, outputVolume.GetName() + '-label') labelArray = slicer.util.array(labelMap.GetName()) labelArray[arrayNode > minValue] = label labelMap.GetImageData().Modified() # Apagando o volume cropped slicer.mrmlScene.RemoveNode(outputVolume) # Criar modelo 3D logging.info('Criar Modelo 3D') parameters = {} parameters["InputVolume"] = labelMap.GetID() parameters["Name"] = labelMap.GetName() + '-model' parameters['ModelSceneFile'] = modelHNode.GetID() modelMaker = slicer.modules.modelmaker slicer.cli.run(modelMaker, None, parameters, True) # Atualiza tela slicer.app.processEvents() # Popular tabela com os dados logging.info('Popular tabela') rowIndex = table.AddEmptyRow() table.SetCellText(rowIndex, 0, node.GetName()) table.SetCellText(rowIndex, 1, str(countValue)) table.SetCellText(rowIndex, 2, str(int(min)) + " - " + str(int(max))) table.SetCellText(rowIndex, 3, str(int(minValue)) + " - " + str(int(max))) rowIndex += 1 # Exibir o resultado logging.info('Exibir resultado') for color in ['Red', 'Yellow', 'Green']: slicer.app.layoutManager().sliceWidget(color).sliceLogic( ).GetSliceCompositeNode().SetBackgroundVolumeID( inputVolume.GetID()) slicer.app.layoutManager().sliceWidget( color).sliceLogic().GetSliceCompositeNode().SetLabelVolumeID( inputLabelMap.GetID()) # Add table to the scene and show it logging.info('Adicionar tabela e exibir') slicer.mrmlScene.AddNode(table) slicer.app.layoutManager().setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpTableView) slicer.app.applicationLogic().GetSelectionNode( ).SetReferenceActiveTableID(table.GetID()) slicer.app.applicationLogic().PropagateTableSelection() table.EndModify(tableWasModified) # Atualiza tela slicer.app.processEvents() logging.info('Processing completed') self.applyButton.setText("Iniciar") self.applyButton.setEnabled(True) self.progressBar.setVisible(False) return
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 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_SlicerRadiomics1(self): """ Ideally you should have several levels of tests. At the lowest level tests should exercise the functionality of the logic with different inputs (both valid and invalid). At higher levels your tests should emulate the way the user would interact with your code and confirm that it still works the way you intended. One of the most important features of the tests is that it should alert other developers when their changes will have an impact on the behavior of your module. For example, if a developer removes a feature that you depend on, your test should break so they know that the feature is needed. """ self.delayDisplay('Starting the test') # # first, get some data # https://github.com/Radiomics/SlicerRadiomics/releases/download/TestData-v1.0.0/lung1_binary.seg.nrrd import urllib dataRelease = 'v1.0.0' dataURLPrefix = 'https://github.com/Radiomics/SlicerRadiomics/releases/download/TestData' dataItems = (('lung1_image.nrrd', slicer.util.loadVolume), ('lung1_label.nrrd', slicer.util.loadLabelVolume), ('lung1_binary.seg.nrrd', slicer.util.loadSegmentation), ('lung1.seg_0.vtp', None), ('lung1.seg_1.vtp', None), ('lung1_surface.seg.vtm', slicer.util.loadSegmentation), ('Params.yaml', None)) for item, loader in dataItems: url = dataURLPrefix + '-' + dataRelease + '/' + item filePath = os.path.join(slicer.app.temporaryPath, item) if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.logger.info('Requesting download %s from %s...\n' % (item, url)) self.assertTrue(urllib.urlretrieve(url, filePath), 'Failed to download from ' + url) if loader: self.logger.info('Loading %s from %s...' % (item, filePath)) self.assertTrue(loader(filePath), 'Failed to load ' + item) self.delayDisplay( 'Finished with download and loading %d volumes' % (slicer.mrmlScene.GetNumberOfNodesByClass('vtkMRMLVolumeNode'))) grayscaleNode = slicer.util.getNode(pattern='lung1_image') labelmapNode = slicer.util.getNode(pattern='lung1_label') binaryNode = slicer.util.getNode(pattern='lung1_binary') surfaceNode = slicer.util.getNode(pattern='lung1_surface') parameterFile = os.path.join(slicer.app.temporaryPath, 'Params.yaml') logic = SlicerRadiomicsLogic() logic.runSync = True # Block Thread until each extraction is done (i.e. run synchronously) self.assertIsNotNone(logic.hasImageData(grayscaleNode)) self.assertIsNotNone(logic.hasImageData(labelmapNode)) featureClasses = ['firstorder'] settings = {} settings['binWidth'] = 25 settings['symmetricalGLCM'] = False settings['label'] = 1 settings['correctMask'] = True enabledImageTypes = {"Original": {}} for maskNode in [labelmapNode, binaryNode, surfaceNode]: tableNode = slicer.vtkMRMLTableNode() tableNode.SetName('lung1_label and ' + maskNode.GetName()) slicer.mrmlScene.AddNode(tableNode) # No callback needed as tests are run synchronously logic.runCLI(grayscaleNode, maskNode, tableNode, featureClasses, settings, enabledImageTypes) logic.showTable(tableNode) for maskNode in [labelmapNode, binaryNode, surfaceNode]: tableNode = slicer.vtkMRMLTableNode() tableNode.SetName('lung1_label and ' + maskNode.GetName() + ' customized with Params.yaml') slicer.mrmlScene.AddNode(tableNode) # No callback needed as tests are run synchronously logic.runCLIWithParameterFile(grayscaleNode, maskNode, tableNode, parameterFile) logic.showTable(tableNode) self.delayDisplay('Test passed!')
def onApplyButton(self): if not self.outputTableSelector.currentNode(): tableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(tableNode) self.outputTableSelector.setCurrentNode(tableNode) logic = SlicerRadiomicsLogic() # Lock GUI self.applyButton.text = 'Working...' self.applyButton.setEnabled(False) slicer.app.processEvents() imageNode = self.inputVolumeSelector.currentNode() maskNode = self.inputMaskSelector.currentNode() if self.manualCustomizationRadioButton.checked: # Set up customization featureClasses = self.getCheckedFeatureClasses() settings = {} settings['binWidth'] = self.binWidthSliderWidget.value settings['symmetricalGLCM'] = self.symmetricalGLCMCheckBox.checked enabledImageTypes = {'Original': {}} logKernelSizesValue = self.logKernelSizes.text if logKernelSizesValue: try: enabledImageTypes['LoG'] = {'sigma': [float(i) for i in logKernelSizesValue.split(',')]} except: self.logger.error('Failed to parse LoG sigma value from string \"' + logKernelSizesValue + '\"') traceback.print_exc() return resampledVoxelSizeValue = self.resampledVoxelSize.text if resampledVoxelSizeValue: try: settings['resampledPixelSpacing'] = [float(i) for i in resampledVoxelSizeValue.split(',')] except: self.logger.error('Failed to parse resampled voxel spacing from string \"' + resampledVoxelSizeValue + '\"') settings['resampledPixelSpacing'] = None traceback.print_exc() return if self.waveletCheckBox.checked: enabledImageTypes['Wavelet'] = {} # Compute features try: logic.runCLI(imageNode, maskNode, self.outputTableSelector.currentNode(), featureClasses, settings, enabledImageTypes, self.onFinished) except: self.logger.error("Feature calculation failed.") traceback.print_exc() else: # Compute Features try: parameterFile = self.parameterFilePathLineEdit.currentPath logic.runCLIWithParameterFile(imageNode, maskNode, self.outputTableSelector.currentNode(), parameterFile, self.onFinished) except: self.logger.error("Feature calculation failed.") traceback.print_exc() logic.showTable(self.outputTableSelector.currentNode())
def onApplyButton(self): if self.verboseCheckBox.checked: # Setup debug logging for the pyradiomics toolbox # PyRadiomics logs to stderr by default, which is picked up by slicer and added to the slicer log. setVerbosity(logging.DEBUG) else: setVerbosity(logging.WARNING) if not self.outputTableSelector.currentNode(): tableNode = slicer.vtkMRMLTableNode() slicer.mrmlScene.AddNode(tableNode) self.outputTableSelector.setCurrentNode(tableNode) logic = SlicerRadiomicsLogic() # Lock GUI self.applyButton.text = 'Working...' self.applyButton.setEnabled(False) slicer.app.processEvents() imageNode = self.inputVolumeSelector.currentNode() labelNode = self.inputMaskSelector.currentNode() segmentationNode = self.inputSegmentationSelector.currentNode() if self.manualCustomizationRadioButton.checked: # Set up customization featureClasses = self.getCheckedFeatureClasses() settings = {} settings['binWidth'] = self.binWidthSliderWidget.value settings['symmetricalGLCM'] = self.symmetricalGLCMCheckBox.checked enabledImageTypes = {'Original': {}} logKernelSizesValue = self.logKernelSizes.text if logKernelSizesValue: try: enabledImageTypes['LoG'] = { 'sigma': [float(i) for i in logKernelSizesValue.split(',')] } except: self.logger.error( 'Failed to parse LoG sigma value from string \"' + logKernelSizesValue + '\"') traceback.print_exc() return resampledVoxelSizeValue = self.resampledVoxelSize.text if resampledVoxelSizeValue: try: settings['resampledPixelSpacing'] = [ float(i) for i in resampledVoxelSizeValue.split(',') ] except: self.logger.error( 'Failed to parse resampled voxel spacing from string \"' + resampledVoxelSizeValue + '\"') settings['resampledPixelSpacing'] = None traceback.print_exc() return if self.waveletCheckBox.checked: enabledImageTypes['Wavelet'] = {} # Compute features try: featuresDict = logic.run(imageNode, labelNode, segmentationNode, featureClasses, settings, enabledImageTypes) logic.exportToTable(featuresDict, self.outputTableSelector.currentNode()) except: self.logger.error("Feature calculation failed.") traceback.print_exc() else: # Compute Features try: parameterFile = self.parameterFilePathLineEdit.currentPath featuresDict = logic.runWithParameterFile( imageNode, labelNode, segmentationNode, parameterFile) logic.exportToTable(featuresDict, self.outputTableSelector.currentNode()) except: self.logger.error("Feature calculation failed.") traceback.print_exc() # Unlock GUI self.applyButton.setEnabled(True) self.applyButton.text = 'Apply' # Show results logic.showTable(self.outputTableSelector.currentNode())
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_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 compareToManualSeg(self, tip, tail, manSegPoints, insertAngle): # get manually selected point manualTip = [0.0, 0.0, 0.0] manSegPoints.GetNthFiducialPosition(0, manualTip) # manualTipString = "{0:.10} {1:.10} {2:.10}".format(manualTip[0], manualTip[1], manualTip[2]) # print("manual tip: " + manualTipString) # calculate the distance between needle tips tipError = numpy.sqrt((tip[0] - manualTip[0])**2 + (tip[1] - manualTip[1])**2 + (tip[2] - manualTip[2])**2) tipErrorString = "{0:.10}".format(tipError) print("Tip error is " + tipErrorString + " mm.") # calculate the directional vectors for the algorithm and manual segmentations algoVector = [a - b for a, b in zip(tip, tail)] manualVector = [a - b for a, b in zip(manualTip, tail)] # normalize the vectors algoLength = numpy.sqrt(algoVector[0]**2 + algoVector[1]**2 + algoVector[2]**2) manualLength = numpy.sqrt(manualVector[0]**2 + manualVector[1]**2 + manualVector[2]**2) algoVector = [a / algoLength for a in algoVector] manualVector = [a / manualLength for a in manualVector] # calculate the dot product and compute the trajectory difference dotProduct = numpy.dot(algoVector, manualVector) trajError = numpy.arccos(dotProduct) trajError = numpy.degrees(trajError) trajErrorString = "{0:.10}".format(trajError) print("Trajectory error is " + trajErrorString + " degrees.") # calculate the active tip error algoActTip = [a * 10 for a in algoVector] algoActTip = [a - b for a, b in zip(tip, algoActTip)] manualActTip = [a * 10 for a in manualVector] manualActTip = [a - b for a, b in zip(manualTip, manualActTip)] actTipError = numpy.sqrt((algoActTip[0] - manualActTip[0])**2 + (algoActTip[1] - manualActTip[1])**2 + (algoActTip[2] - manualActTip[2])**2) actTipErrorString = "{0:.10}".format(actTipError) print("Active tip error is " + actTipErrorString + " mm.") # calculate the difference from expected insertion angle insertString = "{0:.10}".format(insertAngle) print("Expected insertion angle is " + insertString + " degrees.") # calculate insertion angle of algorithmically segmented needle dotProduct = -1 * algoVector[1] algoAngle = numpy.arccos(dotProduct) algoAngle = numpy.degrees(algoAngle) algoAngleString = "{0:.10}".format(algoAngle) print("Segmented insertion angle is " + algoAngleString + " degrees.") # calculate insertion angle error angleDiff = numpy.absolute(insertAngle - algoAngle) angleDiffString = "{0:.10}".format(angleDiff) print("Insertion angle difference is " + angleDiffString + " degrees.") # check if a metrics table has already been created if not slicer.util.getNode('Metrics'): tableNode = slicer.vtkMRMLTableNode() tableNode.SetName('Metrics') col = tableNode.AddColumn() col.SetName('Tip Difference (mm)') col = tableNode.AddColumn() col.SetName('Active Tip Difference (mm)') col = tableNode.AddColumn() col.SetName('Trajectory Difference (deg)') col = tableNode.AddColumn() col.SetName('Segmented Insertion Angle (deg)') col = tableNode.AddColumn() col.SetName('Insertion Angle Difference (deg)') slicer.mrmlScene.AddNode(tableNode) else: tableNode = slicer.util.getNode('Metrics') # populates a table with the metrics values tableNode.AddEmptyRow() row = tableNode.GetNumberOfRows() - 1 tableNode.SetCellText(row, 0, tipErrorString) tableNode.SetCellText(row, 1, actTipErrorString) tableNode.SetCellText(row, 2, trajErrorString) tableNode.SetCellText(row, 3, algoAngleString) tableNode.SetCellText(row, 4, angleDiffString)