def setUp(self): """ Do whatever is needed to reset the state - typically a scene clear will be enough. """ self.UID = '1.3.6.1.4.1.14519.5.2.1.2744.7002.886851941687931416391879144903' self.PatientName = 'QIN-HEADNECK-01-0139' self.tempDicomDatabase = os.path.join(slicer.app.temporaryPath,'PETTest') slicer.mrmlScene.Clear(0) self.originalDicomDatabase = DICOMUtils.openTemporaryDatabase(self.tempDicomDatabase)
def load_DICOM(self): # create temporary database. Save original database path to restore # it when finished self.original_db_path = DICOMUtils.openTemporaryDatabase() #assert temporary database is open # import DICOM to database DICOMUtils.importDicom(self.locations_dict['DICOM'], slicer.dicomDatabase) # get patient name patient_name = slicer.dicomDatabase.nameForPatient( slicer.dicomDatabase.patients()[0]) # open DICOM by name DICOMUtils.loadPatientByName(patient_name) slicer.util.selectModule('SegmentationExtractionModule')
def main(argv): try: logging.info("Start batch RTSTRUCT conversion") # Parse command-line arguments parser = argparse.ArgumentParser( description="Batch Structure Set Conversion") parser.add_argument( "-i", "--input-folder", dest="input_folder", metavar="PATH", default="-", required=True, help= "Folder of input DICOM study (or database path to use existing)") parser.add_argument( "-r", "--ref-dicom-folder", dest="ref_dicom_folder", metavar="PATH", default="", required=False, help= "Folder containing reference anatomy DICOM image series, if stored outside the input study" ) parser.add_argument( "-u", "--use-ref-image", dest="use_ref_image", default=False, required=False, action='store_true', help= "Use anatomy image as reference when converting structure set to labelmap" ) parser.add_argument("-x", "--exist-db", dest="exist_db", default=False, required=False, action='store_true', help="Process an existing database") parser.add_argument("-m", "--export-images", dest="export_images", default=False, required=False, action='store_true', help="Export image data with labelmaps") parser.add_argument("-o", "--output-folder", dest="output_folder", metavar="PATH", default=".", help="Folder for output labelmaps") parser.add_argument("-s", "--export-surfaces", dest="export_surfaces", default=False, required=False, action='store_true', help="Export surface mesh representation") parser.add_argument( "-c", "--show-python-console", dest="show_python_console", default=False, required=False, action='store_true', help= "If this flag is specified then messages are displayed in an interactive Python console and the application does not quit when the script is finished." ) args = parser.parse_args(argv) if args.show_python_console: slicer.util.pythonShell().show() slicer.exit_when_finished = False # Check if SlicerRT is installed try: slicer.modules.dicomrtimportexport except AttributeError: logging.error("Please install SlicerRT extension") return 1 # Check required arguments if args.input_folder == "-": logging.warning('Please specify input DICOM study folder!') if args.output_folder == ".": logging.info( 'Current directory is selected as output folder (default). To change it, please specify --output-folder' ) # Convert to python path style input_folder = args.input_folder.replace('\\', '/') ref_dicom_folder = args.ref_dicom_folder.replace('\\', '/') output_folder = args.output_folder.replace('\\', '/') use_ref_image = args.use_ref_image exist_db = args.exist_db export_images = args.export_images export_surfaces = args.export_surfaces # Perform batch conversion logic = BatchStructureSetConversionLogic() def save_rtslices(output_dir, use_ref_image, ref_image_node_id=None): # package the saving code into a subfunction logging.info("Convert loaded structure set to labelmap volumes") labelmaps = logic.ConvertStructureSetToLabelmap( use_ref_image, ref_image_node_id) logging.info("Save labelmaps to directory " + output_dir) logic.SaveLabelmaps(labelmaps, output_dir) if export_surfaces: logic.SaveModels(output_dir) if export_images: logic.SaveImages(output_dir) logging.info("DONE") if exist_db: logging.info('BatchStructureSet running in existing database mode') DICOMUtils.openDatabase(input_folder) all_patients = slicer.dicomDatabase.patients() logging.info('Processing %d patients...' % len(all_patients)) for patient in all_patients: try: slicer.mrmlScene.Clear(0) # clear the scene DICOMUtils.loadPatientByUID(patient) output_dir = os.path.join(output_folder, patient) if not os.access(output_dir, os.F_OK): os.mkdir(output_dir) save_rtslices(output_dir, use_ref_image) except OSError as e: # Failed to load data from this patient, continue with the next one print(e) else: logging.info('BatchStructureSet running in file mode') ref_volume_file_path = None if os.path.isdir(ref_dicom_folder): # If reference DICOM folder is given and valid, then import reference patient and save its ID logging.info("Import reference anatomy DICOM data from " + ref_dicom_folder) DICOMUtils.openTemporaryDatabase() DICOMUtils.importDicom(ref_dicom_folder) # Save first volume to be used as reference logic.LoadFirstPatientIntoSlicer() scalarVolumeNodes = list( slicer.util.getNodes('vtkMRMLScalarVolume*').values()) if len(scalarVolumeNodes) > 0: refVolNode = scalarVolumeNodes[0] refVolStorageNode = refVolNode.CreateDefaultStorageNode() ref_volume_file_path = os.path.join( output_folder, 'refVolume.nrrd') refVolStorageNode.SetFileName(ref_volume_file_path) refVolStorageNode.WriteData(refVolNode) logging.info("Import DICOM data from " + input_folder) DICOMUtils.openTemporaryDatabase() DICOMUtils.importDicom(input_folder) all_patients = slicer.dicomDatabase.patients() logging.info('Processing %d patients...' % len(all_patients)) for patient in all_patients: try: slicer.mrmlScene.Clear(0) # clear the scene DICOMUtils.loadPatientByUID(patient) output_dir = os.path.join(output_folder, patient) if not os.access(output_dir, os.F_OK): os.mkdir(output_dir) ref_volume_node_id = None if ref_volume_file_path: try: refVolNode = slicer.util.loadVolume( ref_volume_file_path) ref_volume_node_id = refVolNode.GetID() except: pass save_rtslices(output_dir, use_ref_image, ref_volume_node_id) except OSError as e: # Failed to load data from this patient, continue with the next one print(e) except Exception as e: import traceback traceback.print_exc() print(e) return 1 return 0
def main(argv): try: # Parse command-line arguments parser = argparse.ArgumentParser(description="Batch Structure Set Conversion") parser.add_argument("-i", "--input-folder", dest="input_folder", metavar="PATH", default="-", required=True, help="Folder of input DICOM study (or database path to use existing)") parser.add_argument("-x", "--exist-db", dest="exist_db", default=False, required=False, action='store_true', help="Process an existing database") parser.add_argument("-m", "--export-images", dest="export_images", default=False, required=False, action='store_true', help="Export image data with labelmaps") parser.add_argument("-o", "--output-folder", dest="output_folder", metavar="PATH", default=".", help="Folder for output labelmaps") args = parser.parse_args(argv) # Check required arguments if args.input_folder == "-": logging.warning('Please specify input DICOM study folder!') if args.output_folder == ".": logging.info('Current directory is selected as output folder (default). To change it, please specify --output-folder') # Convert to python path style input_folder = args.input_folder.replace('\\', '/') output_folder = args.output_folder.replace('\\', '/') exist_db = args.exist_db export_images = args.export_images # Perform batch conversion logic = BatchStructureSetConversionLogic() def save_rtslices(output_dir): # package the saving code into a subfunction logging.info("Convert loaded structure set to labelmap volumes") labelmaps = logic.ConvertStructureSetToLabelmap() logging.info("Save labelmaps to directory " + output_dir) logic.SaveLabelmaps(labelmaps, output_dir) if export_images: logic.SaveImages(output_dir) logging.info("DONE") if exist_db: logging.info('BatchStructureSet running in existing database mode') DICOMUtils.openDatabase(input_folder) all_patients = slicer.dicomDatabase.patients() logging.info('Must Process Patients %s' % len(all_patients)) for patient in all_patients: slicer.mrmlScene.Clear(0) # clear the scene DICOMUtils.loadPatientByUID(patient) output_dir = os.path.join(output_folder,patient) if not os.access(output_dir, os.F_OK): os.mkdir(output_dir) save_rtslices(output_dir) else: logging.info("Import DICOM data from " + input_folder) DICOMUtils.openTemporaryDatabase() DICOMUtils.importDicom(input_folder) logging.info("Load first patient into Slicer") logic.LoadFirstPatientIntoSlicer() save_rtslices(output_folder) except Exception as e: print(e) sys.exit(0)
def test_Part1DICOM(self,enableScreenshotsFlag=0,screenshotScaleFactor=1): """ Test the DICOM part of the test using the head atlas """ logic = RSNAVisTutorialLogic() logic.enableScreenshots = enableScreenshotsFlag logic.screenshotScaleFactor = screenshotScaleFactor import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import SampleData dicomFilesDirectory = SampleData.downloadFromURL( fileNames='dataset1_Thorax_Abdomen.zip', uris='http://slicer.kitware.com/midas3/download?items=124183')[0] try: self.delayDisplay("Switching to temp database directory") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase('tempDICOMDatabase') self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() dicomWidget = slicer.modules.DICOMWidget 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() logic.takeScreenshot('LoadingADICOMVolume-Loaded','Loaded DICOM Volume',-1) layoutManager = slicer.app.layoutManager() redWidget = layoutManager.sliceWidget('Red') slicer.util.clickAndDrag(redWidget,start=(10,10),end=(10,40)) slicer.util.clickAndDrag(redWidget,start=(10,10),end=(40,10)) logic.takeScreenshot('LoadingADICOMVolume-WL','Changed level and window',-1) redWidget.sliceController().setSliceLink(True) redWidget.sliceController().setSliceVisible(True); logic.takeScreenshot('LoadingADICOMVolume-LinkView','Linked and visible',-1) slicer.util.clickAndDrag(redWidget,button='Right',start=(10,10),end=(10,40)) logic.takeScreenshot('LoadingADICOMVolume-Zoom','Zoom',-1) threeDView = layoutManager.threeDWidget(0).threeDView() slicer.util.clickAndDrag(threeDView) logic.takeScreenshot('LoadingADICOMVolume-Rotate','Rotate',-1) threeDView.resetFocalPoint() logic.takeScreenshot('LoadingADICOMVolume-Center','Center the view',-1) layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalWidescreenView) logic.takeScreenshot('LoadingADICOMVolume-ConventionalWidescreen','Conventional Widescreen Layout',-1) slicer.util.mainWindow().moduleSelector().selectModule('VolumeRendering') logic.takeScreenshot('VolumeRendering-Module','Volume Rendering',-1) volumeRenderingWidgetRep = slicer.modules.volumerendering.widgetRepresentation() abdomenVolume = slicer.mrmlScene.GetFirstNodeByName('6: CT_Thorax_Abdomen') volumeRenderingWidgetRep.setMRMLVolumeNode(abdomenVolume) logic.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) logic.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) logic.takeScreenshot('VolumeRendering-ViewRendering','View Volume Rendering',-1) self.delayDisplay('Skipping Move the Shift slider') redWidget.sliceController().setSliceVisible(False); logic.takeScreenshot('VolumeRendering-SlicesOff','Turn off visibility of slices in 3D',-1) threeDView = layoutManager.threeDWidget(0).threeDView() slicer.util.clickAndDrag(threeDView) logic.takeScreenshot('VolumeRendering-RotateVolumeRendering','Rotate volume rendered image',-1) volumeRenderingNode.SetVisibility(0) logic.takeScreenshot('VolumeRendering-TurnOffVolumeRendering','Turn off volume rendered image',-1) volumeRenderingNode.SetCroppingEnabled(1) annotationROI = slicer.mrmlScene.GetFirstNodeByName('AnnotationROI') annotationROI.SetDisplayVisibility(1) logic.takeScreenshot('VolumeRendering-DisplayROI','Enable cropping and display ROI',-1) redWidget.sliceController().setSliceVisible(True) logic.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) logic.takeScreenshot('VolumeRendering-SizedROI','Position the ROI over a kidney',-1) volumeRenderingNode.SetVisibility(1) logic.takeScreenshot('VolumeRendering-ROIRendering','ROI volume rendered',-1) annotationROI.SetXYZ(15,146,-186) annotationROI.SetRadiusXYZ(138,57,61) logic.takeScreenshot('VolumeRendering-BothKidneys','Rendered both kidneys',-1) self.delayDisplay('Test passed!') except Exception as e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e)) self.delayDisplay("Restoring original database directory") DICOMUtils.closeTemporaryDatabase(originalDatabaseDirectory)
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") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase('tempDICOMDatabase') self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() dicomWidget = slicer.modules.DICOMWidget 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') slicer.util.clickAndDrag(redWidget,start=(10,10),end=(10,40)) slicer.util.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) slicer.util.clickAndDrag(redWidget,button='Right',start=(10,10),end=(10,40)) self.takeScreenshot('LoadingADICOMVolume-Zoom','Zoom',-1) threeDView = layoutManager.threeDWidget(0).threeDView() slicer.util.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() slicer.util.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 test_AlternateReaders(self): """ Test the DICOM loading of sample testing data """ testPass = True import os, json self.delayDisplay("Starting the DICOM test", 100) referenceData = json.JSONDecoder().decode('''[ { "url": "http://slicer.kitware.com/midas3/download/item/292839/Mouse-MR-example-where-GDCM_fails.zip", "fileName": "Mouse-MR-example-where-GDCM_fails.zip", "name": "Mouse-MR-example-where-GDCM_fails", "seriesUID": "1.3.6.1.4.1.9590.100.1.2.366426457713813178933224342280246227461", "expectedFailures": ["GDCM", "Archetype"], "voxelValueQuantity": "(110852, DCM, \\"MR signal intensity\\")", "voxelValueUnits": "(1, UCUM, \\"no units\\")" }, { "url": "http://slicer.kitware.com/midas3/download/item/294857/deidentifiedMRHead-dcm-one-series.zip", "fileName": "deidentifiedMRHead-dcm-one-series.zip", "name": "deidentifiedMRHead-dcm-one-series", "seriesUID": "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.270.0", "expectedFailures": [], "voxelValueQuantity": "(110852, DCM, \\"MR signal intensity\\")", "voxelValueUnits": "(1, UCUM, \\"no units\\")" } ]''') # another dataset that could be added in the future - currently fails for all readers # due to invalid format - see https://issues.slicer.org/view.php?id=3569 #{ "url": "http://slicer.kitware.com/midas3/download/item/293587/RIDER_bug.zip", #"fileName": "RIDER_bug.zip", #"name": "RIDER_bug", #"seriesUID": "1.3.6.1.4.1.9328.50.7.261772317324041365541450388603508531852", #"expectedFailures": [] #} loadingResult = {} # # first, get the data - a zip file of dicom data # self.delayDisplay("Downloading", 100) for dataset in referenceData: try: filePath = slicer.app.temporaryPath + '/' + dataset['fileName'] if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (dataset['fileName'], dataset['url']), 100) urllib.urlretrieve(dataset['url'], filePath) self.delayDisplay('Finished with download\n', 100) self.delayDisplay("Unzipping", 100) dicomFilesDirectory = slicer.app.temporaryPath + dataset['name'] qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) # # insert the data into th database # self.delayDisplay("Switching to temp database directory", 100) originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase('tempDICOMDatabase') self.delayDisplay('Importing DICOM', 100) mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() # # select the series # detailsPopup = slicer.modules.DICOMWidget.detailsPopup detailsPopup.open() # load the data by series UID detailsPopup.offerLoadables(dataset['seriesUID'],'Series') detailsPopup.examineForLoading() loadable = detailsPopup.getAllSelectedLoadables().keys()[0] # # try loading using each of the selected readers, fail # on enexpected load issue # scalarVolumePlugin = slicer.modules.dicomPlugins['DICOMScalarVolumePlugin']() readerApproaches = scalarVolumePlugin.readerApproaches() basename = loadable.name volumesByApproach = {} for readerApproach in readerApproaches: self.delayDisplay('Loading Selection with approach: %s' % readerApproach, 100) loadable.name = basename + "-" + readerApproach volumeNode = scalarVolumePlugin.load(loadable,readerApproach) if not volumeNode and readerApproach not in dataset['expectedFailures']: raise Exception("Expected to be able to read with %s, but couldn't" % readerApproach) if volumeNode and readerApproach in dataset['expectedFailures']: raise Exception("Expected to NOT be able to read with %s, but could!" % readerApproach) if volumeNode: volumesByApproach[readerApproach] = volumeNode self.delayDisplay('Test quantity and unit') if 'voxelValueQuantity' in dataset.keys(): self.assertEqual(volumeNode.GetVoxelValueQuantity().GetAsPrintableString(), dataset['voxelValueQuantity']) if 'voxelValueUnits' in dataset.keys(): self.assertEqual(volumeNode.GetVoxelValueUnits().GetAsPrintableString(), dataset['voxelValueUnits']) # # for each approach that loaded as expected, compare the volumes # to ensure they match in terms of pixel data and metadata # failedComparisons = {} approachesThatLoaded = volumesByApproach.keys() print('approachesThatLoaded %s' % approachesThatLoaded) for approachIndex in range(len(approachesThatLoaded)): firstApproach = approachesThatLoaded[approachIndex] firstVolume = volumesByApproach[firstApproach] for secondApproachIndex in range(approachIndex+1,len(approachesThatLoaded)): secondApproach = approachesThatLoaded[secondApproachIndex] secondVolume = volumesByApproach[secondApproach] print('comparing %s,%s' % (firstApproach, secondApproach)) comparison = slicer.modules.dicomPlugins['DICOMScalarVolumePlugin'].compareVolumeNodes(firstVolume,secondVolume) if comparison != "": print('failed: %s', comparison) failedComparisons[firstApproach,secondApproach] = comparison if len(failedComparisons.keys()) > 0: raise Exception("Loaded volumes don't match: %s" % failedComparisons) self.delayDisplay('%s Test passed!' % dataset['name'], 200) except Exception, e: import traceback traceback.print_exc() self.delayDisplay('%s Test caused exception!\n' % dataset['name'] + str(e), 2000) testPass = False
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 SampleData dicomFilesDirectory = SampleData.downloadFromURL( fileNames='Dcmtk-db.zip', uris=TESTING_DATA_URL + 'MD5/7a43d121a51a631ab0df02071e5ba6ed', checksums='MD5:7a43d121a51a631ab0df02071e5ba6ed')[0] try: self.delayDisplay("Switching to temp database directory") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase( 'tempDICOMDatbase') self.delayDisplay('Start Local DICOM Q/R SCP') import subprocess import os configFilePath = dicomFilesDirectory + '/Dcmtk-db/dcmqrscp.cfg' processCurrentPath = dicomFilesDirectory + '/Dcmtk-db/' print("configFilePath: " + os.path.abspath(configFilePath)) print("processCurrentPath: " + os.path.abspath(processCurrentPath)) dcmqrscpExeOptions = ('/bin', '/../CTK-build/CMakeExternals/Install/bin', '/../DCMTK-install/bin', '/../DCMTK-build/bin', '/../DCMTK-build/bin/Release', '/../DCMTK-build/bin/Debug', '/../DCMTK-build/bin/RelWithDebInfo' '/../DCMTK-build/bin/MinSizeRel') 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') slicer.util.selectModule('DICOM') 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') popen.kill() # Select first patient browserWidget = slicer.modules.DICOMWidget.browserWidget browserWidget.dicomBrowser.dicomTableManager().patientsTable( ).selectFirst() browserWidget.examineForLoading() self.delayDisplay('Loading Selection') browserWidget.loadCheckedLoadables() self.delayDisplay('Change Level') layoutManager = slicer.app.layoutManager() redWidget = layoutManager.sliceWidget('Red') slicer.util.clickAndDrag(redWidget, start=(10, 10), end=(10, 40)) self.delayDisplay('Change Window') slicer.util.clickAndDrag(redWidget, start=(10, 10), end=(40, 10)) self.delayDisplay('Change Layout') layoutManager = slicer.app.layoutManager() layoutManager.setLayout( slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView) self.delayDisplay('Zoom') slicer.util.clickAndDrag(redWidget, button='Right', start=(10, 10), end=(10, 40)) self.delayDisplay('Pan') slicer.util.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') slicer.util.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() slicer.util.clickAndDrag(threeDView) self.delayDisplay('Zoom') threeDView = layoutManager.threeDWidget(0).threeDView() slicer.util.clickAndDrag(threeDView, button='Right') self.delayDisplay('Test passed!') except Exception as e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e)) self.delayDisplay("Restoring original database directory") DICOMUtils.closeTemporaryDatabase(originalDatabaseDirectory)
def main(argv): # Parse command-line arguments parser = argparse.ArgumentParser(description="Batch Structure Set Conversion") parser.add_argument( "-i", "--input-folder", dest="input_folder", metavar="PATH", default="-", required=True, help="Folder of input DICOM study (or database path to use existing)", ) parser.add_argument( "-o", "--output-folder", dest="output_folder", metavar="PATH", default=".", help="Folder for output labelmaps" ) parser.add_argument( "-r", "--register", dest="register", metavar="PATH", default="no_registration", help='"T1" for registration to T1 image, "T2" for registration to T2 image. ' 'Defaults to "no_registration".', ) parser.add_argument( "--export_all_structures", dest="export_all_structures", action="store_true", help="All available structures will be exported. By default, only the VS is exported.", ) parser.set_defaults(export_all_structures=False) args = parser.parse_args(argv) # Check required arguments if args.input_folder == "-": logging.warning("Please specify input DICOM study folder!") if args.output_folder == ".": logging.info( "Current directory is selected as output folder (default). To change it, please specify --output-folder" ) if args.register not in ["no_registration", "T1", "T2"]: logging.error('Invalid value for keyword "--register": choose "T1" or "T2" or "no_registration"') # Convert to python path style input_folder = args.input_folder.replace("\\", "/") output_folder = args.output_folder.replace("\\", "/") register = args.register export_all_structures = args.export_all_structures register_T1_image_and_contour_points_to_T2_image = False register_T2_image_and_contour_points_to_T1_image = False if register == "T1": register_T2_image_and_contour_points_to_T1_image = True elif register == "T2": register_T1_image_and_contour_points_to_T2_image = True if export_all_structures: export_only_tumour_seg = False else: export_only_tumour_seg = True if not os.access(output_folder, os.F_OK): os.mkdir(output_folder) DICOMUtils.openTemporaryDatabase() patient_dirs = glob.glob(os.path.join(input_folder, "vs_gk_*")) # create and compile a regex pattern pattern = re.compile(r"_([0-9]+)_t[1-2]$") case_numbers = [] # first_case = 1 # last_case = 2 # for case_number in range(first_case, last_case + 1): # exclude cases # if case_number in [39, 97, 130, 160, 168, 208, 219, 227]: # continue for i in range(len(patient_dirs)): # get case number from folder name print(pattern.findall(patient_dirs[i])) case_number = pattern.findall(patient_dirs[i])[0] # skip iteration if case has already been dealt with if case_number in case_numbers: continue case_numbers.append(case_number) print(f"case: {case_number}") [ref1, ref2, RTSS1, RTSS2] = import_T1_and_T2_data(input_folder, case_number) ## REGISTRATION if register_T1_image_and_contour_points_to_T2_image: transform_path = os.path.join(input_folder, f"vs_gk_{case_number}" + "_t1", "inv_T1_LPS_to_T2_LPS.tfm") transformNode = slicer.util.loadNodeFromFile(transform_path, filetype="TransformFile") ref1 = register_and_resample( input_node=ref1, reference_node=ref2, transform_node=transformNode, interpolationMode="Linear" ) # also transform contour points RTSS1.SetAndObserveTransformNodeID(transformNode.GetID()) elif register_T2_image_and_contour_points_to_T1_image: transform_path = os.path.join(input_folder, f"vs_gk_{case_number}" + "_t2", "inv_T2_LPS_to_T1_LPS.tfm") transformNode = slicer.util.loadNodeFromFile(transform_path, filetype="TransformFile") ref2 = register_and_resample( input_node=ref2, reference_node=ref1, transform_node=transformNode, interpolationMode="Linear" ) # also transform contour points RTSS2.SetAndObserveTransformNodeID(transformNode.GetID()) ## Create segments from contour files # Create segmentation node where we will store segments segmentationNode_T1 = create_segmentation_node_with_reference_geometry( "SegmentationFromContourPoints_refT1", ref_geometry_image_node=ref1 ) segmentationNode_T2 = create_segmentation_node_with_reference_geometry( "SegmentationFromContourPoints_refT2", ref_geometry_image_node=ref2 ) # load structure contour list from json file # these are registered to original T1 and T2 images (they are not affected by registrations of the ref_geometry # node, which only defines extent and the IJK to RAS transformation) structure_contour_list_T1 = load_LPS_contour_points( os.path.join(input_folder, f"vs_gk_{case_number}" + "_t1", "contours.json") ) structure_contour_list_T2 = load_LPS_contour_points( os.path.join(input_folder, f"vs_gk_{case_number}" + "_t2", "contours.json") ) # create segments for all structures create_segments_from_structure_contour_list(segmentationNode_T1, structure_contour_list_T1) create_segments_from_structure_contour_list(segmentationNode_T2, structure_contour_list_T2) ## export all files if register_T1_image_and_contour_points_to_T2_image: lm_node_in_T2_list = save_labelmaps_from_planar_contour( segmentationNode_T2, ref2, export_only_tumour_seg, case_number, output_folder ) # save contour points registered to T2 image if register_T2_image_and_contour_points_to_T1_image: lm_node_in_T1_list = save_labelmaps_from_planar_contour( segmentationNode_T1, ref1, export_only_tumour_seg, case_number, output_folder ) if not register_T1_image_and_contour_points_to_T2_image and not register_T2_image_and_contour_points_to_T1_image: lm_node_in_T2_list = save_labelmaps_from_planar_contour( segmentationNode_T2, ref2, export_only_tumour_seg, case_number, output_folder ) # save contour points registered to T2 image lm_node_in_T1_list = save_labelmaps_from_planar_contour( segmentationNode_T1, ref1, export_only_tumour_seg, case_number, output_folder ) # save images as nifti files if register_T1_image_and_contour_points_to_T2_image: slicer.util.saveNode( ref1, os.path.join(output_folder, f"vs_gk_{case_number}", f"vs_gk_t1_refT2.nii.gz") ) # pass vol node and destination filename slicer.util.saveNode( ref2, os.path.join(output_folder, f"vs_gk_{case_number}", f"vs_gk_t2_refT2.nii.gz") ) # pass vol node and destination filename if register_T2_image_and_contour_points_to_T1_image: slicer.util.saveNode( ref1, os.path.join(output_folder, f"vs_gk_{case_number}", f"vs_gk_t1_refT1.nii.gz") ) # pass vol node and destination filename slicer.util.saveNode( ref2, os.path.join(output_folder, f"vs_gk_{case_number}", f"vs_gk_t2_refT1.nii.gz") ) # pass vol node and destination filename if not register_T1_image_and_contour_points_to_T2_image and not register_T2_image_and_contour_points_to_T1_image: slicer.util.saveNode( ref1, os.path.join(output_folder, f"vs_gk_{case_number}", f"vs_gk_t1_refT1.nii.gz") ) # pass vol node and destination filename slicer.util.saveNode( ref2, os.path.join(output_folder, f"vs_gk_{case_number}", f"vs_gk_t2_refT2.nii.gz") ) # pass vol node and destination filename sys.exit(0)
parser.add_argument("--input", help="Input DICOM directory") parser.add_argument("--output", help="Output directory") parser.add_argument("--filename", help="File name") args = parser.parse_args() if args.dcmtk and args.gdcm: raise ValueError("Cannot specify both gdcm and dcmtk") if args.dcmtk: setDICOMReaderApproach('DCMTK') if args.gdcm: setDICOMReaderApproach('GDCM') indexer = ctk.ctkDICOMIndexer() dbDir = "/tmp/SlicerDB" print("Temporary directory: " + dbDir) DICOMUtils.openTemporaryDatabase(dbDir) db = slicer.dicomDatabase print("indexing {}".format(args.input)) indexer.addDirectory(db, args.input) indexer.waitForImportFinished() slicer.util.selectModule('DICOM') fileLists = [] for patient in db.patients(): print("Patient:" + patient) for study in db.studiesForPatient(patient): print("Study:" + study) for series in db.seriesForStudy(study): print("Series:" + series) fileLists.append(db.filesForSeries(series))
def main(argv): try: # Parse command-line arguments parser = argparse.ArgumentParser( description="Batch Structure Set Conversion") parser.add_argument( "-i", "--input-folder", dest="input_folder", metavar="PATH", default="-", required=True, help= "Folder of input DICOM study (or database path to use existing)") parser.add_argument( "-r", "--ref-dicom-folder", dest="ref_dicom_folder", metavar="PATH", default="", required=False, help= "Folder containing reference anatomy DICOM image series, if stored outside the input study" ) parser.add_argument( "-u", "--use-ref-image", dest="use_ref_image", default=False, required=False, action='store_true', help= "Use anatomy image as reference when converting structure set to labelmap" ) parser.add_argument("-x", "--exist-db", dest="exist_db", default=False, required=False, action='store_true', help="Process an existing database") parser.add_argument("-m", "--export-images", dest="export_images", default=False, required=False, action='store_true', help="Export image data with labelmaps") parser.add_argument("-o", "--output-folder", dest="output_folder", metavar="PATH", default=".", help="Folder for output labelmaps") args = parser.parse_args(argv) # Check required arguments if args.input_folder == "-": logging.warning('Please specify input DICOM study folder!') if args.output_folder == ".": logging.info( 'Current directory is selected as output folder (default). To change it, please specify --output-folder' ) # Convert to python path style input_folder = args.input_folder.replace('\\', '/') ref_dicom_folder = args.ref_dicom_folder.replace('\\', '/') output_folder = args.output_folder.replace('\\', '/') use_ref_image = args.use_ref_image exist_db = args.exist_db export_images = args.export_images # Perform batch conversion logic = BatchStructureSetConversionLogic() def save_rtslices(output_dir, use_ref_image, ref_image_node_id=None): # package the saving code into a subfunction logging.info("Convert loaded structure set to labelmap volumes") labelmaps = logic.ConvertStructureSetToLabelmap( use_ref_image, ref_image_node_id) logging.info("Save labelmaps to directory " + output_dir) logic.SaveLabelmaps(labelmaps, output_dir) if export_images: logic.SaveImages(output_dir) logging.info("DONE") if exist_db: logging.info('BatchStructureSet running in existing database mode') DICOMUtils.openDatabase(input_folder) all_patients = slicer.dicomDatabase.patients() logging.info('Must Process Patients %s' % len(all_patients)) for patient in all_patients: slicer.mrmlScene.Clear(0) # clear the scene DICOMUtils.loadPatientByUID(patient) output_dir = os.path.join(output_folder, patient) if not os.access(output_dir, os.F_OK): os.mkdir(output_dir) save_rtslices(output_dir, use_ref_image) else: ref_image_node_id = None if os.path.isdir(ref_dicom_folder): # If reference DICOM folder is given and valid, then load that volume logging.info("Import reference anatomy DICOM data from " + ref_dicom_folder) DICOMUtils.openTemporaryDatabase() DICOMUtils.importDicom(ref_dicom_folder) logic.LoadFirstPatientIntoSlicer() # Remember first volume scalarVolumeNodes = list( slicer.util.getNodes('vtkMRMLScalarVolume*').values()) if len(scalarVolumeNodes) > 0: ref_image_node_id = scalarVolumeNodes[0].GetID() logging.info("Import DICOM data from " + input_folder) DICOMUtils.openTemporaryDatabase() DICOMUtils.importDicom(input_folder) logging.info("Load first patient into Slicer") logic.LoadFirstPatientIntoSlicer() save_rtslices(output_folder, use_ref_image, ref_image_node_id) except Exception as e: print(e) sys.exit(0)
def test_Part1DICOM(self): """ Test the DICOM loading of sample testing data """ testPass = True import os, json self.delayDisplay("Starting the DICOM test", 100) referenceData = json.JSONDecoder().decode('''[ { "url": "http://slicer.kitware.com/midas3/download/item/292839/Mouse-MR-example-where-GDCM_fails.zip", "fileName": "Mouse-MR-example-where-GDCM_fails.zip", "name": "Mouse-MR-example-where-GDCM_fails", "seriesUID": "1.3.6.1.4.1.9590.100.1.2.366426457713813178933224342280246227461", "expectedFailures": ["GDCM", "Archetype"] }, { "url": "http://slicer.kitware.com/midas3/download/item/294857/deidentifiedMRHead-dcm-one-series.zip", "fileName": "deidentifiedMRHead-dcm-one-series.zip", "name": "deidentifiedMRHead-dcm-one-series", "seriesUID": "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.270.0", "expectedFailures": [] } ]''') # another dataset that could be added in the future - currently fails for all readers # due to invalid format - see https://issues.slicer.org/view.php?id=3569 #{ "url": "http://slicer.kitware.com/midas3/download/item/293587/RIDER_bug.zip", #"fileName": "RIDER_bug.zip", #"name": "RIDER_bug", #"seriesUID": "1.3.6.1.4.1.9328.50.7.261772317324041365541450388603508531852", #"expectedFailures": [] #} loadingResult = {} # # first, get the data - a zip file of dicom data # self.delayDisplay("Downloading", 100) for dataset in referenceData: try: filePath = slicer.app.temporaryPath + '/' + dataset['fileName'] if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (dataset['fileName'], dataset['url']), 100) urllib.urlretrieve(dataset['url'], filePath) self.delayDisplay('Finished with download\n', 100) self.delayDisplay("Unzipping", 100) dicomFilesDirectory = slicer.app.temporaryPath + dataset['name'] qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) # # insert the data into th database # self.delayDisplay("Switching to temp database directory", 100) originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase('tempDICOMDatabase') self.delayDisplay('Importing DICOM', 100) mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() # # selct the series # detailsPopup = slicer.modules.DICOMWidget.detailsPopup detailsPopup.open() # load the data by series UID detailsPopup.offerLoadables(dataset['seriesUID'],'Series') detailsPopup.examineForLoading() loadable = detailsPopup.getAllSelectedLoadables().keys()[0] # # try loading using each of the selected readers, fail # on enexpected load issue # scalarVolumePlugin = slicer.modules.dicomPlugins['DICOMScalarVolumePlugin']() readerApproaches = scalarVolumePlugin.readerApproaches() basename = loadable.name volumesByApproach = {} for readerApproach in readerApproaches: self.delayDisplay('Loading Selection with approach: %s' % readerApproach, 100) loadable.name = basename + "-" + readerApproach volumeNode = scalarVolumePlugin.load(loadable,readerApproach) if not volumeNode and readerApproach not in dataset['expectedFailures']: raise Exception("Expected to be able to read with %s, but couldn't" % readerApproach) if volumeNode and readerApproach in dataset['expectedFailures']: raise Exception("Expected to NOT be able to read with %s, but could!" % readerApproach) if volumeNode: volumesByApproach[readerApproach] = volumeNode # # for each approach that loaded as expected, compare the volumes # to ensure they match in terms of pixel data and metadata # failedComparisons = {} approachesThatLoaded = volumesByApproach.keys() print('approachesThatLoaded %s' % approachesThatLoaded) for approachIndex in range(len(approachesThatLoaded)): firstApproach = approachesThatLoaded[approachIndex] firstVolume = volumesByApproach[firstApproach] for secondApproachIndex in range(approachIndex+1,len(approachesThatLoaded)): secondApproach = approachesThatLoaded[secondApproachIndex] secondVolume = volumesByApproach[secondApproach] print('comparing %s,%s' % (firstApproach, secondApproach)) comparison = slicer.modules.dicomPlugins['DICOMScalarVolumePlugin'].compareVolumeNodes(firstVolume,secondVolume) if comparison != "": print('failed: %s', comparison) failedComparisons[firstApproach,secondApproach] = comparison if len(failedComparisons.keys()) > 0: raise Exception("Loaded volumes don't match: %s" % failedComparisons) self.delayDisplay('%s Test passed!' % dataset['name'], 200) except Exception, e: import traceback traceback.print_exc() self.delayDisplay('%s Test caused exception!\n' % dataset['name'] + str(e), 2000) testPass = False
def main(argv): try: # Parse command-line arguments parser = argparse.ArgumentParser(description="Batch Structure Set Conversion") parser.add_argument("-i", "--input-folder", dest="input_folder", metavar="PATH", default="-", required=True, help="Folder of input DICOM study (or database path to use existing)") parser.add_argument("-x", "--exist-db", dest="exist_db", default=False, required=False, action='store_true', help="Process an existing database") parser.add_argument("-m", "--export-images", dest="export_images", default=False, required=False, action='store_true', help="Export image data with labelmaps") parser.add_argument("-o", "--output-folder", dest="output_folder", metavar="PATH", default=".", help="Folder for output labelmaps") args = parser.parse_args(argv) # Check required arguments if args.input_folder == "-": logging.warning('Please specify input DICOM study folder!') if args.output_folder == ".": logging.info('Current directory is selected as output folder (default). To change it, please specify --output-folder') # Convert to python path style input_folder = args.input_folder.replace('\\', '/') output_folder = args.output_folder.replace('\\', '/') exist_db = args.exist_db export_images = args.export_images # Perform batch conversion logic = BatchStructureSetConversionLogic() def save_rtslices(output_dir): # package the saving code into a subfunction logging.info("Convert loaded structure set to labelmap volumes") labelmaps = logic.ConvertStructureSetToLabelmap() logging.info("Save labelmaps to directory " + output_dir) logic.SaveLabelmaps(labelmaps, output_dir) if export_images: logic.SaveImages(output_dir) logging.info("DONE") if exist_db: logging.info('BatchStructureSet running in existing database mode') DICOMUtils.openDatabase(input_folder) all_patients = slicer.dicomDatabase.patients() logging.info('Must Process Patients %s' % len(all_patients)) for patient in all_patients: slicer.mrmlScene.Clear(0) # clear the scene DICOMUtils.loadPatientByUID(patient) output_dir = os.path.join(output_folder,patient) if not os.access(output_dir, os.F_OK): os.mkdir(output_dir) save_rtslices(output_dir) else: logging.info("Import DICOM data from " + input_folder) DICOMUtils.openTemporaryDatabase() DICOMUtils.importDicom(input_folder) logging.info("Load first patient into Slicer") logic.LoadFirstPatientIntoSlicer() save_rtslices(output_folder) except Exception, e: print(e)
def test_MissingSlices(self): """ Test behavior of the readers when slices are missing To edit and run this test from the python console, paste this below: reloadScriptedModule('DICOMReaders'); import DICOMReaders; tester = DICOMReaders.DICOMReadersTest(); tester.setUp(); tester.test_MissingSlices() """ testPass = True import os, json self.delayDisplay("Starting the DICOM test") settings = qt.QSettings() settings.setValue("DICOM/ScalarVolume/AcquisitionGeometryRegularization", "transform") import SampleData dicomFilesDirectory = SampleData.downloadFromURL( fileNames='deidentifiedMRHead-dcm-one-series.zip', uris='http://slicer.kitware.com/midas3/download?items=294857', checksums='SHA256:899f3f8617ca53bad7dca0b2908478319e708b48ff41dfa64b6bac1d76529928')[0] self.delayDisplay('Finished with download\n') seriesUID = "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.270.0" seriesRASBounds = [-87.29489517211913, 81.70450973510744, -121.57139587402344, 134.42860412597656, -138.71430206298828, 117.28569793701172] seriesDirectory = "Series 004 [MR - SAG RF FAST VOL FLIP 20]" lastSliceCorners = [[[81.05451202, 133.92860413, 116.78569794], [81.05451202, -122.07139587, 116.78569794]], [[81.05451202, 133.92860413, -139.21429443], [81.05451202, -122.07139587, -139.21429443]]] filesToRemove = [ "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.361.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.362.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.363.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.364.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.365.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.366.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.367.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.368.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.369.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.370.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.371.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.372.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.373.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.374.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.375.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.376.0.dcm", ] try: print('Removing %d files from the middle of the series' % len(filesToRemove)) for file in filesToRemove: filePath = os.path.join(dicomFilesDirectory, seriesDirectory, file) os.remove(filePath) # # insert the data into the database # self.delayDisplay("Switching to temp database directory") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase('tempDICOMDatabase') self.delayDisplay('Importing DICOM') slicer.util.selectModule("DICOM") browserWidget = slicer.modules.DICOMWidget.browserWidget dicomBrowser = browserWidget.dicomBrowser dicomBrowser.importDirectory(dicomFilesDirectory, dicomBrowser.ImportDirectoryAddLink) dicomBrowser.waitForImportFinished() # # select the series # browserWidget.onSeriesSelected([seriesUID]) # load the data by series UID browserWidget.examineForLoading() loadable = list(browserWidget.getAllSelectedLoadables().keys())[0] if len(loadable.warning) == 0: raise Exception("Expected warning about geometry issues due to missing slices!") # # load and correct for acquisition then check the geometry # scalarVolumePlugin = slicer.modules.dicomPlugins['DICOMScalarVolumePlugin']() volumeNode = scalarVolumePlugin.load(loadable) if not numpy.allclose(scalarVolumePlugin.acquisitionModeling.fixedCorners[-1], lastSliceCorners): raise Exception("Acquisition transform didn't fix slice corners!") self.delayDisplay('test_MissingSlices passed!') except Exception as e: import traceback traceback.print_exc() self.delayDisplay('Missing Slices Test caused exception!\n' + str(e)) testPass = False self.delayDisplay("Restoring original database directory") DICOMUtils.closeTemporaryDatabase(originalDatabaseDirectory) slicer.util.selectModule('DICOMReaders') return testPass
def test_MissingSlices(self): """ Test behavior of the readers when slices are missing To edit and run this test from the python console, paste this below: reloadScriptedModule('DICOMReaders'); import DICOMReaders; tester = DICOMReaders.DICOMReadersTest(); tester.setUp(); tester.test_MissingSlices() """ testPass = True import os, json self.delayDisplay("Starting the DICOM test") import SampleData dicomFilesDirectory = SampleData.downloadFromURL( fileNames='deidentifiedMRHead-dcm-one-series.zip', uris='http://slicer.kitware.com/midas3/download?items=294857')[0] self.delayDisplay('Finished with download\n') seriesUID = "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.270.0" seriesRASBounds = [ -87.29489517211913, 81.70450973510744, -121.57139587402344, 134.42860412597656, -138.71430206298828, 117.28569793701172 ] seriesDirectory = "Series 004 [MR - SAG RF FAST VOL FLIP 20]" lastSliceCorners = [[[81.05451202, 133.92860413, 116.78569794], [81.05451202, -122.07139587, 116.78569794]], [[81.05451202, 133.92860413, -139.21429443], [81.05451202, -122.07139587, -139.21429443]]] filesToRemove = [ "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.361.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.362.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.363.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.364.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.365.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.366.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.367.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.368.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.369.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.370.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.371.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.372.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.373.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.374.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.375.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.376.0.dcm", ] try: print('Removing %d files from the middle of the series' % len(filesToRemove)) for file in filesToRemove: filePath = os.path.join(dicomFilesDirectory, seriesDirectory, file) os.remove(filePath) # # insert the data into the database # self.delayDisplay("Switching to temp database directory") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase( 'tempDICOMDatabase') self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() # # select the series # detailsPopup = slicer.modules.DICOMWidget.detailsPopup detailsPopup.open() # load the data by series UID detailsPopup.offerLoadables(seriesUID, 'Series') detailsPopup.examineForLoading() loadable = detailsPopup.getAllSelectedLoadables().keys()[0] if len(loadable.warning) == 0: raise Exception( "Expected warning about geometry issues due to missing slices!" ) # # load and correct for acquisition then check the geometry # scalarVolumePlugin = slicer.modules.dicomPlugins[ 'DICOMScalarVolumePlugin']() volumeNode = scalarVolumePlugin.load(loadable) if not numpy.allclose( scalarVolumePlugin.acquisitionModeling.fixedCorners[-1], lastSliceCorners): raise Exception( "Acquisition transform didn't fix slice corners!") self.delayDisplay('test_MissingSlices passed!') except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Missing Slices Test caused exception!\n' + str(e)) testPass = False
def test_AlternateReaders(self): """ Test the DICOM loading of sample testing data """ testPass = True import os, json self.delayDisplay("Starting the DICOM test") referenceData = [ { "url": "http://slicer.kitware.com/midas3/download?items=292839", "checksum": "SHA256:3450ef9372a3460a2f181c8d3bb35a74b4f0acb10c6e18cfcf7804e1d99bf843", "fileName": "Mouse-MR-example-where-GDCM_fails.zip", "name": "Mouse-MR-example-where-GDCM_fails", "seriesUID": "1.3.6.1.4.1.9590.100.1.2.366426457713813178933224342280246227461", # GDCM rejects loading. # DCMTK reads it but then ITK rejects loading the image with 0 spacing. "expectedFailures": ["GDCM", "Archetype", "DCMTK", "GDCM with DCMTK fallback"], "voxelValueQuantity": "(110852, DCM, \"MR signal intensity\")", "voxelValueUnits": "(1, UCUM, \"no units\")" }, { "url": "http://slicer.kitware.com/midas3/download?items=294857", "checksum": "SHA256:899f3f8617ca53bad7dca0b2908478319e708b48ff41dfa64b6bac1d76529928", "fileName": "deidentifiedMRHead-dcm-one-series.zip", "name": "deidentifiedMRHead-dcm-one-series", "seriesUID": "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.270.0", "expectedFailures": [], "voxelValueQuantity": "(110852, DCM, \"MR signal intensity\")", "voxelValueUnits": "(1, UCUM, \"no units\")" } ] # another dataset that could be added in the future - currently fails for all readers # due to invalid format - see https://issues.slicer.org/view.php?id=3569 #{ "url": "http://slicer.kitware.com/midas3/download/item/293587/RIDER_bug.zip", #"fileName": "RIDER_bug.zip", #"name": "RIDER_bug", #"seriesUID": "1.3.6.1.4.1.9328.50.7.261772317324041365541450388603508531852", #"expectedFailures": [] #} loadingResult = {} # # first, get the data - a zip file of dicom data # self.delayDisplay("Downloading") for dataset in referenceData: try: import SampleData dicomFilesDirectory = SampleData.downloadFromURL( fileNames=dataset['fileName'], uris=dataset['url'], checksums=dataset['checksum'])[0] self.delayDisplay('Finished with download') # # insert the data into the database # self.delayDisplay("Switching to temp database directory") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase('tempDICOMDatabase') self.delayDisplay('Importing DICOM') slicer.util.selectModule("DICOM") browserWidget = slicer.modules.DICOMWidget.browserWidget dicomBrowser = browserWidget.dicomBrowser dicomBrowser.importDirectory(dicomFilesDirectory, dicomBrowser.ImportDirectoryAddLink) dicomBrowser.waitForImportFinished() # # select the series # browserWidget.onSeriesSelected([dataset['seriesUID']]) # load the data by series UID browserWidget.examineForLoading() loadable = list(browserWidget.getAllSelectedLoadables().keys())[0] # # try loading using each of the selected readers, fail # on enexpected load issue # scalarVolumePlugin = slicer.modules.dicomPlugins['DICOMScalarVolumePlugin']() readerApproaches = scalarVolumePlugin.readerApproaches() basename = loadable.name volumesByApproach = {} for readerApproach in readerApproaches: self.delayDisplay('Loading Selection with approach: %s' % readerApproach) loadable.name = basename + "-" + readerApproach volumeNode = scalarVolumePlugin.load(loadable,readerApproach) if not volumeNode and readerApproach not in dataset['expectedFailures']: raise Exception("Expected to be able to read with %s, but couldn't" % readerApproach) if volumeNode and readerApproach in dataset['expectedFailures']: raise Exception("Expected to NOT be able to read with %s, but could!" % readerApproach) if volumeNode: volumesByApproach[readerApproach] = volumeNode self.delayDisplay('Test quantity and unit') if 'voxelValueQuantity' in dataset.keys(): self.assertEqual(volumeNode.GetVoxelValueQuantity().GetAsPrintableString(), dataset['voxelValueQuantity']) if 'voxelValueUnits' in dataset.keys(): self.assertEqual(volumeNode.GetVoxelValueUnits().GetAsPrintableString(), dataset['voxelValueUnits']) # # for each approach that loaded as expected, compare the volumes # to ensure they match in terms of pixel data and metadata # failedComparisons = {} approachesThatLoaded = list(volumesByApproach.keys()) print('approachesThatLoaded %s' % approachesThatLoaded) for approachIndex in range(len(approachesThatLoaded)): firstApproach = approachesThatLoaded[approachIndex] firstVolume = volumesByApproach[firstApproach] for secondApproachIndex in range(approachIndex+1,len(approachesThatLoaded)): secondApproach = approachesThatLoaded[secondApproachIndex] secondVolume = volumesByApproach[secondApproach] print('comparing %s,%s' % (firstApproach, secondApproach)) comparison = slicer.modules.dicomPlugins['DICOMScalarVolumePlugin'].compareVolumeNodes(firstVolume,secondVolume) if comparison != "": print(('failed: %s', comparison)) failedComparisons[firstApproach,secondApproach] = comparison if len(failedComparisons.keys()) > 0: raise Exception("Loaded volumes don't match: %s" % failedComparisons) self.delayDisplay('%s Test passed!' % dataset['name']) except Exception as e: import traceback traceback.print_exc() self.delayDisplay('%s Test caused exception!\n' % dataset['name'] + str(e)) testPass = False self.delayDisplay("Restoring original database directory") DICOMUtils.closeTemporaryDatabase(originalDatabaseDirectory) slicer.util.selectModule('DICOMReaders') logging.info(loadingResult) return testPass
def test_AlternateReaders(self): """ Test the DICOM loading of sample testing data """ testPass = True import os, json self.delayDisplay("Starting the DICOM test") referenceData = [{ "url": "http://slicer.kitware.com/midas3/download?items=292839", "fileName": "Mouse-MR-example-where-GDCM_fails.zip", "name": "Mouse-MR-example-where-GDCM_fails", "seriesUID": "1.3.6.1.4.1.9590.100.1.2.366426457713813178933224342280246227461", "expectedFailures": ["GDCM", "Archetype"], "voxelValueQuantity": "(110852, DCM, \"MR signal intensity\")", "voxelValueUnits": "(1, UCUM, \"no units\")" }, { "url": "http://slicer.kitware.com/midas3/download?items=294857", "fileName": "deidentifiedMRHead-dcm-one-series.zip", "name": "deidentifiedMRHead-dcm-one-series", "seriesUID": "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.270.0", "expectedFailures": [], "voxelValueQuantity": "(110852, DCM, \"MR signal intensity\")", "voxelValueUnits": "(1, UCUM, \"no units\")" }] # another dataset that could be added in the future - currently fails for all readers # due to invalid format - see https://issues.slicer.org/view.php?id=3569 #{ "url": "http://slicer.kitware.com/midas3/download/item/293587/RIDER_bug.zip", #"fileName": "RIDER_bug.zip", #"name": "RIDER_bug", #"seriesUID": "1.3.6.1.4.1.9328.50.7.261772317324041365541450388603508531852", #"expectedFailures": [] #} loadingResult = {} # # first, get the data - a zip file of dicom data # self.delayDisplay("Downloading") for dataset in referenceData: try: import SampleData dicomFilesDirectory = SampleData.downloadFromURL( fileNames=dataset['fileName'], uris=dataset['url'])[0] self.delayDisplay('Finished with download') # # insert the data into th database # self.delayDisplay("Switching to temp database directory") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase( 'tempDICOMDatabase') self.delayDisplay('Importing DICOM') mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() # # select the series # detailsPopup = slicer.modules.DICOMWidget.detailsPopup detailsPopup.open() # load the data by series UID detailsPopup.offerLoadables(dataset['seriesUID'], 'Series') detailsPopup.examineForLoading() loadable = detailsPopup.getAllSelectedLoadables().keys()[0] # # try loading using each of the selected readers, fail # on enexpected load issue # scalarVolumePlugin = slicer.modules.dicomPlugins[ 'DICOMScalarVolumePlugin']() readerApproaches = scalarVolumePlugin.readerApproaches() basename = loadable.name volumesByApproach = {} for readerApproach in readerApproaches: self.delayDisplay('Loading Selection with approach: %s' % readerApproach) loadable.name = basename + "-" + readerApproach volumeNode = scalarVolumePlugin.load( loadable, readerApproach) if not volumeNode and readerApproach not in dataset[ 'expectedFailures']: raise Exception( "Expected to be able to read with %s, but couldn't" % readerApproach) if volumeNode and readerApproach in dataset[ 'expectedFailures']: raise Exception( "Expected to NOT be able to read with %s, but could!" % readerApproach) if volumeNode: volumesByApproach[readerApproach] = volumeNode self.delayDisplay('Test quantity and unit') if 'voxelValueQuantity' in dataset.keys(): self.assertEqual( volumeNode.GetVoxelValueQuantity( ).GetAsPrintableString(), dataset['voxelValueQuantity']) if 'voxelValueUnits' in dataset.keys(): self.assertEqual( volumeNode.GetVoxelValueUnits( ).GetAsPrintableString(), dataset['voxelValueUnits']) # # for each approach that loaded as expected, compare the volumes # to ensure they match in terms of pixel data and metadata # failedComparisons = {} approachesThatLoaded = volumesByApproach.keys() print('approachesThatLoaded %s' % approachesThatLoaded) for approachIndex in range(len(approachesThatLoaded)): firstApproach = approachesThatLoaded[approachIndex] firstVolume = volumesByApproach[firstApproach] for secondApproachIndex in range( approachIndex + 1, len(approachesThatLoaded)): secondApproach = approachesThatLoaded[ secondApproachIndex] secondVolume = volumesByApproach[secondApproach] print('comparing %s,%s' % (firstApproach, secondApproach)) comparison = slicer.modules.dicomPlugins[ 'DICOMScalarVolumePlugin'].compareVolumeNodes( firstVolume, secondVolume) if comparison != "": print('failed: %s', comparison) failedComparisons[firstApproach, secondApproach] = comparison if len(failedComparisons.keys()) > 0: raise Exception("Loaded volumes don't match: %s" % failedComparisons) self.delayDisplay('%s Test passed!' % dataset['name']) except Exception, e: import traceback traceback.print_exc() self.delayDisplay('%s Test caused exception!\n' % dataset['name'] + str(e)) testPass = False
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 SampleData SampleData.downloadFromURL( fileNames='Dcmtk-db.zip', uris='http://slicer.kitware.com/midas3/download?items=18822')[0] try: self.delayDisplay("Switching to temp database directory") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase('tempDICOMDatbase') 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') 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'); 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') slicer.util.clickAndDrag(redWidget,start=(10,10),end=(10,40)) self.delayDisplay('Change Window') slicer.util.clickAndDrag(redWidget,start=(10,10),end=(40,10)) self.delayDisplay('Change Layout') layoutManager = slicer.app.layoutManager() layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView) self.delayDisplay('Zoom') slicer.util.clickAndDrag(redWidget,button='Right',start=(10,10),end=(10,40)) self.delayDisplay('Pan') slicer.util.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') slicer.util.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() slicer.util.clickAndDrag(threeDView) self.delayDisplay('Zoom') threeDView = layoutManager.threeDWidget(0).threeDView() slicer.util.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, enableScreenshotsFlag=0, screenshotScaleFactor=1): """ Test the DICOM part of the test using the head atlas """ logic = RSNAVisTutorialLogic() logic.enableScreenshots = enableScreenshotsFlag logic.screenshotScaleFactor = screenshotScaleFactor import os self.delayDisplay("Starting the DICOM test") # # first, get the data - a zip file of dicom data # import SampleData dicomFilesDirectory = SampleData.downloadFromURL( fileNames='dataset1_Thorax_Abdomen.zip', uris=TESTING_DATA_URL + 'SHA256/17a4199aad03a373dab27dc17e5bfcf84fc194d0a30975b4073e5b595d43a56a', checksums= 'SHA256:17a4199aad03a373dab27dc17e5bfcf84fc194d0a30975b4073e5b595d43a56a' )[0] try: self.delayDisplay("Switching to temp database directory") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase( 'tempDICOMDatabase') slicer.util.selectModule('DICOM') browserWidget = slicer.modules.DICOMWidget.browserWidget dicomBrowser = browserWidget.dicomBrowser dicomBrowser.importDirectory(dicomFilesDirectory, dicomBrowser.ImportDirectoryAddLink) dicomBrowser.waitForImportFinished() # load the data by series UID dicomBrowser.dicomTableManager().patientsTable().selectFirst() browserWidget.examineForLoading() self.delayDisplay('Loading Selection') browserWidget.loadCheckedLoadables() logic.takeScreenshot('LoadingADICOMVolume-Loaded', 'Loaded DICOM Volume', -1) layoutManager = slicer.app.layoutManager() redWidget = layoutManager.sliceWidget('Red') slicer.util.clickAndDrag(redWidget, start=(10, 10), end=(10, 40)) slicer.util.clickAndDrag(redWidget, start=(10, 10), end=(40, 10)) logic.takeScreenshot('LoadingADICOMVolume-WL', 'Changed level and window', -1) redWidget.sliceController().setSliceLink(True) redWidget.sliceController().setSliceVisible(True) logic.takeScreenshot('LoadingADICOMVolume-LinkView', 'Linked and visible', -1) slicer.util.clickAndDrag(redWidget, button='Right', start=(10, 10), end=(10, 40)) logic.takeScreenshot('LoadingADICOMVolume-Zoom', 'Zoom', -1) threeDView = layoutManager.threeDWidget(0).threeDView() slicer.util.clickAndDrag(threeDView) logic.takeScreenshot('LoadingADICOMVolume-Rotate', 'Rotate', -1) threeDView.resetFocalPoint() logic.takeScreenshot('LoadingADICOMVolume-Center', 'Center the view', -1) layoutManager.setLayout(slicer.vtkMRMLLayoutNode. SlicerLayoutConventionalWidescreenView) logic.takeScreenshot('LoadingADICOMVolume-ConventionalWidescreen', 'Conventional Widescreen Layout', -1) slicer.util.mainWindow().moduleSelector().selectModule( 'VolumeRendering') logic.takeScreenshot('VolumeRendering-Module', 'Volume Rendering', -1) volumeRenderingWidgetRep = slicer.modules.volumerendering.widgetRepresentation( ) abdomenVolume = slicer.mrmlScene.GetFirstNodeByName( '6: CT_Thorax_Abdomen') volumeRenderingWidgetRep.setMRMLVolumeNode(abdomenVolume) logic.takeScreenshot('VolumeRendering-SelectVolume', 'Select the volume 6: CT_Thorax_Abdomen', -1) presetsScene = slicer.modules.volumerendering.logic( ).GetPresetsScene() ctCardiac3 = presetsScene.GetFirstNodeByName('CT-Cardiac3') volumeRenderingWidgetRep.mrmlVolumePropertyNode().Copy(ctCardiac3) logic.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) logic.takeScreenshot('VolumeRendering-ViewRendering', 'View Volume Rendering', -1) self.delayDisplay('Skipping Move the Shift slider') redWidget.sliceController().setSliceVisible(False) logic.takeScreenshot('VolumeRendering-SlicesOff', 'Turn off visibility of slices in 3D', -1) threeDView = layoutManager.threeDWidget(0).threeDView() slicer.util.clickAndDrag(threeDView) logic.takeScreenshot('VolumeRendering-RotateVolumeRendering', 'Rotate volume rendered image', -1) volumeRenderingNode.SetVisibility(0) logic.takeScreenshot('VolumeRendering-TurnOffVolumeRendering', 'Turn off volume rendered image', -1) volumeRenderingNode.SetCroppingEnabled(1) annotationROI = slicer.mrmlScene.GetFirstNodeByName( 'AnnotationROI') annotationROI.SetDisplayVisibility(1) logic.takeScreenshot('VolumeRendering-DisplayROI', 'Enable cropping and display ROI', -1) redWidget.sliceController().setSliceVisible(True) logic.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) logic.takeScreenshot('VolumeRendering-SizedROI', 'Position the ROI over a kidney', -1) volumeRenderingNode.SetVisibility(1) logic.takeScreenshot('VolumeRendering-ROIRendering', 'ROI volume rendered', -1) annotationROI.SetXYZ(15, 146, -186) annotationROI.SetRadiusXYZ(138, 57, 61) logic.takeScreenshot('VolumeRendering-BothKidneys', 'Rendered both kidneys', -1) self.delayDisplay('Test passed!') except Exception as e: import traceback traceback.print_exc() self.delayDisplay('Test caused exception!\n' + str(e)) self.delayDisplay("Restoring original database directory") DICOMUtils.closeTemporaryDatabase(originalDatabaseDirectory)
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") originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase( 'tempDICOMDatbase') 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') 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') 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_MissingSlices(self): """ Test behavior of the readers when slices are missing To edit and run this test from the python console, paste this below: reloadScriptedModule('DICOMReaders'); import DICOMReaders; tester = DICOMReaders.DICOMReadersTest(); tester.setUp(); tester.test_MissingSlices() """ testPass = True import os, json self.delayDisplay("Starting the DICOM test", 100) datasetURL = "http://slicer.kitware.com/midas3/download/item/294857/deidentifiedMRHead-dcm-one-series.zip" fileName = "deidentifiedMRHead-dcm-one-series.zip" filePath = os.path.join(slicer.app.temporaryPath,fileName) seriesUID = "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.270.0" seriesRASBounds = [-87.29489517211913, 81.70450973510744, -121.57139587402344, 134.42860412597656, -138.71430206298828, 117.28569793701172] seriesDirectory = "Series 004 [MR - SAG RF FAST VOL FLIP 20]" lastSliceCorners = [[[81.05451202, 133.92860413, 116.78569794], [81.05451202, -122.07139587, 116.78569794]], [[81.05451202, 133.92860413, -139.21429443], [81.05451202, -122.07139587, -139.21429443]]] filesToRemove = [ "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.361.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.362.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.363.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.364.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.365.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.366.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.367.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.368.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.369.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.370.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.371.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.372.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.373.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.374.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.375.0.dcm", "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.376.0.dcm", ] try: if not os.path.exists(filePath) or os.stat(filePath).st_size == 0: self.delayDisplay('Requesting download %s from %s...\n' % (fileName, datasetURL), 100) urllib.urlretrieve(datasetURL, filePath) self.delayDisplay('Finished with download\n', 100) self.delayDisplay("Unzipping", 100) dicomFilesDirectory = slicer.app.temporaryPath + 'MRhead' qt.QDir().mkpath(dicomFilesDirectory) slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory) print('Removing %d files from the middle of the series' % len(filesToRemove)) for file in filesToRemove: filePath = os.path.join(dicomFilesDirectory, seriesDirectory, file) os.remove(filePath) # # insert the data into the database # self.delayDisplay("Switching to temp database directory", 100) originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase('tempDICOMDatabase') self.delayDisplay('Importing DICOM', 100) mainWindow = slicer.util.mainWindow() mainWindow.moduleSelector().selectModule('DICOM') indexer = ctk.ctkDICOMIndexer() indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory, None) indexer.waitForImportFinished() # # select the series # detailsPopup = slicer.modules.DICOMWidget.detailsPopup detailsPopup.open() # load the data by series UID detailsPopup.offerLoadables(seriesUID,'Series') detailsPopup.examineForLoading() loadable = detailsPopup.getAllSelectedLoadables().keys()[0] if len(loadable.warning) == 0: raise Exception("Expected warning about geometry issues due to missing slices!") # # load and correct for acquisition then check the geometry # scalarVolumePlugin = slicer.modules.dicomPlugins['DICOMScalarVolumePlugin']() volumeNode = scalarVolumePlugin.load(loadable) if not numpy.allclose(scalarVolumePlugin.acquisitionModeling.fixedCorners[-1], lastSliceCorners): raise Exception("Acquisition transform didn't fix slice corners!") self.delayDisplay('test_MissingSlices passed!', 200) except Exception, e: import traceback traceback.print_exc() self.delayDisplay('Missing Slices Test caused exception!\n' + str(e), 2000) testPass = False