def tempDirectory(self,key='__SlicerTestTemp__',tempDir=None,includeDateTime=False): """Come up with a unique directory name in the temp dir and make it and return it # TODO: switch to QTemporaryDir in Qt5. Note: this directory is not automatically cleaned up """ if not tempDir: tempDir = qt.QDir(slicer.app.temporaryPath) tempDirName = key if includeDateTime: key += qt.QDateTime().currentDateTime().toString("yyyy-MM-dd_hh+mm+ss.zzz") fileInfo = qt.QFileInfo(qt.QDir(tempDir), tempDirName) dirPath = fileInfo.absoluteFilePath() qt.QDir().mkpath(dirPath) return dirPath
def promptForDatabaseDirectory(self): """Ask the user to pick a database directory. But, if the application is in testing mode, just pick a temp directory """ commandOptions = slicer.app.commandOptions() if commandOptions.testingEnabled: databaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatbase' qt.QDir().mkpath(databaseDirectory) self.onDatabaseDirectoryChanged(databaseDirectory) else: settings = qt.QSettings() databaseDirectory = settings.value('DatabaseDirectory') if databaseDirectory: self.onDatabaseDirectoryChanged(databaseDirectory) else: fileDialog = ctk.ctkFileDialog(slicer.util.mainWindow()) fileDialog.setWindowModality(1) fileDialog.setWindowTitle("Select DICOM Database Directory") fileDialog.setFileMode(2) # prompt for directory fileDialog.connect('fileSelected(QString)', self.onDatabaseDirectoryChanged) label = qt.QLabel( "<p><p>The Slicer DICOM module stores a local database with an index to all datasets that are <br>pushed to slicer, retrieved from remote dicom servers, or imported.<p>Please select a location for this database where you can store the amounts of data you require.<p>Be sure you have write access to the selected directory.", fileDialog) fileDialog.setBottomWidget(label) fileDialog.exec_()
def promptForDatabaseDirectory(self): """Ask the user to pick a database directory. But, if the application is in testing mode, just pick a temp directory """ commandOptions = slicer.app.commandOptions() if commandOptions.testingEnabled: databaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatbase' qt.QDir().mkpath(databaseDirectory) self.onDatabaseDirectoryChanged(databaseDirectory) else: settings = qt.QSettings() databaseDirectory = settings.value('DatabaseDirectory') if databaseDirectory: self.onDatabaseDirectoryChanged(databaseDirectory) else: # pick the user's Documents by default documentsLocation = qt.QDesktopServices.DocumentsLocation documents = qt.QDesktopServices.storageLocation( documentsLocation) databaseDirectory = documents + "/SlicerDICOMDatabase" message = "DICOM Database will be stored in\n\n" message += databaseDirectory message += "\n\nUse the Local Database button in the DICOM Browser " message += "to pick a different location." qt.QMessageBox.information(slicer.util.mainWindow(), 'DICOM', message, qt.QMessageBox.Ok) if not os.path.exists(databaseDirectory): os.mkdir(databaseDirectory) self.onDatabaseDirectoryChanged(databaseDirectory)
def addModule(self, fileName, permanent): """ Loads a module in the Slicer factory while Slicer is running """ logging.info('Module addition process started') # Determine which modules in above are not already loaded factory = slicer.app.moduleManager().factoryManager() myModule = type('moduleType', (), {}) myModule.dirPath = os.path.dirname(fileName) myModule.baseName = os.path.basename(fileName) myModule.key, myModule.fileExtension = os.path.splitext( myModule.baseName) if factory.isLoaded(myModule.key): raise Exception("Abort: Module already loaded") if permanent: # Add module(s) to permanent search paths, if requested settings = slicer.app.revisionUserSettings() rawSearchPaths = list( self._settingsList(settings, "Modules/AdditionalPaths")) searchPaths = [qt.QDir(path) for path in rawSearchPaths] modified = False rawPath = myModule.dirPath path = qt.QDir(rawPath) if path not in searchPaths: searchPaths.append(path) rawSearchPaths.append(rawPath) modified = True if modified: settings.setValue("Modules/AdditionalPaths", rawSearchPaths) # Register requested module(s) factory.registerModule(qt.QFileInfo(fileName)) if not factory.isRegistered(myModule.key): raise Exception("Abort: Failed to register module %s", myModule.key) # Instantiate and load requested module(s) if not factory.loadModules([myModule.key]): raise Exception( "Abort: The module factory manager reported an error. \ One or more of the requested module(s) and/or \ dependencies thereof may not have been loaded.") logging.info('Module addition process completed') return True
def export(self,exportables): for exportable in exportables: # Get node to export node = slicer.mrmlScene.GetNodeByID(exportable.nodeID) if node.GetAssociatedNode() == None or not node.GetAssociatedNode().IsA('vtkMRMLScalarVolumeNode'): error = "Series '" + node.GetNameWithoutPostfix() + "' cannot be exported!" print(error) return error # Get output directory and create a subdirectory. This is necessary # to avoid overwriting the files in case of multiple exportables, as # naming of the DICOM files is static directoryDir = qt.QDir(exportable.directory) directoryDir.mkdir(exportable.nodeID) directoryDir.cd(exportable.nodeID) directory = directoryDir.absolutePath() print("Export scalar volume '" + node.GetAssociatedNode().GetName() + "' to directory " + directory) # Get study and patient nodes studyNode = node.GetParentNode() if studyNode == None: error = "Unable to get study node for series '" + node.GetAssociatedNode().GetName() + "'" print(error) return error patientNode = studyNode.GetParentNode() if patientNode == None: error = "Unable to get patient node for series '" + node.GetAssociatedNode().GetName() + "'" print(error) return error # Assemble tags dictionary for volume export from vtkSlicerSubjectHierarchyModuleMRMLPython import vtkMRMLSubjectHierarchyConstants tags = {} tags['Patient Name'] = exportable.tag(vtkMRMLSubjectHierarchyConstants.GetDICOMPatientNameTagName()) tags['Patient ID'] = exportable.tag(vtkMRMLSubjectHierarchyConstants.GetDICOMPatientIDTagName()) tags['Patient Comments'] = exportable.tag(vtkMRMLSubjectHierarchyConstants.GetDICOMPatientCommentsTagName()) tags['Study ID'] = self.defaultStudyID tags['Study Date'] = exportable.tag(vtkMRMLSubjectHierarchyConstants.GetDICOMStudyDateTagName()) tags['Study Description'] = exportable.tag(vtkMRMLSubjectHierarchyConstants.GetDICOMStudyDescriptionTagName()) tags['Modality'] = exportable.tag('Modality') tags['Manufacturer'] = exportable.tag('Manufacturer') tags['Model'] = exportable.tag('Model') tags['Series Description'] = exportable.tag('SeriesDescription') tags['Series Number'] = exportable.tag('SeriesNumber') # Validate tags if tags['Modality'] == "": error = "Empty modality for series '" + node.GetAssociatedNode().GetName() + "'" print(error) return error #TODO: more tag checks # Perform export exporter = DICOMLib.DICOMExportScalarVolume(tags['Study ID'], node.GetAssociatedNode(), tags, directory) exporter.export() # Success return ""
def setup(self): # Instantiate and connect widgets ... # # Reload and Test area # reloadCollapsibleButton = ctk.ctkCollapsibleButton() reloadCollapsibleButton.text = "Reload && Test" self.layout.addWidget(reloadCollapsibleButton) reloadFormLayout = qt.QFormLayout(reloadCollapsibleButton) # reload button # (use this during development, but remove it when delivering # your module to users) self.reloadButton = qt.QPushButton("Reload") self.reloadButton.toolTip = "Reload this module." self.reloadButton.name = "TCIABrowser Reload" reloadFormLayout.addWidget(self.reloadButton) self.reloadButton.connect('clicked()', self.onReload) # reload and test button # (use this during development, but remove it when delivering # your module to users) self.reloadAndTestButton = qt.QPushButton("Reload and Test") self.reloadAndTestButton.toolTip = "Reload this module and then run the self tests." reloadFormLayout.addWidget(self.reloadAndTestButton) self.reloadAndTestButton.connect('clicked()', self.onReloadAndTest) # # Step 1: selection of the data directory # self.collectionSelector = qt.QComboBox() self.layout.addWidget(self.collectionSelector) self.collectionSelector.connect('currentIndexChanged(QString)', self.collectionSelected) self.studyTable = ItemTable(self.parent, headerName='Study Name') self.layout.addWidget(self.studyTable.widget) self.studyTable.widget.connect('cellClicked(int,int)', self.onStudyCellClicked) self.seriesTable = ItemTable(self.parent, headerName="Series Name") self.layout.addWidget(self.seriesTable.widget) self.loadButton = qt.QPushButton('Load series') self.layout.addWidget(self.loadButton) self.loadButton.connect('clicked()', self.onLoadButtonClicked) # Add vertical spacer self.layout.addStretch(1) # set up temporary directory self.tempDir = slicer.app.temporaryPath + '/TCIABrowser-tmp' print('Temporary directory location: ' + self.tempDir) qt.QDir().mkpath(self.tempDir)
def test_TextureModel1(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") # Download import urllib url = 'https://artec3d-production.s3-eu-west-1.amazonaws.com/3Dmodels/human_eye_obj.zip' zipFilePath = slicer.app.temporaryPath + '/' + 'Human_Eye_obj.zip' extractPath = slicer.app.temporaryPath + '/' + 'Human_Eye_obj' if not os.path.exists(zipFilePath) or os.stat( zipFilePath).st_size == 0: logging.info('Requesting download from %s...\n' % url) urllib.urlretrieve(url, zipFilePath) self.delayDisplay('Finished with download\n') # Unzip self.delayDisplay("Unzipping to %s" % (extractPath)) qt.QDir().mkpath(extractPath) applicationLogic = slicer.app.applicationLogic() applicationLogic.Unzip(zipFilePath, extractPath) # Load slicer.util.loadModel(extractPath + "/Human_Eye_obj.obj") slicer.util.loadVolume(extractPath + "/Human_Eye_obj_0.jpg") self.delayDisplay('Finished with download and loading') # Test modelNode = slicer.util.getNode("Human_Eye_obj") textureNode = slicer.util.getNode("Human_Eye_obj_0") logic = TextureModelLogic() logic.applyTexture(modelNode, textureNode) self.delayDisplay('Test passed!')
def populateParameters( self ): parameterDir = qt.QDir(PARAM_FILES_DIR) for path in parameterDir.entryList(parameterDir.Files): fileInfo = qt.QFileInfo(path) name = fileInfo.baseName().replace("_", " ") self.get('ParameterFileComboBox').addItem(name, fileInfo.absolutePath() + '/' + path) # Add load from file self.get('ParameterFileComboBox').insertSeparator( self.get('ParameterFileComboBox').count) self.get('ParameterFileComboBox').addItem('Load from file...', None) comboBox = self.get('LayoutComboBox') comboBox.addItem('Quad View', slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView) comboBox.addItem('Red Slice', slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView) comboBox.addItem('Yellow Slice', slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpYellowSliceView) comboBox.addItem('Green Slice', slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpGreenSliceView) comboBox.addItem('3D View', slicer.vtkMRMLLayoutNode.SlicerLayoutOneUp3DView) comboBox.addItem('MIP', slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView)
def test_TextureModel1(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") # Download import urllib url = 'https://github.com/Slicer/SlicerTestingData/releases/download/SHA256/752ce9afe8b708fcd4f8448612170f8e730670d845f65177860edc0e08004ecf' zipFilePath = slicer.app.temporaryPath + '/' + 'FemurHeadSurfaceScan.zip' extractPath = slicer.app.temporaryPath + '/' + 'FemurHeadSurfaceScan' if not os.path.exists(zipFilePath) or os.stat( zipFilePath).st_size == 0: logging.info('Requesting download from %s...\n' % url) urllib.urlretrieve(url, zipFilePath) self.delayDisplay('Finished with download\n') # Unzip self.delayDisplay("Unzipping to %s" % (extractPath)) qt.QDir().mkpath(extractPath) applicationLogic = slicer.app.applicationLogic() applicationLogic.Unzip(zipFilePath, extractPath) # Load slicer.util.loadModel(extractPath + "/head_obj.obj") slicer.util.loadVolume(extractPath + "/head_obj_0.png") self.delayDisplay('Finished with download and loading') # Test modelNode = slicer.util.getNode("head_obj") textureNode = slicer.util.getNode("head_obj_0") logic = TextureModelLogic() logic.applyTexture(modelNode, textureNode) self.delayDisplay('Test passed!')
def onPresetSelected( self ): clickedButton = self.getSelectedButton() if clickedButton is None: self.validate() return path = clickedButton.property('Path') presetFiles = qt.QDir(path) self.Presets = {} self.WorkflowConfigData = {} for filename in presetFiles.entryList(['*'], presetFiles.Files): absolutePath = '%s/%s' % (presetFiles.absolutePath(), filename) if filename.endswith('.json'): self.Presets[filename[:-len('.json')]] = absolutePath elif filename.endswith('.dict'): file = open(absolutePath) self.WorkflowConfigData[filename[:-len('.dict')]] = eval(file.read()) self.Workflow.updateConfiguration() self.validate()
def __init__(self, KEV80=False, KEV120=False, inputVolumeName=None): self.lowerThresholdValue = None self.upperThresholdValue = 5000 self.editUtil = EditorLib.EditUtil.EditUtil() self.KEV80 = KEV80 self.KEV120 = KEV120 self.inputVolumeName = inputVolumeName self.calciumLabelNode = None self.CardiacAgatstonMeasuresLUTNode = None # imports custom Slicer lookup color table file self.CardiacAgatstonMeasuresLUTNode = slicer.util.getNode( pattern='CardiacAgatstonMeasuresLUT') if not self.CardiacAgatstonMeasuresLUTNode: import urllib downloads = (( 'http://www.na-mic.org/Wiki/images/4/4e/CardiacAgatstonMeasures_TutorialContestSummer2014.zip', 'CardiacAgatstonMeasures_TutorialContestSummer2014.zip'), ) for url, name in downloads: filePath = os.path.join(slicer.app.temporaryPath, name) if not os.path.exists(filePath) or os.stat( filePath).st_size == 0: print('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) zipFilePath = os.path.join( slicer.app.temporaryPath, 'CardiacAgatstonMeasures_TutorialContestSummer2014.zip') extractPath = os.path.join( slicer.app.temporaryPath, 'CardiacAgatstonMeasures_TutorialContestSummer2014') qt.QDir().mkpath(extractPath) applicationLogic = slicer.app.applicationLogic() applicationLogic.Unzip(zipFilePath, extractPath) lutPath = os.path.join(extractPath, 'CardiacAgatstonMeasuresLUT.ctbl') slicer.util.loadColorTable(lutPath)
def setupUi( self ): self.loadUi('InitialStep.ui') moduleName = 'Workflow' scriptedModulesPath = eval('slicer.modules.%s.path' % moduleName.lower()) scriptedModulesPath = os.path.dirname(scriptedModulesPath) iconsPath = os.path.join(scriptedModulesPath, 'Widgets', 'Resources', 'Icons') buttonGroup = qt.QButtonGroup(self.get('InitialCollapsibleGroupBox')) logic = slicer.modules.workflow.logic() resourceDir = qt.QDir(logic.GetModuleShareDirectory() + '/Resources') for dir in resourceDir.entryList(resourceDir.Dirs | resourceDir.NoDotAndDotDot): pushButton = qt.QPushButton(self.get('InitialCollapsibleGroupBox')) buttonGroup.addButton(pushButton) pushButton.text = dir.replace('_', ' ') pushButton.setProperty('Path', resourceDir.absolutePath() + '/' + dir) pushButton.checkable = True pushButton.connect('clicked()', self.onPresetSelected) pushButton.setIcon(qt.QIcon(os.path.join(iconsPath, dir))) pushButton.setIconSize(qt.QSize(75, 75)) self.PresetButtons.append(pushButton) self.get('InitialCollapsibleGroupBox').layout().addWidget(pushButton)
def section_LoadDicomData(self): try: # Download and unzip test CT DICOM data import urllib downloads = (( 'http://slicer.kitware.com/midas3/download/item/137843/TestDicomCT.zip', self.dicomZipFilePath), ) downloaded = 0 for url, filePath in downloads: if not os.path.exists(filePath) or os.stat( filePath).st_size == 0: if downloaded == 0: self.delayDisplay( 'Downloading input data to folder\n' + self.dicomZipFilePath + '.\n\n It may take a few minutes...', self.delayMs) print('Requesting download from %s...' % (url)) urllib.urlretrieve(url, filePath) downloaded += 1 else: self.delayDisplay( 'Input data has been found in folder ' + self.dicomZipFilePath, self.delayMs) if downloaded > 0: self.delayDisplay('Downloading input data finished', self.delayMs) numOfFilesInDicomDataDir = len([ name for name in os.listdir(self.dicomDataDir) if os.path.isfile(self.dicomDataDir + '/' + name) ]) if (numOfFilesInDicomDataDir != self.expectedNumOfFilesInDicomDataDir): slicer.app.applicationLogic().Unzip(self.dicomZipFilePath, self.dicomDataDir) self.delayDisplay("Unzipping done", self.delayMs) numOfFilesInDicomDataDirTest = len([ name for name in os.listdir(self.dicomDataDir) if os.path.isfile(self.dicomDataDir + '/' + name) ]) self.assertTrue(numOfFilesInDicomDataDirTest == self.expectedNumOfFilesInDicomDataDir) # Open test database and empty it qt.QDir().mkpath(self.dicomDatabaseDir) if slicer.dicomDatabase: self.originalDatabaseDirectory = os.path.split( slicer.dicomDatabase.databaseFilename)[0] else: self.originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', self.dicomDatabaseDir) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(self.dicomDatabaseDir) self.assertTrue(slicer.dicomDatabase.isOpen) # Import test data in database indexer = ctk.ctkDICOMIndexer() self.assertTrue(indexer) indexer.addDirectory(slicer.dicomDatabase, self.dicomDataDir) self.assertTrue(len(slicer.dicomDatabase.patients()) == 1) self.assertTrue(slicer.dicomDatabase.patients()[0]) # Load test data numOfScalarVolumeNodesBeforeLoad = len( slicer.util.getNodes('vtkMRMLScalarVolumeNode*')) numOfSubjectHierarchyNodesBeforeLoad = len( slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*')) patient = slicer.dicomDatabase.patients()[0] studies = slicer.dicomDatabase.studiesForPatient(patient) series = [ slicer.dicomDatabase.seriesForStudy(study) for study in studies ] seriesUIDs = [uid for uidList in series for uid in uidList] dicomWidget.detailsPopup.offerLoadables(seriesUIDs, 'SeriesUIDList') dicomWidget.detailsPopup.loadCheckedLoadables() self.assertTrue( len(slicer.util.getNodes('vtkMRMLScalarVolumeNode*')) == numOfScalarVolumeNodesBeforeLoad + 1) self.assertTrue( len(slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*')) == numOfSubjectHierarchyNodesBeforeLoad + 3) except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e), self.delayMs * 2)
def test_LabelToDICOMSEGConverterRoundTrip(self): print("CTEST_FULL_OUTPUT") dir(slicer.modules) """ Load the data using DICOM module """ import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import urllib downloads = (('http://slicer.kitware.com/midas3/download?items=10881', 'JANCT-CT.zip'), ) self.delayDisplay("Downloading") for url, name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') reportingTempDir = slicer.app.temporaryPath + '/LabelToDICOMSEGConverter' qt.QDir().mkpath(reportingTempDir) dicomFilesDirectory = reportingTempDir + '/dicomFiles' self.cleanupDir(dicomFilesDirectory) qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) try: self.delayDisplay("Switching to temp database directory") tempDatabaseDirectory = reportingTempDir + '/tempDICOMDatbase' qt.QDir().mkpath(tempDatabaseDirectory) self.cleanupDir(tempDatabaseDirectory) if slicer.dicomDatabase: self.originalDatabaseDirectory = os.path.split( slicer.dicomDatabase.databaseFilename)[0] else: self.originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', tempDatabaseDirectory) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory) self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') self.importDICOM(slicer.dicomDatabase, dicomFilesDirectory) patient = slicer.dicomDatabase.patients()[0] studies = slicer.dicomDatabase.studiesForPatient(patient) series = [ slicer.dicomDatabase.seriesForStudy(study) for study in studies ] seriesUIDs = [uid for uidList in series for uid in uidList] dicomWidget.detailsPopup.offerLoadables(seriesUIDs, 'SeriesUIDList') dicomWidget.detailsPopup.examineForLoading() loadablesByPlugin = dicomWidget.detailsPopup.loadablesByPlugin self.delayDisplay('Loading Selection') dicomWidget.detailsPopup.loadCheckedLoadables() # initialize the module with the report and volume volumes = slicer.util.getNodes('vtkMRMLScalarVolumeNode*') self.assertTrue(len(volumes) == 1) (name, volume) = volumes.items()[0] self.delayDisplay('Loaded volume name %s' % volume.GetName()) self.delayDisplay('Configure Module') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule( 'LabelToDICOMSEGConverter') module = slicer.modules.labeltodicomsegconverter.widgetRepresentation( ).self() # add label node volumesLogic = slicer.modules.volumes.logic() labelNode = volumesLogic.CreateAndAddLabelVolume( slicer.mrmlScene, volume, "Segmentation") labelNode.SetAttribute('AssociatedNodeID', volume.GetID()) labelDisplayNode = labelNode.GetDisplayNode() labelDisplayNode.SetAndObserveColorNodeID( 'vtkMRMLColorTableNodeFileGenericAnatomyColors.txt') image = volume.GetImageData() thresh = vtk.vtkImageThreshold() if vtk.vtkVersion().GetVTKMajorVersion() < 6: thresh.SetInput(image) else: thresh.SetInputData(image) thresh.ThresholdBetween(10, 400) thresh.SetInValue(10) thresh.SetOutValue(0) thresh.Update() labelNode.SetAndObserveImageData(thresh.GetOutput()) module.segmentationSelector.setCurrentNode(labelNode) module.volumeSelector.setCurrentNode(volume) self.delayDisplay('Input label initialized') module.outputDir = reportingTempDir + '/Output' # Save the report exportDir = reportingTempDir + '/Output' qt.QDir().mkpath(exportDir) self.cleanupDir(exportDir) module.onLabelExport() self.delayDisplay('Report saved') self.importDICOM(slicer.dicomDatabase, exportDir) slicer.mrmlScene.Clear(0) patient = slicer.dicomDatabase.patients()[0] studies = slicer.dicomDatabase.studiesForPatient(patient) series = [ slicer.dicomDatabase.seriesForStudy(study) for study in studies ] seriesUIDs = [uid for uidList in series for uid in uidList] dicomWidget.detailsPopup.offerLoadables(seriesUIDs, 'SeriesUIDList') dicomWidget.detailsPopup.examineForLoading() loadablesByPlugin = dicomWidget.detailsPopup.loadablesByPlugin self.delayDisplay('Wait', 10000) dicomWidget.detailsPopup.loadCheckedLoadables() volumes = slicer.util.getNodes('vtkMRMLLabelMapVolumeNode*') for n, v in volumes.items(): print('Label volume found: ' + v.GetID()) self.assertTrue(len(volumes) == 1) for name, volume in volumes.items(): image = volume.GetImageData() previousImage = thresh.GetOutput() diff = vtk.vtkImageDifference() if vtk.vtkVersion().GetVTKMajorVersion() < 6: diff.SetInput(thresh.GetOutput()) else: diff.SetInputData(thresh.GetOutput()) diff.SetImage(image) diff.Update() if diff.GetThresholdedError() > 1: self.delayDisplay('Reloaded image does not match') self.assertTrue(False) self.delayDisplay('Test passed') self.delayDisplay("Restoring original database directory") if self.originalDatabaseDirectory: dicomWidget.onDatabaseDirectoryChanged( self.originalDatabaseDirectory) except Exception, e: if self.originalDatabaseDirectory: dicomWidget.onDatabaseDirectoryChanged( self.originalDatabaseDirectory) import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e)) self.assertTrue(False)
def test_CardiacAgatstonMeasures1(self): """ Ideally you should have several levels of tests. At the lowest level tests sould 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 Test Part 1 - Importing heart scan") try: # # first, get some data # m = slicer.util.mainWindow() m.moduleSelector().selectModule('CardiacAgatstonMeasures') import urllib downloads = (( 'http://www.na-mic.org/Wiki/images/4/4e/CardiacAgatstonMeasures_TutorialContestSummer2014.zip', 'CardiacAgatstonMeasures_TutorialContestSummer2014.zip'), ) self.delayDisplay("Downloading") for url, name in downloads: filePath = os.path.join(slicer.app.temporaryPath, name) if not os.path.exists(filePath) or os.stat( filePath).st_size == 0: print('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') self.delayDisplay("Unzipping to %s" % (slicer.app.temporaryPath)) zipFilePath = os.path.join( slicer.app.temporaryPath, 'CardiacAgatstonMeasures_TutorialContestSummer2014.zip') extractPath = os.path.join( slicer.app.temporaryPath, 'CardiacAgatstonMeasures_TutorialContestSummer2014') qt.QDir().mkpath(extractPath) self.delayDisplay("Using extract path %s" % (extractPath)) applicationLogic = slicer.app.applicationLogic() applicationLogic.Unzip(zipFilePath, extractPath) self.delayDisplay( "Loading CardiacAgatstonMeasuresTestInput.nii.gz") inputImagePath = os.path.join( extractPath, 'CardiacAgatstonMeasuresTestInput.nii.gz') slicer.util.loadVolume(inputImagePath) volumeNode = slicer.util.getNode( pattern="CardiacAgatstonMeasuresTestInput") logic = CardiacAgatstonMeasuresLogic() self.assertTrue(logic.hasImageData(volumeNode)) self.delayDisplay( 'Finished with downloading and loading CardiacAgatstonMeasuresTestInput.nii.gz' ) CardiacAgatstonMeasuresLUTNode = slicer.util.getNode( pattern='CardiacAgatstonMeasuresLUT') if not CardiacAgatstonMeasuresLUTNode: self.delayDisplay("Loading CardiacAgatstonMeasuresLUT.ctbl") lutPath = os.path.join(extractPath, 'CardiacAgatstonMeasuresLUT.ctbl') slicer.util.loadColorTable(lutPath) CardiacAgatstonMeasuresLUTNode = slicer.util.getNode( pattern="CardiacAgatstonMeasuresLUT") logic = CardiacAgatstonMeasuresLogic() self.assertTrue( logic.hasCorrectLUTData(CardiacAgatstonMeasuresLUTNode)) self.delayDisplay( 'Finished with downloading and loading CardiacAgatstonMeasuresLUT.ctbl' ) self.delayDisplay('Test Part 1 passed!\n') except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e))
def test_Part1DICOM(self, enableScreenshotsFlag=0, screenshotScaleFactor=1): """ Test the DICOM part of the test using the head atlas """ self.enableScreenshots = enableScreenshotsFlag self.screenshotScaleFactor = screenshotScaleFactor import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import urllib downloads = (('http://slicer.kitware.com/midas3/download?items=124183', 'dataset1_Thorax_Abdomen.zip'), ) self.delayDisplay("Downloading") for url, name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') self.delayDisplay("Unzipping") dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles' qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) try: self.delayDisplay("Switching to temp database directory") tempDatabaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatabase' qt.QDir().mkpath(tempDatabaseDirectory) if slicer.dicomDatabase: originalDatabaseDirectory = os.path.split( slicer.dicomDatabase.databaseFilename)[0] else: originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', tempDatabaseDirectory) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory) self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() dicomWidget.detailsPopup.open() # load the data by series UID dicomWidget.detailsPopup.offerLoadables( '1.3.12.2.1107.5.1.4.50025.30000005060811542834300000776', 'Series') dicomWidget.detailsPopup.examineForLoading() self.delayDisplay('Loading Selection') dicomWidget.detailsPopup.loadCheckedLoadables() self.takeScreenshot('LoadingADICOMVolume-Loaded', 'Loaded DICOM Volume', -1) layoutManager = slicer.app.layoutManager() redWidget = layoutManager.sliceWidget('Red') self.clickAndDrag(redWidget, start=(10, 10), end=(10, 40)) self.clickAndDrag(redWidget, start=(10, 10), end=(40, 10)) self.takeScreenshot('LoadingADICOMVolume-WL', 'Changed level and window', -1) redWidget.sliceController().setSliceLink(True) redWidget.sliceController().setSliceVisible(True) self.takeScreenshot('LoadingADICOMVolume-LinkView', 'Linked and visible', -1) self.clickAndDrag(redWidget, button='Right', start=(10, 10), end=(10, 40)) self.takeScreenshot('LoadingADICOMVolume-Zoom', 'Zoom', -1) threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView) self.takeScreenshot('LoadingADICOMVolume-Rotate', 'Rotate', -1) threeDView.resetFocalPoint() self.takeScreenshot('LoadingADICOMVolume-Center', 'Center the view', -1) layoutManager.setLayout(slicer.vtkMRMLLayoutNode. SlicerLayoutConventionalWidescreenView) self.takeScreenshot('LoadingADICOMVolume-ConventionalWidescreen', 'Conventional Widescreen Layout', -1) slicer.util.mainWindow().moduleSelector().selectModule( 'VolumeRendering') self.takeScreenshot('VolumeRendering-Module', 'Volume Rendering', -1) volumeRenderingWidgetRep = slicer.modules.volumerendering.widgetRepresentation( ) abdomenVolume = slicer.mrmlScene.GetFirstNodeByName( '6: CT_Thorax_Abdomen') volumeRenderingWidgetRep.setMRMLVolumeNode(abdomenVolume) self.takeScreenshot('VolumeRendering-SelectVolume', 'Select the volume 6: CT_Thorax_Abdomen', -1) presetsScene = slicer.modules.volumerendering.logic( ).GetPresetsScene() ctCardiac3 = presetsScene.GetFirstNodeByName('CT-Cardiac3') volumeRenderingWidgetRep.applyPreset(ctCardiac3) self.takeScreenshot('VolumeRendering-SelectPreset', 'Select the Preset CT-Cardiac-3') self.delayDisplay('Skipping: Select VTK CPU Ray Casting') volumeRenderingNode = slicer.mrmlScene.GetFirstNodeByName( 'VolumeRendering') volumeRenderingNode.SetVisibility(1) self.takeScreenshot('VolumeRendering-ViewRendering', 'View Volume Rendering', -1) self.delayDisplay('Skipping Move the Shift slider') redWidget.sliceController().setSliceVisible(False) self.takeScreenshot('VolumeRendering-SlicesOff', 'Turn off visibility of slices in 3D', -1) threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView) self.takeScreenshot('VolumeRendering-RotateVolumeRendering', 'Rotate volume rendered image', -1) volumeRenderingNode.SetVisibility(0) self.takeScreenshot('VolumeRendering-TurnOffVolumeRendering', 'Turn off volume rendered image', -1) volumeRenderingNode.SetCroppingEnabled(1) annotationROI = slicer.mrmlScene.GetFirstNodeByName( 'AnnotationROI') annotationROI.SetDisplayVisibility(1) self.takeScreenshot('VolumeRendering-DisplayROI', 'Enable cropping and display ROI', -1) redWidget.sliceController().setSliceVisible(True) self.takeScreenshot('VolumeRendering-SlicesOn', 'Turn on visibility of slices in 3D', -1) annotationROI.SetXYZ(-79.61, 154.16, -232.591) annotationROI.SetRadiusXYZ(43.4, 65.19, 70.5) self.takeScreenshot('VolumeRendering-SizedROI', 'Position the ROI over a kidney', -1) volumeRenderingNode.SetVisibility(1) self.takeScreenshot('VolumeRendering-ROIRendering', 'ROI volume rendered', -1) annotationROI.SetXYZ(15, 146, -186) annotationROI.SetRadiusXYZ(138, 57, 61) self.takeScreenshot('VolumeRendering-BothKidneys', 'Rendered both kidneys', -1) self.delayDisplay('Test passed!') except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e))
def export(self, exportables): for exportable in exportables: # Get volume node to export shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode( slicer.mrmlScene) if shNode is None: error = "Invalid subject hierarchy" logging.error(error) return error volumeNode = shNode.GetItemDataNode( exportable.subjectHierarchyItemID) if volumeNode is None or not volumeNode.IsA( 'vtkMRMLScalarVolumeNode'): error = "Series '" + shNode.GetItemName( exportable.subjectHierarchyItemID ) + "' cannot be exported as volume sequence" logging.error(error) return error sequenceBrowserNode = self.getSequenceBrowserNodeForMasterOutputNode( volumeNode) if not sequenceBrowserNode: error = "Series '" + shNode.GetItemName( exportable.subjectHierarchyItemID ) + "' cannot be exported as volume sequence" logging.error(error) return error volumeSequenceNode = sequenceBrowserNode.GetSequenceNode( volumeNode) if not volumeSequenceNode: error = "Series '" + shNode.GetItemName( exportable.subjectHierarchyItemID ) + "' cannot be exported as volume sequence" logging.error(error) return error # Get study and patient items studyItemID = shNode.GetItemParent( exportable.subjectHierarchyItemID) if not studyItemID: error = "Unable to get study for series '" + volumeNode.GetName( ) + "'" logging.error(error) return error patientItemID = shNode.GetItemParent(studyItemID) if not patientItemID: error = "Unable to get patient for series '" + volumeNode.GetName( ) + "'" logging.error(error) return error # Assemble tags dictionary for volume export tags = {} tags['Patient Name'] = exportable.tag( slicer.vtkMRMLSubjectHierarchyConstants. GetDICOMPatientNameTagName()) tags['Patient ID'] = exportable.tag( slicer.vtkMRMLSubjectHierarchyConstants. GetDICOMPatientIDTagName()) tags['Patient Comments'] = exportable.tag( slicer.vtkMRMLSubjectHierarchyConstants. GetDICOMPatientCommentsTagName()) if slicer.app.majorVersion >= 5 or (slicer.app.majorVersion == 4 and slicer.app.minorVersion >= 11): tags['Study Instance UID'] = pydicom.uid.generate_uid() else: tags['Study Instance UID'] = dicom.UID.generate_uid() tags['Study ID'] = exportable.tag( slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyIDTagName( )) tags['Study Date'] = exportable.tag( slicer.vtkMRMLSubjectHierarchyConstants. GetDICOMStudyDateTagName()) tags['Study Time'] = exportable.tag( slicer.vtkMRMLSubjectHierarchyConstants. GetDICOMStudyTimeTagName()) tags['Study Description'] = exportable.tag( slicer.vtkMRMLSubjectHierarchyConstants. GetDICOMStudyDescriptionTagName()) tags['Modality'] = exportable.tag('Modality') tags['Manufacturer'] = exportable.tag('Manufacturer') tags['Model'] = exportable.tag('Model') tags['Series Description'] = exportable.tag('SeriesDescription') tags['Series Number'] = exportable.tag('SeriesNumber') tags['Series Date'] = exportable.tag("SeriesDate") tags['Series Time'] = exportable.tag("SeriesTime") if slicer.app.majorVersion >= 5 or (slicer.app.majorVersion == 4 and slicer.app.minorVersion >= 11): tags['Series Instance UID'] = pydicom.uid.generate_uid() tags[ 'Frame of Reference Instance UID'] = pydicom.uid.generate_uid( ) else: tags['Series Instance UID'] = dicom.UID.generate_uid() tags[ 'Frame of Reference Instance UID'] = dicom.UID.generate_uid( ) # Validate tags if tags['Modality'] == "": error = "Empty modality for series '" + volumeNode.GetName( ) + "'" logging.error(error) return error #TODO: more tag checks sequenceItemCount = sequenceBrowserNode.GetMasterSequenceNode( ).GetNumberOfDataNodes() originalSelectedSequenceItemNumber = sequenceBrowserNode.GetSelectedItemNumber( ) masterVolumeNode = sequenceBrowserNode.GetMasterSequenceNode() # initialize content datetime from series datetime contentStartDate = exportable.tag("SeriesDate") contentStartTime = exportable.tag("SeriesTime") import datetime datetimeNow = datetime.datetime.now() if not contentStartDate: contentStartDate = datetimeNow.strftime("%Y%m%d") if not contentStartTime: contentStartTime = datetimeNow.strftime("%H%M%S.%f") contentStartDatetime = self.datetimeFromDicom( contentStartDate, contentStartTime) # Get output directory and create a subdirectory. This is necessary # to avoid overwriting the files in case of multiple exportables, as # naming of the DICOM files is static directoryName = 'VolumeSequence_' + str( exportable.subjectHierarchyItemID) directoryDir = qt.QDir(exportable.directory) directoryDir.mkdir(directoryName) directoryDir.cd(directoryName) directory = directoryDir.absolutePath() logging.info("Export scalar volume '" + volumeNode.GetName() + "' to directory " + directory) for sequenceItemIndex in range(sequenceItemCount): # Switch to next item in the series sequenceBrowserNode.SetSelectedItemNumber(sequenceItemIndex) slicer.app.processEvents() # Compute content date&time # TODO: verify that unit in sequence node is "second" (and convert to seconds if not) timeOffsetSec = float( masterVolumeNode.GetNthIndexValue(sequenceItemIndex) ) - float(masterVolumeNode.GetNthIndexValue(0)) contentDatetime = contentStartDatetime + datetime.timedelta( seconds=timeOffsetSec) tags['Content Date'] = contentDatetime.strftime("%Y%m%d") tags['Content Time'] = contentDatetime.strftime("%H%M%S.%f") # Perform export filenamePrefix = f"IMG_{sequenceItemIndex:04d}_" exporter = DICOMExportScalarVolume(tags['Study ID'], volumeNode, tags, directory, filenamePrefix) exporter.export() # Success return ""
def test_Part1DICOM(self): """ Test the DICOM part of the test using the head atlas """ import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import urllib downloads = (('http://slicer.kitware.com/midas3/download?items=18822', 'Dcmtk-db.zip'), ) self.delayDisplay("Downloading") for url, name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') self.delayDisplay("Unzipping") dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles' qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) try: self.delayDisplay("Switching to temp database directory") tempDatabaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatbase' qt.QDir().mkpath(tempDatabaseDirectory) if slicer.dicomDatabase: originalDatabaseDirectory = os.path.split( slicer.dicomDatabase.databaseFilename)[0] else: originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', tempDatabaseDirectory) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory) self.delayDisplay('Start Local DICOM Q/R SCP') import subprocess import os configFilePath = dicomFilesDirectory + '/Dcmtk-db/dcmqrscp.cfg' processCurrentPath = dicomFilesDirectory + '/Dcmtk-db/' dcmqrscpExeOptions = ( '/bin', '/../CTK-build/CMakeExternals/Install/bin', '/../DCMTK-install/bin', '/../DCMTK-build/bin', ) dcmqrscpExePath = None dcmqrscpExeName = '/dcmqrscp' if slicer.app.os == 'win': dcmqrscpExeName = dcmqrscpExeName + '.exe' for path in dcmqrscpExeOptions: testPath = slicer.app.slicerHome + path + dcmqrscpExeName if os.path.exists(testPath): dcmqrscpExePath = testPath break if not dcmqrscpExePath: raise (UserWarning("Could not find dcmqrscp executable")) args = (dcmqrscpExePath, '-c', configFilePath) popen = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=processCurrentPath) self.delayDisplay('Retrieve DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') dicomWidget.dicomApp.suspendModel() dicomRetrieve = ctk.ctkDICOMRetrieve() dicomRetrieve.setKeepAssociationOpen(True) dicomRetrieve.setDatabase(slicer.dicomDatabase) dicomRetrieve.setCallingAETitle('SlicerAE') dicomRetrieve.setCalledAETitle('DCMTK') dicomRetrieve.setPort(12345) dicomRetrieve.setHost('localhost') dicomRetrieve.getStudy( '1.2.124.113932.1.170.223.162.178.20050502.160340.12640015') dicomWidget.dicomApp.resumeModel() popen.kill() dicomWidget.detailsPopup.open() # click on the first row of the tree index = dicomWidget.tree.indexAt(qt.QPoint(0, 0)) dicomWidget.onTreeClicked(index) self.delayDisplay('Loading Selection') dicomWidget.detailsPopup.loadCheckedLoadables() self.delayDisplay('Change Level') layoutManager = slicer.app.layoutManager() redWidget = layoutManager.sliceWidget('Red') self.clickAndDrag(redWidget, start=(10, 10), end=(10, 40)) self.delayDisplay('Change Window') self.clickAndDrag(redWidget, start=(10, 10), end=(40, 10)) self.delayDisplay('Change Layout') layoutManager = slicer.app.layoutManager() layoutManager.setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView) self.delayDisplay('Zoom') self.clickAndDrag(redWidget, button='Right', start=(10, 10), end=(10, 40)) self.delayDisplay('Pan') self.clickAndDrag(redWidget, button='Middle', start=(10, 10), end=(40, 40)) self.delayDisplay('Center') redWidget.sliceController().fitSliceToBackground() self.delayDisplay('Lightbox') redWidget.sliceController().setLightboxTo6x6() self.delayDisplay('Conventional Layout') layoutManager.setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView) self.delayDisplay('No Lightbox') redWidget.sliceController().setLightboxTo1x1() self.delayDisplay('Four Up Layout') layoutManager.setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView) self.delayDisplay('Shift Mouse') self.clickAndDrag(redWidget, button='None', start=(100, 100), end=(140, 140), modifiers=['Shift']) self.delayDisplay('Conventional, Link, Slice Model') layoutManager.setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView) redWidget.sliceController().setSliceLink(True) redWidget.sliceController().setSliceVisible(True) self.delayDisplay('Rotate') threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView) self.delayDisplay('Zoom') threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView, button='Right') self.delayDisplay('Test passed!') except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e))
def test_Part1DICOM(self): """ Test the DICOM part of the test using the head atlas """ import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import urllib downloads = ( ('http://slicer.kitware.com/midas3/download?items=8610', 'dicom.zip'), ) self.delayDisplay("Downloading") for url,name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') self.delayDisplay("Unzipping") dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles' qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) try: self.delayDisplay("Switching to temp database directory") tempDatabaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatbase' qt.QDir().mkpath(tempDatabaseDirectory) if slicer.dicomDatabase: originalDatabaseDirectory = os.path.split(slicer.dicomDatabase.databaseFilename)[0] else: originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', tempDatabaseDirectory) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory) self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') dicomWidget.dicomApp.suspendModel() indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() dicomWidget.dicomApp.resumeModel() dicomWidget.detailsPopup.open() # click on the first row of the tree index = dicomWidget.tree.indexAt(qt.QPoint(0,0)) dicomWidget.onTreeClicked(index) self.delayDisplay('Loading Selection') dicomWidget.detailsPopup.loadCheckedLoadables() self.delayDisplay('Change Level') layoutManager = slicer.app.layoutManager() redWidget = layoutManager.sliceWidget('Red') self.clickAndDrag(redWidget,start=(10,10),end=(10,40)) self.delayDisplay('Change Window') self.clickAndDrag(redWidget,start=(10,10),end=(40,10)) self.delayDisplay('Change Layout') layoutManager = slicer.app.layoutManager() layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView) self.delayDisplay('Zoom') self.clickAndDrag(redWidget,button='Right',start=(10,10),end=(10,40)) self.delayDisplay('Pan') self.clickAndDrag(redWidget,button='Middle',start=(10,10),end=(40,40)) self.delayDisplay('Center') redWidget.sliceController().fitSliceToBackground() self.delayDisplay('Lightbox') redWidget.sliceController().setLightboxTo6x6() self.delayDisplay('Conventional Layout') layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView) self.delayDisplay('No Lightbox') redWidget.sliceController().setLightboxTo1x1() self.delayDisplay('Four Up Layout') layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView) self.delayDisplay('Shift Mouse') self.clickAndDrag(redWidget,button='None',start=(100,100),end=(140,140),modifiers=['Shift']) self.delayDisplay('Conventional, Link, Slice Model') layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView) redWidget.sliceController().setSliceLink(True) redWidget.sliceController().setSliceVisible(True); self.delayDisplay('Rotate') threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView) self.delayDisplay('Zoom') threeDView = layoutManager.threeDWidget(0).threeDView() self.clickAndDrag(threeDView,button='Right') self.delayDisplay('Test passed!') except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e))
def onStartStopDicomPeer(self, flag): if flag: import os self.startStopDicomPeerButton.setEnabled(False) dicomFilesDirectory = slicer.app.temporaryPath configFilePath = dicomFilesDirectory + '/Dcmtk-db/dcmqrscp.cfg' processCurrentPath = dicomFilesDirectory + '/Dcmtk-db/' msgBox = qt.QMessageBox() msgBox.setText( 'Do you want to choose local DCMTK database folder?') msgBox.setStandardButtons(qt.QMessageBox.Yes | qt.QMessageBox.No) val = msgBox.exec_() if (val == qt.QMessageBox.Yes): print 'Yes' dicomFilesDirectory = qt.QFileDialog.getExistingDirectory( None, 'Select DCMTK database folder') configFilePath = dicomFilesDirectory + '/dcmqrscp.cfg' processCurrentPath = dicomFilesDirectory else: downloads = ( ('http://slicer.kitware.com/midas3/download?items=18822', 'Dcmtk-db.zip'), ) print 'Downloading' import urllib for url, name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat( filePath).st_size == 0: print 'Requesting download %s from %s...\n' % (name, url) urllib.urlretrieve(url, filePath) print 'Finished with download' print 'Unzipping' qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) import subprocess dcmqrscpExeOptions = ( '/bin', '/../CTK-build/CMakeExternals/Install/bin', '/../DCMTK-install/bin', '/../DCMTK-build/bin', ) dcmqrscpExePath = None dcmqrscpExeName = '/dcmqrscp' if slicer.app.os == 'win': dcmqrscpExeName = dcmqrscpExeName + '.exe' for path in dcmqrscpExeOptions: testPath = slicer.app.slicerHome + path + dcmqrscpExeName if os.path.exists(testPath): dcmqrscpExePath = testPath break if not dcmqrscpExePath: raise (UserWarning("Could not find dcmqrscp executable")) args = (dcmqrscpExePath, '-c', configFilePath) print 'Start DICOM peer' self.popen = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=processCurrentPath) self.startStopDicomPeerButton.setEnabled(True) else: print 'Stop DICOM peer' self.popen.kill()
def test_Part2PETCT(self): """ Test using the PETCT module """ self.delayDisplay("Starting the test") # # first, get some data # import urllib downloads = ( ('http://slicer.kitware.com/midas3/download?items=9185', 'RSNA2011_PETCT.zip', slicer.util.loadScene), ) for url,name,loader in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: print('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) if loader: print('Loading %s...\n' % (name,)) loader(filePath) self.delayDisplay('Finished with download and loading\n') zipFilePath = slicer.app.temporaryPath + '/' + 'RSNA2011_PETCT.zip' extractPath = slicer.app.temporaryPath + '/' + 'RSNA2011_PETCT' qt.QDir().mkpath(extractPath) applicationLogic = slicer.app.applicationLogic() applicationLogic.Unzip(zipFilePath, extractPath) try: logic = RSNA2012QuantLogic() mainWindow = slicer.util.mainWindow() layoutManager = slicer.app.layoutManager() threeDView = layoutManager.threeDWidget(0).threeDView() redWidget = layoutManager.sliceWidget('Red') redController = redWidget.sliceController() greenWidget = layoutManager.sliceWidget('Green') greenController = greenWidget.sliceController() yellowWidget = layoutManager.sliceWidget('Yellow') yellowController = yellowWidget.sliceController() viewNode = threeDView.mrmlViewNode() cameras = slicer.util.getNodes('vtkMRMLCameraNode*') for cameraNode in cameras.values(): if cameraNode.GetActiveTag() == viewNode.GetID(): break self.delayDisplay('Configure View') threeDView.resetFocalPoint() self.clickAndDrag(threeDView,button='Right') redWidget.sliceController().setSliceVisible(True); yellowWidget.sliceController().setSliceVisible(True); self.delayDisplay('Show Volumes') mainWindow.moduleSelector().selectModule('Volumes') compositNode = redWidget.mrmlSliceCompositeNode() compositNode.SetForegroundOpacity(0.6) volumeSelector = slicer.util.findChildren(name='ActiveVolumeNodeSelector')[0] scalarWidget = slicer.util.findChildren(name='qSlicerScalarVolumeDisplayWidget')[0] colorSelector = slicer.util.findChildren(scalarWidget, name='ColorTableComboBox')[0] PET1 = slicer.util.getNode('PET1') volumeSelector.setCurrentNode(PET1) PETHeat = slicer.util.getNode('PET-Heat') colorSelector.setCurrentNode(PETHeat) self.delayDisplay('Scroll Slices') for offset in xrange(-1000,-700,20): redController.setSliceOffsetValue(offset) for offset in xrange(-20,20,2): greenController.setSliceOffsetValue(offset) for offset in xrange(-20,20,2): yellowController.setSliceOffsetValue(offset) self.delayDisplay('SUV Computation') slicer.util.selectModule('PETStandardUptakeValueComputation') parameters = { "PETDICOMPath": extractPath + '/' + 'PET1', "PETVolume": slicer.util.getNode('PET1'), "VOIVolume": slicer.util.getNode('PET1-label'), } suvComputation = slicer.modules.petstandarduptakevaluecomputation self.CLINode1 = None self.CLINode1 = slicer.cli.run(suvComputation, self.CLINode1, parameters, delete_temporary_files=False) waitCount = 0 while self.CLINode1.GetStatusString() != 'Completed' and waitCount < 100: self.delayDisplay( "Running SUV Computation... %d" % waitCount ) waitCount += 1 self.delayDisplay("Second time point") parameters = { "PETDICOMPath": extractPath + '/' + 'PET2', "PETVolume": slicer.util.getNode('PET2'), "VOIVolume": slicer.util.getNode('PET2-label'), } suvComputation = slicer.modules.petstandarduptakevaluecomputation self.CLINode2 = None self.CLINode2 = slicer.cli.run(suvComputation, self.CLINode2, parameters, delete_temporary_files=False) waitCount = 0 while self.CLINode2.GetStatusString() != 'Completed' and waitCount < 100: self.delayDisplay( "Running SUV Computation... %d" % waitCount ) waitCount += 1 premax = float(self.CLINode1.GetParameterAsString('SUVMax').split()[0].strip(',')) postmax = float(self.CLINode2.GetParameterAsString('SUVMax').split()[0].strip(',')) self.delayDisplay("Check the numbers: is %g 16.6 greater than %g?" % (premax, postmax)) percent = 100 * (postmax - premax) / premax if abs(percent - 16.61) > 1: raise "Oh no! the calculation is off" self.delayDisplay("Calculated percent change is %g" % percent) self.delayDisplay('Test passed!') except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e))
def loadModules(self, path, depth=1): # Get list of modules in specified path modules = ModuleInfo.findModules(path, depth) # Determine which modules in above are not already loaded factory = slicer.app.moduleManager().factoryManager() loadedModules = factory.instantiatedModuleNames() candidates = [m for m in modules if m.key not in loadedModules] # Prompt to load additional module(s) if len(candidates): dlg = LoadModulesDialog(self.parent.window()) dlg.setModules(candidates) if dlg.exec_() == qt.QDialog.Accepted: modulesToLoad = dlg.selectedModules # Add module(s) to permanent search paths, if requested if dlg.addToSearchPaths: settings = slicer.app.revisionUserSettings() rawSearchPaths = list( settings.value("Modules/AdditionalPaths")) searchPaths = [qt.QDir(path) for path in rawSearchPaths] modified = False for module in modulesToLoad: rawPath = os.path.dirname(module.path) path = qt.QDir(rawPath) if not path in searchPaths: searchPaths.append(path) rawSearchPaths.append(rawPath) modified = True if modified: settings.setValue("Modules/AdditionalPaths", rawSearchPaths) # Register requested module(s) failed = [] for module in modulesToLoad: factory.registerModule(qt.QFileInfo(module.path)) if not factory.isRegistered(module.key): failed.append(module) if len(failed): md = qt.QMessageBox(self.parent.window()) md.icon = qt.QMessageBox.Critical md.standardButtons = qt.QMessageBox.Close md.windowTitle = "Module loading failed" if len(failed) > 1: md.text = "The following modules could not be registered:" else: md.text = "The '%s' module could not be registered:" % failed[ 0].key failedFormat = "<ul><li>%(key)s<br/>(%(path)s)</li></ul>" md.informativeText = "".join( [failedFormat % m.__dict__ for m in failed]) md.exec_() return # Instantiate and load requested module(s) if not factory.loadModules( [module.key for module in modulesToLoad]): md = qt.QMessageBox(self.parent.window()) md.icon = qt.QMessageBox.Critical md.standardButtons = qt.QMessageBox.Close md.windowTitle = "Error loading module(s)" md.text = ( "The module factory manager reported an error. " "One or more of the requested module(s) and/or " "dependencies thereof may not have been loaded.") md.exec_()
def test_Part3PETCT(self, enableScreenshotsFlag=0, screenshotScaleFactor=1): """ Test using the PETCT module """ self.enableScreenshots = enableScreenshotsFlag self.screenshotScaleFactor = screenshotScaleFactor self.delayDisplay("Starting the test") # # first, get some data # import urllib downloads = (('http://slicer.kitware.com/midas3/download?items=124185', 'dataset3_PETCT.zip'), ) self.delayDisplay("Downloading") for url, name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: print('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') self.delayDisplay("Unzipping to %s" % (slicer.app.temporaryPath)) zipFilePath = slicer.app.temporaryPath + '/' + 'dataset3_PETCT.zip' extractPath = slicer.app.temporaryPath + '/' + 'dataset3_PETCT' qt.QDir().mkpath(extractPath) self.delayDisplay("Using extract path %s" % (extractPath)) applicationLogic = slicer.app.applicationLogic() applicationLogic.Unzip(zipFilePath, extractPath) self.delayDisplay("Loading PET_CT_pre-treatment.mrb") preTreatmentPath = extractPath + '/PET_CT_pre-treatment.mrb' slicer.util.loadScene(preTreatmentPath) self.takeScreenshot('PETCT-LoadedPre', 'Loaded pre-treatement scene', -1) try: logic = RSNAQuantTutorialLogic() mainWindow = slicer.util.mainWindow() layoutManager = slicer.app.layoutManager() threeDView = layoutManager.threeDWidget(0).threeDView() redWidget = layoutManager.sliceWidget('Red') redController = redWidget.sliceController() greenWidget = layoutManager.sliceWidget('Green') greenController = greenWidget.sliceController() yellowWidget = layoutManager.sliceWidget('Yellow') yellowController = yellowWidget.sliceController() viewNode = threeDView.mrmlViewNode() cameras = slicer.util.getNodes('vtkMRMLCameraNode*') for cameraNode in cameras.values(): if cameraNode.GetActiveTag() == viewNode.GetID(): break threeDView.resetFocalPoint() self.clickAndDrag(threeDView, button='Right') redWidget.sliceController().setSliceVisible(True) yellowWidget.sliceController().setSliceVisible(True) self.takeScreenshot('PETCT-ConfigureView', 'Configure View', -1) mainWindow.moduleSelector().selectModule('Volumes') compositNode = redWidget.mrmlSliceCompositeNode() compositNode.SetForegroundOpacity(0.2) self.takeScreenshot('PETCT-ShowVolumes', 'Show Volumes with lesion', -1) compositNode.SetForegroundOpacity(0.5) self.takeScreenshot('PETCT-CTOpacity', 'CT1 volume opacity to 0.5', -1) yellowWidget.sliceController().setSliceVisible(False) greenWidget.sliceController().setSliceVisible(True) self.takeScreenshot('PETCT-ShowSlices', 'Show axial and sagittal slices', -1) self.delayDisplay('SUV Computation') if not hasattr(slicer.modules, 'petstandarduptakevaluecomputation'): self.delayDisplay( "PET SUV Computation not available, skipping the test.") return slicer.util.selectModule('PETStandardUptakeValueComputation') parameters = { "PETDICOMPath": extractPath + '/' + 'PET1', "PETVolume": slicer.util.getNode('PET1'), "VOIVolume": slicer.util.getNode('PET1-label'), } suvComputation = slicer.modules.petstandarduptakevaluecomputation self.CLINode1 = None self.CLINode1 = slicer.cli.run(suvComputation, self.CLINode1, parameters, delete_temporary_files=False) waitCount = 0 while self.CLINode1.GetStatusString( ) != 'Completed' and waitCount < 100: self.delayDisplay("Running SUV Computation... %d" % waitCount) waitCount += 1 # close the scene slicer.mrmlScene.Clear(0) self.delayDisplay("Loading PET_CT_post-treatment.mrb") postTreatmentPath = extractPath + '/PET_CT_post-treatment.mrb' slicer.util.loadScene(postTreatmentPath) self.takeScreenshot('PETCT-LoadedPost', 'Loaded post-treatement scene', -1) compositNode.SetForegroundOpacity(0.5) self.takeScreenshot('PETCT-CT2Opacity', 'CT2 volume opacity to 0.5', -1) redController.setSliceOffsetValue(-165.01) self.takeScreenshot('PETCT-LarynxUptake', 'Mild uptake in the larynx and pharynx', -1) redController.setSliceOffsetValue(-106.15) self.takeScreenshot('PETCT-TumorUptake', 'No uptake in the area of the primary tumor', -1) self.delayDisplay('Test passed!') except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e))
def test_ReportingAIMRoundTrip(self): print("CTEST_FULL_OUTPUT") """ Load the data using DICOM module """ import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import urllib downloads = ( ('http://slicer.kitware.com/midas3/download?items=10881', 'JANCT-CT.zip'), ) self.delayDisplay("Downloading") for url,name in downloads: filePath = slicer.app.temporaryPath + '/' + name if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (name, url)) urllib.urlretrieve(url, filePath) self.delayDisplay('Finished with download\n') reportingTempDir = slicer.app.temporaryPath+'/Reporting' print('Temporary directory location: '+reportingTempDir) qt.QDir().mkpath(reportingTempDir) dicomFilesDirectory = reportingTempDir + '/dicomFiles' self.cleanupDir(dicomFilesDirectory) qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) try: self.delayDisplay("Switching to temp database directory") tempDatabaseDirectory = reportingTempDir + '/tempDICOMDatbase' qt.QDir().mkpath(tempDatabaseDirectory) self.cleanupDir(tempDatabaseDirectory) if slicer.dicomDatabase: self.originalDatabaseDirectory = os.path.split(slicer.dicomDatabase.databaseFilename)[0] else: self.originalDatabaseDirectory = None settings = qt.QSettings() settings.setValue('DatabaseDirectory', tempDatabaseDirectory) dicomWidget = slicer.modules.dicom.widgetRepresentation().self() dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory) self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') #dicomWidget.dicomApp.suspendModel() indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() patient = slicer.dicomDatabase.patients()[0] studies = slicer.dicomDatabase.studiesForPatient(patient) series = [slicer.dicomDatabase.seriesForStudy(study) for study in studies] seriesUIDs = [uid for uidList in series for uid in uidList] dicomWidget.detailsPopup.offerLoadables(seriesUIDs, 'SeriesUIDList') dicomWidget.detailsPopup.examineForLoading() loadablesByPlugin = dicomWidget.detailsPopup.loadablesByPlugin self.delayDisplay('Loading Selection') dicomWidget.detailsPopup.loadCheckedLoadables() # initialize the module with the report and volume volumes = slicer.util.getNodes('vtkMRMLScalarVolumeNode*') self.assertTrue(len(volumes) == 1) (name,volume) = volumes.items()[0] self.delayDisplay('Loaded volume name %s' % volume.GetName()) self.delayDisplay('Configure Module') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('Reporting') reporting = slicer.modules.reporting.widgetRepresentation().self() report = slicer.mrmlScene.CreateNodeByClass('vtkMRMLReportingReportNode') report.SetReferenceCount(report.GetReferenceCount()-1) slicer.mrmlScene.AddNode(report) report.SetFindingLabel(7) reporting.reportSelector.setCurrentNode(report) self.delayDisplay('Setting volume to %s' % volume.GetName()) reporting.volumeSelector.setCurrentNode(volume) slicer.app.processEvents() # place some markups and add a segmentation label # add fiducial fidNode = slicer.vtkMRMLAnnotationFiducialNode() fidName = "AIM Round Trip Test Fiducial" fidNode.SetName(fidName) fidNode.SetSelected(1) fidNode.SetDisplayVisibility(1) fidNode.SetLocked(0) # TODO: ask Nicole where this is assigned in the regular workflow fidNode.SetAttribute('AssociatedNodeID',volume.GetID()) print("Calling set fid coords") startCoords = [15.8, 70.8, -126.7] fidNode.SetFiducialCoordinates(startCoords[0],startCoords[1],startCoords[2]) print("Starting fiducial coordinates: "+str(startCoords)) slicer.mrmlScene.AddNode(fidNode) # add ruler rulerNode = slicer.vtkMRMLAnnotationRulerNode() rulerNode.SetName('Test Ruler') m = vtk.vtkMatrix4x4() volume.GetIJKToRASMatrix(m) ijk0 = [0,0,1,1] ijk1 = [50,50,1,1] ras0 = m.MultiplyPoint(ijk0) ras1 = m.MultiplyPoint(ijk1) rulerNode.SetPosition1(19.386751174926758, 68.528785705566406, -127.69000244140625) rulerNode.SetPosition2(132.72709655761719, -34.349384307861328, -127.69000244140625) rulerNode.SetAttribute('AssociatedNodeID',volume.GetID()) slicer.mrmlScene.AddNode(rulerNode) slicer.app.processEvents() # add label node volumesLogic = slicer.modules.volumes.logic() labelNode = volumesLogic.CreateAndAddLabelVolume(slicer.mrmlScene, volume, "Segmentation") labelDisplayNode = labelNode.GetDisplayNode() labelDisplayNode.SetAndObserveColorNodeID('vtkMRMLColorTableNodeFileGenericAnatomyColors.txt') image = volume.GetImageData() thresh = vtk.vtkImageThreshold() if vtk.vtkVersion().GetVTKMajorVersion() < 6: thresh.SetInput(image) else: thresh.SetInputData(image) thresh.ThresholdBetween(10,400) thresh.SetInValue(report.GetFindingLabel()) thresh.SetOutValue(0) thresh.Update() labelNode.SetAndObserveImageData(thresh.GetOutput()) reporting.segmentationSelector.setCurrentNode(labelNode) # Save the report exportDir = reportingTempDir+'/Output' qt.QDir().mkpath(exportDir) self.cleanupDir(exportDir) report.SetStorageDirectoryName(exportDir) reportingLogic = slicer.modules.reporting.logic() print("Before saving report") reportingLogic.SaveReportToAIM(report) self.delayDisplay('Report saved') slicer.mrmlScene.Clear(0) # parse on patient level, find segmentation object, load and make sure # it matches the input # close the scene and load the report, check consistency # try to load back the saved AIM import glob print glob.glob(exportDir+'/*') xmlFiles = glob.glob(exportDir+'/*xml') print xmlFiles self.assertTrue(len(xmlFiles) == 1) reporting.importAIMFile = xmlFiles[0] reporting.onReportImport() self.delayDisplay('Report loaded from AIM! Test passed.') self.delayDisplay("Restoring original database directory") if self.originalDatabaseDirectory: dicomWidget.onDatabaseDirectoryChanged(self.originalDatabaseDirectory) except Exception, e: if self.originalDatabaseDirectory: dicomWidget.onDatabaseDirectoryChanged(self.originalDatabaseDirectory) import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e)) self.assertTrue(False)